1. Введение
Возникла ситуация, в которой загрузка ЦП оставалась на уровне 100% в работающей базе данных PostgreSQL. При выяснении причины в центре проблемы оказался autovacuum. Ранее я воспринимал его лишь как фоновый процесс, который редко требует внимания, но благодаря этому инциденту смог хотя бы частично понять, как работает autovacuum и почему он может даже вызывать конкуренцию за блокировки. Сначала я обобщу возникшую проблему, а затем систематизирую полученные в ходе анализа и устранения неполадки сведения об autovacuum и блокировках.
2. Что произошло
В периоды высокой нагрузки фоновые задания очистки выполнялись с задержкой, а xid_age определённой таблицы постепенно увеличивался. В конце концов он превысил пороговое значение, и PostgreSQL принудительно запустил autovacuum самостоятельно. По совпадению, этот процесс в тот момент обрабатывал таблицу, в данных которой возникли проблемы, из-за чего он долгое время не мог завершиться и непрерывно потреблял ресурсы ЦП.
Сначала я предположил, что причиной был обычный всплеск нагрузки или неэффективные запросы, однако после расследования выяснилось, что корень проблемы был гораздо более фундаментальным. Проверив выполняющиеся запросы, я обнаружил, что процесс, потреблявший ресурсы ЦП, был не обычным запросом приложения, а autovacuum в виде 'autovacuum: VACUUM ... (to prevent wraparound)'. Я узнал, что PostgreSQL принудительно запустил его для предотвращения переполнения идентификаторов транзакций и что остановить его обычными способами, например завершением процесса или отключением autovacuum, невозможно.
Изменения состояния системы в ходе этого процесса можно обобщить следующим образом.
|
Этап |
Состояние |
|---|---|
|
Нормальная работа |
Нормальная работа, xid_age находится в пределах нормы |
|
Этап накопления проблемы |
Задержка фоновых заданий очистки → xid_age приближается к пороговому значению |
|
Момент возникновения инцидента |
Принудительный запуск autovacuum для предотвращения переполнения идентификаторов транзакций; процесс завис при обработке проблемной таблицы → загрузка ЦП остаётся на уровне 100% |
|
После устранения проблемы |
Пороговое значение восстановлено, загрузка ЦП и работа сервиса вернулись к норме |
Реагирование проходило в три основных этапа. Сначала, подтвердив, что процессом, потребляющим ресурсы ЦП, был autovacuum для предотвращения переполнения идентификаторов транзакций, и учитывая, как отмечалось ранее, что этот режим нельзя остановить обычными средствами, мы временно повысили настройку порогового значения вместо непосредственного завершения процесса. Это позволило исключить проблемную таблицу из числа объектов принудительной обработки. Убедившись, что загрузка ЦП вернулась к нормальному уровню, мы создали резервную копию и заново инициализировали проблемную таблицу, чтобы устранить основное повреждение. Наконец, мы вернули временно повышенную настройку к исходному значению, повторно проверили состояние ЦП и сервиса и завершили реагирование.
-- 1) 원인 진단: CPU를 점유 중인 프로세스 확인
SELECT pid, query FROM pg_stat_activity WHERE state = 'active';
-- 2) 긴급 조치: wraparound 방지 대상에서 제외되도록 임계값 임시 상향
ALTER SYSTEM SET autovacuum_freeze_max_age = 500000000;
-- 3) 문제 테이블 정리 후 임시 설정 원복
TRUNCATE TABLE target_table;
ALTER SYSTEM SET autovacuum_freeze_max_age = 200000000;
Ключевым критерием в результатах запроса 1 является наличие в значении запроса фразы 'autovacuum: VACUUM ... (to prevent wraparound)'. Если эта фраза присутствует, это означает, что перед нами не обычный autovacuum, а принудительно запущенный режим предотвращения переполнения идентификаторов транзакций. Как объяснялось ранее, непосредственное завершение процесса неэффективно. И наоборот, если отображается только 'autovacuum: VACUUM ...' без этой фразы, это обычная операция очистки, и, как правило, беспокоиться не о чем.
3. Что такое autovacuum?
3.1 Autovacuum: что делает этот процесс?
Даже когда выполняется UPDATE или DELETE, PostgreSQL не удаляет существующие строки немедленно; вместо этого он оставляет их как мёртвые кортежи (MVCC). Autovacuum — это фоновый процесс, который очищает накопившиеся мёртвые кортежи и замораживает идентификаторы транзакций, чтобы их можно было использовать повторно. На первый взгляд это может показаться задачей DBA, однако шаблоны запросов и использование транзакций в приложениях напрямую влияют на нагрузку autovacuum и конкуренцию за блокировки. Поэтому разработчикам полезно понимать хотя бы основные принципы его работы.
|
Термин |
Описание |
|---|---|
|
dead tuple |
Строка, которая больше не является действительной из-за UPDATE/DELETE, но остаётся, поскольку ещё не была физически удалена. |
|
MVCC |
Метод управления параллелизмом PostgreSQL, позволяющий нескольким транзакциям одновременно просматривать данные, соответствующие разным моментам времени. |
|
autovacuum |
Фоновый автоматический процесс очистки PostgreSQL, который удаляет мёртвые кортежи и замораживает идентификаторы транзакций. |
|
xid / wraparound |
PostgreSQL использует 32-битные идентификаторы транзакций (xid). Если старые xid не очищаются, идентификаторы могут зациклиться (wraparound), что потенциально может нарушить согласованность данных. |
|
lock |
Блокировка, применяемая для сохранения согласованности, когда несколько сессий одновременно обращаются к одному объекту. В зависимости от типа запроса используются разные уровни блокировок. |
3.2 Почему и как накапливается xid_age? — autovacuum для предотвращения переполнения идентификаторов транзакций
xid_age продолжает увеличиваться при каждой транзакции, и единственный способ уменьшить его — завершить операцию заморозки таблицы, выполняемую autovacuum. Однако если xid_age быстро накапливается из-за интенсивного трафика записи, а autovacuum не успевает завершиться из-за конкуренции за I/O или задержек обработки, разрыв между скоростью накопления и скоростью очистки продолжает увеличиваться. Если этот разрыв не сокращается и возраст идентификатора транзакции (xid) в конечном итоге превышает пороговое значение, PostgreSQL принудительно запускает autovacuum для этой таблицы, чтобы предотвратить переполнение. Поскольку этот режим является последней линией защиты согласованности данных, его нельзя остановить обычными способами, такими как завершение процесса или отключение autovacuum. В этом инциденте взаимодействие с проблемной таблицей также привело к длительному потреблению ресурсов и повлияло на работу сервиса.

