Перейти к основному содержимому

Регулярная очистка

примечание

Эта страница переведена нейросетью GigaChat.

Базы данных PostgreSQL требуют периодического обслуживания, известного как VACUUM. Во многих случаях достаточно разрешить выполнение VACUUM службой AUTOVACUUM. Возможно, потребуется настроить параметры автозагрузки, описанные там, чтобы получить наилучшие результаты для ситуации. Некоторые администраторы баз данных захотят дополнить или заменить действия службы командами, управляемыми вручную VACUUM, которые обычно выполняются по расписанию сценариями cron или Планировщика задач. Чтобы правильно настроить управление VACUUM вручную, необходимо понять проблемы, обсуждаемые в следующих нескольких подразделах. Администраторы, полагающиеся на AUTOVACUUM, все равно могут захотеть просмотреть этот материал, чтобы помочь им понять и настроить AUTOVACUUM.

Основы очистки

Команда PostgreSQL VACUUM должна регулярно обрабатывать каждую таблицу по нескольким причинам:

  1. Для восстановления или повторного использования дискового пространства, занимаемого обновленными или удаленными строками.
  2. Чтобы обновить статистику данных, используемую планировщиком запросов PostgreSQL.
  3. Чтобы обновить карту видимости, которая ускоряет только индексные сканирования.
  4. Для защиты от потери очень старых данных из-за зацикливания идентификаторов транзакций или мультитранзакций.

Каждая из этих причин диктует выполнение операций VACUUM различной частоты и объема, как объясняется в следующих подразделах.

Существует два варианта VACUUM: стандартный VACUUM и VACUUM FULL. VACUUM FULL может освободить больше места на диске, но работает намного медленнее. Кроме того, стандартная форма VACUUM может выполняться параллельно с производственными операциями базы данных. (Команды, такие как SELECT, INSERT, UPDATE и DELETE будут продолжать функционировать нормально, хотя не возможно изменить определение таблицы командами, такими как ALTER TABLE, пока она очищается.) VACUUM FULL требует блокировки ACCESS EXCLUSIVE на таблице, над которой он работает, и поэтому не может быть выполнена параллельно с другим использованием таблицы. В общем, следовательно, администраторы должны стремиться использовать стандартную VACUUM и избегать VACUUM FULL.

VACUUM создает значительное количество трафика ввода-вывода, что может привести к ухудшению производительности для других активных сеансов.

Восстановление дискового пространства

В PostgreSQL при UPDATE или DELETE строки старая версия строки не удаляется немедленно. Этот подход необходим для получения преимуществ многоверсионного управления конкуренцией (MVCC): старую версию строки нельзя удалять до тех пор, пока она все еще потенциально видна другим транзакциям. Но в конце концов устаревшая или удаленная версия строки больше не представляет интереса ни для одной транзакции. Тогда занимаемое ею место должно быть освобождено для повторного использования новыми строками, чтобы избежать беспредельного роста требований к дисковому пространству. Это делается с помощью команды VACUUM.

Стандартная форма VACUUM удаляет мертвые версии строк в таблицах и индексах и помечает свободное пространство для будущего повторного использования. Однако она не возвращает это пространство операционной системе, за исключением особого случая, когда одна или несколько страниц в конце таблицы становятся полностью свободными и можно легко получить эксклюзивную блокировку таблицы. В отличие от этого, VACUUM FULL активно уплотняет таблицы, записывая полную новую версию файла таблицы без мертвого пространства. Это минимизирует размер таблицы, но может занять много времени. Также требуется дополнительное место на диске для новой копии таблицы до завершения операции.

Обычной целью регулярной уборки является выполнение стандартных операций VACUUM достаточно часто, чтобы избежать необходимости в VACUUM FULL. Служба autovacuum пытается работать таким образом и фактически никогда не выдает команду VACUUM FULL. В этом подходе идея заключается не в том, чтобы поддерживать таблицы минимального размера, а в поддержании устойчивого состояния использования дискового пространства: каждая таблица занимает пространство, эквивалентное ее минимальному размеру плюс сколько бы места ни использовалось между запусками VACUUM. Хотя с помощью VACUUM FULL можно уменьшить таблицу до минимального размера и вернуть дисковое пространство операционной системе, в этом нет большого смысла, если таблица снова вырастет в будущем. Таким образом, умеренно частые стандартные запуски VACUUM являются лучшим способом поддержания сильно обновляемых таблиц, чем редкие запуски VACUUM FULL.

