PHP удаляет старые сессионные файлы "на хитах". То есть при каждой загрузке страницы сайта. А если сайт имеет большую посещаемость, то PHP приходится обрабатывать за один хит десятки гигабайт данных, что замедляет сайт. Но есть отличное решение - вынести эту процедуру на планировщик CRON и блокировать её выполнение на хитах.
Примеры в этой статье будут полезны тем, кто хранит сессии PHP стандартным способом: в файлах. Как это делаю я. В статье "Зачем хранить сессии в базе данных?" писал о том, что совершенно не согласен с теми, кто хранит PHP сессии в оперативной памяти (ОЗУ). На сайте с посещаемостью 10 000 в день и длительностью хранения сессии в 3 месяца размер всех сессионных файлов достигает нескольких десятков гигабайт. Если запихнуть это исполинское количество данных в ОЗУ, то сервер начнёт горько плакать. Конечно, если Вы не миллиардер с гипер огромным и дорогим сервером. Поэтому разбираем случай для тех, кто хранит сессии в файлах.
Отключение стандартного механизма чистки сессий
У PHP есть две директивы: session.gc_probability и session.gc_divisor. Они и задают периодичность (а точнее вероятность) удаления старых сессий. Наша задача выставить их в такое значение, чтобы они никогда не инициировали удаление старых сессий. Потому что удалять их будем другим инструментом, через CRON скрипт.
Как сказано в мануале:
session.gc_divisor в сочетании с session.gc_probability определяет вероятность запуска функции сборщика мусора (gc, garbage collection) при каждой инициализации сессии. Вероятность рассчитывается как gc_probability/gc_divisor, то есть 1/100 означает, что функция gc запускается в одном случае из ста, или 1% при каждом запросе. session.gc_divisor по умолчанию имеет значение 100.
Поэтому для остановки механизма очищения сессий достаточно задать session.gc_probability = 0. Делается это в файле .htaccess, в корневой директории сайта:
php_value session.gc_probability 0
Теперь стандартный механизм очищения сессии не будет работать при загрузке страниц сайта. Но если оставить всё как есть, то папка с сессиями начнём бесконечно расти, пока у сервера не закончится дисковое пространство. Поэтому быстро переходим к следующему шагу - написанию своего запроса на удаление старых сессий.
Удаление сессий PHP по CRON запросу
Сразу приведу пример CRON команды, которую использую для удаления старых сессий на проекте со временем жизни сессии в 90 дней:
0 * * * * alexgur /usr/bin/php -d session.save_path=/tmp/php_sessions/alexgur.ru -d session.gc_probability=1 -d session.gc_divisor=1 -d session.gc_maxlifetime=7776000 -r "session_start(); session_destroy();" &>/dev/null
Теперь разберём всю строку по порядку:
- 0 * * * * - означает, что запускается команда раз в час
- alexgur - имя пользователя, от которого запускается команда. Имя должно совпадать с именем пользователя, от которого выполняются скрипты сайтов. Иначе доступа к сессионной папке у пользователя не будет, и скрипт не сработает - нет прав доступа.
- /usr/bin/php - обработчик php
- -d session.save_path=/tmp/php_sessions/alexgur.ru - директива, которая указывает на папку, где хранятся сессии сайта. Если не знаете, где у сайта хранятся сессии, то вызовите phpinfo() и найдите значение "session.save_path"
- "-d session.gc_probability=1" и "-d session.gc_divisor=1" - оба значения ставим одинаковыми, чтобы при делении получить 100%. Тогда в 100% случаев будет запущен процесс удаления старых сессий
- -d session.gc_maxlifetime=7776000 - директива, определяющая максимальную продолжительность жизни сессии. Время жизни сессии - это количество секунд, прошедшее с последнего изменения сессионного файла. 7776000 секунд - это 90 дней. По умолчанию gc_maxlifetime = 1440. Обязательно выставите то значение, которое используется на сайте, иначе сессии будут удаляться раньше, чем нужно.
- -r "session_start(); session_destroy();" - необходимо вызвать функцию создания сессии, чтобы инициировать процессы удаления. Говорят, что ещё в PHP 7 версии есть специальная функция для запуска чистильщика сессий "session_gc();". Но я решил не рисковать в этом примере, а то вдруг кто-то ещё сидит на PHP старых версий.
- &>/dev/null - заставляет оба потока вывода отправляться в небытие. Ведь нам ничего не нужно выводить в CRON журнал после выполнения этой команды.
Дело сделано! Теперь при загрузке страниц сайта PHP не будет сканировать десятки гигабайт сессионных файлов, чтобы найти и удалить просроченные.
Зачем шарудить файлы каждый час если время жизни сесси исчисляется десятками дней? От того что конкретная сессия удалится часом позже или часом раньше уже ни чего не изменится. В сравнении с 90 днями - это мелочи. Учитывая что при таком огромном времени жизни сессий, в папке явно тысячи файлов, процедура просмотра всех их, и выявления старых - довольно дорогая. Разумнее было бы запускать очистку раз в сутки часа в 4 утра - когда минимальный онлайн.
4 4 * * *
Да, раз в неделю - это тоже вариант. Кому как подойдёт, пусть так и удаляет. Лишь бы не на хитах 🙂