Цитата мудреца

Голосование

Если бы Вам нужно было переслать другу файл, то при каком максимальном размере файла Вы бы пользовались электронной почтой?
 
Система Orphus. Если вы заметили ошибку на сайте, нажмите сюда.
Загружается, подождите...
Начало сайта Материалы сайта Программы PHP-скрипты

Версия для печати

Кэширование запросов MySQL

Здесь представлены некоторые мои разработки на PHP. Это в, основном, служебные скрипты, которые работают в составе других скриптов и не могут быть протестированы здесь непосредственно.

Андрей, благодарю за данный скрипт.

Пока что одна проблема у меня с переводом его на рельсы mysqli - в fields названия полей не прописываются, туда пишется NULL
При этом я в вашем скрипте поменял методы, дабы не путать их с mysqli. (это для себя, дабы не запутаться, хотя думаю, что всем тоже будет удобно)

Вот Код срипта с моими коментариями в том месте, где у меня не получается...
Код: Выделить всё
<?php
/**
* Модуль class.mysqlcache.php V1.0.1 Вт 14 Авг 2007
* Copyright (C) Андрей Якушев, 2007. http://avy.ru
* ---------------------------------------------------------------------
* MySQLCache - class
* Класс предназначен для кэширования результатов MySQL-запросов SELECT.
* В специальной директории создаются файлы с именем, состоящим из
* хэша запроса.
* Файл содержит результат запроса с дополнительной информацией по нему.
* К моменту вызова класса соединение с БД должно быть установлено.
* ---------------------------------------------------------------------
*/

class MySqlCache extends base {
   //Путь к директории кэш-файлов
   var $CachePath = CACHE;     //Необходимо ввести полный путь

   //Имя файла с информацией о пиковой нагрузке
   var $PeakFilename = '!peak.txt';

   //Флаг, при установке которого ошибки запросов выводятся на экран
   var $Debug = true;

   //Флаг, указывающий, что данные выдаются из кэша
   var $FromCache = false;

   //Дата формирования данных
   var $DataDate = 0;

   //Численный код ошибки выполнения последней операции с MySQL
   var $errno = 0;

   //Строка ошибки последней операции с MySQL
   var $error = '';

   //Информация о пиковой нагрузке
   var $Peak = array
   (
      0,    //Время выполнения
      '',   //Дата выполнения
      '',   //Запрос
      '',   //Вызвавший скрипт
   );

   //Номер следующей выдаваемой строки
   var $NextRowNo = 0;

   //Массив результатов запроса
   var $ResultData = array
   (
      'fields' => array(),
      'data' => array(),
   );

/*
*--------------------------------------------------------------------------
* Конструктор
* Принимает в качестве параметра запрос SELECT
* и время валидности предыдущего запроса ($valid) в секундах, если такой существует.
* Возвращает логическое значение результата запроса.
* Если запрос не SELECT, то возвращает результат выполнения этого запроса.
* Это просто заглушка; никакие атрибуты класса при этом не затронутся.
*--------------------------------------------------------------------------
*/
function MySQLCache($query, $valid = 5)   {
   parent::__construct();
   if ($this->CachePath == '')   {
         $this->CachePath = dirname(__FILE__);
      }
      $query = trim($query);
      if (!eregi('^SELECT', $query))
      {
         return $this->query($query);      /////////////////////////////////////////////
         //   mysql_query($query);
      }
      $filename = $this->CachePath . '/' . md5($query) . '.txt';
      /* Попытка чтения кэш-файла */
      if ((@$file = fopen($filename, 'r')) && filemtime($filename) > (time() - $valid))
      {
         flock($file, LOCK_SH);
         $serial = file_get_contents($filename);
         $this->ResultData = unserialize($serial);
         $this->DataDate = filemtime($filename);
         $this->FromCache = true;
         fclose($file);
         return true;
      }
      if ($file)
      {
         fclose($file);
      }
      /* Выполнение запроса */
      $time_start = microtime(true);
      @ $SQLResult = $this->query($query);      //////////mysqli_result Object/////////////////
               //   mysql_query($query);
      $time_end = microtime(true);
      $this->DataDate = time();
      $time_exec = $time_end - $time_start;
      /* Обработка ошибки запроса */
      if (!$SQLResult)
      {
         if ($this->Debug)
         {
            die('Error from query "' . $query . '": ' . mysql_error());
         }
         else
         {
            $this->errno = mysql_errno();
            $this->error = mysql_error();
            return false;
         }
      }
      /* Проверка пиковой нагрузки */
      $peak_filename = $this->CachePath . '/' . $this->PeakFilename;
      if (@$file = fopen($peak_filename, 'r'))
      {
         flock($file, LOCK_SH);
         $fdata = file($peak_filename);
         foreach ($fdata as $key => $value)
         {
            $this->Peak[$key] = trim($value);
         }
         $this->Peak[0] = floatval($this->Peak[0]);
      }
      if ($file)
      {
         fclose($file);
      }
      if ($time_exec > $this->Peak[0])
      {
         $this->Peak = array
         (
            $time_exec,
            date('r'),
            $query,
            $_SERVER['SCRIPT_FILENAME'],
         );
         $file = fopen($peak_filename, 'w');
         flock($file, LOCK_EX);
         fwrite($file, implode("\n", $this->Peak));
         fclose($file);
      }
      /* Получение названия полей */
      $nf = $SQLResult->field_count;      ///////////////////////////////
      //mysql_num_fields($SQLResult);
      for ($i = 0; $i < $nf; $i++)
      {
         $this->ResultData['fields'][$i] = $SQLResult->fetch_fields($i);      ////////////////////////////
        ///// ВОТ ТУТ Я НЕ ПОЛУЧАЮ НАЗВАНИЕ ПОЛЯ В РЕЗУЛЬТАТЕ //////////////
         //   mysql_fetch_field($SQLResult, $i);
      }
      /* Получение данных */
      $nr = $SQLResult->num_rows;   ////////////////////////////////////////
      //   mysql_num_rows($SQLResult);
      for ($i = 0; $i < $nr; $i++)
      {
         $this->ResultData['data'][$i] = $SQLResult->fetch_row();   /////////////////////////////////
         //   mysql_fetch_row($SQLResult);
      }
      /* Запись кэша */
      $file = fopen($filename, 'w');
      flock($file, LOCK_EX);
      fwrite($file, serialize($this->ResultData));
      fclose($file);
      return true;
   }