Некоторые администраторы предпочитают самостоятельно планировать уборку, например, выполнять всю работу ночью, когда нагрузка низкая. Трудность выполнения уборки по фиксированному графику заключается в том, что если у таблицы наблюдается неожиданный всплеск активности обновления, она может раздуваться настолько, что действительно необходимо выполнить операцию VACUUM FULL, чтобы восстановить пространство. Использование службы autovacuum устраняет эту проблему, поскольку служба планирует уборку динамически в ответ на активность обновления. Неразумно полностью отключать служба, если только у вас нет чрезвычайно предсказуемой рабочей нагрузки. Одним из возможных компромиссов является настройка параметров служба таким образом, чтобы он реагировал только на необычно высокую активность обновления, тем самым предотвращая выход ситуации из-под контроля, тогда как запланированные запуски VACUUM ожидаются для выполнения основной работы при типичной нагрузке.

Для тех, кто не использует AUTOVACUUM, типичный подход заключается в планировании полной VACUUM базы данных один раз в день во время периода низкого использования, дополненного более частой очисткой часто обновляемых таблиц при необходимости. (В некоторых установках с исключительно высокой частотой обновления самые загруженные таблицы очищаются так часто, как каждые несколько минут.) Если у вас есть несколько баз данных в кластере, не забудьте очищать каждую из них; в этом может помочь программа vacuumdb.

Совет

Простой VACUUM может быть неудовлетворительным, когда таблица содержит большое количество мертвых версий строк в результате массовой операции обновления или удаления. Если у вас есть такая таблица и нужно вернуть избыточное дисковое пространство, которое она занимает, потребуется использовать VACUUM FULL, либо альтернативно CLUSTER или одну из вариантов переписывания таблицы ALTER TABLE. Эти команды переписывают всю новую копию таблицы и создают для нее новые индексы. Все эти опции требуют блокировки ACCESS EXCLUSIVE. Обратите внимание, что они также временно используют дополнительное дисковое пространство примерно равное размеру таблицы, поскольку старые копии таблиц и индексов не могут быть освобождены до завершения новых копий.

Если у вас есть таблица, содержимое которой удаляется периодически, рассмотрите возможность сделать это с помощью TRUNCATE вместо использования DELETE за которым следует VACUUM. TRUNCATE удаляет все содержимое таблицы немедленно, без необходимости последующего выполнения VACUUM или VACUUM FULL для восстановления теперь неиспользуемого дискового пространства. Недостатком является то, что строгие семантики MVCC нарушаются.

Обновление статистики планировщика

Планировщик запросов PostgreSQL полагается на статистическую информацию о содержимом таблиц для создания хороших планов запросов. Эти статистические данные собираются командой ANALYZE, которая может быть вызвана сама по себе или как необязательный шаг в VACUUM. Важно иметь разумно точные статистические данные, иначе из-за плохого выбора планов производительность базы данных может снизиться.

Служба autovacuum, если он включен, будет автоматически выдавать команды ANALYZE каждый раз, когда содержимое таблицы достаточно изменится. Однако администраторы могут предпочесть полагаться на операции ANALYZE, запланированные вручную, особенно если известно, что обновление таблицы не повлияет на статистику «интересных» столбцов. Служба планирует ANALYZE строго в зависимости от количества вставленных или обновленных строк; он не знает, приведет ли это к значимым статистическим изменениям.

Кортежи, измененные в разделах и дочерних элементах наследования, не вызывают анализа родительской таблицы. Если родительская таблица пуста или редко изменяется, она может никогда не обрабатываться autovacuum, и статистика по дереву наследования в целом не будет собрана. Чтобы поддерживать статистику в актуальном состоянии, необходимо вручную запускать ANALYZE для родительской таблицы.

Как и в случае с очисткой для восстановления пространства, частое обновление статистики более полезно для сильно обновляемых таблиц, чем для редко обновляемых. Но даже для сильно обновляемой таблицы может не быть необходимости в обновлении статистики, если статистическое распределение данных меняется незначительно. Простое эмпирическое правило - подумать о том, как сильно меняются минимальные и максимальные значения столбцов таблицы. Например, столбец timestamp, содержащий время обновления строки, будет иметь постоянно увеличивающееся максимальное значение по мере добавления и обновления строк; такой столбец, вероятно, будет нуждаться в более частом обновлении статистики, чем, скажем, столбец, содержащий URL-адреса страниц, к которым обращались на веб-сайте. Столбец URL может получать изменения так же часто, но статистическое распределение его значений, вероятно, изменяется относительно медленно.

Можно запускать ANALYZE для определенных таблиц и даже только для определенных столбцов таблицы, что позволяет обновлять некоторые статистические данные чаще, чем другие, если этого требует ваше приложение. На практике, однако, обычно лучше просто проанализировать всю базу данных, поскольку это быстрая операция. ANALYZE использует статистически случайную выборку строк таблицы, а не считывает каждую строку.

Совет

Хотя настройка частоты для каждого столбца может быть не очень продуктивной, возможно, стоит провести настройку уровня детализации статистики, собираемой ANALYZE для каждого столбца. Столбцы, которые интенсивно используются в предложениях ANALYZE и имеют сильно неравномерные распределения данных, могут потребовать более детального гистограммы данных, чем другие столбцы. См. WHERE или измените значение по умолчанию для всей базы данных с помощью параметра конфигурации ALTER TABLE SET STATISTICSdefault_statistics_target.

Кроме того, по умолчанию доступна ограниченная информация о селективности функций. Однако, если создать объект статистики или индекс выражений, который использует вызов функции, будет собрана полезная статистика о функции, что может значительно улучшить планы запросов, использующих индекс выражений.

Служба autovacuum не выдает команды ANALYZE для внешних таблиц, поскольку у него нет средств определения того, как часто это может быть полезно. Если ваши запросы требуют статистики по внешним таблицам для правильного планирования, рекомендуется вручную запускать управляемые вручную команды ANALYZE для этих таблиц по подходящему графику.

Совет

Служба autovacuum не выдает команды ANALYZE для секционированных таблиц. Родительские объекты наследования будут анализироваться только в том случае, если сам родитель был изменен - изменения в дочерних таблицах не вызывают автоанализ основной таблицы. Если вашим запросам требуется статистика по основным таблицам для надлежащего планирования, необходимо периодически выполнять ручное обновление ANALYZE для этих таблиц, чтобы поддерживать актуальность статистики.

Обновление карты видимости

VACUUM ведет карту видимости для каждой таблицы, чтобы отслеживать, какие страницы содержат только кортежи, которые гарантированно видны всем активным транзакциям (и всем будущим транзакциям до тех пор, пока страница снова не будет изменена). Это имеет две цели. Во-первых, VACUUM может пропустить такие страницы при следующем запуске, так как здесь нечего чистить.

Во-вторых, это позволяет PostgreSQL отвечать на некоторые запросы, используя только индекс, без обращения к базовой таблице. Поскольку индексы PostgreSQL не содержат информации о видимости кортежей, при обычном сканировании индекса для каждого совпадающего элемента индекса выполняется поиск кортежа в куче, чтобы проверить, должен ли он быть виден текущей транзакции. Сканирование только по индексу, с другой стороны, сначала проверяет карту видимости. Если известно, что все кортежи на странице видны, выборка из кучи может быть пропущена. Это наиболее полезно для больших наборов данных, где карта видимости может предотвратить обращение к диску. Карта видимости значительно меньше кучи, поэтому ее можно легко кэшировать, даже если куча очень велика.

Предотвращение сбоев при зацикливании счетчика транзакций

Семантика транзакций MVCC в PostgreSQL зависит от возможности сравнения номеров идентификаторов транзакции (XID): версия строки с XID вставки больше текущего XID транзакции находится в «будущем» и не должна быть видна текущей транзакции. Но поскольку идентификаторы транзакций имеют ограниченный размер (32 бита), кластер, который работает долгое время (более 4 миллиардов транзакций), будет страдать от оборачивания идентификатора транзакции: счетчик XID оборачивается до нуля, и все сразу транзакции, которые были в прошлом, кажутся находящимися в будущем – это означает, что их вывод становится невидимым. Короче говоря, катастрофическая потеря данных. (На самом деле данные все еще там, но это слабое утешение, если нет возможности получить к ним доступ). Чтобы этого избежать, необходимо периодически выполнять очистку каждой таблицы в каждой базе данных хотя бы один раз каждые два миллиарда транзакций.

Причина того, что периодическое выполнение очистки решает проблему, заключается в том, что VACUUM пометит строки как замороженные, указывая, что они были вставлены транзакцией, которая была завершена достаточно давно, чтобы эффекты вставляющей транзакции были наверняка видны всем текущим и будущим транзакциям. Нормальные XID сравниваются с использованием арифметики по модулю-2^32^. Это означает, что для каждого нормального XID существует два миллиарда XID, которые являются «старше», и два миллиарда, которые являются «новее»; другим способом сказать это то, что нормальное пространство XID является круговым без конечной точки. Поэтому, как только создается версия строки с определенным нормальным XID, эта версия строки будет казаться «в прошлом» в течение следующих двух миллиардов транзакций, независимо от того, о каком нормальном XID идет речь. Если версия строки все еще существует после более чем двух миллиардов транзакций, она внезапно появится в будущем. Чтобы предотвратить это, PostgreSQL резервирует специальный XID, FrozenTransactionId, который не подчиняется обычным правилам сравнения XID и всегда считается старше любого нормального XID. Замороженные версии строк рассматриваются так, как будто вставляющий XID был равен FrozenTransactionId, поэтому они будут казаться «в прошлом» для всех обычных транзакций независимо от проблем с оборачиванием, и такие версии строк будут действительны до тех пор, пока они не будут удалены, независимо от того, сколько времени это займет.

Примечание

В версиях PostgreSQL до 9.4 замораживание было реализовано путем фактической замены XID вставки строки на FrozenTransactionId, что было видно в системной колонке xmin строки. В новых версиях просто устанавливается бит флага, сохраняется исходный xmin строки для возможного использования при судебно-медицинской экспертизе. Однако строки с xmin, равным FrozenTransactionId (2), все еще могут быть найдены в базах данных, обновленных с помощью pg_upgrade из версий до 9.4.

Кроме того, системные каталоги могут содержать строки с xmin, равными BootstrapTransactionId, указывающими на то, что они были вставлены во время первой фазы initdb. Как и FrozenTransactionId, этот специальный XID рассматривается как более старый, чем любой нормальный XID.

vacuum_freeze_min_age контролирует, насколько старым должно быть значение XID, прежде чем строки, содержащие этот XID, будут заморожены. Увеличение этого параметра может избежать ненужной работы, если строки, которые иначе были бы заморожены, вскоре будут изменены снова, но уменьшение этого параметра увеличивает количество транзакций, которые могут пройти перед повторной очисткой таблицы.

VACUUM использует карту видимости для определения страниц таблицы, которые должны быть просканированы. Обычно она пропускает страницы, не содержащие мертвых версий строки, даже если эти страницы могут все еще содержать версии строк со старыми значениями XID. Поэтому обычные VACUUM не всегда замораживают каждую старую версию строки в таблице. Когда это происходит, VACUUM в конечном итоге потребуется выполнить агрессивную уборку aggressive vacuum, которая заморозит все подходящие незамороженные значения XID и MXID, включая те, которые находятся на всех видимых, но не полностью замороженных страницах. На практике большинство таблиц требуют периодической агрессивной уборки. vacuum_freeze_table_age контролирует, когда VACUUM делает это: сканируются все видимые, но не полностью замороженные страницы, если количество транзакций, прошедших с момента последнего такого сканирования, превышает vacuum_freeze_table_age минус vacuum_freeze_min_age. Установка vacuum_freeze_table_age равным нулю заставляет VACUUM всегда использовать свою агрессивную стратегию.

Максимальное время, в течение которого таблица может оставаться без уборки мусора, составляет два миллиарда транзакций минус значение vacuum_freeze_min_age на момент последней агрессивной уборки. Если бы она оставалась без уборки дольше этого времени, могло бы произойти повреждение данных. Чтобы гарантировать, что это не произойдет, автозагрузка применяется к любой таблице, которая может содержать незамороженные строки с XID старше возраста, указанного параметром конфигурации autovacuum_freeze_max_age. (Это произойдет даже в том случае, если автозагрузка отключена.)

Это означает, что если таблицу больше не убирают другим способом, автозагрузка будет применяться к ней примерно один раз каждые autovacuum_freeze_max_age минус vacuum_freeze_min_age транзакции. Для таблиц, которые регулярно очищаются от ненужных данных для восстановления пространства, это имеет мало значения. Однако для статических таблиц (включая таблицы, в которые вставляются данные, но нет обновлений или удалений) нет необходимости проводить очистку для восстановления места, поэтому может быть полезно попытаться максимизировать интервал между принудительными автозагрузками очень больших статических таблиц. Очевидно, можно сделать это либо путем увеличения autovacuum_freeze_max_age, либо уменьшения vacuum_freeze_min_age.

Эффективный максимум для vacuum_freeze_table_age составляет 0,95 * autovacuum_freeze_max_age; настройка выше этого значения будет ограничена максимальным значением. Значение больше, чем autovacuum_freeze_max_age не имело бы смысла, поскольку по достижении этого значения в любом случае вызывалась бы автоочистка для предотвращения зацикливания, а коэффициент умножения 0,95 оставляет некоторое пространство для выполнения ручной VACUUM перед тем, как это произойдет. В качестве общего правила, vacuum_freeze_table_age следует установить на значение немного ниже autovacuum_freeze_max_age, оставив достаточный зазор, чтобы регулярно запланированная VACUUM или автоподстройка, вызванная нормальной активностью удаления и обновления, выполнялась в этом окне. Установка его слишком близко может привести к антиобертонным автоподстройкам, даже если таблица недавно была очищена для восстановления пространства, тогда как более низкие значения приводят к более частой агрессивной очистке.

Единственным недостатком увеличения autovacuum_freeze_max_agevacuum_freeze_table_age вместе с ним) является то, что подкаталоги pg_xact и pg_commit_ts кластера баз данных займут больше места, поскольку они должны хранить статус подтверждения и (если track_commit_timestamp включен) отметку времени всех транзакций до горизонта autovacuum_freeze_max_age. Статус подтверждения использует два бита на транзакцию, поэтому, если autovacuum_freeze_max_age установлен на максимально допустимое значение в два миллиарда, можно ожидать, что pg_xact вырастет примерно до половины гигабайта, а pg_commit_ts - примерно до 20 ГБ. Если это незначительно по сравнению с общим размером вашей базы данных, рекомендуется установить autovacuum_freeze_max_age на максимальное допустимое значение. В противном случае установите его в зависимости от того, что готовы разрешить для хранения pg_xact и pg_commit_ts. (По умолчанию, 200 миллионов транзакций, соответствует примерно 50 МБ памяти pg_xact и около 2 ГБ памяти pg_commit_ts).

Одним из недостатков уменьшения vacuum_freeze_min_age является то, что он может вызвать ненужную работу VACUUM: замораживание версии строки - пустая трата времени, если строка вскоре после этого изменяется (что приводит к получению нового XID). Таким образом, настройка должна быть достаточно большой, чтобы строки не замерзали до тех пор, пока они вряд ли изменятся еще раз.

Чтобы отслеживать возраст самых старых незамерзающих XID в базе данных, VACUUM хранит статистику XID в системных таблицах pg_class и pg_database. В частности, столбец relfrozenxid строки таблицы pg_class содержит самый старый оставшийся незамерзший XID в конце последнего VACUUM, который успешно продвинул relfrozenxid (обычно последний агрессивный VACUUM). Аналогично, столбец datfrozenxid строки базы данных pg_database представляет собой нижнюю границу незамороженных XID, появляющихся в этой базе данных - это просто минимум значений relfrozenxid для каждой таблицы внутри базы данных. Удобный способ изучить эту информацию - выполнить запросы, такие как:

SELECT c.oid::regclass as table_name,
greatest(age(c.relfrozenxid),age(t.relfrozenxid)) as age
FROM pg_class c
LEFT JOIN pg_class t ON c.reltoastrelid = t.oid
WHERE c.relkind IN ('r', 'm');

SELECT datname, age(datfrozenxid) FROM pg_database;

Столбец age измеряет количество транзакций от XID отсечки до текущего XID транзакции.

Совет

Когда параметр команды VACUUM указан, VERBOSE печатает различные статистики о таблице. Это включает информацию о том, как relfrozenxid и relminmxid продвинулись вперед. Те же подробности появляются в журнале сервера, когда журнал AUTOVACUUM (управляемый log_autovacuum_min_duration) сообщает об операции VACUUM, выполняемой AUTOVACUUM.

VACUUM обычно сканирует только страницы, которые были изменены с момента последней очистки, но relfrozenxid может быть продвинут только тогда, когда будет просканирована каждая страница таблицы, которая может содержать не замороженные XID. Это происходит, когда relfrozenxid старше более чем на vacuum_freeze_table_age транзакций, когда используется опция FREEZE для VACUUM, или когда все страницы, которые еще не полностью заморожены, требуют очистки для удаления мертвых версий строки. Когда VACUUM сканирует каждую страницу в таблице, которая еще не полностью заморожена, она должна установить age(relfrozenxid) на значение немного больше, чем настройка vacuum_freeze_min_age, которая использовалась (больше на количество транзакций, начатых после начала VACUUM). VACUUM установит relfrozenxid на самый старый XID, который остается в таблице, поэтому возможно, что конечное значение будет намного новее, чем строго требуется. Если ни одна операция relfrozenxid по продвижению VACUUM не выполняется на таблице до достижения autovacuum_freeze_max_age, вскоре будет принудительно выполнена автоматическая очистка таблицы.

Если по какой-то причине autovacuum не может удалить старые XID из таблицы, система начнет выдавать предупреждения, подобные этому, когда самые старые XID базы данных достигнут сорока миллионов транзакций от точки зацикливания:

WARNING:  database "mydb" must be vacuumed within 39985967 transactions
HINT: To avoid a database shutdown, execute a database-wide VACUUM in that database.

