Каждые 5 минут транзакции в высоконагруженной PostgreSQL базе внезапно замирают на 3 - 7 секунд. Мониторинг показывает скачок IO Wait до 100%, а p99 latency улетает в космос. Причина кроется в дефолтных настройках механизма checkpoint, который превращает фоновый сброс грязных страниц в дисковый шторм.
По умолчанию параметр max_wal_size установлен на скромные 1 ГБ, а checkpoint_completion_target равен 0.5. Это значит, что при накоплении 1 ГБ WAL ядро и PostgreSQL обязаны сбросить все модифицированные буферы из Shared Buffers на накопитель ровно за половину времени от предыдущего чекпоинта. На SSD NVMe класса Enterprise с IOPS 500k это работает терпимо, но в облачных инстансах с ограниченным IOPS (например, AWS EBS gp3 с честными 3000 IOPS) процесс превращается в катастрофу.
База начинает агрессивно выплевывать гигабайты грязных страниц (dirty pages) на накопитель короткими очередями. Происходит переполнение системного page cache и дискового контроллера, очередь запросов (io_depth) забивается, а бэкграунд-процесс checkpointer упирается в ограничение по пропускной способности. В этот момент ядро Linux через демон pdflush или writeback начинает принудительно тормозить все процессы, записывающие данные, чтобы разгрести переполненные буферы. Так рождаются те самые микрофризы, во время которых замирает весь пул соединений.
Чтобы избавиться от этих фризов, нужно изменить три ключевых параметра в postgresql.conf:
1. Увеличиваем объем WAL между чекпоинтами до реальных размеров рабочей нагрузки. Для систем с высокой интенсивностью записи ставим max_wal_size = '64GB' (или хотя бы '16GB'), а min_wal_size = '4GB'. Это позволит чекпоинтам срабатывать реже и сгладит пики записи.
2. Размазываем процесс сброса во времени. Устанавливаем checkpoint_completion_target = '0.9'. Теперь PostgreSQL будет распределять запись грязных страниц на 90% интервала между чекпоинтами, снижая мгновенную интенсивность IOPS почти в два раза.
3. Ограничиваем скорость фоновой записи, чтобы она не выжигала весь доступный диск. Параметр checkpoint_warning = '30s' поможет вовремя отловить в логах ситуации, когда чекпоинты отрабатывают быстрее заданного интервала (сигнал к тому, что max_wal_size снова пора увеличивать).
На уровне Linux Kernel также важно зафиксировать параметры виртуальной памяти в sysctl.conf:
vm.dirty_background_ratio = 5
vm.dirty_ratio = 10
Это заставит ядро начинать фоновый сброс грязных страниц раньше (при заполнении 5% RAM), не дожидаясь жесткого блокирующего сброса при достижении 10%. В результате дисковая подсистема работает в линейном режиме без микрофризов и деградации p99, а TCO инфраструктуры снижается за счет отказа от избыточного provisioning IOPS на дисках.
- Инфраструктурный аудит и калькулятор TCO кластера: @Personnel_run_bot (/tma)
- База знаний и разборы: ftops.space
- ftops.space | Run-As-Daemon Infrastructure
| # | Наименование новости | Тональность | Информативность | Дата публикации |
|---|---|---|---|---|
| 1 | Каждые 5 минут транзакции в PostgreSQL замирают на 3 - ... | 0 | 9.34 | 22-09-2026 |
| 2 | PostgreSQL Autovacuum Internals and Benchmark | 0 | 4.74 | 01-07-2026 |
| 3 | Fixing Common PostgreSQL Performance Bottlenecks | 0 | 5.59 | 04-12-2020 |
| 4 | The insert benchmark on a small server : Postgres 12.22 through 18.3 | 0 | 11.9 | 29-03-2026 |
| 5 | [Перевод] Статистика PostgreSQL: почему запросы выполняются медленно | 0 | 7.74 | 19-08-2026 |
| 6 | Асинхронный I/O в PostgreSQL или история выходного дня | 0 | 9.78 | 17-08-2026 |
| 7 | The Real Operational Cost of Vacuuming in PostgreSQL | 0 | 10.7 | 08-02-2026 |
| 8 | PostgreSQL для бэкендера: 10 фич, которыми мало пользуются, а зря | 5 | 7 | 30-06-2026 |
| 9 | Linux наконец-то не тормозит или пятничный релакс | 0 | 13.18 | 07-08-2026 |