   // Количество полей в запросе
   function cache_num_fields()
   {
      return sizeof($this->ResultData['fields']);
   }

   // Название указанной колонки результата запроса
   function cache_field_name($num)
   {
      if (isset($this->ResultData['fields'][$num]))
      {
         return $this->ResultData['fields'][$num]->name;
      }
      else
      {
         return false;
      }
   }

   // Информация о колонке из результата запроса в виде объекта
   function cache_fetch_field($num)
   {
      if (isset($this->ResultData['fields'][$num]))
      {
         return $this->ResultData['fields'][$num];
      }
      else
      {
         return false;
      }
   }

   // Длина указанного поля
   function cache_field_len($num)
   {
      if (isset($this->ResultData['fields'][$num]))
      {
         return $this->ResultData['fields'][$num]->max_length;
      }
      else
      {
         return false;
      }
   }

   // Тип указанного поля результата запроса
   function cache_field_type($num)
   {
      if (isset($this->ResultData['fields'][$num]))
      {
         return $this->ResultData['fields'][$num]->type;
      }
      else
      {
         return false;
      }
   }

   // Флаги указанного поля результата запроса
   function cache_field_flags($num)
   {
      if (!isset($this->ResultData['fields'][$num]))
      {
         return false;
      }
      $result = array();
      if ($this->ResultData['fields'][$num]->not_null)
      {
         $result[] = 'not_null';
      }
      if ($this->ResultData['fields'][$num]->primary_key)
      {
         $result[] = 'primary_key';
      }
      if ($this->ResultData['fields'][$num]->unique_key)
      {
         $result[] = 'unique_key';
      }
      if ($this->ResultData['fields'][$num]->multiple_key)
      {
         $result[] = 'multiple_key';
      }
      if ($this->ResultData['fields'][$num]->blob)
      {
         $result[] = 'blob';
      }
      if ($this->ResultData['fields'][$num]->unsigned)
      {
         $result[] = 'unsigned';
      }
      if ($this->ResultData['fields'][$num]->zerofill)
      {
         $result[] = 'zerofill';
      }
      if ($this->ResultData['fields'][$num]->binary)
      {
         $result[] = 'binary';
      }
      if ($this->ResultData['fields'][$num]->enum)
      {
         $result[] = 'enum';
      }
      if ($this->ResultData['fields'][$num]->auto_increment)
      {
         $result[] = 'auto_increment';
      }
      if ($this->ResultData['fields'][$num]->timestamp)
      {
         $result[] = 'timestamp';
      }
      return implode(' ', $result);
   }

   // Количество рядов результата запроса /
   function cache_num_rows()
   {
      return sizeof($this->ResultData['data']);
   }

   /* Обрабатывает ряд результата запроса и возвращает неассоциативный массив */
   function cache_fetch_row()
   {
      if (($this->NextRowNo+1) > $this->cache_num_rows())
      {
         return false;
      }
      $this->NextRowNo++;
      return $this->ResultData['data'][$this->NextRowNo - 1];
   }

   /* Обрабатывает ряд результата запроса и возвращает ассоциативный массив */
   function cache_fetch_assoc()
   {
      if (($this->NextRowNo + 1) > $this->cache_num_rows())
      {
         return false;
      }
      for ($i = 0; $i < $this->cache_num_fields(); $i++)
      {
         $result[$this->ResultData['fields'][$i]]->name =   // убрано в конце ->name
            $this->ResultData['data'][$this->NextRowNo][$i];
      }
      $this->NextRowNo++;
      return $result;
   }
}
?>
Ответить


Андрей, все получилось, я немного паниковал, но вот что сделал:

Код: Выделить всё
      /* Получение названия полей */
      $nf = $SQLResult->field_count;      ///////////////////////////////
      //mysql_num_fields($SQLResult);
      for ($i = 0; $i < $nf; $i++)
      {
        $this->ResultData['fields'][$i] = $SQLResult->fetch_field();      ////////////////////////////
         //   mysql_fetch_field($SQLResult, $i);
      }

за слешами стоят рельсы от mysql-движка
новый вариант уже на движке mysqli
Ответить


Может глупый вопрос, а может и нет...

Андрей, у меня вопрос:
А не создаст ли скрипт практически полную копию БД, коли будет работать достаточно длительное время и как этого избежать???
Ответить


По поводу первого - ничего не могу сказать. Тестировал только под MySQL.
По поводу второго - создаст. Обязательно создаст, и при чём размером большим, чем сама база.
Я избегаю этого следующим образом: крон запускает периодически скрипт, который удаляет файлы из папки кэша, возраст которых больше часа.
Ответить


скрипт для Cron`a

хм, Андрей, я так и предполагал.
Судя по всему вы хоститесь на выделеном сервере, что позволяет себе спокойно делать крон-задачи.
А если обычный пользователь, который хочет пользоваться скриптом не имет такой возможности (не прелдоставляет провайдер на легких тарифах такую услугу, а оч.нужно), то как эту задачку вы бы решили через всеми любимый ПХП? :)

ЗЫ. я тоже над этим подумаю, но пока голова забита отладкой и системой аутефикации. :roll:
Ответить


Ну, сначала немного рекламы. :)
У меня не выделенный сервер. Я хощусь на Петерхосте. Там крон включается уже с тарифного плана "Диоген" (1500 руб./год).
Ну а уж если совсем нет возможности крона, то его можно эмулировать, как это сделано в phpBB:
в каждую страницу включается вызов скрипта кроноэмулятора. Тот, в свою очередь читает файл собственного сочинения, где он записывает дату предыдущего запуска той или иной задачи. И если срок перезапуска истёк, то он запускает нужную задачу, а потом делает пометку в этом файле, что выполнил её тогда-то.
Таким образом, любой человек, зашедший на сайт, может инициировать запуск той или иной задачи.
Минусы: задача может быть достаточно сложная и выполняться долго. Человек может не дождаться конца и отменить загрузку страницы. Отсюда есть два выхода: либо учесть возможность того, что задача будет выполнена не до конца, либо, если у Вас есть авторизация, то запускать крон только если заходите Вы. А Вы-то уж будете знать, что нужно дождаться выполнения скрипта до конца.
Ответить


Андрей, благодарствю, тоже смотрел в сторону Петерхоста, но на "Диогене" там нету крона :)
http://peterhost.ru/tariffs/diogenes

Так что наш выбор падает на следующий вариант, что на 1260 подороже, но зато все в наличии :)

Уклонились в сторону.... в пинципе пока вопросов больше нету, а так тоже сколнялся к тому же решению, но пока думаю, что все же у хостера возьму тариф "Сенека".
Ответить


Joomla

Hi.
Андрей, я не так хорошо знаю фреймворк Жумлы, но хочу попробовать твой механизм.
Подскажи пож-та как прикрутить твой обработчик к жумле (1.5) или хотя бы в каком направлении копать.

Заранее спасибо.

Удачи.
Ответить


korkunov, в любом движке должен быть модуль, который запускается при каждом показе страницы. Желательно, чтобы он не запускался во время каких-нибудь внутренних отработчиков. Нужно постараться вставить вызов скрипта как можно раньше в этом модуле.
Ответить


Андрей, бодрое время суток!

Я вот что подумал, а если взять и попробовать прикрутить к скрипту проверку времени создания файла запроса к БД.
В результате используя таймаут, который проставляется в классе можно из списка файлов будет сразу выделить те, что подлежат удалению и удалять их...
Хотя с другой стороны, не будет ли проблем с тем, что будут перекрывающиеся запросы на удаление...
Но их тогда можно просто подавить (возникающие ошибки).

Вообщем жду коментариев на мою мысль...
Ответить


След.

Вернуться в PHP-скрипты



Кто сейчас на сайте

Зарегистрированные пользователи: нет зарегистрированных пользователей