Перейти к основному содержимому
Вернуться в категорию

Пул соединений с базой данных: PgBouncer и ProxySQL

Настройка пула соединений с базой данных с помощью PgBouncer и ProxySQL, connection pooling для MySQL и PostgreSQL, интеграция с Node.js/Python/PHP и оптимизация производительности.

Время чтения: 18 dk База данных
connection poolingpgbouncerproxysqlmysqlpostgresqlпроизводительностьбаза данныхоптимизация
Автор
REXE Teknoloji Network & Security Team
Редактор
REXE Teknoloji Technical Editorial
Первая публикация
Последнее обновление

Пул соединений с базой данных: PgBouncer и ProxySQL

Пул соединений с базой данных (connection pooling) — это техника, позволяющая приложениям повторно использовать заранее созданные соединения вместо открытия нового соединения для каждого запроса. Этот подход устраняет накладные расходы на установку соединения, ограничивает количество соединений на сервере базы данных и значительно повышает общую производительность приложения.

Зачем использовать пул соединений?

Каждое соединение с базой данных потребляет память и ресурсы CPU на стороне сервера. В высоконагруженных приложениях:

СценарийБез пула соединенийС пулом соединений
1000 одновременных запросов1000 соединений с БД20-50 соединений с БД
Время соединения5-50мс на каждый запросНоль (повторное использование)
Использование памятиВысокоеНизкое
МасштабируемостьОграниченнаяВысокая

PgBouncer для PostgreSQL и ProxySQL для MySQL/MariaDB — наиболее распространённые решения для пула соединений. Оба проверены в production-окружениях.

Установка PgBouncer (PostgreSQL)

Установка

hljs bash
# Ubuntu/Debian
apt update
apt install -y pgbouncer

# CentOS/AlmaLinux
dnf install -y pgbouncer

# Проверка версии
pgbouncer --version

Настройка pgbouncer.ini

hljs bash
nano /etc/pgbouncer/pgbouncer.ini
hljs ini
[databases]
; Псевдоним базы данных = параметры подключения
myapp = host=127.0.0.1 port=5432 dbname=myapp
analytics = host=127.0.0.1 port=5432 dbname=analytics

[pgbouncer]
; Адрес и порт прослушивания
listen_addr = 127.0.0.1
listen_port = 6432

; Аутентификация
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt

; Режим пула: session, transaction, statement
pool_mode = transaction

; Лимиты соединений
max_client_conn = 1000
default_pool_size = 25
min_pool_size = 5
reserve_pool_size = 5
reserve_pool_timeout = 3

; Тайм-ауты соединений
server_idle_timeout = 600
client_idle_timeout = 0
server_connect_timeout = 15

; Настройки логирования
logfile = /var/log/postgresql/pgbouncer.log
pidfile = /var/run/postgresql/pgbouncer.pid

; Административный интерфейс
admin_users = pgbouncer_admin
stats_users = pgbouncer_stats

Создание списка пользователей

hljs bash
# Получить хэши пользователей из PostgreSQL
psql -U postgres -c "SELECT '\"' || usename || '\"' || ' \"' || passwd || '\"' FROM pg_shadow;"

# Создать файл userlist.txt
nano /etc/pgbouncer/userlist.txt
"appuser" "md5HASH_HERE"
"pgbouncer_admin" "md5HASH_HERE"
hljs bash
# Запустить сервис
systemctl start pgbouncer
systemctl enable pgbouncer

# Проверить статус
systemctl status pgbouncer

Режимы пула PgBouncer

hljs ini
; SESSION режим: одно серверное соединение на каждое клиентское
; Наиболее совместимый, наименее эффективный
pool_mode = session

; TRANSACTION режим: соединение удерживается только на время транзакции
; Рекомендуемый режим, идеален для большинства приложений
pool_mode = transaction

; STATEMENT режим: соединение освобождается после каждого SQL-оператора
; Наиболее эффективный, но не поддерживает prepared statements
pool_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;

Установка ProxySQL (MySQL/MariaDB)

Установка

