С технической точки зрения в PHP есть встроенный механизм сессий (для борьбы с Альцгеймером), который позволяет автоматически (средствами PHP) записывать пользователю в cookie идентификатор сессии, а данные, относящиеся к этой сессии, хранить где-то у себя.
Где именно - зависит от параметра session.save_handler, и по умолчанию это файлы.
С файлами у нас сразу горсть проблем:
- Система сразу попадает под вид нагрузки "случайная запись/чтение мелких файлов". Если у вас SSD, это не так критично, а обычные жесткие диски сильно страдают.
- Даже самый маленький файл сессии занимает на диске не меньше, чем размер блока. Таким образом, при блоке 4Кб, даже если вы записали в сессию 1 байт, на диске будет занято 4Кб. И 1024 сессии по 1 байту займут на диске не 1Кб, а 4Мб.
- По умолчанию все файлы сессий складываются в одну папку, без вложенности. Соответственно, следует сразу спросить себя: не пострадает ли файловая система от наличия в одной папке пары миллионов файлов.
- Есть встроенная возможность разложить файлы сессий по дереву папок, но при этом выключится автоматическая очистка файлов истекших сессий (это, кстати, очередной забавный нюанс, о котором не все знают).
- Автоматическая же очистка файлов истекших сессий сильно нагружает диск и производится в том же процессе, что и обслуживание клиентского запроса. То есть каждый 100-й (по умолчанию) запрос будет отдаваться существенно медленнее остальных 99.
- А еще хранение файлов на диске исключает балансирование нагрузки между разными серверами. Потому что следующий запрос обслуживать будет соседний сервер, а у него в файлы сессии ничегошеньки не записано.
К счастью, взрослые дядьки уже написали для нас другие методы хранения данных сессий. Например, модуль redis для PHP предоставляет нам возможность хранить данные в базе данных Redis.
Это позволяет нам решить массу проблем одним махом (натурально, достаточно изменить session.save_handler и дописать параметры для соединения с Redis).
- Соединение с Redis можно делать по сети. Сразу появляется возможность балансировать нагрузку на сервера с PHP.
- А еще можно для балансировки размножить сам Redis штатными средствами, то есть все вообще замечательно.
- Redis тоже имеет накладные расходы на хранение данных, но не такие большие.
- Redis легко справляется с огромным числом ключей (это база данных "ключ-значение", где ключом выступает идентификатор сессии, а значением - её данные), их натурально можно записывать миллиарды, он и не почешется.
- Нет необходимости включать в клиентский процесс сборку мусора. Сборка мусора при хранении в файлах включает обход всех файлов, чтение их, и проверку, не истекли ли связанные с ними сессии. В случае хранения в redis, ключу сразу присваивается время жизни, и можно положиться на встроенный в Redis механизм инвалидации. Нагрузка будет размазана равномерно, и нет необходимости проверять все ключи, раз Redis уже знает, какие у них сроки годности.
К сожалению, тут опять есть небольшой нюанс, о котором следует знать. По умолчанию сессия в Redis не блокируется, поэтому одновременный доступ к данным может привести к их искажению или потере. На этот счет есть неплохая статья, показывающая как люди это обходят.
Проблема хранения сессий - одна из базовых, поэтому не стоит писать свой обработчик. Достаточно выбрать из уже имеющихся. Тем более, чем было бы разумно вознестись над функциями ядра в более высокий уровень абстракции.
Свои нюансы связаны не только с хранением данных сессии на сервере, но и с хранением самого идентификатора сессии у клиента.
Этот идентификатор генерируется автоматически и хранится в cookies пользователя.
Бояре помнят времена, когда cookies поддерживались не всеми клиентскими браузерами. С тех времен сохранился механизм передачи идентификатора через формы и ссылки. По умолчанию отключен, но желающие могут изучить документацию к параметрам семейства session.use_trans_sid. После этого крайне рекомендуется почитать о такой вещи, как фиксация сессии, из-за которой этот параметр и был по умолчанию отключен.
С cookies связана еще одна популярная проблема.
Разработчик, не прочитавший руководство целиком, может возжелать, чтобы его сессии хранились, скажем, неделю. Он ставит подходящее значение в session.gc_maxlifetime и радуется. Ровно до тех пор, пока не выясняется, что сессия куда-то делась.
Оказывается, надо не забыть обязательно настроить соответствующим образом другой параметр: session.cookie_lifetime. По умолчанию, в нем стоит 0, то есть cookie хранится до закрытия браузера. Как только cookie из браузера пропадает, возникает проблема: сервер продолжает считать данные валидными, но браузер забыл идентификатор, по которому их можно достать.
Это очень частая проблема, и на неё стоит обратить особое внимание.
И последнее.
Если "мсье знает толк в извращениях", можно переопределить функцию, создающую идентификатор сессии. Например, вместо случайного md5 порождать подписанный токен с гарантированной уникальностью.
В обычной жизни пригодится единицам.