Database Reliability Engineering (DBRE)

Представьте, что ваше приложение — это ресторан. Фронтенд — это зал с официантами, бэкенд — это кухня, а база данных — это холодильник и кладовая. Можно поменять меню (код), можно переставить столы (интерфейс), но если холодильник сломался, а кладовая сгорела — ресторан закрыт. Всё, что вы наготовили, пропало.

База данных это всегда состояние (state). А состояние — это ответственность. В отличие от stateless-сервисов (которые можно просто перезапустить), база данных хранит то, без чего бизнес не работает: заказы, деньги, профили пользователей. Потерять БД значит потерять бизнес.

Почему БД нужен отдельный SRE

Stateless-микросервис как одноразовый стаканчик: сломался — выкинул, запустил новый. База данных это дубовый бочонок с вискариком, который выдерживали тридцать три года. Его нельзя просто "перезапустить". Ему нужны:

  • Резервные копии — на случай, если бочонок треснул
  • Репликация — чтобы у вас был не один бочонок, а два, и если один пропал, второй подстрахует
  • План восстановления — чёткая инструкция, что делать, когда всё пошло не так.

Без этого база данных — ваше единственное узкое место, точка отказа (single point of failure).

Резервное копирование: 3-2-1 и друзья

Резервное копирование (backup) — это не про "сохранить файлик". Это про ответ на вопрос: "Если всё умрёт прямо сейчас, сколько данных мы потеряем и сколько времени будем подниматься?"

Ответы на этот вопрос — это RTO и RPO, про которые уже написано в главе про RTO/RPO. Здесь напомню главное правило работы с бэкапами:

Правило 3-2-1: у вас должно быть 3 копии данных, на 2 разных носителях, и 1 из них за пределами текущей площадки (off-site, другой дата-центр, S3 в другом регионе).

На практике это выглядит так:

  • Основная БД работает на сервере в ДЦ-1
  • Реплика (синхронная или async) — в ДЦ-1 или ДЦ-2
  • Ежедневный backup — в S3 в том же регионе
  • Еженедельный backup — в S3 в другом регионе или в холодном (cold) хранилище
# Пример: pg_dump с загрузкой в S3 (через утилиту)
pg_dump -Fc mydb > /tmp/mydb.dump
aws s3 cp /tmp/mydb.dump s3://my-backups/db/daily/mydb-$(date +%Y-%m-%d).dump

Важно

Бэкап, который никто никогда не проверял восстановлением — это не бэкап, а мусор. Раз в месяц делайте restore на тестовую БД и проверяйте целостность.

Миграции без даунтайма

Самая страшная операция с БД для инженера — изменить схему на живой базе. ALTER TABLE на таблице с миллионом строк может заблокировать её на часы. А если это таблица, в которую пишут заказы — сервис встал.

Паттерн expand-migrate-contract для zero-downtime миграций выглядит так (о нём же есть в главе про техники, но с точки зрения сетевых паттернов):

  1. Expand — добавляем новое поле/таблицу, но старый код всё ещё пишет и читает по-старому
  2. Migrate — приложение начинает писать одновременно в старое и новое место, фоновый процесс переносит исторические данные
  3. Contract — когда убедились, что всё работает, убираем старую структуру

Пример для PostgreSQL без даунтайма:

-- 1. Expand: добавляем колонку (NOT NULL с дефолтом, чтобы не блокировать)
ALTER TABLE orders ADD COLUMN customer_phone VARCHAR(20) DEFAULT '';

-- 2. Приложение начинает писать в новую колонку
-- Фоновый процесс: миграция старых данных
UPDATE orders SET customer_phone = phone WHERE customer_phone = '';

-- 3. Contract: удаляем старую колонку
ALTER TABLE orders DROP COLUMN phone;

На больших таблицах используйте инструменты вроде pt-online-schema-change (Percona) или gh-ost (GitHub). Они делают миграцию через временную таблицу и триггеры, не блокируя основную.

Репликация и отказоустойчивость

У вас есть одна БД. Она упала. Всё. Приложение не работает. Решение? Репликация — завести копию данных на другом сервере.

Синхронная репликация — мастер ждёт подтверждения от реплики, что данные записаны. Данные не теряются (RPO = 0), но время ответа (latency) растёт. Подходит для денежных транзакций.

Асинхронная репликация — мастер отправил данные и забыл. Быстро, но если мастер упал до того, как реплика получила данные — они потеряны (RPO > 0). Подходит для аналитики, логов, некритичных данных.

