PHP и тесният участък „връзки към базата“: пулери, прокси и практика
В много стекове заявките вече не са „убийци“, а продукшънът все пак удря too many connections, резервирани слотове или зависвания след деплой. Честата причина не е бавен SQL, а аритметиката на връзките: моделът на заявка в PHP създава пикове connect + auth + TLS, а базата има твърд таван на паралелни backend-ове. Пулерите и управляваните прокси държат малък стабилен пул сървърни сесии зад много краткотрайни PHP клиенти.
Свързани материали: Бази под натоварване · Sail: бази и Docker
Съдържание
- Защо PHP влошава проблема
- Какви са реалните лимити
- Пулери и прокси между слоевете
- PostgreSQL: PgBouncer
- MySQL / MariaDB: ProxySQL
- Управлявани облачни прокси
- Други пулери: PgCat, Odyssey, pgpool-II
- Laravel
- Какво пулерът не лекува
- Чеклист
Защо PHP влошава проблема
Класическият PHP-FPM обработва заявка, вика услуги, връща отговор и освобождава ресурсите на заявката. Без persistent connections всяка заявка към базата обикновено отваря TCP, удостоверява се, при нужда TLS, после изпълнява SQL.
Под натоварване:
pm.max_childrenопределя едновременните PHP процеси на машината; ако повечето заявки удрят базата, може да са нужни толкова паралелни сесии на възел.- Работници на опашки (
queue:work, Horizon) са дълготрайни — всеки паралелен работник често държи отворена връзка по време на задачата. - Хоризонтално мащабиране умножава всичко.
След рестарт и при скокове на трафик базата вижда буря от връзки. Дори max_connections да стига, удрят памет на backend (особено Postgres) и разход за удостоверяване.
Какви са реалните лимити
- Глобален
max_connections; резервирани слотове за админ/репликация намаляват капацитета за приложението. - RAM на връзка — безкрайно вдигане на лимита води до OOM.
- Закъснение при свързване — TLS и парола на всяка заявка се натрупват.
- Thundering herd — всички PHP процеси се свързват наведнъж.
Броете всички програми с SQL: уеб, работници, cron, CLI, админки.
Пулери и прокси между слоевете
Пулерът стои между PHP и СУБД. PHP се свързва към пулера; пулерът поддържа по-малък пул реални връзки към Postgres/MySQL и ги преизползва.
Плюсове: по-малко backend-ове на сървъра на базата, мультиплексиране, по-плавно поведение при пикове.
Минуси: още един хоп, променена семантика на сесията според режима, риск пулерът да стане ново тесно място, ако не е оразмерен.
PostgreSQL: PgBouncer
| Режим | Поведение | Laravel |
|---|---|---|
| Session | Един сървърен конект за цялата клиентска сесия | Пълна съвместимост: SET, LISTEN, temp tables, prepared statements като при директна връзка. По-малко печалба от мультиплексиране при дълги клиенти. |
| Transaction | Връщане на сървърния конект в пула след COMMIT/ROLLBACK | Силно мультиплексиране за кратки HTTP заявки. Чупи сесийни настройки между транзакции; именуваните prepared statements често изискват настройка на драйвера. |
| Statement | След всеки оператор | Почти никога за ORM. |
Prepared statements: при смяна на физическата връзка именуваните prepare се чупят — клиентска емулация (PDO::ATTR_EMULATE_PREPARES) или безименни/simple протокол според драйвера.
Подавайте application_name за проследяване в pg_stat_activity.
MySQL / MariaDB: ProxySQL
ProxySQL — прокси на MySQL протокол: маршрутизация, правила, read/write split, пул и настройваемо мультиплексиране. Помага да се ограничат backend връзките, докато стотици FPM деца се вързват към проксито.
MySQL Router и облачни балансьори не винаги мультиплексират по същия начин — проверете документацията. MariaDB MaxScale — според издание и лиценз.
Управлявани облачни прокси
RDS Proxy и аналози предлагат пулинг и по-гладък failover. Ограниченията за сесия и prepared statements остават — вижте матрицата за вашия драйвер.
Други пулери: PgCat, Odyssey, pgpool-II
- PgCat и Odyssey — пулери за Postgres; сравнете режими, метрики и особености на драйвера с PgBouncer.
- pgpool-II — често за репликация и маршрутизация; по-тежък за експлоатация, ако ви трябва само мультиплексиране.
Laravel
config/database.php— PDO опции според пулера.- Read/write и
sticky— съгласувайте с лаг на репликите. - Octane / Swoole — дълготрайни работници: внимавайте с транзакции и състояние между заявки.
- Horizon —
concurrency × workers= постоянна натовареност на пула. - Инструменти за дебъг не оставяйте в прод да разтягат транзакции.
Често DB_HOST сочи към пулера:
DB_HOST=pgbouncer.internal
DB_PORT=6432
Оразмерете пула според реалната паралелност на заявки, не само според pm.max_children.
Какво пулерът не лекува
- N+1 и липсващи индекси.
- Дълги транзакции (външни HTTP вътре в транзакция).
- Тежки DDL / миграции — понякога е нужен директен път към базата.
Чеклист
- Инвентаризация на всички процеси с SQL.
- Сравнение с
max_connectionsи памет на връзка. - Избор на режим / правила според ORM и драйвер.
- Натоварване с prepared statements и нужни сесийни функции.
- Мониторинг на опашката пред пулера и активните връзки към СУБД.
Пулерът „по средата“ превръща стотици кратки PHP сесии в десетки реални backend-ове на базата — формата, в която повечето OLTP системи работят най-предсказуемо.