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

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

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

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

24.1.1. Основы процессов очистки #

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

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

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

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

VACUUM создает значительный объем ввода-вывода, что может вызывать плохую производительность для других активных сессий. Существуют конфигурационные параметры, которые можно настроить для снижения влияния на производительность фоновой очистки — см. Раздел 19.4.4.

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

В Tantor SE при выполнении операций UPDATE или DELETE строка не удаляется немедленно. Такой подход необходим для получения преимуществ многоверсионного контроля параллелизма (MVCC, см. Глава 13): версия строки не должна быть удалена, пока она может быть видима для других транзакций. Однако, со временем, устаревшая или удаленная версия строки больше не представляет интереса для любой транзакции. Занимаемое ею пространство должно быть освобождено для повторного использования новыми строками, чтобы избежать неограниченного роста требований к дисковому пространству. Для этого используется команда VACUUM.

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

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

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

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

Подсказка

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

Подсказка

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

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

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

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

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

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

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

Подсказка

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

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

Подсказка

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

Подсказка

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

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

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

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

24.1.5. Предотвращение сбоев из-за зацикливания идентификаторов транзакций #

Tantor SE's MVCC семантика транзакций зависит от возможности сравнения идентификаторов транзакций (XID): версия строки с идентификатором вставки, большим текущего идентификатора транзакции, находится "в будущем" и не должна быть видимой для текущей транзакции. Но так как идентификаторы транзакций имеют ограниченный размер (32 бита), кластер, работающий длительное время (более чем 4 миллиарда транзакций), столкнется с проблемой "зацикливания" идентификаторов транзакций: счетчик XID оборачивается в ноль, и внезапно транзакции, которые были в прошлом, кажутся будущими - что означает, что их вывод становится невидимым. Вкратце, катастрофическая потеря данных. (На самом деле данные все еще есть, но это мало утешает, если вы не можете получить к ним доступ). Чтобы избежать этого, необходимо выполнять очистку каждой таблицы в каждой базе данных хотя бы один раз каждые два миллиарда транзакций.

Причина, по которой периодическая очистка решает проблему, заключается в том, что VACUUM помечает строки как замороженные, указывая, что они были вставлены транзакцией, которая была подтверждена достаточно давно, чтобы последствия вставки были наверняка видны всем текущим и будущим транзакциям. Обычные XID сравниваются с использованием арифметики по модулю modulo-232. Это означает, что для каждого обычного XID существует два миллиарда XID, которые старше и два миллиарда, которые новее; т.е. пространство обычных XID является круговым без конечной точки. Поэтому, как только версия строки была создана с определенным обычным XID, версия строки будет казаться в прошлом для следующих двух миллиардов транзакций, независимо от того, о каком обычном XID мы говорим. Если версия строки все еще существует после более чем двух миллиардов транзакций, она внезапно покажется в будущем. Чтобы предотвратить это, Tantor SE резервирует специальный XID, FrozenTransactionId, который не следует обычным правилам сравнения XID и всегда считается старше каждого обычного XID. Замороженные версии строк обрабатываются так, как если бы XID транзакции, вставившей строку, был FrozenTransactionId, так что они будут казаться в прошлом для всех обычных транзакций, независимо от проблем с переполнением, и такие версии строк будут действительны до их удаления, независимо от того, сколько времени пройдет.

Примечание

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

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

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

VACUUM использует карту видимости для определения, какие страницы таблицы должны быть просканированы. Обычно он пропускает страницы, которые не содержат никаких удаленных версий строк, даже если на этих страницах все еще есть версии строк с устаревшими значениями XID. Поэтому обычные VACUUM не всегда замораживают все старые версии строк в таблице. Когда это происходит, VACUUM в конечном итоге должен выполнить агрессивную очистку, которая замораживает все подходящие незамороженные значения XID и MXID, включая те, которые находятся на страницах, видимых, но не замороженных. На практике большинство таблиц требуют периодической агрессивной очистки. vacuum_freeze_table_age управляет тем, когда VACUUM выполняет это: страницы, видимые, но не замороженные, сканируются, если количество транзакций, прошедших с момента последнего такого сканирования, больше, чем vacuum_freeze_table_age минус vacuum_freeze_min_age. Установка vacuum_freeze_table_age в 0 заставляет 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_age (а также vacuum_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, VACUUM выводит различные статистические данные о таблице. Это включает информацию о том, как продвинулись 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, который остается в таблице, поэтому конечное значение может быть намного более свежим, чем требуется строго. Если на таблице не выполняется VACUUM с увеличением relfrozenxid до достижения autovacuum_freeze_max_age, скоро будет запущено автоматическая очистка для таблицы.

Если по какой-то причине автоочистка не удаляет старые 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 может выполняться нормально. Вопреки тому, что указано в подсказке, нет необходимости или желания останавливать постмастер или входить в однопользовательский режим для восстановления нормальной работы. Вместо этого выполните следующие шаги:

  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. Как только нормальная работа будет восстановлена, убедитесь, что autovacuum правильно настроен в целевой базе данных, чтобы избежать будущих проблем.

Примечание

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

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

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

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

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

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

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

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

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

24.1.5.2. Сжатие pg_xact и pg_multixact принудительно. #

В Tantor SE есть 64-битные идентификаторы транзакций, которые не подвержены циклическим ошибкам и не требуют арифметики по модулю 2^32 для их сравнения. Каждый заголовок кортежа содержит два идентификатора транзакций, поэтому увеличение их размера привело бы к значительным издержкам. Поэтому идентификаторы внутри страницы остались 32-битными, и к заголовку каждой страницы было добавлено смещение, называемое эпохой, которое добавляется к идентификаторам внутри страницы для сравнения. В соответствии с этим изменением теперь для 64-битных идентификаторов транзакций используется тип bigint вместо ранее использовавшегося типа integer. Когда новый идентификатор транзакции не помещается в существующую страницу в соответствии с ее эпохой, эта эпоха сдвигается. При необходимости эта одна страница также может быть заморожена. Оба этих действия выполняются "на лету". Циклические ошибки на уровне страницы становятся возможными только в том случае, если кто-то удерживает образ, в котором накопилось более 4 миллиардов транзакций.

24.1.6. Демон автоматической очистки #

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

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

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

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

vacuum threshold = vacuum base threshold + vacuum scale factor * number of tuples

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

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

vacuum insert threshold = vacuum base insert threshold + vacuum insert scale factor * number of tuples

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

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

analyze threshold = analyze base threshold + analyze scale factor * number of tuples

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

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

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

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

Когда работает несколько рабочих процессов, параметры задержки на основе стоимости автоочистки (см. Раздел 19.4.4) распределяются сбалансированно между всеми работающими рабочими процессами, чтобы общее влияние на I/O системы было одинаковым, независимо от количества фактически работающих рабочих процессов. Однако, любые рабочие процессы, обрабатывающие таблицы, для которых установлены параметры хранения autovacuum_vacuum_cost_delay или autovacuum_vacuum_cost_limit, не учитываются в алгоритме балансировки.

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

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

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