Пул соединений с базой данных: PgBouncer и ProxySQL
Настройка пула соединений с базой данных с помощью PgBouncer и ProxySQL, connection pooling для MySQL и PostgreSQL, интеграция с Node.js/Python/PHP и оптимизация производительности.
Пул соединений с базой данных: PgBouncer и ProxySQL
Пул соединений с базой данных (connection pooling) — это техника, позволяющая приложениям повторно использовать заранее созданные соединения вместо открытия нового соединения для каждого запроса. Этот подход устраняет накладные расходы на установку соединения, ограничивает количество соединений на сервере базы данных и значительно повышает общую производительность приложения.
Зачем использовать пул соединений?
Каждое соединение с базой данных потребляет память и ресурсы CPU на стороне сервера. В высоконагруженных приложениях:
Сценарий
Без пула соединений
С пулом соединений
1000 одновременных запросов
1000 соединений с БД
20-50 соединений с БД
Время соединения
5-50мс на каждый запрос
Ноль (повторное использование)
Использование памяти
Высокое
Низкое
Масштабируемость
Ограниченная
Высокая
PgBouncer для PostgreSQL и ProxySQL для MySQL/MariaDB — наиболее распространённые решения для пула соединений. Оба проверены в production-окружениях.
# Запустить сервис
systemctl start pgbouncer
systemctl enable pgbouncer
# Проверить статус
systemctl status pgbouncer
Режимы пула PgBouncer
hljs ini
; SESSION режим: одно серверное соединение на каждое клиентское; Наиболее совместимый, наименее эффективныйpool_mode = session
; TRANSACTION режим: соединение удерживается только на время транзакции; Рекомендуемый режим, идеален для большинства приложенийpool_mode = transaction
; STATEMENT режим: соединение освобождается после каждого SQL-оператора; Наиболее эффективный, но не поддерживает prepared statementspool_mode = statement
Мониторинг PgBouncer
hljs bash
# Подключиться к административной консоли
psql -h 127.0.0.1 -p 6432 -U pgbouncer_admin pgbouncer
# Статистика пула
SHOW POOLS;
# Клиентские соединения
SHOW CLIENTS;
# Серверные соединения
SHOW SERVERS;
# Общая статистика
SHOW STATS;
# Перезагрузить конфигурацию
RELOAD;
# Подключиться к административному интерфейсу (по умолчанию: admin/admin)
mysql -h 127.0.0.1 -P 6032 -u admin -padmin
hljs sql
-- Добавить серверы MySQLINSERT INTO mysql_servers (hostgroup_id, hostname, port, weight, max_connections)
VALUES
(0, '127.0.0.1', 3306, 1000, 200), -- Группа записи (master)
(1, '192.168.1.101', 3306, 1000, 200), -- Группа чтения (replica 1)
(1, '192.168.1.102', 3306, 1000, 200); -- Группа чтения (replica 2)-- Добавить пользователяINSERT INTO mysql_users (username, password, default_hostgroup)
VALUES ('appuser', 'StrongPass123!', 0);
-- Правила маршрутизации: SELECT на группу чтенияINSERT INTO mysql_query_rules (rule_id, active, match_pattern, destination_hostgroup, apply)
VALUES
(1, 1, '^SELECT.*FOR UPDATE', 0, 1), -- SELECT FOR UPDATE -> master
(2, 1, '^SELECT', 1, 1); -- Остальные SELECT -> replica-- Применить изменения
LOAD MYSQL SERVERS TO RUNTIME;
SAVE MYSQL SERVERS TO DISK;
LOAD MYSQL USERS TO RUNTIME;
SAVE MYSQL USERS TO DISK;
LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;
Мониторинг ProxySQL
hljs sql
-- Статус пула соединенийSELECT*FROM stats_mysql_connection_pool;
-- Статистика запросовSELECT*FROM stats_mysql_query_digest ORDERBY sum_time DESC LIMIT 10;
-- Статус серверовSELECT*FROM mysql_servers;
-- Активные соединенияSELECT*FROM stats_mysql_processlist;
const mysql = require('mysql2/promise');
// Создать пул соединенийconst pool = mysql.createPool({
host: '127.0.0.1',
port: 6033, // Порт ProxySQLuser: 'appuser',
password: 'StrongPass123!',
database: 'myapp',
waitForConnections: true,
connectionLimit: 20, // Размер пулаqueueLimit: 0, // Неограниченная очередьenableKeepAlive: true,
keepAliveInitialDelay: 0
});
// Получить и использовать соединение из пулаasyncfunctiongetUsers() {
const [rows] = await pool.execute('SELECT * FROM users WHERE active = ?', [1]);
return rows;
// Соединение автоматически возвращается в пул
}
from sqlalchemy import create_engine, text
from sqlalchemy.pool import QueuePool
# PostgreSQL + PgBouncer
engine = create_engine(
'postgresql://appuser:StrongPass123!@127.0.0.1:6432/myapp',
poolclass=QueuePool,
pool_size=10,
max_overflow=20,
pool_timeout=30,
pool_recycle=1800,
pool_pre_ping=True,
echo=False
)
with engine.connect() as conn:
result = conn.execute(text('SELECT * FROM users WHERE active = :active'), {'active': 1})
users = result.fetchall()
[pgbouncer]; Для высоконагруженных окруженийmax_client_conn = 2000default_pool_size = 50min_pool_size = 10reserve_pool_size = 10; Время жизни соединенияserver_lifetime = 3600server_idle_timeout = 300; TCP оптимизацияtcp_keepalive = 1tcp_keepidle = 60tcp_keepintvl = 10tcp_keepcnt = 5
Настройки оптимизации ProxySQL
hljs sql
-- Увеличить размер пула соединенийUPDATE global_variables SET variable_value ='500'WHERE variable_name ='mysql-max_connections';
-- Время повторного использования соединенияUPDATE global_variables SET variable_value ='3600000'WHERE variable_name ='mysql-connection_max_age_ms';
LOAD MYSQL VARIABLES TO RUNTIME;
SAVE MYSQL VARIABLES TO DISK;
Заключение
Пул соединений драматически повышает производительность базы данных в высоконагруженных приложениях. PgBouncer для PostgreSQL и ProxySQL для MySQL — наиболее зрелые и надёжные решения. Правильный выбор режима пула (transaction режим обычно обеспечивает наилучший баланс) и интеграция с вашим языком программирования позволят достичь максимальной производительности базы данных на серверах REXE.
Часто задаваемые вопросы
Какой режим пула использовать в PgBouncer?
Для большинства приложений рекомендуется режим 'transaction'. В этом режиме соединение удерживается только на время транзакции и сразу возвращается в пул. Режим 'session' наиболее совместим, но наименее эффективен. Режим 'statement' наиболее эффективен, но вызывает проблемы при использовании prepared statements. ORM-фреймворки (Django, Rails) работают без проблем в transaction режиме.
Как настроить разделение чтения/записи в ProxySQL?
В ProxySQL используются hostgroup'ы: hostgroup 0 для записи (master), hostgroup 1 для чтения (replica). Добавив правила в таблицу mysql_query_rules, можно направлять SELECT-запросы на replica, а INSERT/UPDATE/DELETE — на master. Это распределяет нагрузку чтения по репликам.
Как определить оптимальный размер пула соединений?
Общее правило: размер пула = (количество ядер CPU * 2) + количество дисков. Например, для 4-ядерного сервера обычно достаточно 10-20 соединений. Слишком много соединений увеличивает накладные расходы на переключение контекста. Найдите оптимальное значение с помощью нагрузочного тестирования pgbench или sysbench. Мониторьте текущее использование командой SHOW POOLS; в PgBouncer.
Обязательно ли использовать пул соединений в Node.js?
Да, в production-окружении это абсолютно необходимо. Открытие нового соединения при каждом HTTP-запросе медленно (задержка 5-50мс) и перегружает сервер базы данных. Библиотеки mysql2 и pg имеют встроенную поддержку пула. Хорошей отправной точкой является установка connectionLimit в 10-20 на экземпляр сервера приложений.
Prepared statements не работают с PgBouncer, что делать?
Это известное ограничение transaction режима. Решения: 1) переключитесь на session режим (снижение производительности), 2) отключите prepared statements на стороне приложения (в Node.js pg: prepare: false), 3) PgBouncer 1.21+ поддерживает prepared statements в transaction режиме — обновите версию.
Пошаговая установка MySQL и MariaDB на Linux, mysql_secure_installation, создание баз данных и пользователей, основные SQL-команды, настройка удалённого доступа и оптимизация производительности.
Установка PostgreSQL на Ubuntu/Debian и RHEL/CentOS, создание пользователей и баз данных, основные SQL-команды, настройка удалённого подключения и резервное копирование. Пошаговое руководство.
Установка Redis и Memcached на Ubuntu/Debian, основные команды, настройки персистентности, конфигурация TTL, сравнение Redis и Memcached, интеграция с приложениями.