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()
...
-><strong>useQueryCache </strong>()
-><strong>useResultCache</strong>()
...
->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 "$i\r";
// Случайная 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:\home\doctrine-test>symfony doctrine-test
1266870147
1266872222
====================================================
<strong>2075</strong>
Z:\home\doctrine-test>symfony doctrine-test
1266872876
1266873507
====================================================
<strong>631</strong>
Z:\home\doctrine-test>symfony doctrine-test
1266877034
1266877157
====================================================
<strong>123</strong>
Z:\home\doctrine-test>symfony doctrine-test
1266877387
1266877478
====================================================
<strong>91</strong>
Z:\home\doctrine-test>symfony doctrine-test
1266878158
1266878233
====================================================
<strong>75</strong>
Z:\home\doctrine-test>symfony doctrine-test
1266878598
1266878658
====================================================
<strong>60</strong>
Z:\home\doctrine-test>symfony doctrine-test
1266880037
1266880096
====================================================
<strong>59</strong>
Нисходящий тренд на лицо. Первый запуск занял > 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 серверах ;)
Doctrine + memcached: использование и тестирование
Doctrine ORM имеет встроенный кэш-менеджер, который умеет кэшировать в
Узнав сей факт решил воспользоваться встроенным механизмом и протестировать механизм кэширования.
Включение кэширования в symfony
Расширяем возможности конфигурации. В
config/ProjectConfiguration.class.phpдобавляем метод конфигурирования Doctrine:Итак, если загружено расширение memcached (это подпорка для того чтобы работало без проблем и без кэширования), определяем сервер (серверы), создаем инстанс драйвера кэша. Кэшировать будем запросы Doctrine::ATTR_QUERY_CACHE (разобранные Doctrine-запросы, чтобы не парсить их каждый раз заново) и результаты запросов Doctrine::ATTR_RESULT_CACHE.
Собственно для того чтобы кэшировались запросы придется использовать такую конструкцию:
Вообще говоря, судя по документации Doctrine, прописав атрибуты менеджера мы обеспечиваем себя кэшированием. На самом деле у меня кэширование не заработало пока не использовал в конструкторе запросов use*Cache().
Собственно вот. Переходим к тестированию.
Тестирование кэширования
Для того чтобы оценить эффект от кэширования напишем простой таск, который будет в цикле выполнять однотипные запросы, но со случайными параметрами. Создаем класс
lib/task/doctrineTestTask.class.php:Конфигуратор – без неожиданностей:
Тестовый метод:
getRandLocality() особого значения не имеет (но результат того запроса тоже кэшируем), поэтому с find() пришлось уйти на Doctrine_Query (пока не смотрел как подружить find* с кэшированием).
Для тестирования под win понадобятся memcached dll и MemCacheD Manager.
Результаты тестирования (последовательный запуски – чтобы прокэшировать данные):
Нисходящий тренд на лицо. Первый запуск занял > 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 серверах ;)