Doctrine ORM имеет встроенный кэш-менеджер, который умеет кэшировать в
- Memcached
- APC
- DataBase (видимо имеется в виду некая плоская БД с быстрым доступом, типа SQLite)
Узнав сей факт решил воспользоваться встроенным механизмом и протестировать механизм кэширования.
Включение кэширования в symfony
Расширяем возможности конфигурации. В config/ProjectConfiguration.class.php
добавляем метод конфигурирования Doctrine:
/** * Конфигурация ORM * @param Doctrine_Manager $manager */ public function configureDoctrine( Doctrine_Manager $manager ) { if ( extension_loaded( 'memcache' ) ) { $servers = array( 'host' => 'localhost', 'port' => 11211, 'persistent' => true ); $cacheDriver = new Doctrine_Cache_Memcache( array( 'servers' => $servers, 'compression' => false )); $manager = Doctrine_Manager::getInstance(); $manager->setAttribute( Doctrine::ATTR_QUERY_CACHE, $cacheDriver ); $manager->setAttribute( Doctrine::ATTR_RESULT_CACHE, $cacheDriver ); } }
Итак, если загружено расширение memcached (это подпорка для того чтобы работало без проблем и без кэширования), определяем сервер (серверы), создаем инстанс драйвера кэша. Кэшировать будем запросы Doctrine::ATTR_QUERY_CACHE (разобранные Doctrine-запросы, чтобы не парсить их каждый раз заново) и результаты запросов Doctrine::ATTR_RESULT_CACHE.
Собственно для того чтобы кэшировались запросы придется использовать такую конструкцию:
Doctrine_Query::create() ... ->useQueryCache () ->useResultCache() ... ->execute ();
Вообще говоря, судя по документации Doctrine, прописав атрибуты менеджера мы обеспечиваем себя кэшированием. На самом деле у меня кэширование не заработало пока не использовал в конструкторе запросов use*Cache().
На самом деле это не есть гуд, так как при таком подходе нельзя выключить кэширование сразу и хитрое конфигурирование не убережет нас от падения продуктовой среды, если в ней нет memcached.
Собственно вот. Переходим к тестированию.
Тестирование кэширования
Для того чтобы оценить эффект от кэширования напишем простой таск, который будет в цикле выполнять однотипные запросы, но со случайными параметрами. Создаем класс lib/task/doctrineTestTask.class.php
:
class doctrineTestTask extends sfDoctrineBaseTask { }
Конфигуратор – без неожиданностей:
protected function configure() { $this->briefDescription = 'Test doctrine memcached'; $this->detailedDescription = <<<EOF Test doctrine memcached EOF; }
Тестовый метод:
protected function execute($arguments = array(), $options = array()) { // макс ID $q = Doctrine_Query::create() ->select ( "MAX(l.id) as max, MIN(l.id) as min" ) ->from ( "Locality l" ) ->useResultCache() ->execute () ->toArray (); // тестовый цикл echo ( $start = time() ) . "n"; for( $i = 0; $i < 10000; $i++ ) { echo "$ir"; // Случайная locality while( !( $locality ) ) { $locality = $this->getRandLocality( $q[0]["min"], $q[0]["max"] ); } // собственно тестовый запрос с несколькими джойнами. Таблицы от тысяч до пости сотни тысяч строк. Для теста сойдут. $search_query = Doctrine_Query::create() ->select ( "prop.*, loc.*, haskw.*, kw.*" ) ->from ( "Property prop, prop.Locality loc, prop.PropertyHasKeyword haskw, haskw.Keyword kw" ) ->where ( "loc.lft >= ? AND loc.rgt <= ?", array( $locality->lft, $locality->rgt ) ) ->offset ( 10 * rand( 1, 100 ) ) ->limit ( 10 ) ->useResultCache() ->execute (); } echo ( $end = time() ) . "n"; echo "====================================================n" . ($end - $start); }
getRandLocality() особого значения не имеет (но результат того запроса тоже кэшируем), поэтому с find() пришлось уйти на Doctrine_Query (пока не смотрел как подружить find* с кэшированием).
Для тестирования под win понадобятся memcached dll и MemCacheD Manager.
Результаты тестирования (последовательный запуски – чтобы прокэшировать данные):
Z:homedoctrine-test>symfony doctrine-test 1266870147 1266872222 ==================================================== 2075 Z:homedoctrine-test>symfony doctrine-test 1266872876 1266873507 ==================================================== 631 Z:homedoctrine-test>symfony doctrine-test 1266877034 1266877157 ==================================================== 123 Z:homedoctrine-test>symfony doctrine-test 1266877387 1266877478 ==================================================== 91 Z:homedoctrine-test>symfony doctrine-test 1266878158 1266878233 ==================================================== 75 Z:homedoctrine-test>symfony doctrine-test 1266878598 1266878658 ==================================================== 60 Z:homedoctrine-test>symfony doctrine-test 1266880037 1266880096 ==================================================== 59
Нисходящий тренд на лицо. Первый запуск занял > 2000 секунд (в кэше ничего не было). Повторный запуск оказался в 3 раза быстрее, последующий в 5 раз быстрее. После этого начали подходить к пределу возможностей кэшера и последующие 4 запуска уменьшили время лишь в два раза. Но там не менее, всего с первого запуска скорость выполнения теста уменьшилась более чем в 35 раз.
Состояние memcached:
Memcached Server 1.2.4
Uptime 04:13:26
Items count 13859
Total items 27711 11
Hits 86088
Misses 15994
Used bytes 8695086
Bytes read 4513330164
Bytes writen 4547347068
Max bytes 134217728
Итого, наш тест в памяти memcached занял примерно 8 Мб. Неплохо, неплохо. Готовим memcached на production серверах 😉
Что-то я запутался с этими мемкешами. Вроде как memcache и memcached разные вещи? И doctrine имеет встроенную поддержку memcache. Или я чего-то не понимаю?
https://www.google.ru/#hl=ru&newwindow=1&safe=active&output=search&sclient=psy-ab&q=memcache+%D0%B8+memcached&oq=memcache+%D0%B8+memcached&aq=f&aqi=g1&aql=&gs_l=hp.3..0.415l415l0l986l1l1l0l0l0l0l111l111l0j1l1l0.frgbld.&pbx=1&bav=on.2,or.r_gc.r_pw.r_cp.r_qf.,cf.osb&fp=45ddeb5f6772cf89&biw=1293&bih=855
См. первую ссылку из гугла )
Хм, думал какое-нибудь уведомление на почту должно свалиться, не дождался. =) Спасибо за ответ, я и запутался благодаря этим статьям. Как понял, у нас есть сервер memcached, а вот клиентские библиотеки для php разные: memcache и memcached. И вот читаю статью:
if ( extension_loaded( ‘memcache’ ) ) <– это по коду
А ниже текст "Итак, если загружено расширение memcached"… Даже если и опечатка (или опять непонимание с моей стороны?),то это условие я и вовсе убирал, доктрина мне ошибку вываливала. Собственно, на этом ступор у меня и возник моменте.
Позволю себе дополнить вас, может кому еще пригодится.
Ничто не мешает сделать проверку на мемкешед перед кешированием.
$q = Doctrine_Query::create() ->
select( "MAX(l.id) as max, MIN(l.id) as min" )->
from( "Locality l" );
if (extension_loaded( 'memcache' )) {
$q->useResultCache();
}
$q->execute()
->toArray();
Поправьте, если я ошибаюсь, но у себя я реализовал так.
Да, я тоже так делал, например когда переводили сессии на мемкеш – локально мемкеш не требовался и по наличию модуля либо включался, либо нет.