По умолчанию checkpoint_timeout — 5 минут, а max_wal_size — 1 GB: PostgreSQL запускает checkpoint каждые 5 минут или раньше, если накопилось больше гигабайта WAL. На нагруженной базе второе условие срабатывает первым, и checkpoint случается заметно чаще, чем каждые 5 минут — сервер постоянно сбрасывает грязные буферы на диск, и это видно по скачкам latency в самые непредсказуемые моменты.
Проверить, насколько часто это происходит, можно по логу: если включить checkpoint_warning (по умолчанию 30 секунд) и checkpoint’ы начинают случаться чаще этого порога, PostgreSQL сам пишет в лог предупреждение, что max_wal_size стоит увеличить. Частое появление такого сообщения — не повод для паники с первого раза, но если оно встречается регулярно, значит параметры checkpoint пора пересматривать.
Лечится это увеличением max_wal_size (в современных версиях разумный старт — 4-16 GB в зависимости от объёма записи) и, отдельно, настройкой checkpoint_completion_target. Этот параметр — доля от checkpoint_timeout, за которую должен завершиться сам процесс записи грязных буферов: PostgreSQL размазывает I/O так, чтобы checkpoint закончился примерно к этой отметке, а не писал всё одним рывком в начале интервала. Дефолт — 0.9, то есть запись растягивается почти на весь интервал между checkpoint’ами, и именно это сглаживает всплески на графике дисковой нагрузки:
# postgresql.conf
checkpoint_timeout = 15min
max_wal_size = 8GB
checkpoint_completion_target = 0.9
Поднимать checkpoint_completion_target выше 0.9 официальная документация не рекомендует — checkpoint включает не только запись буферов, и при значении, близком к 1.0, велик шанс, что он не успеет закончиться вовремя, а это снова скачки I/O, только в худшем, непредсказуемом месте. Обратная сторона медали у большого checkpoint_timeout и max_wal_size — более долгий recovery после падения сервера, потому что WAL-сегментов для replay накапливается больше. Тюнинг checkpoint — это всегда компромисс между гладким I/O в рабочем режиме и временем восстановления после сбоя, их нужно держать в голове одновременно, а не оптимизировать только одну сторону.
#postgresql #dba
