PostgreSQL

Настройка WAL и checkpoint

Discord: 100K сообщений/секунду в PostgreSQL. Дефолтный synchronous_commit = on. TPS = 12K. После synchronous_commit = off для счётчиков присутствия и read receipts - TPS = 190K. Те же 5 строк конфига. Понимание WAL-параметров - разница между арендой 10 серверов и использованием 1.

  • **Discord** - synchronous_commit=off для non-critical data (presence, read receipts): 15x рост TPS без изменения кода приложения
  • **GitLab** - max_wal_size = 4 ГБ, checkpoint_timeout = 15 мин: снижение I/O spikes во время checkpoint на 70%, стабильный latency P99
  • **AWS RDS** - автоматически настраивает wal_buffers, max_wal_size и checkpoint параметры при изменении класса инстанса

wal_buffers: буфер записи WAL

**wal_buffers** - область shared memory для буферизации WAL-записей перед сбросом на диск. WAL writer сбрасывает буфер на диск при COMMIT транзакции или при заполнении буфера. Дефолт: -1 (автовычисление = 1/32 от shared_buffers, максимум 64 МБ). При высоком OLTP обычно достаточно.

**synchronous_commit** влияет на wal_buffers: при synchronous_commit=off PostgreSQL не ждёт fsync WAL при COMMIT. Транзакция подтверждается клиенту до сброса WAL на диск. Быстрее, но при сбое возможна потеря последних ~200 мс транзакций. Подходит для некритичных данных (счётчики, статистика).

synchronous_commit = off. PostgreSQL подтвердил COMMIT клиенту. Сервер упал через 50 мс. Что произошло с транзакцией?

Checkpoint: настройка сброса dirty pages

**Checkpoint** - периодический сброс всех dirty pages из shared_buffers на диск. После checkpoint WAL до этой точки можно удалить (не нужен для recovery). Проблема: наивный checkpoint сбрасывает всё сразу - I/O spike. checkpoint_completion_target растягивает запись на 90% checkpoint_timeout.

0

1

Войти

**Большой checkpoint_timeout** означает дольше восстановление после сбоя - PostgreSQL должен воспроизвести WAL с последнего checkpoint. Компромисс: checkpoint_timeout = 15 мин даёт хороший баланс между I/O и RTO. Для fast_recovery: checkpoint_timeout = 5 мин.

checkpoints_req >> checkpoints_timed (принудительные checkpoints чаще плановых). Что нужно изменить?

max_wal_size: буфер WAL

**max_wal_size** - максимальный объём WAL между checkpoint'ами. Если WAL вырос до max_wal_size - принудительный checkpoint вне расписания. Больше max_wal_size = реже принудительные checkpoints и меньше I/O spikes, но больше WAL на диске и дольше recovery при сбое.

**Bulk load паттерн:** при загрузке больших объёмов данных (миллиарды строк) временно увеличивать max_wal_size до 32-64 ГБ, checkpoint_timeout до 1 часа. После загрузки CHECKPOINT; вернуть обычные значения. Это снижает I/O overhead при ETL в 3-5 раз.

max_wal_size = 16 ГБ. checkpoint_timeout = 15 мин. PostgreSQL падает. Сколько WAL максимально нужно воспроизвести при recovery?

commit_delay: групповой COMMIT

**commit_delay** - задержка перед fsync WAL при COMMIT. Если за это время другие транзакции дойдут до COMMIT - PostgreSQL делает один fsync для группы. Group commit снижает нагрузку на I/O при высоком OLTP. Дефолт: 0 (без задержки). Работает только если active_backends > commit_siblings (дефолт: 5).

**commit_delay полезен на HDD, менее критичен на NVMe.** На NVMe SSD fsync занимает ~50-100 мкс, и group commit не даёт значительного выигрыша. На HDD fsync = 10-30 мс, group commit сокращает число операций и даёт 2-5x рост TPS при OLTP нагрузке.

commit_delay = 100, commit_siblings = 5. В системе только 3 активных backend. Что происходит при COMMIT?

synchronous_commit: компромисс latency vs durability

**synchronous_commit** управляет гарантиями durability при COMMIT. Параметр можно менять на уровне сессии или транзакции - разные части приложения могут иметь разные требования. Критичные финансовые транзакции - synchronous_commit=on. Счётчики просмотров - synchronous_commit=off.

**synchronous_commit=off не ломает консистентность БД.** Транзакции всё равно атомарны: либо полностью применяются, либо нет. Риск только в потере последних нескольких транзакций при сбое сервера. Внутри сервера гонки нет.

synchronous_commit = off может привести к частичному применению транзакции (часть строк есть, часть нет)

synchronous_commit = off не нарушает atomicity и consistency. Транзакция либо полностью применена, либо нет. Риск только в потере последних транзакций при сбое питания

WAL гарантирует atomicity: транзакция записывается в WAL полностью до COMMIT или откатывается. synchronous_commit управляет только тем, ждать ли fsync WAL. При сбое без fsync транзакция просто не найдёт себя в WAL и откатится - но частичного применения не будет

Приложение: 70% транзакций - клики/аналитика, 30% - финансовые операции. Как настроить synchronous_commit?

Итоги

  • **wal_buffers = 64 МБ** - дефолт (автовычисление) обычно достаточно. Мониторинг: buffers_backend в pg_stat_bgwriter
  • **checkpoint_completion_target = 0.9** + увеличение checkpoint_timeout до 15-30 мин = меньше I/O spikes. checkpoints_req > checkpoints_timed = увеличить max_wal_size
  • **max_wal_size = 4-16 ГБ** для production. Больше = реже принудительные checkpoints, но дольше recovery
  • **commit_delay** полезен на HDD при высоком OLTP. На NVMe - negligible benefit
  • **synchronous_commit** можно менять на уровне транзакции: off для аналитики, on для финансовых операций

Связанные темы

WAL-параметры связаны с несколькими механизмами PostgreSQL:

  • WAL: Write-Ahead Log — Базовые концепции WAL - сегменты, LSN, checkpoint - необходимы для понимания этих параметров
  • Streaming Replication — synchronous_commit = on/remote_write/remote_apply работает только при наличии synchronous standby
  • Настройка памяти — shared_buffers и wal_buffers оба в shared memory - размер одного влияет на доступность для другого

Вопросы для размышления

  • Приложение выполняет 50K INSERT/сек. synchronous_commit = on. Диск - 7200 RPM HDD (150 IOPS fsync). Посчитать максимальный TPS при таком constraint. Что даст synchronous_commit = off?
  • max_wal_size = 2 ГБ. В 14:00 начинается bulk load 10M строк за 5 минут. pg_stat_bgwriter показывает checkpoints_req резко растёт. Как настроить чтобы избежать excess checkpoints во время bulk load?
  • checkpoint_timeout = 5 мин, max_wal_size = 1 ГБ. Сервер упал. Каков максимальный RTO (время на recovery)? Как увеличение checkpoint_timeout до 30 мин меняет RTO?

Связанные уроки

  • db-03-acid
Настройка WAL и checkpoint