Контрольная точка: почему база ровно и предсказуемо замирает раз в несколько минут
Если посмотреть на график нагрузки диска под базой достаточно долго, почти всегда видна одна и та же картина: ровный фон — и на нём периодические короткие всплески, похожие один на другой, как под линейку. Многие администраторы принимают это за проблему — «что-то раз в несколько минут грузит диск» — и начинают искать несуществующий баг. На самом деле это контрольная точка, checkpoint, и она делает именно то, для чего создана. В этой статье разберём, что накапливается в памяти базы между такими всплесками, зачем нужен сброс на диск разом, а не по одной странице, и как читать этот паттерн, чтобы не тратить время на поиск проблемы там, где её нет.
Содержание
Страница данных живёт в памяти дольше, чем кажется
Когда транзакция меняет строку в таблице, база не бежит тут же переписывать файл данных на диске. Строка живёт внутри страницы (обычно 8 КБ в PostgreSQL, похожий принцип у MySQL/InnoDB с его страницами по 16 КБ) — и именно страница, а не отдельная строка, является единицей обмена с диском. Страница, с которой сейчас работает движок, загружена в буферный кэш — область разделяемой памяти (shared_buffers в PostgreSQL, buffer pool в InnoDB). Если её содержимое изменилось, а на диск это изменение ещё не записано, страницу называют «грязной» (dirty page).
Дальше с этой грязной страницей может произойти что угодно: её могут читать и менять ещё десяток раз в течение следующих секунд или минут. Если бы база физически переписывала страницу файла данных на каждое изменение, диск бы захлёбывался — одна и та же страница пишется по кругу ради одной строки, которая меняется чаще соседних. Поэтому грязная страница остаётся в памяти и накапливается там вместе с другими такими же, пока не наступит момент её сброса.
Гарантию сохранности при этом даёт не страница данных, а журнал упреждающей записи — WAL. Именно в него база записывает факт изменения синхронно, при коммите, и именно на этом держится обещание «данные не потеряются при сбое». Если вы ещё не разбирались, как устроена эта запись «дважды» — сначала в журнал, потом когда-нибудь в файл данных — есть отдельный разбор: что такое WAL и зачем писать дважды. Здесь важно другое следствие: раз страница данных откладывается, а журнал растёт с каждой транзакцией, кто-то должен периодически разгребать это накопление. Этим и занимается checkpoint.
Что делает контрольная точка и зачем она нужна
Контрольная точка — это операция, которая берёт все грязные страницы, накопившиеся в буферном кэше с прошлого checkpoint, и сбрасывает их на диск. Не одну страницу, а разом весь набор, который успел накопиться. После того как все они физически записаны и подтверждены (fsync), база отмечает в журнале: «до этой позиции WAL всё, что было изменено, уже гарантированно лежит в файлах данных».
Смысл в двух вещах.
Во-первых, ограничение размера журнала. WAL растёт, пока изменения не «осели» в файлах данных. До момента checkpoint журнал нельзя переиспользовать или удалить старые сегменты — база должна быть готова проиграть его целиком с последней контрольной точки в случае сбоя. Если checkpoint не наступает подолгу, журнал растёт неограниченно, и это отдельная больная тема — ей посвящён разбор почему растёт размер WAL. Checkpoint — это именно тот механизм, который позволяет журналу не расти бесконечно: как только грязные страницы сброшены, старые сегменты WAL становятся не нужны для восстановления и могут быть переработаны или удалены.
Во-вторых — и это часто недооценивают — контрольная точка ограничивает время восстановления после сбоя. Если сервер упал (отключилось питание, убило процесс, перезагрузили хост), при старте база не читает файлы данных «как есть» — она проигрывает WAL начиная с последней контрольной точки, применяя все изменения, которые не успели попасть в файлы данных до сбоя. Чем дальше в прошлом последняя контрольная точка, тем больше журнала нужно проиграть и тем дольше база будет недоступна после рестарта. Без checkpoint восстановление после аварии на нагруженной базе могло бы занимать часы вместо секунд-минут. Контрольная точка — это буквально контракт «мы не уйдём в проигрывание журнала дальше, чем на N минут (или N мегабайт) назад».
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверПочему это не просто фоновая уборка, а разовая операция
Важно понимать разницу между checkpoint и обычной фоновой записью. У базы есть отдельный процесс (background writer в PostgreSQL), который и без всякого checkpoint потихоньку выталкивает часть грязных страниц на диск в спокойном режиме — просто чтобы буферный кэш не был забит целиком грязными страницами и чтобы серверу было проще найти свободный буфер под новые данные. Эта фоновая запись размазана по времени и обычно не создаёт заметного всплеска.
Checkpoint — другое дело. Это не поддержание порядка, а закрытие эпохи: «зафиксировать состояние на текущий момент». Он должен гарантированно сбросить на диск все страницы, изменённые с прошлой контрольной точки, — не часть из них, а все, потому что именно на этом основана гарантия «WAL до этой позиции больше не нужен для восстановления». Частично сброшенный набор такую гарантию дать не может. Поэтому checkpoint — это операция с чётким началом и концом, а не постоянный процесс, и именно поэтому она видна на графиках как всплеск, а не как ровный фон.
Почему всплеск дисковой нагрузки заметен, а не бесплатен
Даже если между двумя контрольными точками прошло немного времени, за это время под нагрузкой успевает накопиться заметное количество грязных страниц — во многих СУБД одна и та же горячая страница (например, индекс на первичном ключе активно растущей таблицы) переписывается в памяти десятки раз, но сбросить её на диск нужно один раз при checkpoint. Когда наступает момент сброса, движку нужно за ограниченное время записать все эти страницы физически на диск и дождаться подтверждения (fsync), что они там действительно лежат, а не просто уехали в кэш операционной системы.
Отсюда и всплеск: на короткое время параллельно с обычными операциями чтения-записи диск получает пачку дополнительных операций записи. На вращающихся дисках это ощущается сильнее всего — головкам приходится метаться между записью грязных страниц и обслуживанием обычных запросов. На SSD и NVMe эффект мягче, но не исчезает совсем: очередь запросов на запись растёт, и у операций, которые обычно укладываются в доли миллисекунды, задержка на секунды-другие подрастает — иногда заметно для клиента, который ждёт ответа именно в этот момент.
Важно: это не деградация и не признак того, что диск не справляется с базовой нагрузкой. Это разовая операция, которая должна выполниться, чтобы система оставалась в безопасном состоянии — с ограниченным журналом и предсказуемым временем восстановления. Плата за отложенную запись — это временный всплеск при её реализации; так это устроено, альтернативы без последствий здесь нет.
Как отличить checkpoint на графике от настоящей проблемы
Признаки, что вы видите именно checkpoint, а не деградацию:
- Периодичность. Всплески повторяются с примерно одинаковым интервалом — это интервал по времени (
checkpoint_timeoutв PostgreSQL, по умолчанию 5 минут) либо по объёму накопленного WAL (max_wal_size, по умолчанию 1 ГБ), смотря что наступит раньше. - Форма всплеска. Заметный рост записи на диск (
iostat -x 1, столбцыw/s,wMB/s,%util), который длится секунды или первые минуты, а затем возвращается к фоновому уровню. - Корреляция с процессом. В PostgreSQL всплеск совпадает по времени с активностью процесса
checkpointer— это видно вpg_stat_activityи в логе, если включеноlog_checkpoints. Запись в лог покажет длительность checkpoint и объём записанных буферов. - Отсутствие роста задержек «навсегда». После всплеска задержки возвращаются к норме. Если рост задержек не проходит, а только накапливается — это уже не checkpoint, а, например, диск, который не успевает за постоянной нагрузкой, или медленно растущий bloat в таблицах.
Проверить гипотезу просто: включите логирование контрольных точек и посмотрите, совпадают ли моменты замедления по мониторингу с записями в логе.
-- postgresql.conf
log_checkpoints = on
После перезагрузки конфигурации (SELECT pg_reload_conf(); или systemctl reload postgresql, перезапуск не нужен) в логе появятся строки вида:
LOG: checkpoint starting: time
LOG: checkpoint complete: wrote 4213 buffers (25.7%); 0 WAL file(s) added,
0 removed, 2 recycled; write=42.881 s, sync=1.204 s, total=44.912 s
Если время всплеска на графике диска совпадает со временем между checkpoint starting и checkpoint complete — вопрос закрыт, это плановая операция, а не инцидент.
Как сгладить всплеск, если он мешает
Полностью убрать checkpoint нельзя — без него журнал будет расти бесконечно, а время восстановления станет непредсказуемым. Но всплеск можно растянуть во времени и сделать менее концентрированным.
- Растянуть запись самого checkpoint. Параметр
checkpoint_completion_targetв PostgreSQL задаёт долю интервала между checkpoint, за которую нужно успеть записать все грязные страницы. Значение ближе к 0.9 означает «пиши постепенно почти весь интервал», а не «выгрузи всё в первые секунды». Это не уменьшает объём записи, но размазывает её тоньше — пиковая нагрузка на диск в моменте становится ниже. - Реже, но крупнее — или чаще, но мельче. Увеличение
checkpoint_timeoutиmax_wal_sizeснижает частоту checkpoint, но каждый отдельный checkpoint становится тяжелее (накопится больше грязных страниц) и время восстановления после сбоя увеличится. Уменьшение — обратный компромисс: чаще, но каждый раз легче. Здесь нет универсально правильного значения — это баланс между заметностью всплесков и допустимым временем восстановления после аварии. - Развести журнал и данные физически. Если WAL и файлы данных пишутся на один и тот же диск, всплеск checkpoint конкурирует за пропускную способность с синхронной записью WAL при каждом коммите — а она чувствительна к задержкам сильнее всего. Вынос WAL на отдельный диск снижает шанс, что checkpoint «заденет» латентность коммитов.
- Проверить и настроить планировщик ввода-вывода на уровне ОС. То, как ядро распределяет приоритеты между потоками записи, тоже влияет на то, насколько сильно checkpoint «пробивает» задержки обычных запросов — подробнее в разборе настройки планировщика ввода-вывода.
- Использовать более быстрый диск, если фон и так близок к пределу. Если базовая нагрузка на диск уже держит утилизацию на 60-70%, любой дополнительный всплеск будет заметен сильнее, чем на диске с запасом. Здесь помогает не столько тонкая настройка checkpoint, сколько апгрейд хранилища — NVMe вместо сетевого блочного тома, например.
Ни одна из этих настроек не убирает всплеск полностью — цель в том, чтобы он укладывался в допустимую задержку для ваших клиентов, а не был незаметен вовсе. Если задержки в моменты checkpoint критичны для приложения (например, это платёжный шлюз с жёстким SLA по времени ответа), обычно комбинируют сразу несколько мер: растянутый checkpoint_completion_target, отдельный диск под WAL и запас по IOPS на хранилище.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Checkpoint — это то же самое, что резервная копия?
Нет. Контрольная точка про внутреннюю согласованность буферного кэша и файлов данных, а не про резервное копирование. Некоторые инструменты бэкапа (например, pg_basebackup в специальном режиме) действительно запускают checkpoint перед стартом копирования, чтобы зафиксировать согласованную точку, но это частный случай использования механизма, а не его основное назначение.
Можно ли отключить checkpoint полностью?
Нет, штатной возможности отключить его нет — и не должно быть: без периодического сброса грязных страниц журнал вырастет до предела диска, а восстановление после любого сбоя займёт непредсказуемо долгое время. Можно только менять частоту и то, насколько растянуто во времени идёт запись.
Почему checkpoint иногда почти не заметен, а иногда даёт ощутимый провал?
Заметность зависит от того, сколько страниц успело стать грязными с прошлой контрольной точки, и от того, насколько диск уже загружен фоновой нагрузкой в этот момент. На тихой базе ночью checkpoint может пройти почти незаметно; на пиковой нагрузке в рабочие часы тот же checkpoint по объёму данных даст более заметный провал, потому что накладывается на и так загруженный диск.
Что делать, если всплески происходят слишком часто?
Проверьте, что именно триггерит checkpoint — время (checkpoint_timeout) или объём WAL (max_wal_size). Если каждый раз в логе видно, что checkpoint наступает раньше таймаута из-за достижения max_wal_size, увеличение этого параметра снизит частоту ценой более тяжёлых отдельных сбросов.
Влияет ли это на MySQL/InnoDB так же, как на PostgreSQL?
Идея та же — InnoDB тоже накапливает грязные страницы в buffer pool и периодически сбрасывает их (fuzzy checkpoint), — но детали параметров и алгоритмов свои: innodb_io_capacity, adaptive flushing и так далее. Общий принцип «страницы копятся в памяти, потом сбрасываются пачкой ради ограничения журнала и времени восстановления» одинаков для большинства СУБД с журналированием.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →