УрокСистема кэширования Drupal 7. Часть третья: ускоряемся за счёт изменения места хранения кэша.
По умолчанию Друпал хранит весь кэш в базе данных (см. сегменты кэша). Однако для высоконагруженного проекта чем меньше вы нагружаете базу данных - тем быстрее будет отдаваться контент и, соответственно, вы минимизуете риск образования бутылочного горлышка на вашем сайте. Сегодня я хотел бы рассказать каким образом можно вынести хранение кэша в другое место, а так же спалить достаточно простой способ тюнинга сайта, чтобы он смог выдержать большую нагрузку для анонимных посетителей.
Меняем хранилище кэша
Чтобы несколько разгрузить базу данных, хорошей практикой считается вынос кэша за её пределы. Например, в оперативную память. Логика здесь простая - зачем нам обращаться к жёсткому диску, если можно получить данные быстрее из оперативной памяти, которая, собственно, для этого и предназначена? Одим из путей, позволяющий это реализовать, является использование демона Memcached. На его примере я и объясню как это работает.
Memcached устанавливается на сервер достаточно просто, мануал по установке вы всегда найдёте в поисковике (ну или тут :)). Если вы не умеете устанавливать дополнительные пакеты на сервер - попросите своего хостера :)
В общем, будем считать, что Memcached установлен на сервере. Теперь надо переместить кэшированные данные из базы данных в оперативную память. Для этого первым делом надо скачать модуль Memcache Storage, который позволяет интегрировать Друпал с демоном мемкэша. В общем случае это делается добавлением следующих строк в settings.php:
$conf['cache_backends'][] = 'sites/all/modules/memcache_storage/memcache_storage.inc'; $conf['cache_default_class'] = 'MemcacheStorage';
Первой строкой мы указали путь к файлу, где хранится класс, который использует функции для работы с оперативной памятью и реализует интерфейс DrupalCacheInterface. Следующей строкой указано название этого класса.
Этими строками мы указали Друпалу какой класс отвечает за работу с кэшем. После этого можно включить модуль Memcache Storage и, возможно, насладиться ускоренной работой сайта.
Почему возможно? Потому что несмотря на то, что этими нехитрыми действиями мы перенесли хранение кэша в оперативную память, на самом деле не так гладно, как хотелось бы. Есть несколько нюансов:
1. Мемкэш - это сложная система, использующая оперативную память. Важную часть в эффективной работе с ним занимает настройка фрагментации этой самой памяти. Без этих знаний мэмкэш вам тоже даст хороший прирост производительности, однако вы всегда будете знать, что могли бы выжать из него гораздо больше :) По настройке фрагментации памяти советую почитать здесь, тут, здесь и вот тут.
2. Очистка кэша. У мемкэша есть некоторые проблемы в очистке кэша. К сожалению, решаются они опять же индивидуально. Т.е. если раньше при нажатии на "очистить кэш" на сайте у вас из БД физически удалялся весь кэш, то в мемкэше этого не происходит. Это связано с тем, что в его основыву закладывался метод "чем проще - тем лучше". В результате было получено достаточно быстродейственное хранилище кэша, однако со своими нюансами.
upd (13.03.2013): теперь проблема с временем хранения кэша выглядит решённой.
3. Кэш форм. В статье про сегменты кэша я указывал проблему быстрорастущего сегмента с кэшем форм. При генерации формы каждый раз в сегмент кэша добавляется 2 записи с достаточно большим количеством данных. Соответственно на среднем сайте с 20к уников в сутки за неделю может скопиться порядка 4-7гб кэша форм. Это те данные, которые просто не разумно хранить в оперативной памяти ввиду её большого размера и бесполезности в аспекте производительности. Поэтому делается небольшой финт ушами:
$conf['cache_backends'][] = 'sites/all/modules/memcache_storage/memcache_storage.inc'; $conf['cache_default_class'] = 'MemcacheStorage'; $conf['cache_class_cache_form'] = 'DrupalDatabaseCache';
Как видите, кэш форм мы вернули обратно в базу данных. Это довольно распространённая практика при распределении кэша между различными сегментами. Более подробно о настройке Memcache Storage в Друпале можно почитать в README.txt модуля.
Выносить кэш не обязательно в memcached, есть ещё масса других расширений, которые позволяют хранить данные вне БД. Например - APC, Boost, Varnish, XCache и так далее.
Загрузка страницы без обращения к базе данных
Как я и обещал - на закуску расскажу что-то интересное. В статье про сегменты кэша я писал про кэш страниц - Друпал кэширует полностью HTML вывод страниц для анонимных пользователей. По-умолчанию во второй фазе бутстрапа, если для страницы есть кэш, то он загружается из базы данных и сразу же отдаётся пользователю, на чём процесс бутстрапа и останавливается. Однако раз уж мы перенесли кэш в оперативную память, зачем нам вообще дёргать базу данных? Вот такими настройками вы можете сказать Друпалу, что он может отдавать страницу анониму из другого хралища (в нашем случае - оперативной памяти), не затрагивая базу данных:
$conf['cache_backends'][] = 'sites/all/modules/memcache_storage/memcache_storage.inc'; $conf['cache_default_class'] = 'MemcacheStorage'; $conf['cache_class_cache_form'] = 'DrupalDatabaseCache'; // Настройка, позволяющая избежать подключения к БД. $conf['page_cache_without_database'] = TRUE;
На некоторых своих проектах я использую мемкэш только для кэширования анонимных страниц. Выглядит это таким образом:
$conf['cache_backends'][] = 'sites/all/modules/memcache_storage/memcache_storage.inc'; $conf['cache_default_class'] = 'DrupalDatabaseCache'; $conf['cache_class_cache_page'] = 'MemcacheStorage'; // Настройка, позволяющая избежать подключения к БД. $conf['page_cache_without_database'] = TRUE;
При таких настройках в оперативной памяти хранятся лишь страницы для анонимов, остальной же кэш лежит по прежнему в базе данных (и нам, кстати, никто не мешает вынести оставшийся кэш в другие хранилища).
Однако, как показала практика, такие настройки не всегда будут иметь необходимый эффект. Ведь в бутстрапе всё равно вызываются хуки hook_boot() и hook_exit(). А в этих хуках могут быть обращения к базе. Например, модуль ядра Statistics содержит функцию statistics_exit(), которая начинается следующим образом:
function statistics_exit() { global $user; // When serving cached pages with the 'page_cache_without_database' // configuration, system variables need to be loaded. This is a major // performance decrease for non-database page caches, but with Statistics // module, it is likely to also have 'statistics_enable_access_log' enabled, // in which case we need to bootstrap to the session phase anyway. drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES); ...
Как видим, даже если мы вынесли кэш в оперативную память и попросили Друпал не поднимать базу данных для кэшированных данных анонимов, код других разработчиков может всё равно затрагивать БД. Избежать этого нам позволит следующая настройка:
$conf['cache_backends'][] = 'sites/all/modules/memcache_storage/memcache_storage.inc'; $conf['cache_default_class'] = 'DrupalDatabaseCache'; $conf['cache_class_cache_page'] = 'MemcacheStorage'; // Настройка, позволяющая избежать подключения к БД. $conf['page_cache_without_database'] = TRUE; // Настройка, позволяющая избежать вызова hook_exit() и hook_boostrap(). $conf['page_cache_invoke_hooks'] = FALSE;
Таким образом мы просим Друпал не только не дёргать БД, но и не вызывать хуки бутстрапа. Но даже в такой реализации есть небольшой нюанс :) По официальной документации всё должно работать без проблем. Однако на практике возникла проблема с сохранением кэша страниц. В ядре Друпала есть баг, который независимо от настроек принудительно архивирует кэш страницы, в результате чего он не всегда может её потом правильно отдать пользователю. Любителям почитать прикладываю ссылку на issue с проблемой, остальным - патч, который это решает. Ещё хотел бы обратить внимание на то, что оно всё работает только если в настройках производительности на сайте включена опция "Сжатие кэшированных страниц".
Множественные хранилища кэша
Отдельным пунктом хочу обратить ваше внимание на то, что выносить кэш вы можете не только в одно хранилище данных. Вы можете хоть каждый сегмент перемещать в своё хралище данных, лишь бы в этом был смысл. Используемые для этого модули, опять же, подбираются индивидуально, в зависимости от нужд сайта. Однако я люблю использовать такую связку:
// Выносим кэш страниц в Varnish. $conf['cache_backends'][] = 'sites/all/modules/varnish/varnish.cache.inc'; $conf['cache_class_cache_page'] = 'VarnishCache'; // Выносим общий кэш и кэш бутстрапа в APC. $conf['cache_backends'][] = 'sites/all/modules/apc/drupal_apc_cache.inc'; $conf['cache_class_cache'] = 'DrupalAPCCache'; $conf['cache_class_cache_bootstrap'] = 'DrupalAPCCache'; // Выносим кэш форм в базу данных, чтобы не засорять оперативную память. $conf['cache_class_cache_form'] = 'DrupalDatabaseCache'; // Остальной кэш будет храниться в мемкэше. $conf['cache_backends'][] = 'sites/all/modules/memcache_storage/memcache_storage.inc'; $conf['cache_default_class'] = 'MemcacheStorage'; // Просим Друпал загружать кэш из Varnish'a без бутстрапа базы данных. $conf['page_cache_without_database'] = TRUE; $conf['page_cache_invoke_hooks'] = FALSE;
- Spleshka
- 10.02.2013
- 27544
Комментарии
Спасибо за отличный материал!
А nginx ты до кучи используешь или вместо варниша (спалила заголовки Drupalace.ru) ? Я использую микрокеш nginx (https://github.com/perusio/drupal-with-nginx/blob/D7/apps/drupal/microcache_proxy.conf) и пока довольна. Вопрос естественно остаётся открытым для авторизованных граждан.
PS.
GET http://drupalace.ru/sites/all/modules/bueditor/icons/page_white_code.png 404 (Not Found)
kalabro, да пожалуйста) nginx используется на друпаласе только потому, что он стоит на it-patrol'e с минимальным тарифом, а там связка apache+nginx используется на всех серверах. Так бы, пожалуй, заменил его на варниш.
а чем потестить результативность? тест ab подойдет? я у себя добился лучшего результата с filecache, но может быть дело в самом сервере и он быстрее не может…
А какая версия Varnish у вас стоит? поставил себе 3 версию и вместо увеличения скорости получил тормоза. нагрузка на сервер растет, скорость отдачи в 20 раз медленее…
Версия не сильно важна. Важнны настройки сервера + конфигурация варниша. Конфиги варниша можно, например, у Ромки посмотреть http://romka.eu/blog/primer-rabochego-konfiga-varnish.
Подскажите, если конфигурация немного другая: Boost, APC, Filecache, то какие должны быть настройки в settings.php, точнее как изменить данную настройку:
$conf['cache_class_cache_form'] = 'DrupalDatabaseCache';
чтобы сразу в Filecache записывался кэш форм, при условии что стандартные строчки для filecache, которые добавляются в settings.php:
$conf['cache_backends'] = array('sites/all/modules/filecache/filecache.inc');
$conf['cache_default_class'] = 'DrupalFileCache';
Но если их добавить вместе со строчками APC, сайт перестаёт грузится:
PHP Fatal error: Class 'DrupalAPCCache' not found in /home/user_name/public_html/includes/cache.inc on line 31
Как их вместе заставить корректно работать APC и filecache ?
Я полагаю, вы не правильно добавляете файлы в бэкенд. Вы добавляете так:
А надо так (не перекрывать их друг другом):
После чего уже настраивать. Полный пример конфига может выглядеть так:
Насколько я помню, для Boost в сеттингсах ничего прописывать не надо, он складывает кэш в файлы, а потом отдаёт их мимо Друпала, напрямую с сервера.
хм, такая же ошибка только не находит класс мемкеша и $conf['cache_backends'][] прописывал, помогло только include_once
Спасибо, действительно заработало.
Подскажите, а нужно ли использовать дополнительные настройки (последние 4 строчки), будут ли они иметь эффект (для Drupal 7.20, 1024 мб ОЗУ, 600 mhz, для APC 100 мб, filecache temp папка около 400 мб = 10 000 файлов, запуск крон каждый 1 час, сброс кэша Boost каждые 6 часов):
$conf['cache_backends'][] = 'sites/all/modules/apc/drupal_apc_cache.inc';
$conf['cache_class_cache'] = 'DrupalAPCCache';
$conf['cache_class_cache_bootstrap'] = 'DrupalAPCCache';
$conf['cache_backends'][] = 'sites/all/modules/filecache/filecache.inc';
$conf['cache_class_cache_form'] = 'DrupalFileCache';
$conf['cache_default_class'] = 'DrupalAPCCache';
$conf['cache_class_cache_page'] = 'DrupalAPCCache';
$conf['filecache_fast_pagecache'] = TRUE;
$conf['page_cache_without_database'] = TRUE;
$conf['page_cache_invoke_hooks'] = FALSE;
Если вы используете Boost, то в последних четырёх строчках вообще смысла нет. Кэш и так отдаётся мимо друпала, затрагивается только .htaccess.
Подскажите, а зачем использовать одновременно APC и Memcache в вашей любимой связке модулей? Чем плох только Memcache?
А какие варианты тестирования производительности вы бы посоветовали? Вот пробовал ab с сервера из той же страны. С быстрым каналом. И никакого результата. Что с обычным кешированием, что с filecache с хранилищем в оперативной памяти и там и там 500 запросов в секунду выдает. Что я делаю не так?
При включении $conf['page_cache_without_database'] = TRUE; у меня фатал: PHP Fatal error: Call to undefined function db_query() in /var/www/faprus/data/www/faprus.ru/includes/cache.inc Что я мог сделать не так?
Комментировать