24.1. Регламентная очистка #
Базы данных Postgres Pro требуют периодического проведения процедуры обслуживания, которая называется очисткой. Во многих случаях очистку достаточно выполнять с помощью демона автоочистки, который описан в Подразделе 24.1.6. Возможно, в вашей ситуации для получения оптимальных результатов потребуется настроить описанные там же параметры автоочистки. Некоторые администраторы СУБД могут дополнить или заменить действие этого демона командами VACUUM
(обычно они выполняются по расписанию в заданиях cron или Планировщика задач). Чтобы правильно организовать очистку вручную, необходимо понимать темы, которые будут рассмотрены в следующих подразделах. Администраторы, которые полагаются на автоочистку, возможно, всё же захотят просмотреть этот материал, чтобы лучше понимать и настраивать эту процедуру.
24.1.1. Основные принципы очистки #
Команды VACUUM
в Postgres Pro должны обрабатывать каждую таблицу по следующим причинам:
- Для высвобождения или повторного использования дискового пространства, занятого изменёнными или удалёнными строками.
- Для обновления статистики по данным, используемой планировщиком запросов Postgres Pro.
- Для обновления карты видимости, которая ускоряет сканирование только индекса.
- Для сжатия
pg_xact
иpg_multixact
.
Разные причины диктуют выполнение действий 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. Высвобождение дискового пространства #
В Postgres Pro команды 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. Обновление статистики планировщика #
Планировщик запросов в Postgres Pro, выбирая эффективные планы запросов, полагается на статистическую информацию о содержимом таблиц. Эта статистика собирается командой ANALYZE
, которая может вызываться сама по себе или как дополнительное действие команды VACUUM
. Статистика должна быть достаточно точной, так как в противном случае неудачно выбранные планы запросов могут снизить производительность базы данных.
Демон автоочистки, если он включён, будет автоматически выполнять ANALYZE
после существенных изменений содержимого таблицы. Однако администраторы могут предпочесть выполнение ANALYZE
вручную, в частности, если известно, что производимые в таблице изменения не повлияют на статистику по «интересным» столбцам. Демон же планирует выполнение ANALYZE
в зависимости только от количества вставленных или изменённых строк; он не знает, приведут ли они к значимым изменениям статистики.
Изменение кортежей в дочерних таблицах и секциях не считается поводом для запуска анализа родительской таблицы. Если родительская таблица пуста или изменяется редко, процесс автоочистки может никогда не затронуть её и статистика иерархии наследования собираться не будет. Поэтому необходимо выполнять ANALYZE
для родительской таблицы вручную, чтобы поддерживать статистику в актуальном состоянии.
Как и процедура очистки для высвобождения пространства, частое обновление статистики полезнее для интенсивно изменяемых таблиц, нежели для тех таблиц, которые изменяются редко. Однако даже в случае часто изменяемой таблицы обновление статистики может не требоваться, если статистическое распределение данных меняется слабо. Как правило, достаточно оценить, насколько меняются максимальное и минимальное значения в столбцах таблицы. Например, максимальное значение в столбце timestamp
, хранящем время изменения строки, будет постоянно увеличиваться по мере добавления и изменения строк; для такого столбца может потребоваться более частое обновление статистики, чем, к примеру, для столбца, содержащего адреса страниц (URL), которые запрашивались с сайта. Столбец с URL-адресами может меняться столь же часто, однако статистическое распределение его значений, вероятно, будет изменяться относительно медленно.
Команду ANALYZE
можно выполнять для отдельных таблиц и даже просто для отдельных столбцов таблицы, поэтому, если того требует приложение, одни статистические данные можно обновлять чаще, чем другие. Однако на практике обычно лучше просто анализировать всю базу данных, поскольку это быстрая операция, так как ANALYZE
читает не каждую отдельную строку, а статистически случайную выборку строк таблицы.
Подсказка
Хотя индивидуальная настройка частоты ANALYZE
для отдельных столбцов может быть не очень полезной, смысл может иметь настройка детализации статистики, собираемой командой ANALYZE
. Для столбцов, которые часто используются в предложениях WHERE
, и имеют очень неравномерное распределение данных, может потребоваться более детальная, по сравнению с другими столбцами, гистограмма данных. В таких случаях можно воспользоваться командой ALTER TABLE SET STATISTICS
или изменить значение по умолчанию параметра уровня БД default_statistics_target.
Кроме того, по умолчанию информация об избирательности функций ограничена. Однако если вы создаёте объект статистики или индекс по выражению с вызовом функции, об этой функции будет собрана полезная статистическая информация, которая может значительно улучшить планы запросов, задействующие данный индекс.
Подсказка
Демон автоочистки не выполняет команды ANALYZE
для сторонних таблиц, поскольку он не знает, как часто это следует делать. Если для получения качественных планов вашим запросам необходима статистика по сторонним таблицам, будет хорошей идеей дополнительно запускать ANALYZE
для них по подходящему расписанию.
Подсказка
Демон автоочистки не выполняет команды ANALYZE
для секционированных таблиц. В иерархии наследования анализ родительской таблицы проводится, только если меняются данные в ней самой — изменения в дочерней таблице не считаются поводом для автоанализа родительской таблицы. Если для получения качественных планов вашим запросам необходима статистика по родительским таблицам, нужно периодически выполнять ANALYZE
вручную, чтобы их статистика была актуальной.
24.1.4. Обновление карты видимости #
Процедура очистки поддерживает карты видимости для каждой таблицы, позволяющие определить, в каких страницах есть только записи, заведомо видимые для всех активных транзакций (и всех будущих транзакций, пока страница не будет изменена). Это имеет два применения. Во-первых, сам процесс очистки может пропускать такие страницы при следующем запуске, поскольку на этих страницах вычищать нечего.
Во-вторых, с такими картами Postgres Pro может выдавать результаты некоторых запросов, используя только индекс, не обращаясь к данным таблицы. Так как индексы Postgres Pro не содержат информацию о видимости записей, при обычном сканировании по индексу необходимо извлечь соответствующую запись из таблицы и проверить её видимость для текущей транзакции. Поэтому при сканировании только индекса, наоборот, сначала проверяется карта видимости. Если известно, что все записи на странице видимы, то выборку из таблицы можно пропустить. Это наиболее полезно с большими наборами данных, когда благодаря карте видимости можно оптимизировать чтение с диска. Карта видимости значительно меньше таблицы, поэтому она легко помещается в кеш, даже когда объём самих страниц очень велик.
24.1.5. Принудительное сжатие pg_xact
и pg_multixact
#
В Postgres Pro семантика транзакций MVCC зависит от возможности сравнения номеров идентификаторов транзакций (XID): версия строки, у которой XID добавившей её транзакции больше, чем XID текущей транзакции, относится «к будущему» и не должна быть видна в текущей транзакции.
В Postgres Pro Enterprise 9.6 были введены 64-битные идентификаторы транзакций, которые не подвержены зацикливанию и для их сравнения не требуется арифметика по модулю 232. Заголовок каждого кортежа содержит два идентификатора транзакций, поэтому увеличение их размера повлекло бы большие издержки. Поэтому идентификаторы внутри страницы остались 32-битными, а в заголовок каждой страницы добавлено смещение, которое называется базовым XID и добавляется к идентификаторам внутри страницы, чтобы их можно было сравнивать. Более подробно это описано в Разделе 66.1.
Периодическое выполнение очистки решает эту проблему, потому что процедура VACUUM
помечает строки как замороженные, указывая, что они были вставлены транзакцией, зафиксированной достаточно давно, так что эффект добавляющей транзакции с точки зрения MVCC определённо будет виден во всех текущих и будущих транзакциях. Для этого Postgres Pro резервирует особый идентификатор, FrozenTransactionId
, который всегда считается старее любого обычного XID. Замороженные версии строк обрабатываются так, как если бы их вставила транзакция с XID FrozenTransactionId
, то есть они относятся «к прошлому» для всех обычных транзакций.
Когда новый идентификатор транзакции не подходит для существующей страницы, согласно её базового XID, происходит смещение. При необходимости эта одна страница также может замораживаться. Оба этих действия выполняются «на лету». Зацикливание на уровне страниц оказывается возможным, только если кто-то будет удерживать снимок, в котором накопилось больше 4 миллиардов транзакций. Даже в высоконагруженных системах, генерирующих до 2^32 транзакций ежедневно, до первой «автоочистки для предотвращения зацикливания» пройдёт 2^31 дней. Регламентная очистка не должна выполняться немедленно, и администратор базы данных планирует её на период наименьшей нагрузки в системе для минимального влияния на работу базы данных. Кроме того, очистку можно запускать реже в системах с количеством транзакций, близких к указанным выше: несколько раз в год, а не каждые несколько дней.
Операция замораживания данных командой VACUUM
нужна для сжатия pg_xact
и pg_multixact
. По историческим причинам операция автоочистки для сжатия pg_xact
и pg_multixact
сохранила название «автоочистка для предотвращения зацикливания».
Примечание
В версиях PostgreSQL до 9.4 замораживание было реализовано как замена XID добавления строки специальным идентификатором FrozenTransactionId
, который можно было увидеть в системной колонке xmin
данной строки. В новых версиях просто устанавливается битовый флаг, а исходный xmin
строки сохраняется для возможного расследования в будущем. Однако строки с xmin
, равным FrozenTransactionId
(2), можно по-прежнему встретить в базах данных, обновлённых (с применением pg_upgrade) с версий до 9.4.
Также системные каталоги могут содержать строки со значением xmin
, равным BootstrapTransactionId
(1), показывающим, что они были вставлены на первом этапе initdb. Как и FrozenTransactionId
, этот специальный XID считается более старым, чем любой обычный XID.
Параметр vacuum_freeze_min_age определяет, насколько старым должен стать XID, чтобы строки с таким XID были заморожены. Увеличение его значения помогает избежать ненужной работы, если строки, которые могли бы быть заморожены в ближайшее время, будут изменены ещё раз, а уменьшение приводит к увеличению количества транзакций, которые могут выполниться, прежде чем потребуется очередная очистка таблицы.
VACUUM
определяет, какие страницы таблицы нужно сканировать, анализируя карту видимости. Обычно при этой операции пропускаются страницы, в которых нет мёртвых версий строк, даже если в них могут быть версии строк со старыми XID. Таким образом, обычная команда VACUUM
не всегда замораживает все старые версии строк, имеющиеся в таблице. Когда это имеет место, операция VACUUM
в конце концов должна провести агрессивную очистку и заморозить все подходящие незамороженные значения XID и MXID, в том числе на полностью видимых, но не полностью замороженных страницах. На практике периодическая агрессивная очистка требуется для большинства таблиц. Когда VACUUM
будет делать это — зависит от параметра vacuum_freeze_table_age: полностью видимые, но не полностью замороженные страницы будут сканироваться, если число транзакций, прошедших со времени последнего такого сканирования, оказывается больше чем vacuum_freeze_table_age
минус vacuum_freeze_min_age
. Если vacuum_freeze_table_age
равно 0, VACUUM
будет применять агрессивную стратегию всегда.
Для любой таблицы, которая может содержать значения 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
, не имело бы смысла, поскольку по достижении этого значения в любом случае вызывалась бы автоочистка для сжатия pg_xact
и pg_multixact
, а коэффициент 0.95 оставляет немного времени для того, чтобы запустить VACUUM
вручную до того, как это произойдёт. Как правило, установленное значение vacuum_freeze_table_age
должно быть несколько меньше autovacuum_freeze_max_age
, чтобы оставленный промежуток был достаточен для выполнения в этом окне VACUUM
по расписанию или автоочистки, управляемой обычной активностью операций удаления и изменения. Если это значение будет слишком близким к максимуму, автоочистка для сжатия pg_xact
и pg_multixact
будет выполняться, даже если таблица только что была очищена для высвобождения пространства, в то же время при небольшом значении будет чаще производиться агрессивная очистка.
Единственный минус увеличения 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
(обычно это последняя операция агрессивной очистки). Аналогично столбец 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
, а также о количестве новых замороженных страниц. Те же сведения вносятся в журнал сервера, когда в него записываются сообщения об операции VACUUM
, выполненной процессом автоочистки (этим управляет параметр log_autovacuum_min_duration).
Обычно VACUUM
сканирует только те страницы, которые изменялись после последней очистки, однако relfrozenxid
может увеличиться только при сканировании всех страниц таблицы, включая те, что могут содержать незамороженные XID. Это происходит, когда возраст relfrozenxid
превышает vacuum_freeze_table_age
транзакций, когда VACUUM
вызывается с указанием FREEZE
, или когда оказывается, что очистку для удаления мёртвых версий строк нужно провести во всех ещё не замороженных страницах. Когда VACUUM
сканирует в таблице каждую ещё не полностью замороженную страницу, значение age(relfrozenxid)
в результате должно стать немного больше, чем установленное значение vacuum_freeze_min_age
(больше на число транзакций, начатых с момента запуска VACUUM
). VACUUM
установит для relfrozenxid
значение, равное самому старому XID, остающемуся в таблице, поэтому возможно, что окончательное значение будет новее, чем требовалось. Если по достижении autovacuum_freeze_max_age
для таблицы ни разу не будет увеличен relfrozenxid
, в скором времени для неё будет принудительно запущена автоочистка.
24.1.5.1. Сжатие pg_multixact
#
Идентификаторы мультитранзакций используются для поддержки блокировки строк несколькими транзакциями одновременно. Поскольку в заголовке строки есть только ограниченное пространство для хранения информации о блокировках, в нём указывается «идентификатор множественной транзакции», или идентификатор мультитранзакции для краткости, когда строку блокируют одновременно несколько транзакций. Информация о том, какие именно идентификаторы транзакций относятся к определённой мультитранзакции, хранится отдельно в подкаталоге pg_multixact
, а в поле xmax
в заголовке строки сохраняется только идентификатор мультитранзакции. Как и идентификаторы транзакций, идентификаторы мультитранзакций исполнены в виде 64-битного счётчика со смещением, что требует аккуратного управления их возрастом и очисткой хранилища. Существует отдельная область, в которой содержится список членов каждой мультитранзакции, где счётчики также 64-битные.
Когда 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.
24.1.6. Демон автоочистки #
В Postgres Pro имеется не обязательная, но настоятельно рекомендуемая к использованию функция, называемая автоочисткой, предназначение которой — автоматизировать выполнение команд VACUUM
и ANALYZE
. Когда автоочистка включена, она проверяет, в каких таблицах было вставлено, изменено или удалено много строк. При этих проверках используются средства сбора статистики; поэтому автоочистка будет работать, только если параметр track_counts имеет значение true
. В конфигурации по умолчанию автоочистка включена и соответствующие параметры имеют подходящие значения.
«Демон автоочистки» на самом деле состоит из нескольких процессов. Существует постоянный фоновый процесс, называемый процессом запуска автоочистки, который отвечает за запуск рабочих процессов автоочистки для всех баз данных. Этот контролирующий процесс распределяет работу по времени, стараясь запускать рабочий процесс для каждой базы данных каждые 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
, превышает «пороговое значение очистки». Пороговое значение очистки определяется как:
порог очистки = базовый порог очистки + коэффициент доли для очистки * количество кортежей
где базовый порог очистки — значение autovacuum_vacuum_threshold, коэффициент доли — autovacuum_vacuum_scale_factor, а количество кортежей — pg_class
.reltuples
.
Таблица также очищается, если число кортежей, добавленных после предыдущей очистки, превышает установленный порог очистки при добавлении, который определяется так:
порог очистки при добавлении = базовый порог очистки при добавлении + коэффициент доли для очистки при добавлении * количество кортежей
Базовый порог очистки при добавлении и коэффициент доли для очистки при добавлении определяются параметрами 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
.
В секционированных таблицах кортежи не хранятся напрямую и, следовательно, не обрабатываются автоочисткой. (Автоочистка обрабатывает секции таблицы так же, как и другие таблицы.) К сожалению, это означает, что автоочистка не запускает ANALYZE
для секционированных таблиц, в результате чего создаются неоптимальные планы для запросов, ссылающихся на статистику секционированных таблиц. Эту проблему можно обойти, вручную запуская ANALYZE
для секционированных таблиц при их первом заполнении, а также всякий раз, когда распределение данных в их секциях существенно меняется.
Автоочистка не обрабатывает временные таблицы. Поэтому очистку и сбор статистики в них нужно производить с помощью SQL-команд в обычном сеансе.
Используемые по умолчанию пороговые значения и коэффициенты берутся из postgresql.conf
, однако их (и многие другие параметры, управляющие автоочисткой) можно переопределить для каждой таблицы; за подробностями обратитесь к разделу Параметры хранения. Если какие-либо значения определены через параметры хранения таблицы, при обработке этой таблицы действуют они, а в противном случае — глобальные параметры. За более подробной информацией о глобальных параметрах обратитесь к Разделу 19.10.
Когда выполняются несколько рабочих процессов, параметры задержки автоочистки по стоимости (см. Подраздел 19.4.4) «распределяются» между всеми этими процессами, так что общее воздействие на систему остаётся неизменным, независимо от их числа. Однако этот алгоритм распределения нагрузки не учитывает процессы, обрабатывающие таблицы с индивидуальными значениями параметров хранения autovacuum_vacuum_cost_delay
и autovacuum_vacuum_cost_limit
.
Рабочие процессы автоочистки обычно не мешают выполнению других команд. Если какой-либо процесс попытается получить блокировку, конфликтующую с блокировкой SHARE UPDATE EXCLUSIVE
, которая удерживается в ходе автоочистки, автоочистка прервётся и процесс получит нужную ему блокировку. Конфликтующие режимы блокировок отмечены в Таблице 13.2. Однако если автоочистка выполняется для предотвращения зацикливания идентификаторов транзакций (т. е. описание запроса автоочистки в представлении pg_stat_activity
заканчивается на (to prevent wraparound)
), автоочистка не прерывается без ручного вмешательства.
Предупреждение
При частом выполнении таких команд, как ANALYZE, которые затребуют блокировки, конфликтующие с SHARE UPDATE EXCLUSIVE
, может получиться так, что автоочистка не будет успевать завершаться в принципе.