Зберігайте сесії у базі даних

Дата оновлення перекладу 2022-12-23

Зберігайте сесії у базі даних

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"
  1. Друга опція, заснована на Symfony, полягає у конфігурації сесій Redis наступним чином.

Спочатку, визначіть сервіс Symfony для зʼєднання з сервером Redis:

  • YAML
  • XML
  • PHP
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:

  • YAML
  • XML
  • PHP
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 використовувати цей сервіс в якості обробника сесії:

  • YAML
  • XML
  • PHP
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. Щоб використати це, спочатку зареєструйте новий сервіс обробника з ідентифікаційними даними вашої БД:

  • YAML
  • XML
  • PHP
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 використовувати цей сервіс в якості обробника сесії:

  • YAML
  • XML
  • PHP
1
2
3
4
5
# config/packages/framework.yaml
framework:
    session:
        # ...
        handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler

Конфігурація імен таблиць та стовпців сесії

Таблиця, використовувана для зберігання сесій, називається sessions за замовчуванням, і визначає визначені імена стовпців. Ви можете сконфігурувати ці значення за допомогою передачі другого аргумента сервісу PdoSessionHandler:

  • YAML
  • XML
  • PHP
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 в якості аргумента:

  • YAML
  • XML
  • PHP
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 використовувати цей сервіс в якості обробника сесії:

  • YAML
  • XML
  • PHP
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:

  • YAML
  • XML
  • PHP
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):
Імʼя поля, де зберігати життєвий цикл сесії.