Кэш и сессии в памяти - memcached, Redis, проверка и отказ
Переводим кэш платформы и хранилище сессий из файлов в память. Разбираем секции настроек, своё подключение, проверку и поведение сайта при отказе хранилища.
Механика
Кэш платформы и хранилище сессий - два независимых потребителя внешней памяти. Настраиваются они в разных секциях файла настроек, и одна настройка вторую не включает.
Выбор конкретного хранилища определяется задачей, а не привычкой команды. Memcached годится только под кэш, а Redis обслуживает и кэш, и сессии, и очереди сообщений.
Переключение кэша меняет только движок хранения и ничего никуда не переносит. Старый кэш на диске остаётся лежать нетронутым, просто перестаёт использоваться, и место под ним освобождают руками.
Свои обращения к хранилищу идут через объявленное подключение. В секции подключений заводят запись со своим именем и классом соединения, а в коде берут её по этому имени и работают методами записи и чтения.
Нативный объект самого расширения PHP доступен отдельным вызовом. Его берут только под операции, которых нет в обычном интерфейсе соединения: полная очистка, установка значения при отсутствии ключа и подобные.
Расширения memcache и memcached - разные вещи. У них разные классы соединения и разные единицы времени в настройках: у первого таймаут в секундах, у второго в миллисекундах.
Несколько серверов хранилища перечисляют в настройке списком. Доля запросов регулируется весом: равные значения дают равномерное распределение, больший вес - больше обращений к этому узлу.
Redis в режиме мастера и реплик настраивают одним пишущим адресом. Реплики он находит сам, а для нескольких равноправных мастеров перечисляют все узлы и добавляют параметры кластера.
Секцию подключений закрывают признаком только для чтения. Без него реквизиты хранилища можно случайно подменить прямо во время работы страницы.
Внешнее хранилище - новая точка отказа, и она ведёт себя по-разному. Недоступный кэш означает медленный сайт, недоступное хранилище сессий - разлогин всех посетителей разом.
Шаги
- Проверить, что нужное расширение PHP установлено на всех узлах сайта.
- Поднять сервис и закрыть его от внешней сети на уровне адреса и правил.
- Объявить подключение в секции настроек и закрыть её от изменений.
- Переключить кэш платформы на движок в памяти и проверить страницы.
- Перевести сессии в Redis, если хранилище одно на несколько узлов.
- Убедиться, что данные действительно ложатся в хранилище, а не мимо.
- Продумать поведение сайта при отказе сервиса и завести наблюдение.
Код
Проверяем расширение и доступность сервиса:
php -m | grep -E '^(redis|memcached|memcache)$'redis-cli ping # ответ PONG означает, что сервис отвечаетsystemctl status redis memcached --no-pager | grep -E 'Active|Loaded'echo stats | nc 127.0.0.1 11211 | head -5# расширение и сервис - разные вещи: бывает одно без другогоПроверка занимает минуту и снимает половину будущих вопросов. Расширение без сервиса и сервис без расширения выглядят на сайте одинаково: кэш молча остаётся на диске.
Переключаем кэш платформы:
'cache' => ['value' => [ 'type' => ['class_name' => '\\Bitrix\\Main\\Data\\CacheEngineMemcached'], 'memcache' => ['host' => '127.0.0.1', 'port' => '11211'], 'sid' => $_SERVER['DOCUMENT_ROOT'] . '#01', // свой ключ на каждый сайт]],Ключ разделения нужен, когда одним сервисом пользуются несколько сайтов. Без него сайты видят записи друг друга, и очистка кэша одного из них уносит чужие данные заодно со своими.
Объявляем своё подключение к Redis:
'connections' => ['value' => [ 'default' => [/* основное подключение к базе */], 'custom.redis' => [ 'className' => '\\Bitrix\\Main\\Data\\RedisConnection', 'host' => '127.0.0.1', 'port' => 6379, ],], 'readonly' => true],Подключение живёт рядом с основным и получает своё имя. Признак только для чтения ставят всегда: он запрещает подменять реквизиты хранилища во время работы скрипта.
Пишем и читаем свои значения:
/** @var \Bitrix\Main\Data\RedisConnection $connection */$connection = \Bitrix\Main\Application::getConnection('custom.redis');$connection->set('vendor:rate', '78.5');$rate = $connection->get('vendor:rate');
$connection->getResource()->setnx('vendor:lock', '1'); // нативная команда// ключи именуют с префиксом проекта: хранилище часто общее на несколько задачОбычные запись и чтение идут методами соединения. Нативный объект расширения берут только под то, чего в этом интерфейсе нет, и обкладывают проверкой на доступность сервиса.
Переносим сессии в Redis:
'session' => ['value' => [ 'mode' => 'default', 'handlers' => ['general' => [ 'type' => 'redis', // один пишущий адрес: реплики Redis находит сам 'servers' => [['host' => '127.0.0.1', 'port' => '6379']], ]],]],Сессии в памяти обязательны, когда узлов сайта больше одного. При отказе сервиса посетители теряют вход разом, поэтому сессии переносят туда, где за доступностью следят так же серьёзно, как за базой.
Убеждаемся, что данные действительно легли в хранилище:
redis-cli dbsize # число ключей растёт после захода на сайтredis-cli --scan --pattern '*' | head -5echo 'stats' | nc 127.0.0.1 11211 | grep -E 'curr_items|get_hits'# ноль ключей при живом сайте означает, что кэш идёт мимо хранилищаПустое хранилище при работающем сайте - главный признак незаметной ошибки в настройках. Платформа в этом случае не ругается, а просто продолжает писать кэш на диск.
Закрываем сервис от внешней сети:
grep -E '^bind|^protected-mode' /etc/redis/redis.confss -lntp | grep -E '6379|11211' # слушать надо локальный адрес# открытый наружу Redis - готовый способ отдать сайт вместе с сессиямиОба сервиса рассчитаны на закрытый контур и не проверяют, кто к ним обращается. Доступ к ним ограничивают адресом прослушивания и правилами сети, а не надеждой на неизвестность порта.
Ограничения
Memcached не хранит сессии и очереди. Попытка перевести сессии на него заканчивается неработающей авторизацией, и это не настройка, а свойство самого сервиса.
Память ограничена и заполняется. При нехватке места хранилище вытесняет старые записи, поэтому кэш в памяти - ускорение, а не гарантия сохранности данных.
Отказ хранилища сказывается на сайте сразу. Кэш даёт замедление и всплеск запросов к базе, сессии - потерю входа у всех посетителей одновременно.
Общий сервис на несколько сайтов требует разделения. Разные базы Redis, разные экземпляры сервиса или разный ключ кэша - иначе сайты перетирают данные друг друга.
Перенос кэша в память не отменяет работы с тегами. Тегированный сброс и управляемый кэш работают так же, меняется только место хранения записей.
Типичные проблемы
Кэш переключили, а сайт быстрее не стал.
Данные по-прежнему пишутся на диск: расширение не установлено или сервис не отвечает. Число ключей в хранилище остаётся нулевым при работающем сайте.
После переноса сессий никто не может войти.
Сессии переведены на memcached, который их не обслуживает. Хранилище сессий в памяти даёт только Redis.
Настройки таймаута ведут себя странно.
Перепутаны расширения memcache и memcached: у них разные классы соединения и разные единицы времени. Одно значение означает секунды, другое - миллисекунды.
Очистка кэша одного сайта уносит кэш соседнего.
Два сайта пользуются одним сервисом с одинаковым ключом разделения. Ключ задают своим для каждого сайта, либо разводят сайты по разным экземплярам.
Сайт лёг вместе с перезапуском хранилища.
Сессии живут в памяти сервиса, а сервис перезапустили без предупреждения. Перезапуск такого хранилища планируют как перезапуск базы, а не как мелкую операцию.
Частые вопросы
Что выбрать: memcached или Redis?
Redis, если нужны сессии, очереди или сохранность данных между перезапусками. Memcached остаётся разумным выбором там, где хранится только кэш и терять его не жалко.
Переносится ли старый кэш при переключении?
Нет, старые файлы остаются лежать на диске и просто перестают читаться. Их удаляют отдельно, чтобы освободить место.
Нужно ли выносить сервис на отдельную машину?
На одном узле - нет, локальный сервис быстрее. Отдельная машина нужна, когда узлов сайта несколько и хранилище должно быть общим.
Как понять, что кэш действительно в памяти?
По числу ключей в хранилище: оно растёт при заходах на сайт. Нулевое значение при живом сайте означает ошибку в настройках.
Что будет, если сервис недоступен?
Кэш деградирует до обращений к базе, и сайт замедляется. Хранилище сессий недоступным быть не должно: его отказ выкидывает всех посетителей.
Смежное
- Настройки проекта - оглавление подтемы
- Веб-кластер: репликация, чтение со слейва, сессии и кэш - зачем эти хранилища нужны в кластере
- Настройки проекта: файл ядра, опции модуля, разные стенды - где живут эти секции
- Вынос базы и кэша на отдельные машины: когда и как - вторая сторона: сервер и узлы
- Кэширование своей выборки: ключ, теги, сброс - кэш на уровне кода
- Копия сайта не открывается на стенде: разбор причин - чужой сервер кэша в настройках копии
- Could not start session by PHP: сессия не стартует - когда сессия не заводится вовсе
- Внешняя база данных: второе соединение, запросы, ORM - соседняя запись в тех же подключениях
- Медленный сайт на практике - когда кэш в памяти вообще нужен
- База данных: PostgreSQL, Redis, memcached, шардинг - устройство хранилищ целиком
- Сервисы и конфигурация D7 - как ядро читает эти секции