239  /  330

Кеширование при проектировании сайта

Просмотров: 4380 (Статистика ведётся с 06.02.2017)
Цитатник веб-разработчиков.

Максим Месилов: В 1С-Битрикс есть производительная система кеширования. Она используется как в стандартных компонентах системы, так может быть применена и при самостоятельной разработке. Основная её задача - снизить время отклика сайта и повысить его устойчивость при нагрузках.

Кеширование

Кеширование - это технология позволяющая кешировать результаты работы редко обновляемых и ресурсоемких кусков кода (например, активно работающих с базой данных).

Закладывая в ТЗ проекта интенсивное использование технологии кеширования, можно максимально ограничить нагрузку на базу данных. Это позволит нашему проекту выдержать в будущем значительные нагрузки.

Примечание: Возможность использования кеширования в проекте лучше заранее предусмотреть в ТЗ т.к. его внедрение после запуска проекта обходится значительно дороже и сложнее.

Для реализации кеширования в системе созданы два класса:

  • CPageCache - для кеширования HTML
  • CPHPCache - для кеширования HTML и PHP переменных

Результаты кеширования сохраняются в виде файлов в каталоге /bitrix/cache/. Если время кеширования не истекло, то вместо ресурсоемкого кода будет подключен предварительно созданный файл кеша.

Правильное использование кеширования позволяет увеличить общую производительность сайта на порядок. Однако необходимо учитывать, что неразумное использование кеширования может привести к серьезному увеличению размера каталога /bitrix/cache/.

Пример кеширования HTML

<?
// создаем объект
$obCache = new CPageCache; 

// время кеширования - 30 минут
$life_time = 30*60; 

// формируем идентификатор кеша в зависимости от всех параметров 
// которые могут повлиять на результирующий HTML
$cache_id = $ELEMENT_ID.$IBLOCK_TYPE.$USER->GetUserGroupString(); 

// инициализируем буферизирование вывода
if($obCache->StartDataCache($life_time, $cache_id, "/")):

    // выбираем из базы параметры элемента инфоблока
    if($arIBlockElement = GetIBlockElement($ELEMENT_ID, $IBLOCK_TYPE)):
        echo "<pre>"; print_r($arIBlockElement); echo "</pre>";
    endif;

    // записываем предварительно буферизированный вывод в файл кеша
    $obCache->EndDataCache(); 

endif;
?>

В данном примере, метод CPageCache::StartDataCache проверит наличие неистекшего и валидного файла кеша. Если такой файл существует, то он будет подключен и выведен на экран, в противном случае будет включена буферизация. Результаты буферизации будут записаны в файл кеша методом CPageCache::EndDataCache.

В первом параметре метода CPageCache::StartDataCache задается интервал времени в секундах, в течение которого файл кеша будет считаться валидным и не истекшим (интервал времени отсчитывается с момента создания файла кеша).

Во втором параметре указывается уникальный идентификатор данного экземпляра кеша. Здесь необходимо подчеркнуть, что если какие-либо переменные могут повлиять на результат выполнения кешируемого кода, то их значения необходимо включить в этот идентификатор.

Например:

  • Если на результат выполнения кешируемого кода может повлиять авторизация текущего пользователя, то к идентификатору необходимо добавить результат выполнения функции CUser::GetUserGroupString, возвращающей строку c ID групп текущего пользователя.
  • Если в кешируемом коде вы используете постраничную навигацию, то в данный идентификатор обязательно нужно включить результат работы функции CDBResult::NavStringForCache.
  • При использовании сортировки необходимо также помнить, что в идентификатор необходимо включить значения переменных хранящих в себе поле, по которому идет сортировка и порядок сортировки (например, $by.$sort).

Пример кеширования HTML и PHP переменных

<?
// создаем объект
$obCache = new CPHPCache; 

// время кеширования - 30 минут
$life_time = 30*60; 

// формируем идентификатор кеша в зависимости от всех параметров 
// которые могут повлиять на результирующий HTML
$cache_id = $ELEMENT_ID.$SECTION_ID.$USER->GetUserGroupString(); 

// если кеш есть и он ещё не истек то
if($obCache->InitCache($life_time, $cache_id, "/")):
    // получаем закешированные переменные
    $vars = $obCache->GetVars();
    $SECTION_TITLE = $vars["SECTION_TITLE"];
else :
    // иначе обращаемся к базе
    $arSection = GetIBlockSection($SECTION_ID);
    $SECTION_TITLE = $arSection["NAME"];
endif;

// добавляем пункт меню в навигационную цепочку
$APPLICATION->AddChainItem($SECTION_TITLE, $SECTION_URL."SECTION_ID=".$SECTION_ID);