(Ручной VACUUM должен решить проблему, как предполагает подсказка; но обратите внимание, что VACUUM должен выполняться суперпользователем, иначе он не сможет обработать системные каталоги, которые предотвратят его способность продвигать базу данных datfrozenxid.) Если эти предупреждения игнорируются, система откажется назначать новые XID, если осталось менее трех миллионов транзакций до зацикливания:

ERROR:  database is not accepting commands to avoid wraparound data loss in database "mydb"
HINT: Stop the postmaster and vacuum that database in single-user mode.

В этом состоянии любые транзакции, уже находящиеся в процессе выполнения, могут продолжаться, но могут быть запущены только транзакции только для чтения. Операции, изменяющие записи базы данных или усекающие отношения, завершатся сбоем. Команда VACUUM все еще может быть выполнена нормально. Вопреки тому, что говорит подсказка, нет необходимости останавливать postmaster или входить в однопользовательский режим, чтобы восстановить нормальную работу. Вместо этого следуйте шагам:

  1. Устраните старые подготовленные транзакции. Их можно найти, проверив pg_prepared_xacts для строк, где age(transactionid) велико. Такие транзакции должны быть зафиксированы или отменены.
  2. Завершите длительные открытые транзакции. Их можно найти, проверив pg_stat_activity для строк, где age(backend_xid) или age(backend_xmin) велико. Такие транзакции должны быть зафиксированы или отменены, либо сеанс может быть завершен с использованием pg_terminate_backend.
  3. Удалите все старые слоты репликации. Используйте pg_stat_replication, чтобы найти слоты, где age(xmin) или age(catalog_xmin) велико. Во многих случаях такие слоты были созданы для репликации на серверы, которые больше не существуют или которые были отключены в течение длительного времени. Если удаляется слот для сервера, который продолжает существовать и может пытаться подключиться к этому слоту, вероятно, потребуется перестроить данного репликатора.
  4. Выполните VACUUM в целевой базе данных. Простейший способ - выполнить VACUUM во всей базе данных; чтобы сократить время, необходимое для этого, также можно вручную выдать команды VACUUM для таблиц, где relminxid является самым старым. Не используйте VACUUM FULL в этом сценарии, потому что он требует XID и поэтому потерпит неудачу, за исключением режима суперпользователя, где вместо этого будет потреблен XID, тем самым увеличивая риск оборачивания идентификатора транзакции. Также не используйте VACUUM FREEZE, поскольку это приведет к выполнению большего объема работы, чем необходимо для восстановления нормальной работы.
  5. После восстановления нормальной работы убедитесь, что автоочистка правильно настроена в целевой базе данных, чтобы избежать проблем в будущем.
Примечание

В более ранних версиях иногда было необходимо остановить postmaster и выполнить VACUUM базы данных в однопользовательском режиме. В типичных сценариях в этом больше нет необходимости, поэтому по возможности следует избегать этих действий, поскольку они могут привести к сбою системы и повышают риски, так как отключают защиту от зацикливания идентификатора транзакции, предназначенную для предотвращения потери данных. Использовать однопользовательский режим в этом сценарии следует лишь при желании выполнить TRUNCATE или DROP ненужных таблиц, чтобы избежать необходимости выполнять для них VACUUM. Резерв в три миллиона транзакций позволяет администратору это сделать. За подробной информацией об использовании однопользовательского режима обратитесь к странице справки по postgres.

Мультитранзакции и зацикливание

Идентификаторы мультитранзакций используются для поддержки блокировки строк несколькими транзакциями. Поскольку в заголовке кортежа есть ограниченное пространство для хранения информации о блокировке, эта информация кодируется как «идентификатор нескольких транзакций», или кратко идентификатор мультитранзакции, всякий раз, когда несколько транзакций одновременно блокируют строку. Информация о том, какие идентификаторы транзакций включены в любой конкретный идентификатор мультитранзакции, хранится отдельно в подкаталоге pg_multixact, а сам идентификатор мультитранзакции появляется только в поле xmax в заголовке кортежа. Как и идентификаторы транзакций, идентификаторы мультитранзакций реализованы как 32-разрядный счетчик и соответствующее хранилище, все из которых требуют тщательного управления старением, очистки хранилища и обработки обертки. Существует отдельная область хранения, которая содержит список участников каждой мультитранзакции, который также использует 32-разрядный счетчик и который также должен управляться.

При каждом сканировании любой части таблицы, VACUUM заменяет любой идентификатор мультитранзакции, который он встречает и который старше, чем vacuum_multixact_freeze_min_age, другим значением, которое может быть нулевым значением, отдельным идентификатором транзакции или более новым идентификатором мультитранзакции. Для каждой таблицы pg_class.relminmxid хранит самый старый возможный идентификатор мультитранзакции, все еще встречающийся в любой кортежной таблице. Если это значение старше, чем vacuum_multixact_freeze_table_age, то принудительно выполняется агрессивная уборка. Как обсуждалось в предыдущем разделе, агрессивная уборка означает, что будут пропущены только те страницы, которые гарантированно заморожены. Для определения его возраста pg_class.relminmxid можно использовать mxid_age().

