← Назад к вопросам
Почему нельзя установить 100000 соединений с БД?
2.0 Middle🔥 81 комментариев
#DevOps и инфраструктура#Базы данных (SQL)
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Почему нельзя установить 100000 соединений с БД
Установка 100 тысяч соединений с БД невозможна или непрактична по нескольким причинам, связанным с ресурсами операционной системы и самой СУБД.
1. Ограничение операционной системы (файловые дескрипторы)
Каждое соединение требует файлового дескриптора (file descriptor). ОС имеет лимиты:
# Проверить лимит пользователя
ulimit -n # Обычно 1024 или 4096
# Проверить системный лимит
cat /proc/sys/fs/file-max # Может быть 2097152 на сервере
# Увеличить лимит (требует sudo)
ulimit -n 100000 # Временно
# Постоянно в /etc/security/limits.conf
# * soft nofile 100000
# * hard nofile 100000
import resource
# Проверить текущий лимит
soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
print(f"Soft: {soft}, Hard: {hard}")
# Попытка увеличить
try:
resource.setrlimit(resource.RLIMIT_NOFILE, (100000, hard))
except (ValueError, PermissionError) as e:
print(f"Ошибка: {e}")
2. Ограничения PostgreSQL
PostgreSQL имеет встроенные ограничения:
-- Просмотр текущего лимита
SHOW max_connections;
-- По умолчанию: 100
-- Максимум, что можно установить
SET max_connections = 10000;
-- На каждое соединение требуется память
-- ~600KB на соединение (shared_buffers + рабочая память)
import psycopg2
import concurrent.futures
def try_connect(conn_id):
"""Попытка создать соединение"""
try:
conn = psycopg2.connect(
host="localhost",
database="mydb",
user="postgres",
password="password",
timeout=2
)
# Соединение откроется, но будут ошибки
conn.close()
return True
except psycopg2.OperationalError as e:
return False
# Попытка открыть 1000 соединений
with concurrent.futures.ThreadPoolExecutor(max_workers=100) as executor:
results = list(executor.map(try_connect, range(1000)))
successful = sum(1 for r in results if r)
print(f"Успешных: {successful}/1000") # Вероятно меньше 100
3. Память на сервере БД
Каждое соединение потребляет памяти на БД:
PostgreSQL: ~600KB per connection
MySQL: ~1-2MB per connection
MongoDB: ~1MB per connection
Для 100K соединений:
100,000 * 0.6MB = 60GB памяти (только на соединения!)
-- Расчёт памяти в PostgreSQL
SELECT
(SELECT setting::int FROM pg_settings WHERE name=max_connections) as max_conns,
(SELECT setting::int FROM pg_settings WHERE name=shared_buffers) / 1024 as shared_buffers_mb
-- На 100K соединений:
-- max_connections = 100000
-- shared_buffers * max_connections = ОЧЕНЬ МНОГО памяти
4. Сетевые ограничения
# Лимит на количество открытых портов
# Обычно: 65535 (2^16 - 1) портов
# При127.0.0.1:* можно открыть максимум 65535 соединений
# На разных IP может быть больше
# Проверить текущие соединения
netstat -an | grep ESTABLISHED | wc -l
5. Производительность обработки
Даже если открыть 100K соединений, БД не сможет их эффективно обработать:
import psycopg2
import time
# Измерение нагрузки
connections = []
start = time.time()
for i in range(100):
try:
conn = psycopg2.connect(
host="localhost",
database="mydb",
user="postgres",
password="password"
)
connections.append(conn)
except psycopg2.OperationalError:
print(f"Не удалось подключиться после {i} соединений")
break
elapsed = time.time() - start
print(f"Открыто {len(connections)} соединений за {elapsed:.2f}s")
print(f"Память БД заполнена на {len(connections) * 0.6}MB")
# Закрытие
for conn in connections:
conn.close()
6. Правильный подход: Connection pooling
Вместо создания 100K соединений используй пулинг:
from psycopg_pool import ConnectionPool
import asyncio
# Пул из 20 переиспользуемых соединений
async def main():
async with await ConnectionPool.connect(
"postgresql://user:password@localhost/mydb",
min_size=5,
max_size=20 # Максимум 20 активных соединений
) as apool:
# Обслуживание 10000 параллельных запросов
async with apool.connection() as conn:
result = await conn.execute("SELECT 1")
print(result)
# Или с SQLAlchemy
from sqlalchemy import create_engine
engine = create_engine(
"postgresql://user:password@localhost/mydb",
pool_size=20, # Количество соединений в пуле
max_overflow=10, # Дополнительные временные соединения
pool_recycle=3600 # Переиспользовать каждый час
)
7. Мониторинг текущих соединений
# Проверить текущие соединения в PostgreSQL
SELECT COUNT(*) FROM pg_stat_activity;
# Найти долгие запросы
SELECT pid, usename, state, query, query_start FROM pg_stat_activity
WHERE state != idle
ORDER BY query_start DESC;
# Завершить лишние соединения
SELECT pg_terminate_backend(pid) FROM pg_stat_activity
WHERE pid <> pg_backend_pid() AND usename = myuser;
8. Практическая рекомендация
import psycopg_pool
# Оптимальная конфигурация
class DatabaseConfig:
# Для приложения с 10 рабочих (gunicorn)
POOL_SIZE = 10 # 1 соединение на рабочего
MAX_OVERFLOW = 5 # Запас на пиковые нагрузки
POOL_RECYCLE = 3600 # Переиспользовать каждый час
# Это даст максимум 15 соединений при пиковой нагрузке
# Вместо 100K
async_engine = create_engine(
"postgresql://...",
pool_size=DatabaseConfig.POOL_SIZE,
max_overflow=DatabaseConfig.MAX_OVERFLOW,
pool_recycle=DatabaseConfig.POOL_RECYCLE
)
Итоговые выводы
| Проблема | Ограничение | Решение |
|---|---|---|
| Файловые дескрипторы ОС | 1024-4096 по умолчанию | Увеличить ulimit |
| Лимит БД | 100 соединений по умолчанию | Увеличить max_connections |
| Память сервера | 600KB на соединение | Connection pooling |
| Производительность | Невозможно обработать все параллельно | Использовать пул из 20-50 соединений |
| Порты | 65535 портов максимум | Разные IP адреса |
Правильный подход: использовать 20-50 переиспользуемых соединений через пулинг вместо попытки открыть 100K. Это даст лучшую производительность и стабильность.