Заморозка для 64-битных идентификаторов транзакций
Краткое описание
Поддержка 64-битных идентификаторов транзакций с версии 6.1.0 Pangolin сократила необходимость в постоянной очистке таблиц и упростила настройку autovacuum при поддержке конкурентной нагрузки с высоким TPS. Остались в прошлом проблемы отставания интенсивности заморозки от скорости приема транзакций:
- потери производительности при неотложных
autovacuum: to prevent wraparound; - неотвратимые инциденты с переходом СУБД в режим «только для чтения» по ошибке
ERROR: database is not accepting commands to avoid wraparound data loss in database "name".
Однако, в основе архитектуры сервера остался тот же механизм MVCC, требующий постоянного периодического обслуживания.
Контекст и область применения
Заморозка идентификаторов транзакций и мультитранзакций постоянно применяется в поддержке MVCC для того, чтобы освобождать как сами идентификаторы, так и связанные с ними данными.
Архитектура, принципы и концепции
Во время обычной очистки таблицы, ручной (VACUUM) или автоматической (autovacuum), алгоритм выбирает страницы (блоки) для заморозки по:
- карте видимости, пропуская полностью видимые блоки;
- (новое в PG18+, Pangolin 8.1.0+) ранней выборке части полностью видимых блоков таблицы, до
vacuum_max_eager_freeze_failure_rateнеуспешных попыток заморозки от объема таблицы, чтобы постараться избежать следующего пункта; - порогу ручной или периодической полной заморозки
vacuum_freeze_table_age– для «слишком старой» по горизонту (смотрите ниже) таблицы будут выбраны и заморожены все блоки, кроме уже замороженных по карте видимости; - порогу принудительной полной заморозки
autovacuum_freeze_max_age– то же, только очистка принудительно запустится, даже если для остальных целей она выключена; - порогу полной заморозки с максимальной агрессивностью
vacuum_failsafe_age– этот параметр в Pangolin неактуален, поскольку инцидентов с переполнением счетчика транзакций при 64-битных xid не ожидается, и в последующих версиях Pangolin он будет удален.
Записи замораживаются, если возраст их транзакции xmin старше, чем vacuum_freeze_min_age. Замороженные записи отмечаются признаком заморозки HEAP_XMIN_FROZEN в структуре t_infomask, после этого статус их видимости считается всегда ясным. Необходимость посещения CLOG для уточнения статуса видимости отменяется, поскольку идентификатор породившей кортеж транзакции освобожден, самой транзакции больше нет и ее статус не может измениться.
Полностью замороженные страницы процесс заморозки отмечает признаком all-frozen в карте видимости таблицы.
Для каждой из таблиц хранится ее горизонт заморозки – возраст транзакции при самой старой из незамороженных записей таблицы, pg_class.relfrozenxid.
Только полная заморозка отношения (без пропуска полностью видимых блоков) может уточнить и обновить (продвинуть вперед) этот горизонт. Для баз в кластере хранятся горизонты баз в pg_database.datfrozenxid – старейшие из горизонтов таблиц по каждой базе. Если обработанный пакет таблиц включал самую старую таблицу (ту, которая определяла горизонт своей базы), то VACUUM/autovacuum дополнительно обновит и продвинет вперед горизонт базы.
В нескольких директориях сервера хранятся сопутствующие транзакциям данные, общие для кластера баз (смотрите ниже). Эти данные периодически очищаются, обычно в процессе выполнения контрольной точки. При этом процесс очистки не имеет права убирать данные, еще нужные для каких-то из транзакций. Эта граница определяется по старейшему из горизонтов заморозки баз (pg_database.datfrozenxid). Минимальную отметку использует для усечения данных PGDATA/pg_xact функция vac_truncate_clog(), постоянно вызываемая при регламентной очистке.
Заморозка мультитранзакций
Когда изменение одного и того же кортежа интересно нескольким транзакциям одновременно, сервер не имеет возможности разместить номера всех интересующихся транзакций в заголовке кортежа. Поля только два, xmin и xmax. Чтобы несколько транзакций могли одновременно удерживать блокировку одной и той же строки, сервер формирует новый объект – мультитранзакцию, MultiXact. Для идентификаторов мультитранзакций выделено отдельное пространство номеров, multixid. Их разрядность в Pangolin – также 64 бита. Информация об участниках мультитранзакции хранится в специальных файлах каталога pg_multixact.
Для мультитранзакций в MVCC тоже требуется периодическая замена старых номеров на новые, чтобы избежать хранения лишней устаревшей информации.
Заморозка мультитранзакций выполняется процессом очистки (VACUUM), который заменяет устаревшие multixact id на новые (или на обычный номер транзакции, если блокировка удерживается только одной транзакцией).
За заморозку мультитранзакций отвечают параметры, аналогичные параметрам обычной заморозки:
vacuum_multixact_freeze_min_age— минимальный возраст мультитранзакции для заморозки.vacuum_multixact_freeze_table_age— возраст, при котором выполняется полная заморозка всех мультитранзакций в таблице.autovacuum_multixact_freeze_max_age— порог, при достижении которого автоочистка запускается принудительно.vacuum_multixact_failsafe_age— максимально допустимый возраст для предотвращения аварийных ситуаций – в Pangolin неактуален, не предвидится инцидентов с переполнением счетчика 64-битныхmultixid, в последующих версиях Pangolin будет удален.
Заморозка мультитранзакций затрагивает поле xmax версии строки, если оно содержит номер мультитранзакции. Если блокировка удерживается только одной транзакцией, xmax заменяется на обычный номер транзакции.
Информация о мультитранзакциях кешируется в общей памяти сервера и журналируется для защиты от сбоев.
Если не выполнять заморозку вовремя, возможна деградация производительности в таких сценариях.
- Увеличение времени обработки блокировок, задержки при доступе к строкам: для прояснения видимости группы интересующихся транзакций придется искать по гигабайтам информации из
pg_multixact, а затем уточнять статус транзакций по пропорционально разросшемуся журналу статусов транзакций,clog. - Невозможность удаления старых данных. Без заморозки старые мультитранзакции остаются в системе, что мешает удалению устаревших файлов и увеличивает объем хранимых данных, а также замедляет работу автоочистки.
Процесс заморозки мультитранзакций по таблице продвигает pg_class.relminmxid. Минимум из этих значений по каждой базе сохраняется в pg_database.datminmxid, а минимум из них отмечается в pg_control. Эта отметка применяется для усечения данных PGDATA/pg_multixact в той же vac_truncate_clog().
32-битные номера транзакций в описании кортежа
Реализация 64-битного счетчика транзакций в Pangolin не изменяет разрядности идентификаторов транзакций xmin и xmax в заголовках кортежей. По-прежнему используется 32 бита. Добавляется специальное пространство в каждое табличной странице (блоке), где хранится «эпоха» или «базовое значение». Это старшие 32 бита из 64-битного базового значения xid (pd_xid_base) или multixid (pd_multi_base).
Поскольку внутри блока номера транзакций 32-битные, из-за незамороженной старой (мульти)транзакции на уровне блока может случиться «локальный wraparound». Это ситуация, когда необходимо добавить/изменить кортеж в свежей (мульти)транзакции с текущим pd_xid_base/pd_multi_base, но где-то в блоке есть кортеж, номер (мульти)транзакции которого относится к «прежним эпохам» меньших pd_xid_base/pd_multi_base. И этот номер не «изъят из системы» заморозкой.
В этом случае:
- Корректируется базовое значение
pd_xid_base/pd_multi_base. - Обновляются поля
t_xmin/t_xmaxво всех версиях строк на странице. - Если базовое значение не скорректировать в силу наличия версий строк, созданных старыми транзакциями, выполняется удаление мертвых версий строк и заморозка оставшихся старых версий строк на этой странице. Успех удаления мертвых версий и заморозки не гарантирован – в блоке может быть много записей, в том числе связанных с активными (мульти)транзакциями.
- Если после этих действий не удалось изменить базу, выдается ошибка.
Рекомендации и лучшие практики
В Pangolin изменены максимальные значения и значения по умолчанию параметров:
| Параметр | Значение по умолчанию (было → стало) | Максимальное значение (было → стало) |
|---|---|---|
vacuum_freeze_min_age | без изменений | 1000000000 → 0x7FFFFFFFFFFFFFFF |
vacuum_freeze_table_age | без изменений | 2000000000 → 0x7FFFFFFFFFFFFFFF |
vacuum_multixact_freeze_min_age | без изменений | 1000000000 → 0x7FFFFFFFFFFFFFFF |
vacuum_multixact_freeze_table_age | без изменений | 2000000000 → 0x7FFFFFFFFFFFFFFF |
autovacuum_freeze_max_age | 200000000 → 10000000000 | 2000000000 → 0x7FFFFFFFFFFFFFFF |
autovacuum_multixact_freeze_max_age | 400000000 → 20000000000 | 2000000000 → 0x7FFFFFFFFFFFFFFF |
Следующие параметры неактуальны и фактически не используются: vacuum_defer_cleanup_age, vacuum_failsafe_age, vacuum_multixact_failsafe_age. В следующих версиях будут исключены из списка возможных параметров для использования.
Видно, что возможно (и уже сделано по умолчанию для порогов принудительной заморозки) значительное отдаление запуска заморозки.
При этом процедура заморозки все еще требуется для отслеживания самой старой транзакции для удаления лишних файлов сопутствующих данных. Подвержены:
- статусы транзакций CLOG и связанные с ними файлы в
PGDATA/pg_xact; - статусы мультитранзакций и связанные с ними данные о смещениях (offsets) и о членах (members) мультитранзакций в
PGDATA/pg_multixact; - информация о вложенных транзакциях в
PGDATA/pg_subtrans; - метки времени фиксации транзакций, отслеживание которых включается параметром
track_commit_timestamp, вPGDATA/pg_commit_ts; - статусы сериализуемых транзакций в
PGDATA/pg_serial.
Директории очищаются во время VACUUM или при выполнении контрольной точки. Увеличение интервала заморозки, не говоря о полной ее отмене, потенциально влечет неконтролируемый рост директорий, не содержащих пользовательских данных до десятков эксабайт.
При использовании xid64 размер файлов в директориях PGDATA/pg_multixact и PGDATA/pg_subtrans увеличивается, поскольку в них хранятся номера транзакций. Например, для хранения информации о 2 млрд (мульти)транзакций необходимо предусмотреть:
| Директория | XID32 2 млрд | XID64 2 млрд | Во сколько раз требуется больше места на диске | Максимальный объем для XID64¹ 2 млрд * 2 млрд |
|---|---|---|---|---|
PGDATA/pg_xact | 512 МБ | 512 МБ | 1 | 1 ЭБ |
PGDATA/pg_multixact/offsets | 8 ГБ | 16 ГБ | 2 | 32 ЭБ |
PGDATA/pg_multixact/members | 10,2 ГБ | 18,5 ГБ | 1.81 | 37 ЭБ |
PGDATA/pg_subtrans | 8 ГБ | 16 ГБ | 2 | 32 ЭБ |
PGDATA/pg_commit_ts | 18.5 ГБ | 18.5 ГБ | 1 | 37 ЭБ |
¹ 1 ЭБ – эксабайт (ЭБ, EB), это 1024 петабайта или 1 048 576 терабайта. Для файл-системы ext4 размер тома в 1 ЭБ разработчики называют теоретическим максимальным, большие тома поддерживает xfs.
Также, при отсутствии заморозки:
- признак
all-frozenне будет выставляться в карте видимости таблицы, это затрудняет оптимизацию VACUUM (обычно он при заморозке пропускает полностью замороженные страницы); - отсутствие признака заморозки у записей (
HEAP_XMIN_FROZENвt_infomask) заставит активные сессии чаще посещать CLOG, чтобы уточнять неясные (еще не отраженные в hint bits) статусы транзакций; - будут накапливаться страницы, содержащие кортежи, относящиеся к очень старым транзакциям (старше $2^31-1$) и требующие для модификации (внесения свежей транзакции) немедленной заморозки с корректировкой базового
xid/multixid; сбой такой немедленной заморозки возвращает системную ошибку.
Ссылки по теме
Функция vac_truncate_clog().