Агрессивные VACUUM, независимо от их причины, гарантированно смогут продвинуть relminmxid таблицы. В конечном итоге, когда все таблицы во всех базах данных будут просканированы и их самые старые значения мультитранзакций будут продвинуты, информация о старых мультитранзакциях может быть удалена с диска.

В качестве устройства безопасности агрессивное сканирование уборки будет выполняться для любой таблицы, возраст мультитранзакции которой больше, чем autovacuum_multixact_freeze_max_age. Кроме того, если объем памяти, занимаемый членами мультитранзакций, превышает 2 ГБ, агрессивные сканирования уборки будут происходить чаще для всех таблиц, начиная с тех, у которых самый старый возраст мультитранзакции. Оба этих типа агрессивных сканирований будут происходить даже в том случае, если автоподборка фактически отключена.

Аналогично случаю XID, если автоподборка не удается очистить старые идентификаторы MXID из таблицы, система начнет выдавать предупреждающие сообщения, когда самые старые идентификаторы MXID базы данных достигнут сорока миллионов транзакций от точки оборачивания. И точно так же, как в случае с XID, если эти предупреждения игнорируются, система откажется генерировать новые идентификаторы MXID, как только останется менее трех миллионов до оборачивания.

Нормальная работа при исчерпании MXID может быть восстановлена почти так же, как и при исчерпании XID. Следуйте тем же шагам, что и в предыдущем разделе, но с учетом следующих различий:

  1. Выполняющиеся транзакции и подготовленные транзакции могут игнорироваться, если нет шансов, что они могут появиться в мультитранзакции.
  2. Информация о MXID не отображается напрямую в системных представлениях, таких как pg_stat_activity; однако поиск старых XID все еще является хорошим способом определения того, какие транзакции вызывают проблемы с завершением работы MXID.
  3. Исчерпание XID заблокирует все операции записи, а исчерпание MXID заблокирует только подмножество операций записи, именно те, которые связаны с блокировками строк, требующими MXID.

Служба Autovacuum

PostgreSQL имеет необязательную, но настоятельно рекомендуемую функцию под названием autovacuum (автоочистка), предназначенную для автоматизации выполнения команд VACUUM и ANALYZE. При включении autovacuum проверяет таблицы, в которых было вставлено, обновлено или удалено большое количество кортежей. Эти проверки используют средство сбора статистики; поэтому autovacuum не может быть использован, если track_counts не установлен на true. В конфигурации по умолчанию AUTOVACUUM включено, и соответствующие параметры конфигурации установлены соответствующим образом.

На самом деле «служба autovacuum» состоит из нескольких процессов. Существует постоянный процесс-служба, называемый запускающим процессом autovacuum, который отвечает за запуск рабочих процессов autovacuum для всех баз данных. Этот контролирующий процесс распределяет работу по времени, стараясь запускать рабочий процесс для каждой базы данных каждые autovacuum_naptime секунд. (Поэтому, если установка содержит N баз данных, новый рабочий процесс будет запущен каждые autovacuum_naptime/N секунд.) Одновременно разрешено работать максимум autovacuum_max_workers рабочим процессам. Если есть больше чем autovacuum_max_workers баз данных для обработки, следующая база данных будет обработана сразу после завершения работы первого рабочего процесса. Каждый рабочий процесс будет проверять каждую таблицу в своей базе данных и выполнять команды VACUUM и/или ANALYZE при необходимости. Параметр log_autovacuum_min_duration можно установить для мониторинга активности рабочих процессов autovacuum.

Если несколько больших таблиц одновременно становятся подходящими для VACUUM в течение короткого промежутка времени, все рабочие процессы autovacuum могут оказаться занятыми очисткой этих таблиц в течение длительного периода. Это приведет к тому, что другие таблицы и базы данных не будут очищаться до тех пор, пока рабочий процесс не станет доступен. Нет ограничений на то, сколько рабочих процессов может находиться в одной базе данных, но рабочие процессы стараются избегать повторения работы, уже выполненной другими рабочими процессами. Обратите внимание, что количество запущенных рабочих процессов не учитывается в пределах max_connections или superuser_reserved_connections.