Рисунок 1. Этапы, приводящие к запуску autovacuum для предотвращения переполнения идентификаторов транзакций
4. Другой сценарий конкуренции за блокировки, вызванной autovacuum
Хотя это не было непосредственной причиной инцидента, я воспользовался этой возможностью, чтобы систематизировать другие ситуации, в которых autovacuum может вызывать конкуренцию за блокировки.
4.1 Конфликт между обычным autovacuum и DDL
Поскольку обычный autovacuum использует относительно лёгкую блокировку SHARE UPDATE EXCLUSIVE, он, как правило, без проблем сосуществует с такими запросами, как SELECT / INSERT / UPDATE. Проблема возникает при выполнении DDL, которому требуется блокировка ACCESS EXCLUSIVE, например ALTER TABLE во время развёртывания. Если autovacuum уже обрабатывает таблицу, DDL должен ждать завершения autovacuum, а обычные запросы, поступающие после этого, также могут оказаться в длинной очереди ожидания. Это одна из распространённых причин явления, при котором «сервис внезапно остановился во время развёртывания».
|
Режим блокировки |
Типичный сценарий использования |
Связь с autovacuum |
|---|---|---|
|
ACCESS SHARE |
SELECT |
Конфликт отсутствует |
|
ROW EXCLUSIVE |
INSERT / UPDATE / DELETE |
Конфликт отсутствует |
|
SHARE UPDATE EXCLUSIVE |
autovacuum, CREATE INDEX CONCURRENTLY |
Конфликт между идентичными режимами (не могут выполняться одновременно) |
|
ACCESS EXCLUSIVE |
ALTER TABLE, DROP TABLE, TRUNCATE |
Конфликт (ожидает завершения autovacuum) |

Рисунок 2. Конфликт блокировок между Autovacuum и DDL
4.2 Проблемы, вызванные длительными транзакциями
Если утечка соединения в приложении или транзакция, оставленная открытой во время отладки, остаются без внимания слишком долго, autovacuum не может очистить мёртвые кортежи, созданные до начала этой транзакции. В результате таблицы и индексы могут вырасти больше необходимого, что приводит к раздуванию и потенциальному снижению производительности запросов.
5. Заключение
Если не контролировать autovacuum, полагая, что это фоновый процесс, который хорошо работает самостоятельно, в обычных условиях проблем может не возникать, однако при одновременном воздействии таких факторов, как увеличение трафика, длительные транзакции и неоптимальные настройки, это может повлиять на весь сервис. Поскольку такие показатели, как xid_age и доля мёртвых кортежей, регулярно не отслеживаются, проблему можно не заметить, пока она не достигнет критического уровня. Регулярный мониторинг этих показателей также позволит реагировать с большим запасом времени, прежде чем будут достигнуты условия для принудительного запуска.
Sean