Кеширование с помощью статических переменных и в моделях CakePHP

Как вы оцениваете свой уровень как программиста? Несмотря на то, что меня Влад дважды похвалил :), я свой уровень оцениваю на 3 с плюсом (по 5-бальной шкале). Каждый день узнаю такие интересные финты, что хочется посыпать голову пеплом, за то, что сам до этого не додумался.

Сегодня узнал способ легко кешировать результаты функции, которые не меняются при условии одинаковых входящих параметров.

PHP:
  1. function test($input) {
  2.     static $cache = array();
  3.     if (isset($cache[$input])) return $cache[$input];
  4.  
  5.     // Сама функция, которая как на зло очень долгая
  6.     // Обрабатываем $input и получаем $result
  7.  
  8.     return $cache[$input] = $result;
  9. }

Если результаты очень объёмные и слишком расточительно кешировать всё, можно проверять время и кешировать только то, расчёт чего занимает больше 1 секунды.

PHP:
  1. function test($input) {
  2.     static $cache = array();
  3.     if (isset($cache[$input])) return $cache[$input];
  4.     $startTime = microtime(true);
  5.  
  6.     // Сама функция, которая как на зло иногда быстрая, а иногда очень долгая
  7.     // Обрабатываем $input и получаем $result
  8.  
  9.     if (microtime(true)-$startTime > 1) $cache[$input] = $result;
  10.     return $result;
  11. }

Я иногда делаю кеширование в моделях CakePHP. Причины кеширования - таблица содержит больше 200 тысяч записей (схожие принципы используются в других таблицах с десятками миллионов записей), запросов очень много, но в течение дня запросы часто идут к одним и тем же записям. Дописывается в таблицу что-то очень редко. Полностью модель довольно сложная и имеет множество взаимосвязей, я показываю только маленькую часть, чтобы можно было понять принцип.

PHP:
  1. <?php
  2. class Word extends AppModel {
  3.  
  4.     var $name = 'Word';
  5.     var $validate = array(
  6.         'id' => VALID_NUMBER,
  7.         'name' => VALID_NOT_EMPTY,
  8.     );
  9.     var $actsAs = array('NameList'=>array());
  10.  
  11.     var $cache = array();
  12.  
  13.     function __construct($id=false, $table=null, $ds=null) {
  14.         $this->cache = Cache::read('word_list');
  15.         if ($this->cache===false) {
  16.             $this->cache = array();
  17.         }
  18.  
  19.         return parent::__construct($id, $table, $ds);
  20.     }
  21.  
  22.     function __destruct() {
  23.         Cache::write('word_list', $this->cache);
  24.  
  25.         //return parent::__destruct(); // AppModel hasn't __destruct method
  26.     }
  27.  
  28.     function getNames($ids) {
  29.         $newIds = array();
  30.         $result = array();
  31.         foreach ($ids as $id) {
  32.             if (!isset($this->cache[$id])) {
  33.                 $newIds[] = $id;
  34.             } else {
  35.                 $result[$id] = $this->cache[$id];
  36.             }
  37.         }
  38.  
  39.         $newIds = $this->generateList(array('Word.id'=>$newIds));
  40.         if (!empty($newIds)) foreach ($newIds as $id=>$name) {
  41.             $this->cache[$id] = $name;
  42.             $result[$id] = $name;
  43.         }
  44.  
  45.         return $result;
  46.     }
  47. }
  48. ?>


Понравилось?

  1. Подпишись через RSS
  2. Расскажи о http://php.southpark.com.ua друзьям.
    Все способы хороши: ICQ, E-mail, свой блог, комментарий в чужом блоге или сообщение на форуме
  3. Добавь статью на news2.ru, Хабрахабр или в закладки

Огромное спасибо!

И не стесняйтесь комментировать - у меня стоит плагин, который убирает rel="nofollow" у людей, которые написали больше 5 комментариев.

RSS feed | Trackback URI

11 комментариев »

Comment by larin
2007-10-23 18:44:37

, ... . .

