Тема 4. Проектирование структур баз данных — Практическое занятие №10: исполняемый файл проекта, этап 1

1. Проблематика: почему прямое подключение убивает СУБД

Наивный подход «Запрос = Сокет»

  • Каждый запрос клиента инициирует новое TCP-соединение к СУБД.
  • Задержка на рукопожатие (Handshake) составляет 100–200 мс на запрос.
  • Сервер СУБД выделяет отдельный рабочий процесс и до 10 МБ RAM на клиента.
  • При 500 пользователях сервер падает: FATAL: sorry, too many clients already.

Промышленная архитектура

  • Использование постоянного пула предварительно открытых сокетов.
  • Аренда соединения потоком приложения занимает менее 1 мс.
  • Сервер базы данных работает в стабильном температурном и ресурсном режиме.
  • Предсказуемая масштабируемость и высокая пропускная способность (RPS).

2. Строка подключения: URI и Key-Value

Строка подключения передает сетевому драйверу исчерпывающие инструкции для соединения с СУБД:

Стандарт RFC 3986 (URI-формат)

postgresql://db_user:SecretPassword123!@db.company.internal:5432/app_db?sslmode=verify-full&connect_timeout=5

Стандарт libpq (Key-Value)

host=db.company.internal port=5432 dbname=app_db user=db_user password=SecretPassword123! sslmode=verify-full connect_timeout=5

3. Ключевые параметры строки подключения

Хост и Сеть

  • host — IP (127.0.0.1) или DNS-имя сервера.
  • port — сетевой порт службы (5432 для PostgreSQL).
  • dbname — имя целевого каталога базы данных.

Аутентификация

  • user — учетная запись роли в СУБД.
  • password — пароль (шифруется при передаче через SCRAM-SHA-256).
  • Исключить хранение паролей в кодовой базе!

Управление отказоустойчивостью

  • connect_timeout — лимит ожидания TCP/TLS (3–5 сек).
  • application_name — имя сервиса для системного мониторинга pg_stat_activity.

4. Сетевое рукопожатие (Handshake) шаг за шагом

Многоуровневый процесс установления связи:

1. L4 TCP Handshake:   Client SYN  --->  Server SYN-ACK  --->  Client ACK (10-30 мс)
2. L7 TLS Handshake:   SSLRequest  --->  TLS Exchange    --->  ECDHE Keys (30-70 мс)
3. DB Authentication:  StartupMsg  --->  SASL Challenge  --->  AuthOk     (40-100 мс)
4. Init Environment:   SET DateStyle, client_encoding, timezone           (10-20 мс)
------------------------------------------------------------------------------------
ИТОГО НАКЛАДНЫХ РАСХОДОВ: 100-220 мс на ОДИН прямой коннект!

Пул соединений удерживает этот сокет прогретым и исключает накладные расходы при повторных транзакциях.

5. Архитектура пула соединений (Connection Pool)

Механика работы пула

  • Acquire (Аренда): поток берет готовый сокет из очереди за 0.2–0.5 мс.
  • Execute (Работа): поток выполняет SQL-запрос через сокет.
  • Release (Возврат): поток вызывает close(), и сокет возвращается в пул без разрыва TCP-канала.

Масштабирование сокетов

  • min_connections — базовый неснижаемый пул активных сессий (например, 2–5).
  • max_connections — абсолютный верхний потолок соединений (например, 10–20).
  • idle_timeout — закрытие неиспользуемых соединений сверх минимума.

6. Инженерный расчет размера пула (PostgreSQL)

Золотая формула PostgreSQL & HikariCP:

Optimal Pool Size = (CPU Cores * 2) + Disk Spindles

Пример расчета для 4-ядерного сервера:

Сервер с 4 CPU и быстрым NVMe SSD:
Pool Size = (4 * 2) + 1 = 9..10 соединений.
Этот компактный пул обслужит тысячи параллельных веб-запросов за счет отсутствия блокировок CPU.

Заблуждение новичка:

«Если у меня 1000 пользователей, поставлю пул на 1000 соединений».
Результат: перегрузка планировщика ОС (Context Switching), деградация дисковой очереди и гарантированное падение СУБД.

7. Защита сетевого канала: Режимы SSL/TLS

sslmode=disable

Трафик идет в открытом виде. Учетные записи и конфиденциальные данные доступны любому перехватчику в локальной сети (ARP-spoofing).

sslmode=require

Трафик шифруется, но подлинность сервера не валидируется. Уязвимо для атаки Man-in-the-Middle (подмена сертификата).

sslmode=verify-ca

Сертификат сервера должен быть строго подписан доверенным удостоверяющим центром (Root CA).

sslmode=verify-full (Стандарт безопасности)