Таблицы, значение которых превышает autovacuum_freeze_max_age транзакций, всегда подвергаются VACUUM (это также относится к тем таблицам, для которых максимальный возраст заморозки был изменен с помощью параметров хранения; см. ниже). В противном случае, если количество кортежей, устаревших с момента последнего VACUUM, превышает «порог VACUUM», таблица подвергается VACUUM. Порог VACUUM определяется как:

порог очистки = базовый порог очистки + коэффициент масштабирования очистки * количество кортежей

где базовый порог очистки равен autovacuum_vacuum_threshold, коэффициент масштабирования VACUUM равен autovacuum_vacuum_scale_factor, а количество кортежей равно pg_class.reltuples.

Таблица также подвергается VACUUM, если количество вставленных кортежей с момента последней операции VACUUM превысило определенный порог вставки, который определяется следующим образом:

порог очистки при добавлении = базовый порог очистки при добавлении + коэффициент доли для очистки при добавлении * количество кортежей

где базовый порог очистки при добавлении равен autovacuum_vacuum_insert_threshold, а коэффициент доли для очистки при добавлении равен autovacuum_vacuum_insert_scale_factor. Такие процедуры могут позволить пометить части таблицы как все видимые и также разрешить замораживание кортежей, что может уменьшить объем работы, необходимый при последующих операциях очистки. Для таблиц, которые получают INSERT операции, но не получают или почти не получают операций UPDATE/DELETE, может быть полезно снизить минимальный возраст заморозки таблицы autovacuum_freeze_min_age, так как это может позволить ранее проведенным операциям VACUUM заморозить кортежи. Количество устаревших кортежей и количество вставленных кортежей получаются из кумулятивной системы статистики; это полуточный подсчет, обновляемый каждой операцией UPDATE, DELETE и INSERT (он является только полуточным, потому что некоторая информация может быть потеряна под сильной нагрузкой). Если значение relfrozenxid таблицы старше, чем vacuum_freeze_table_age транзакций, выполняется агрессивная операция VACUUM для заморозки старых кортежей и продвижения relfrozenxid вперед; в противном случае сканируются только те страницы, которые были изменены с момента последнего VACUUM.

Для анализа используется аналогичное условие: порог, определяемый как:

порог анализа = базовый порог анализа + масштабный коэффициент анализа * количество кортежей

сравнивается с общим количеством кортежей, вставленных, обновленных или удаленных с момента последнего ANALYZE.

Разделенные таблицы не хранят непосредственно кортежи и, следовательно, не обрабатываются автоочисткой. (Автоочистка обрабатывает разделы таблиц так же, как и другие таблицы.) К сожалению, это означает, что автоочистка не выполняет ANALYZE для разнесенных таблиц, и это может привести к неоптимальным планам для запросов, которые ссылаются на статистику разнесенной таблицы. Можно обойти эту проблему, вручную выполнив ANALYZE на разнесенных таблицах при их первом заполнении и снова всякий раз, когда распределение данных в их разделах существенно изменяется.

Временные таблицы не могут быть доступны автоочисткой. Поэтому соответствующие операции очистки и анализа должны выполняться с помощью команд сеансового SQL.

Пороговые значения и коэффициенты масштабирования по умолчанию берутся из postgresql.conf, но их можно переопределить (и многие другие параметры управления автоматической очисткой) на основе каждой таблицы; см. Параметры хранилища для получения дополнительной информации. Если настройка была изменена через параметры хранения таблицы, это значение используется при обработке этой таблицы; в противном случае используются глобальные настройки.

Когда выполняются несколько рабочих процессов, параметры задержки автоочистки по стоимости «уравновешиваются» между всеми выполняемыми рабочими процессами таким образом, чтобы общее воздействие ввода-вывода на систему было одинаковым независимо от количества фактически работающих рабочих процессов. Однако рабочие процессы, обрабатывающие таблицы, для которых установлены параметры хранения на уровне таблиц autovacuum_vacuum_cost_delay или autovacuum_vacuum_cost_limit, не учитываются в алгоритме балансировки.

Рабочие процессы автоочистки обычно не блокируют другие команды. Если процесс пытается получить блокировку, которая конфликтует с блокировкой SHARE UPDATE EXCLUSIVE, удерживаемой автоочисткой, получение блокировки прервет работу автоочистки. Для конфликтующих режимов блокировки см. таблицу 13.2. Однако если автоочистка выполняется для предотвращения повреждения транзакционной идентичности (т.е. имя запроса автоочистки в представлении pg_stat_activity заканчивается на (to prevent wraparound)), автоочистка автоматически не прерывается.

Предупреждение

Регулярное выполнение команд, которые получают блокировки, конфликтующие с блокировкой SHARE UPDATE EXCLUSIVE, (например, ANALYZE), может фактически предотвратить завершение автоочистки.