static , .

 
Comment by
2007-10-23 20:53:37

@larin: . ?

memcached ;)

 
Comment by
2007-10-28 16:06:42

Zend framework Cache Function.
CakePHP Zend.
Zend . .

PS:

 
2007-10-29 19:32:59

@Евгений: ну, не знаю. почитал
http://framework.zend.com/manual/en/zend.cache.html

и не нашёл ничего, чего бы не смог сделать с помощью кейковского Cache. Не то, чтобы Zend_Cache плохой - он действительно более продвинутый, но он в стиле Zend, а не Cake.

Например, в Cake разделено просто кеширование и кеширование View/Element, что позволяет использовать кеш в FrontEnd, даже не думая о настройках. В Zend придётся думать, настраивать. Я предпочитаю о кеше не думать, пусть фреймворки за меня думают. Хотя, наверное, я так говорю, потому что для всех моих проектов с головой хватало одного выделенного сервера; для более масштабных проектов есть смысл в более вдумчивой оптимизации.

 
Comment by KPOTOB
2007-10-30 09:19:34

IMHO не полное решение:
1. В чем будет разница между http://dev.mysql.com/doc/refman/5.0/en/query-cache.html и помещением Cake cache в таблицу?
2. Что мешает использовать var $cacheQueries = true; в AppModel?
3. DRY нарушено - если всетаки надо кэшить (к примеру DB сильно далеко, или не DB вовсе а обертка над сервисом), почему не сделать "приватными" методы возможно подлежащие кэшированию и использовать __call в AppModel для реализации кэширования?

 
2007-10-30 19:49:20

@KPOTOB:
1. Cake cache хранится в файлах (по умолчанию). Если файлов не миллион, а записей в MySql - миллион, то будет быстрее обратиться к файлу.
2. Кеширование должно быть не только в пределах одной сессии.
Cake API :: AppModel :: $cacheQueries
00194 * Whether or not to cache queries for this model. This enables in-memory
00195 * caching only, the results are not stored beyond this execution.
...
00200 var $cacheQueries = false;
3. Согласен. Так как этот способ кеширования используется в нескольких моделях, то моя реализация ужасна с точки зрения DRY. Только, наверное, в стиле Cake лучше реализовать это с помощью Behavior, переопределяя метод beforeFind (&$model, $query). Интересная мысль, спасибо. Меня тоже это сильно смущало.

 
Comment by KPOTOB
2007-10-31 09:21:42

@Владимир Лучанинов: А как быть с инвалидацией кэша?

 
2007-10-31 10:37:37

@KPOTOB:
В Cake автоматически перестраивается кеш View, Elements при изменении данных.
Кеш, который делается вручную Cache::write, имеет ограниченный срок жизни (по умолчанию - час).
Это не оно?

 
Comment by KPOTOB
2007-10-31 11:55:31

@Владимир Лучанинов: в том часе и дело - если источник - какаянибуль жирная таблица разузлования для производства то наверное не плохо - а если чтото динамическое - то хреново

 
Comment by KPOTOB
2007-10-31 12:07:39

Добавлю к предыдущиму
Кэш запросов в базе инвалидиться атоматически при записи;
var $cacheQueries = true в моем случае при вызове findAll с рекурсией 4 (не нужные ассоциации убраны через unbindModel) убали колво запросов с ~2200 до ~250;
Кэш view будет работать быстрее - в общем не сильно позитив, а горя хлебнуть можно прилично при двух кэшах в app

 
2007-10-31 19:03:40

@KPOTOB: Да, в этом случае согласен. Динамическое - хреново, если рекурсия 4.
Но ведь таких случаев мало. Поэтому я люблю, чтобы за меня думал динамический фреймворк. А потом с профайлером находятся часто вызываемые узкие места и дорабатываются вручную. Если всего несколько раз в день делается такая сложная операция и она занимает 7 секунд вместо 0.7, то часто можно не обращать на это внимание.

 
Имя (required)
E-mail (required - never shown publicly)
URL
Текст комментария
You may use <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> in your comment.