Блокировка PHP сессий

Что такое сессии в PHP

Технология сессии в PHP являются простым способом хранения информации для отдельно взятого пользователя сайта. Например: товары, добавленные в корзину магазина, настройки уведомлений и т.д. При первом запросе сайта к серверу, в браузере сохраняется в cookies уникальный идентификатор сессии пользователя. Затем идентификатор или связка идентификатора с IP адресом идентифицирует пользователя. Это может использоваться для сохранения состояния между запросами страниц. Идентификаторы сессий обычно отправляются браузеру через сессионный Cookie и используются для получения имеющихся данных сессии.

Сессии используют простую технологию: PHP будет либо получать данные существующей сессии, используя переданный идентификатор (обычно из сессионного cookie), или, если ничего не передавалось, будет создана новая сессия. PHP заполнит суперглобальную переменную $_SESSION сессионной информацией после того, как будет запущена сессия. Когда PHP завершает работу, он автоматически сериализует содержимое суперглобальной переменной $_SESSION и отправляет для сохранения, используя сессионный обработчик для записи сессии.

По умолчанию PHP использует внутренний обработчик files для сохранения сессий, который установлен в INI-переменной session.save_handler. Этот обработчик сохраняет данные на сервере в директории, указанной в конфигурационной директиве session.save_path.

Самый простой пример использования сессии, например, вывод количества обращений к странице для каждого пользователя:

<?php
 
session_start();
if (!isset($_SESSION['count'])) {
    $_SESSION['count'] = 0;
} else {
    $_SESSION['count']++;
}
 
echo $_SESSION['count'];

пример работы скрипта можно посмотреть по адресу http://beget.com/session_test.php.

Каким способом возможна блокировка сессий

На сайте php в секции описания работы сессий есть примечание (http://php.net/manual/ru/session.examples.basic.php):

Сессии, использующие файлы (по умолчанию в PHP), блокируют файл сессии сразу при открытии сессии функцией session_start() или косвенно при указании session.auto_start. После блокировки, ни один другой скрипт не может получить доступ к этому же файлу сессии, пока он не будет закрыт или при завершении скрипта или при вызове функции session_write_close().

Скорее всего это станет проблемой для сайтов, которые активно используют AJAX и делают несколько одновременных запросов. Простейшим путем решить эту проблему будет вызов функции session_write_close() сразу же как только все требуемые изменения в сессии будут сделаны, предпочтительно ближе к началу работы скрипта. Также можно использовать другой механизм сессии, который поддерживает конкурентный доступ.

В последнее время проблема блокирования сессий становится всё более частой. Отчасти это связано с усложнением сайтов и необходимостью производить больше вычислений на строне сервера, а так же с большим распространением AJAX. К сожалению, не всегда логика приложения, особенно если она сложная, позволяет эффективно ограничить время блокировки конкурирующих за сессию процессов. Ситуация усугубляется еще тем, что 3-5 подобных клиентов способны быстро забить зависшими и простаивающими в ожидании процессами PHP-воркеры, в результате чего сайт начнёт выдавать 5XX ошибку.

Самый простой пример блокировки сессий:

<?php
session_start();
echo «Start»;
sleep(30);// только для одного скрипта
?>

Если открыть этот файл сначала в первой вкладке, а потом во второй — вторая вкладка будет ждать, пока не доработает первая. То есть фактически вторая вкладка будет дожидаться, пока первая не освободит файл сессии (что занимает в конкретном случае 30 секунд). Данная проблема хорошо описана в блоге компании 1С-Битрикс на Habrahabr.

Какие варианты решения данной проблемы существуют

Для хранения сессий можно использовать БД, такие как MySQL или PostgreSQL (что не совсем правильно, учитывая возможности большинства БД и возможную скорость работы в данной задаче), Memcached (не гарантирует хранение сессии, возможно ее удаление) и Redis, который мы считаем оптимальным хранилищем. По скорости он не уступает Memcached, но при этом может гарантировать сохранность данных.

И самое главное приемущество Redis - при хранении в нем сессий они не блокируются.

В рамках нашей панели управления (cp.beget.com) можно включить хранение сессий в Redis для всех сайтов на аккаунте. Для этого необходимо перейти в раздел "Сайты", и далее отметить галочку - "Хранить сессии всех сайтов в Redis".

Главное меню, панели управления BeGet Включение хранения сессий в redis для всех сайтов

Дополнительные материалы