// начинаем буферизирование вывода
if($obCache->StartDataCache()):

    // выбираем из базы параметры элемента инфоблока
    if($arIBlockElement = GetIBlockElement($ELEMENT_ID, $IBLOCK_TYPE)):
        echo "<pre>"; print_r($arIBlockElement); echo "</pre>";
    endif;

    // записываем предварительно буферизированный вывод в файл кеша
    // вместе с дополнительной переменной
    $obCache->EndDataCache(array(
        "SECTION_TITLE" => $SECTION_TITLE
        )); 
endif;
?>

Отличие данного примера от предыдущего заключается в том, что помимо HTML также кешируется PHP переменная $SECTION_TITLE. Структура и тип кешируемых PHP переменных могут быть произвольными. Метод CPHPCache::InitCache выполняет проверку на наличие неистекшего и валидного файла кеша. Если данный файл найден, то происходит его подключение, при этом все закешированные переменные будут доступны после использования метода CPHPCache::GetVars. Метод CPHPCache::EndDataCache, помимо буферизированного HTML кода, записывает в файл кеша также PHP переменные.

Для отключения кеширования на одной странице, необходимо авторизоваться с административными правами и вызвать эту страницу с параметром &clear_cache=Y. Если, авторизовавшись с административными правами, вызвать любую страницу с параметром &clear_cache_session=Y, то кеш будет отключен для всех страниц, которые будут просмотрены в рамках сессии. Файлы кеша можно удалить в административной части на закладке Очистка файлов кеша страницы Настройки > Настройки продукта > Автокеширование.

Смотрите также:


Альтернативные способы хранения кеша

Мы убедились, что использовать технологию кеширования для веб-сайта нужно, значит необходимо предусмотреть кеширование ещё на стадии написания ТЗ.

Допустим, через определенное время, наш веб-сайт стал популярным ресурсом. Благодаря активному использованию технологии кеширования мы надежно защитили базу данных от высокой нагрузки, и она может выдержать 3-5 кратное превышение нагрузки.

Однако, у нас, из-за специфики веб-сайта, скопился большой объем самих закешированных данных, использование которых (десятки тысяч файлов) вызывает "некоторые" неудобства - возросла нагрузка на дисковую подсистему. А также, к сожалению, при разработке вкрались ошибки и наши закешированные данные чистятся не полностью, поэтому постепенно их объем на диске становится все больше и больше...


Решения есть:

memcached

При использовании memcached временные данные будут храниться в оперативной памяти. Можно выделить для хранения кеша недорогой сервер с несколькими гигабайтами памяти.

При этом, устаревшие данные будут автоматически вытесняться и наш кеш перестанет "расползаться" по системе, пожирая все больше и больше места. Допустим, мы выделили в memcached 4GB места для кеширования и можем быть уверенными, что больше 4GB кеш не вырастет, а наименее часто используемые данные будут вытесняться (алгоритм LRU). Очень удобно и эффективно.

Подключение memcached осуществляется в файле dbconn.php.

Для эффективной работы системы настройки по умолчанию нужно изменить. Примеры:

// Мемкеш с объемом памяти 2ГБ 8 потоками и работой через tcp
PORT="11211"
USER="memcached"
MAXCONN="10240"
CACHESIZE="2048"
OPTIONS="-t 8"

// настройки в dbconn.php
define("BX_CACHE_TYPE", "memcache");
define("BX_CACHE_SID", $_SERVER["DOCUMENT_ROOT"]."#01");
define("BX_MEMCACHE_HOST", "127.0.0.1");
define("BX_MEMCACHE_PORT", "11211");
// Мемкеш с объемом памяти 2ГБ 8 потоками и работой через сокет
PORT="11211"
USER="bitrix"
MAXCONN="10240"
CACHESIZE="2048"
OPTIONS="-t 8 -s /tmp/memcached.sock"

// настройки в dbconn.php
define("BX_CACHE_TYPE", "memcache");
define("BX_CACHE_SID", $_SERVER["DOCUMENT_ROOT"]."#01");
define("BX_MEMCACHE_HOST", "unix:///tmp/memcached.sock");
define("BX_MEMCACHE_PORT", "0");

Если memcached используется на одной машине, там же, где и находится сам веб-сервер, то использование сокета будет быстрее.

Ссылки по теме:


APC

Подключение APC осуществляется в файле dbconn.php:

// настройки в dbconn.php
define("BX_CACHE_TYPE", "apc");
define("BX_CACHE_SID", $_SERVER["DOCUMENT_ROOT"]."#01");

Подробнее про установку и настройку APC смотрите в документации www.php.net/apc.