hljs bash
# Для Ubuntu/Debian
wget -O - 'https://repo.proxysql.com/ProxySQL/proxysql-2.x.x/repo_pub_key' | apt-key add -
echo deb https://repo.proxysql.com/ProxySQL/proxysql-2.x.x/$(lsb_release -sc)/ ./ | tee /etc/apt/sources.list.d/proxysql.list
apt update
apt install -y proxysql2

# Альтернатива: скачать пакет напрямую
wget https://github.com/sysown/proxysql/releases/download/v2.5.5/proxysql_2.5.5-ubuntu22_amd64.deb
dpkg -i proxysql_2.5.5-ubuntu22_amd64.deb

# Запустить сервис
systemctl start proxysql
systemctl enable proxysql

Настройка ProxySQL

hljs bash
# Подключиться к административному интерфейсу (по умолчанию: admin/admin)
mysql -h 127.0.0.1 -P 6032 -u admin -padmin
hljs sql
-- Добавить серверы MySQL
INSERT 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 ORDER BY sum_time DESC LIMIT 10;

-- Статус серверов
SELECT * FROM mysql_servers;

-- Активные соединения
SELECT * FROM stats_mysql_processlist;

Встроенный пул соединений MySQL

Плагин Thread Pool, доступный в MySQL 8.0+:

hljs ini
# /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
# Thread pool
plugin-load-add = thread_pool.so
thread_pool_size = 16
thread_pool_max_active_query_threads = 32
thread_pool_max_unused_threads = 16
thread_pool_stall_limit = 500

# Лимиты соединений
max_connections = 500
back_log = 100
wait_timeout = 600
interactive_timeout = 600

Интеграция с Node.js

Пул соединений с mysql2

hljs javascript
const mysql = require('mysql2/promise');

// Создать пул соединений
const pool = mysql.createPool({
  host: '127.0.0.1',
  port: 6033,          // Порт ProxySQL
  user: 'appuser',
  password: 'StrongPass123!',
  database: 'myapp',
  waitForConnections: true,
  connectionLimit: 20,  // Размер пула
  queueLimit: 0,        // Неограниченная очередь
  enableKeepAlive: true,
  keepAliveInitialDelay: 0
});

// Получить и использовать соединение из пула
async function getUsers() {
  const [rows] = await pool.execute('SELECT * FROM users WHERE active = ?', [1]);
  return rows;
  // Соединение автоматически возвращается в пул
}

Пул соединений с PostgreSQL (pg)

hljs javascript
const { Pool } = require('pg');

const pool = new Pool({
  host: '127.0.0.1',
  port: 6432,          // Порт PgBouncer
  user: 'appuser',
  password: 'StrongPass123!',
  database: 'myapp',
  max: 20,             // Максимум соединений
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000,
});

async function query(text, params) {
  const start = Date.now();
  const res = await pool.query(text, params);
  const duration = Date.now() - start;
  console.log('Время запроса:', duration, 'мс');
  return res;
}

Интеграция с Python

Пул соединений с SQLAlchemy

hljs python
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()

Интеграция с PHP

Пул соединений с PDO

hljs php
<?php
$dsn = 'mysql:host=127.0.0.1;port=6033;dbname=myapp;charset=utf8mb4';
$options = [
    PDO::ATTR_PERSISTENT => true,
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES => false,
];

try {
    $pdo = new PDO($dsn, 'appuser', 'StrongPass123!', $options);
} catch (PDOException $e) {
    throw new RuntimeException('Ошибка подключения к БД: ' . $e->getMessage());
}

Оптимизация производительности

Настройки оптимизации PgBouncer

hljs ini
[pgbouncer]
; Для высоконагруженных окружений
max_client_conn = 2000
default_pool_size = 50
min_pool_size = 10
reserve_pool_size = 10

; Время жизни соединения
server_lifetime = 3600
server_idle_timeout = 300

; TCP оптимизация
tcp_keepalive = 1
tcp_keepidle = 60
tcp_keepintvl = 10
tcp_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 режиме — обновите версию.

Связанные статьи

Установка и базовое управление PostgreSQL

Установка PostgreSQL на Ubuntu/Debian и RHEL/CentOS, создание пользователей и баз данных, основные SQL-команды, настройка удалённого подключения и резервное копирование. Пошаговое руководство.

15 мин
postgresqlбаза данныхустановка
Сетевой трафикВходящий GbpsИсходящий Gbps