Кворум (quorum) — когда у вас больше двух реплик, и коммит считается успешным, если подтверждение пришло от большинства (N/2 + 1). Это про согласованность (consistency). Используется в распределённых БД (Cassandra, ScyllaDB, CockroachDB).

-- Пример: настройка синхронной репликации в PostgreSQL
ALTER SYSTEM SET synchronous_standby_names = 'FIRST 1 (replica1)';

Failover — переключение на реплику при падении мастера. Бывает ручной (человек смотрит, проверяет, переключает) и автоматический (Patroni, Stolon, Orchestrator). Автоматический — быстрее, но есть риск split-brain (обе БД думают, что они главные, и данные расходятся).

Capacity Planning для БД

У БД своя специфика ёмкости — она не про CPU, а про I/O и память. Подробно про общий capacity planning — в соответствующей главе. Здесь — базы данных:

  • Хранилище (storage) — самый очевидный ресурс. Следите за скоростью заполнения (fill rate). Правило: предупреждение за 30 дней до заполнения, алерт за 7 дней.
  • IOPS (Input/Output Operations Per Second) — сколько операций чтения/записи может выполнять диск. Если IOPS закончились, любая операция в БД становится медленной. Типичная картина: запрос на 10 строк выполняется минуту.
  • Connection pool — каждый запрос к БД занимает соединение. Если их слишком много — БД задыхается. Баланс: не голодать (клиенты ждут соединения) и не топить (1000 одновременных запросов ложат БД).
# PostgreSQL: мониторинг размера БД
SELECT pg_size_pretty(pg_database_size('mydb'));

# MySQL: мониторинг IOPS
SHOW GLOBAL STATUS LIKE 'Innodb_data_reads';
SHOW GLOBAL STATUS LIKE 'Innodb_data_writes';

Типовые сценарии отказов

Thundering Herd (бегущее стадо)

Представьте: упал сервер с БД. Все микросервисы, которые к нему ходили, получили ошибку. По таймауту — retry. И вот 100 сервисов одновременно стучатся в БД, которая только что поднялась, и она падает снова. Классика.

Решение: используйте jitter (случайная задержка перед retry) и circuit breaker (см. главу про техники), чтобы не долбить упавшую БД всем миром.

Connection Exhaustion (истощение соединений)

У БД есть лимит соединений. Каждое соединение — ресурс. Если приложение открывает соединения и не закрывает (connection leak), лимит исчерпан, и новые запросы падают с ошибкой "too many connections".

Решение: connection pooler вроде PgBouncer (для PostgreSQL) или ProxySQL (для MySQL). Он держит пул соединений с БД и распределяет запросы приложения через этот пул, не открывая новое соединение на каждый запрос.

# Пример конфигурации PgBouncer
[databases]
mydb = host=127.0.0.1 port=5432 dbname=mydb

[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 6432
pool_mode = transaction
max_client_conn = 200
default_pool_size = 25

Lock Contention (гонка за блокировками)

Два запроса пытаются обновить одну строку одновременно. Один блокирует строку, второй ждёт. Если таких запросов много — БД стоит, все ждут блокировок.

Решение: короткие транзакции, правильные индексы, мониторинг pg_locks / innodb_lock_waits.

-- PostgreSQL: кто кого заблокировал?
SELECT blocked_locks.pid     AS blocked_pid,
       blocking_locks.pid    AS blocking_pid
FROM pg_catalog.pg_locks blocked_locks
JOIN pg_catalog.pg_locks blocking_locks ON blocking_locks.locktype = blocked_locks.locktype;

Connection Pooling — must have

Любая production БД должна работать через connection pooler. Без него вы гарантированно столкнётесь с connection exhaustion при первом же всплеске трафика.

Популярные решения:

  • PgBouncer — лёгкий, быстрый, работает в режиме transaction pooling (соединение живёт только на время транзакции)
  • ProxySQL — для MySQL, с поддержкой правил маршрутизации запросов (можно отправить SELECT на реплику, INSERT на мастер)
  • internal pool фреймворка — HikariCP (Java), SQLAlchemy pool (Python), но они не спасают от скачков так же эффективно, как внешний pooler

Резюме

DBRE это не про настройку PostgreSQL или MySQL. Это про подход к данным как к самому ценному активу компании. Бекапы, репликация, миграции без даунтайма, ёмкость дисков и пул соединений — это те кирпичи, из которых строится надёжная работа с данными.

И помните: если ваш план восстановления БД не тестировался ни разу — у вас нет плана восстановления.