Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Чому база даних «закінчується» за кількістю з'єднань і що таке пул з'єднань?

Кожне з'єднання з базою коштує ресурсів на сервері бази: у PostgreSQL це окремий процес з власною пам'яттю (мегабайти), у MySQL - потік. Тому кількість з'єднань обмежена (max_connections), і її не можна просто підняти до тисяч - база витратить пам'ять і процесор на керування з'єднаннями замість запитів.

Як PHP-застосунок вичерпує з'єднання: кожен процес PHP-FPM (чи воркер черги, чи воркер Octane) тримає власне з'єднання.

4 вебсервери × 50 процесів PHP-FPM   = 200 з'єднань
3 сервери воркерів × 20 процесів      =  60
планувальник, Horizon, Pulse, cron     ~  20

Додали сервери під час піку - і наступний запит отримує too many connections. Причому з'єднань багато, а активних запитів у кожен момент - одиниці: більшість процесів PHP у цей час чекає на мережу, рендерить шаблон чи стоїть без діла.

Пул з'єднань - проміжний сервіс між застосунком і базою (PgBouncer для PostgreSQL, ProxySQL для MySQL, керовані пули в хмарах):

  • застосунок відкриває сотні «дешевих» з'єднань до пулу;
  • пул тримає невелику кількість справжніх з'єднань до бази (наприклад, 30) і видає їх на час транзакції;
  • база бачить рівно стільки з'єднань, скільки може ефективно обслужити.

Режими PgBouncer:

  • session - з'єднання закріплене за клієнтом на весь сеанс (мало виграшу);
  • transaction - з'єднання видається лише на час транзакції - найефективніший, але ламає функції, що живуть довше транзакції: SET на рівні сесії, advisory locks на сесію, LISTEN/NOTIFY, підготовлені запити поза протоколом;
  • statement - на кожен оператор, найобмеженіший.

Для Laravel з PgBouncer у режимі transaction - вимкнути емульовані/постійні підготовлені запити, якщо вони несумісні, і не покладатися на стан сесії між транзакціями.

Інші важелі:

  • обмежити кількість процесів PHP-FPM і воркерів реальною потребою;
  • закривати з'єднання в довгоживучих воркерах між задачами, якщо задачі рідкісні;
  • скоротити тривалість транзакцій - менше часу з'єднання зайняте.

Помилка, якої варто уникати: «вирішити» проблему, піднявши max_connections до тисяч - база почне деградувати від кількості з'єднань раніше, ніж від запитів.

Докладніше в документації: PgBouncer

Схожі питання