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 миграций выглядит так (о нём же есть в главе про техники, но с точки зрения сетевых паттернов):
- Expand — добавляем новое поле/таблицу, но старый код всё ещё пишет и читает по-старому
- Migrate — приложение начинает писать одновременно в старое и новое место, фоновый процесс переносит исторические данные
- 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. Это про подход к данным как к самому ценному активу компании. Бекапы, репликация, миграции без даунтайма, ёмкость дисков и пул соединений — это те кирпичи, из которых строится надёжная работа с данными.
И помните: если ваш план восстановления БД не тестировался ни разу — у вас нет плана восстановления.