Параметр cacheenginenone

Очень часто встречающаяся проблема при работе с кешем - в закладке Хранение кеша страницы Настройки > Производительность > Панель производительности появляется параметр cacheenginenone, а кеширование при этом не производится. По сути данный параметр - это указание системы, что выбранный вами способ хранения кеша не работает. Можно сказать, что подходящего способа кеширования для системы нет, поэтому она и не будет выполнять его, как будто оно не включено вовсе.

Для того, чтобы решить эту проблему, нужно правильно настроить способ кеширования. Например, данный параметр может появиться, если вы указываете, что кеш должен храниться c помощью APC, но данное приложение настроено неправильно или отключено. Вам нужно проверить его настройки или включить его, если требуется.

Смотрите также:


Кеширование в собственных компонентах

Подробное описание кеширования в компонентах описано в уроке Кеширование в собственных компонентах в рамках главы Компоненты.



Рекомендации по работе с кешированием

  • Оптимизировать проект так, чтобы без использования кеширования сложность и количество запросов было минимально. Например, для облегчения запросов, возможно, потребуется создание специфических для проекта индексов. Также, в некоторых случаях, будет эффективнее разбить один сложный запрос на несколько более простых и легких. Это снизит общую нагрузку к базе и позволит более эффективно использовать кеширование.

    Цитатник веб-разработчиков.

    Юналиев Рамиль: Если разработчик делает 1000 запросов и говорит "и так сойдет, оно же кешируется", то это конечно неправильно, любым инструментом нужно уметь пользоваться.

    Цитатник веб-разработчиков.

    Сербул Александр: Иногда во время разработки активно используются технологии кеширования, однако при возрастании нагрузки база данных подвергается перегрузкам. Почему? Для предупреждения данного риска необходимо сначала обеспечить наиболее оптимальную работу с базой данных с выключенным кешированием, прежде чем его включать (рекомендую менеджерам интернет-проектов взять это на заметку и включить в чеклист контроля качества проекта). Типичный кейс данной "ловушки" такой - при включенном кешировании кастомизированное меню использует 0 SQL-запросов, при выключенном - 5000!

  • Подобрать оптимальное время кеширования с направлением в большую сторону. Малое время заставляет обновляться кеш чаше, для больших объемов кешируемых данных это будет создавать дополнительную (лишнюю) нагрузку на сервер, особенно если данные не меняются.

    Лучше установить большее время кеширования, а актуальность данных в кеше поддерживать с помощью Тегированного кеширования. Тогда кеш будет обновляться только при изменении данных, а соответственно не будет лишней нагрузки на сервер, как в случае с малым временем, когда кеш создается по истечении указанного периода времени независимо от актуальности данных.

  • При формировании идентификатора кеша необходимо учитывать только нужные параметры, избегая излишней вариативности.
    Неправильный ИД будет приводить к тому, что кеширование будет не эффективно из-за того, что большая часть хитов будет попадать мимо кеша.

    Пример создания кеша для новостей, когда срок активности новости скоро истекает, и кеш достаточно будет создать на весь день всего один раз:

    // Не правильный ИД, так как в данном случае на каждый хит будет создан новый кеш.
    // (При условии что нам надо выбрать все элементы которые на данный день активны.)
    
    $arFilter = array(
        'IBLOCK_ID' => 19,
        'ACTIVE' => 'Y',
        '>=PROPERTY_end_date' => date("Y-m-d H:i:s")
    );
    
    $CACHE_TIME = 86400;
    $CACHE_ID = "some_key_iblock".implode('_', $arFilter);
    $obCache = new CPHPCache;
    
    if($obCache->InitCache($CACHE_TIME, $CACHE_ID, "/"))
    {
        $arVars = $obCache->GetVars();
    }
    
    // Правильный ИД, так как в данном случае кеш будет создан на весь день.
    // (При условии что нам надо выбрать все элементы которые на данный день активны.)
    $arFilter = array(
        'IBLOCK_ID' => 19,
        'ACTIVE' => 'Y',
        '>=PROPERTY_end_date' => date("Y-m-d 00:00:00")
    );
    
    $CACHE_TIME = 86400;
    $CACHE_ID = "some_key_iblock".implode('_', $arFilter);
    $obCache = new CPHPCache;
    
    if($obCache->InitCache($CACHE_TIME, $CACHE_ID, "/"))
    {
        $arVars = $obCache->GetVars();
    }
    


Ссылки по теме:

10
Курсы разработаны в компании «1С-Битрикс»

Если вы нашли неточность в тексте, непонятное объяснение, пожалуйста, сообщите нам об этом в комментариях.
Развернуть комментарии