Проверяется подпись доверенного Root CA и строгое совпадение хоста подключения с именем в сертификате (CN/SAN).

8. Внешняя конфигурация: JSON, YAML и .env

В соответствии с методологией 12-Factor App, секреты никогда не коммитятся в Git:

config.json

{
  "db": {
    "host": "localhost",
    "port": 5432,
    "user": "app_user",
    "pool_max": 10
  }
}

config.yaml

database:
  host: localhost
  port: 5432
  user: app_user
  pool:
    max_size: 10

.env (Секреты)

DB_HOST=127.0.0.1
DB_PORT=5432
DB_USER=app_user
DB_PASS=SecretPass!
DB_SSL=verify-full

9. Первичная инициализация: Health Check (SELECT 1)

Fail-Fast принцип при запуске программы

Перед запуском пользовательского интерфейса или приемом HTTP-трафика исполняемый файл обязан протестировать доступность СУБД:

// 1. Инициализация пула соединений
Pool pool = new ConnectionPool(config);

// 2. Тестовый пинг (Health Check)
try (Connection conn = pool.getConnection()) {
    ResultSet rs = conn.createStatement().executeQuery("SELECT 1;");
    if (rs.next()) {
        logger.info("Успешное подключение к СУБД PostgreSQL. База готова.");
    }
} catch (SQLException ex) {
    logger.fatal("Сбой инициализации базы данных: " + ex.getMessage());
    System.exit(1); // Аварийный выход до приема пользовательских запросов
}

10. Диагностика и логирование сетевых сбоев

Типичные коды ошибок СУБД

  • ECONNREFUSED (08001): порт закрыт, служба СУБД не запущена или фаервол блокирует порт 5432.
  • ETIMEDOUT: маршрут к узлу недоступен, пакеты сбрасываются.
  • 28P01: неверный логин или пароль пользователя (invalid password).
  • 3D000: целевая база данных не существует (database does not exist).

Формат записи в системный лог

[2026-10-01 10:35:12] [ERROR] [DB_CONN]
Host: 127.0.0.1:5432
Error: Connection refused (08001)
Cause: PostgreSQL service is inactive.
Action: Retry 1/3 scheduled in 5s.

11. Дифференцированные практические сценарии

Вариант 1: Складской учет

Десктопное приложение: настройка строки подключения через config.json, создание пула из 5 соединений, валидация SELECT 1.

Вариант 2: Медицинская регистратура

Защита персональных данных: сертификаты SSL/TLS (sslmode=verify-full), обработка таймаута при недоступности сервера.

Вариант 3: Банковский терминал

Транзакционный узел: промышленный пул (HikariCP/pgBouncer), перехват connection refused, логирование в syslog.

Вариант 4: Образовательный портал

Веб-сервис колледжа: переменные окружения .env, замер задержки первого запроса (latency benchmark).

12. Антипаттерны разработки: чего делать нельзя

Хардкод паролей

Указание паролей в исходном коде. Пароль попадет в публичный коммит Git и база данных будет скомпрометирована ботами за секунды.

Connection Leak

Открытие сокета без блока finally или контекстного менеджера. Соединение теряется, и пул мгновенно парализуется.

Бесконечный таймаут

Установка timeout=0. При обрыве соединения приложение бесконечно зависает, подвешивая весь интерфейс.

13. Критерии оценивания практической работы

Критериальная 4-балльная шкала занятия:

  • Параметры Connection String (2 балла): корректное конфигурирование хоста, порта, БД, логина, параметров SSL и таймаута в файле без хардкода.
  • Архитектура пула соединений (1 балл): расчет и обоснование размеров пула (min/max connections, idle_timeout), защита от утечек сокетов.
  • Обработка ошибок и самопроверка (1 балл): реализация Health Check (SELECT 1) и логирование сетевых сбоев (connection refused, timeout).

Работа выполняется в интерактивном листе zanyatie.html с сохранением в LocalStorage.

14. Инженерный фольклор: Little Bobby Tables

Веб-комикс xkcd #327: Exploits of a Mom (Little Bobby Tables)
Тематический интернет-мем: «Exploits of a Mom» (культовый веб-комикс про «Little Bobby Tables», xkcd №327).
Автор: Randall Munroe. Первоисточник: xkcd.com/327. Лицензия: Creative Commons Attribution-NonCommercial 2.5 Generic (CC BY-NC 2.5).
Связь с темой занятия: Мама назвала сына Robert'); DROP TABLE Students; --. Когда школьная информационная система попыталась вставить имя ученика без параметров через конкатенацию, база данных стерла всех студентов года. Занятие 27 учит будущих разработчиков: надежный исполняемый модуль начинается с безопасной строки подключения, контролируемого пула соединений и строгой изоляции запросов!
1 / 15