Храните сессии в базе данных
Дата обновления перевода 2023-07-10
Храните сессии в базе данных
Symfony по умолчанию хранит сессии в файлах. Если ваше приложение обслуживается несколькими серверами, вам понадобится вместо этого использовать базу данных, чтобы сессии работали в разных серверах.
Symfony может хранить сессии в различных БД (относительных, NoSQL и "ключ-значение"), но рекомендует БД "ключ-значение" вроде Redis для наилучшей производительности.
Храните сессии в БД "ключ-значение" (Redis)
Этот раздел предполагает, что у вас есть полностью рабочий сервер Redis, а также установленное и сконфигурированное расширение phpredis.
У вас есть два разных варианта использования Redis для хранения сессий:
(1) Первый вариант, основанный на PHP, заключается в конфигурации обработчика сессии
Redis прямо в файле сервера php.ini
:
1 2 3
; php.ini
session.save_handler = redis
session.save_path = "tcp://192.168.0.178:6379?auth=REDIS_PASSWORD"
(2) Второй вариант, основанный на Symfony, заключается в конфигурации сессий Redis следующим образом.
Для начала, определите сервис Symfony для соединения с сервером Redis:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
# config/services.yaml
services:
# ...
Redis:
# вы также можете использовать классы \RedisArray, \RedisCluster или \Predis\Client
class: Redis
calls:
- connect:
- '%env(REDIS_HOST)%'
- '%env(int:REDIS_PORT)%'
# раскомментируйте следующее, если ваш сервер Redis требует пароль
# - auth:
# - '%env(REDIS_PASSWORD)%'
Теперь передайте это соединение \Redis
как аргумент сервиса, ассоциированный с
RedisSessionHandler.
Этот аргумента такж может быть \RedisArray
, \RedisCluster
, \Predis\Client
,
и RedisProxy
:
1 2 3 4 5 6 7 8 9 10
# config/services.yaml
services:
# ...
Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler:
arguments:
- '@Redis'
# вы можете по желанию передать массив опций. Единственными опциями являются 'prefix' и 'ttl',
# которые определяют используемый для ключей префикс, чтобы избежать коллизий на сервере Redis
# и срока окончания действия для любой заданной записи (в секундах), по умолчанию - 'sf_s' и null:
# - { 'prefix': 'my_prefix', 'ttl': 600 }
Далее, используйте опцию конфигурации handler_id , чтобы указать Symfony использовать этот сервис в качестве обработчика сессии:
1 2 3 4 5
# config/packages/framework.yaml
framework:
# ...
session:
handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler
Вот и все! Symfony теперь будет использовать ваш сервер Redis для чтения и записи данных сессии. Главный недостаток этого решения в том, что Redis не производит блокировки сесии, поэтому вы можете столкнуться с состояниями гонки при доступе к сессиям. Например, вы можете увидеть ошибку "Невалидный токен CSRF", так как два запроса были сделаны параллельно, и только первый из них сохранил CSRF-токен в сессии.
See also
Если вы используете Memcached вместо Redis, следуйте схожему подходу, но замените
RedisSessionHandler
на MemcachedSessionHandler.
Храните сессии в реляционной базе данных (MariaDB, MySQL, PostgreSQL)
Symfony включает в себя PdoSessionHandler для хранения сессий в реляционных БД вроде MariaDB, MySQL и PostgreSQL. Чтобы использовать это, для начала зарегистрируйте новый сервис обработчика с идентификационными данными вашей БД:
1 2 3 4 5 6 7 8 9 10 11
# config/services.yaml
services:
# ...
Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler:
arguments:
- '%env(DATABASE_URL)%'
# вы также можете использовать конфигурацию PDO, но это требует передачи двух аргументов
# - 'mysql:dbname=mydatabase; host=myhost; port=myport'
# - { db_username: myuser, db_password: mypassword }
Tip
При использовании MySQL в качестве БД, DSN, определенная в DATABASE_URL
может
содержать опции charset
и unix_socket
в качестве параметров строки запроса.
Затем, используйте опцию конфигурации handler_id , чтобы указать Symfony использовать этот сервис в качестве обработчика сесии:
1 2 3 4 5
# config/packages/framework.yaml
framework:
session:
# ...
handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
Конфигурация имен таблиц и столбцов сессии
Таблица, используемая дл хранения сессий, называется sessions
по умолчанию,
и определяет определенные имена столбцов. Вы можете сконфигурировать эти значения
с помощью передачи второго аргумента сервису PdoSessionHandler
:
1 2 3 4 5 6 7 8
# config/services.yaml
services:
# ...
Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler:
arguments:
- '%env(DATABASE_URL)%'
- { db_table: 'customer_session', db_id_col: 'guid' }
Вот параметры, которые вы можете сконфигурировать:
db_table
(по умолчаниюsessions
):- Имя таблицы сессий в вашей БД;
db_username
: (по умолчанию:''
)-
Имя пользователя, используемое для соединения, используя конфигурацию
PDO (если используется соединение, основанное на переменной окружения
DATABASE_URL
, оно переопределяет имя пользователя, определенное в переменной окружения). db_password
: (по умолчанию:''
)-
Парль, используемый для соединения, при использовании конфигурации PDO
(если используется соединение, основанное на переменной окружения
DATABASE_URL
, оно переопределяет пароль, определенный в переменной окружения). db_id_col
(по умолчаниюsess_id
):-
Имя столбца, где хранить ID сессии (тип столбца:
VARCHAR(128)
); db_data_col
(по умолчаниюsess_data
):-
Имя столбца, где хранить данные сессии (тип столбца:
BLOB
); db_time_col
(по умолчаниюsess_time
):-
Имя столбца, где хранить временную отметку создания сессии (тип столбца:
INTEGER
); db_lifetime_col
(по умолчаниюsess_lifetime
):-
Имя столбца, где хранить жизненный цикл сессии (тип столбца:
INTEGER
); db_connection_options
(по умолчанию:[]
)- Массив опций соединения, относящихся к драйверу;
lock_mode
(по умолчанию:LOCK_TRANSACTIONAL
)-
Стратегия блокировки БД для избежания состояний гонки. Возможные значения -
LOCK_NONE
(не блокировать),LOCK_ADVISORY
(блокировать на уровне приложения) иLOCK_TRANSACTIONAL
(блокировать на уровне строчки).
Подготовка базы данных к хранению сессий
До сохранения сессий в БД, вы должны создать таблицу, хранящую информацию. Обработчик сессии предоставляет метод под названием createTable() для настройки этой таблицы для вас, в соответствии с используемым движком БД:
1 2 3 4 5
try {
$sessionHandlerService->createTable();
} catch (\PDOException $exception) {
// таблица не могла быть создана по какой-то причине
}
Если вы предпочитаете устанавливать таблицу самостоятельно, рекомендуется сгенрировать пустую миграцию БД с помощью следующей команды:
1
$ php bin/console doctrine:migrations:generate
Затем, найдите соотетствующий SQL для вашей БД ниже, добавьте его к файлу, и запустите миграцию с помощью следующей команды:
1
$ php bin/console doctrine:migrations:migrate
MariaDB/MySQL
1 2 3 4 5 6 7
CREATE TABLE `sessions` (
`sess_id` VARBINARY(128) NOT NULL PRIMARY KEY,
`sess_data` BLOB NOT NULL,
`sess_lifetime` INTEGER UNSIGNED NOT NULL,
`sess_time` INTEGER UNSIGNED NOT NULL,
INDEX `sessions_sess_lifetime_idx` (`sess_lifetime`)
) COLLATE utf8mb4_bin, ENGINE = InnoDB;
Note
Тип столбца BLOB
(который используется createTable()
по умолчанию)
хранит до 64 кБ. Если данные сессии пользователя превышают это значение,
может быть вызвано исключение, или их сессия может быть беззвучно сброшена.
Рассмотрите использование MEDIUMBLOB
, если вам нужно больше места.
PostgreSQL
1 2 3 4 5 6 7
CREATE TABLE sessions (
sess_id VARCHAR(128) NOT NULL PRIMARY KEY,
sess_data BYTEA NOT NULL,
sess_lifetime INTEGER NOT NULL,
sess_time INTEGER NOT NULL
);
CREATE INDEX sessions_sess_lifetime_idx ON sessions (sess_lifetime);
Microsoft SQL Server
1 2 3 4 5 6 7
CREATE TABLE sessions (
sess_id VARCHAR(128) NOT NULL PRIMARY KEY,
sess_data NVARCHAR(MAX) NOT NULL,
sess_lifetime INTEGER NOT NULL,
sess_time INTEGER NOT NULL,
INDEX sessions_sess_lifetime_idx (sess_lifetime)
);
Храните сессии в базе данных NoSQL (MongoDB)
Symfony включает в себя MongoDbSessionHandler для хранения сессий в БД MongoDB NoSQL. Для начала, убедитесь, что у вас есть рабочее соединение MongoDB в вашем приложении Symfony, как объясняется в статье конфигурация DoctrineMongoDBBundle.
Затем, зарегистрируйте новый обработчик сервиса для MongoDbSessionHandler
, и
передайте соединение MongoDB в качестве аргумента:
1 2 3 4 5 6 7
# config/services.yaml
services:
# ...
Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler:
arguments:
- '@doctrine_mongodb.odm.default_connection'
Далее, используйте опцию конфигурации handler_id , чтобы указать Symfony использовать этот сервис в качестве обработчика сессии:
1 2 3 4 5
# config/packages/framework.yaml
framework:
session:
# ...
handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler
Note
MongoDB ODM 1.x работает только с наследуемым дравером, что больше не поддерживается
классом сессии Symfony. Установите пакет alcaeus/mongo-php-adapter
, чтобы
извлечь основной объект \MongoDB\Client
, или обновить MongoDB ODM до 2.0.
Вот и все! Symfony теперь будет использовать ваш сервер MongoDB, чтобы считывать и записывать данные сессии. Вам не нужно делать ничего, чтобы запустить коллекцию вашей сессии. Однако, вы можете захотеть добавить индекс, чтобы улучшить производительность вашего сбора мусора. Выполните это из оболочки MongoDB:
1 2
use session_db
db.session.createIndex( { "expires_at": 1 }, { expireAfterSeconds: 0 } )
Конфигурация имен полей сессии
Коллекция, используемая для хранения сессий, определяет определенные имена
полей. Вы можете сконфигурировать эти значения со вторым аргументом, переданным
сервису MongoDbSessionHandler
:
1 2 3 4 5 6 7 8
# config/services.yaml
services:
# ...
Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler:
arguments:
- '@doctrine_mongodb.odm.default_connection'
- { id_field: '_guid', 'expiry_field': 'eol' }
Вот параметры, которые вы можете сконфигурировать:
id_field
(по умолчанию_id
):- Имя поля, где хранить ID сессии;
data_field
(по умолчаниюdata
):- Имя поля, где хранить данные сессии;
time_field
(по умолчаниюtime
):- Имя поля, где хранить временную отметку создания сессии;
expiry_field
(по умолчаниюexpires_at
):- Имя поля, где хранить жизненный цикл сессии.