Заморозка в PostgreSQL
Заморозка может проходить в двух режимах: "ленивый режим" (lazy mode) и "нетерпеливый режим" (eager mode). Режим, в котором будет выполнена заморозка, зависит от определенных условий.
Примечание: Concurrent VACUUM часто называют "ленивым вакуумом".
В lazy mode заморозка сканирует только те страницы, которые содержат “мертвые” кортежи, используя соответствующие VM целевых таблиц.
В отличие от этого, в eager mode сканируются все страницы, независимо от того, содержит ли каждая из них “мертвые” кортежи или нет. Данный режим также обновляет системные каталоги, связанные с заморозкой, и по возможности удаляет ненужные части CLOG.
Подразделы 6.3.1 и 6.3.2 посвящены этим двум режима. В разделе 6.3.3 описано, как улучшить процесс заморозки в рамках eager mode.
Lazy Mode
При запуске процесса FREEZE PostgreSQL вычисляет значение freezeLimit_txid и замораживает кортежи, чей t_xmin меньше freezeLimit_txid.
freezeLimit_txid определяется следующим образом:
freezeLimit_txid=(OldestXmin−vacuum_freeze_min_age)
где OldestXmin - самый старый txid среди текущих транзакций..
Например, если на момент выполнения команды VACUUM выполняются три транзакции (txid 100, 101 и 102), OldestXmin равен 100. Если других транзакций нет, то OldestXmin - это txid, который выполняет команду VACUUM. В данном случае vacuum_freeze_min_age - это параметр конфигурации, который по умолчанию составляет 50 000 000.
Рис.71 иллюстрирует конкретный пример: таблица Table_1 состоит из трех страниц, каждая из которых содержит три кортежа. При выполнении команды VACUUM текущий txid равен 50 002 500, других транзакций нет. В этом случае OldestXmin равен 50 002 500, а freezeLimit_txid равен 2500. Заморозка выполняется следующим образом:
-
0 страница:
Три кортежа замораживаются, поскольку все значения t_xmin меньше, чем freezeLimit_txid. Кроме того, Tuple_1 удаляется в виду того, что содержит мертвый кортеж; -
1 страница:
В соответствии с VM эту страницу можно пропустить; -
2 страница:
Tuple_7 и Tuple_8 замораживаются; Tuple_7 удаляется.
Прежде чем процесс VACUUM завершится, происходит обновление статистики, связанной с VACUUM, например, pg_stat_all_tables' n_live_tup, n_dead_tup, last_vacuum, vacuum_count и т. д.
Как показано в примере, приведенном выше, lazy mode может быть не в состоянии заморозить полностью все кортежи, поскольку он может пропустить некоторые страницы.
Eager Mode
Eeager mode компенсирует недостатки lazy mode. Он сканирует абсолютно все страницы, проверяя все кортежи, содержащиеся в таблицах, обновляет соответствующие системные каталоги и по возможности удаляет ненужные файлы и страницы CLOG.
Eager mode возможен при выполнении следующего условия:
pg_database.datfrozenxid<(OldestXmin−vacuum_freeze_table_age)
В приведенном выше условии pg_database.datfrozenxid представляет собой столбцы системного каталога pg_database, который содержит самый старый замороженный txid для каждой базы данных. Подробности будут описаны чуть позже. Vacuum_freeze_table_age является параметром конфигурации (по умолчанию 150 000 000).
На рисунке 72 показан конкретный пример. Из Table_1 удалены Tuple_1 и Tuple_7. Tuple_10 и Tuple_11 перенесены на вторую страницу. При выполнении команды VACUUM текущий txid равен 150 002 000, других транзакций нет. Таким образом, OldestXmin = 150 002 000, а freezeLimit_txid = 100 002 000. В этом случае вышеприведенное условие выполняется, потому что:
1821<(150002000−150000000)
Поэтому заморозка в режиме eager mode выполняется следующим образом:
-
0 страница:
Несмотря на то, что все кортежи были заморожены, Tuple_2 и Tuple_3 все же были проверены; -
1 страница:
Три кортежа на этой странице были заморожены, поскольку все значения t_xmin меньше, чем freezeLimit_txid. Обратите внимание, что в lazy mode эта страница была пропущена; -
2 страница:
Tuple_10 был заморожен. Tuple_11 - нет.
После замораживания каждой таблицы обновляется pg_class.relfrozenxid целевой таблицы. pg_class - это системный каталог, каждый столбец pg_class.relfrozenxid содержит последний замороженный xid соответствующей таблицы. В нашем примере pg_class.relfrozenxid таблицы Table_1 обновляется до текущего значения freezeLimit_txid (т. е. 100 002 000), что означает, что все кортежи, чей t_xmin меньше 100 002 000 в таблице Table_1, будут заморожены.
Перед тем, как процесс VACUUM будет завершен, при необходимости обновляется pg_database.datfrozenxid. Каждый столбец pg_database.datfrozenxid содержит минимальное значение pg_class.relfrozenxid в соответствующей базе данных. Например, если в eager mode заморожена только таблица Table_1, то pg_database.datfrozenxid этой базы данных не обновляется, поскольку pg_class.relfrozenxid других отношений (как других таблиц, так и системных каталогов, видимых из текущей базы данных) не был изменен (рис. 73(1)). Если все отношения в текущей базе данных заморожены в eager mode, то pg_database.datfrozenxid базы данных обновляется, поскольку pg_class.relfrozenxid всех отношений для этой базы данных обновляется до текущего значения freezeLimit_txid (рис. 73(2)).
Примечание: Как отобразить pg_class.relfrozenxid и pg_database.datfrozenxid
В следующем примере первый запрос показывает relfrozenxids всех видимых отношений в БД 'testdb', а второй запрос - pg_database.datfrozenxld БД 'testdb:
testdb=# VACUUM table_1;
VACUUM
testdb=# SELECT n.nspname as "Schema", c.relname as "Name", c.relfrozenxid
FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind IN ('r','')
AND n.nspname <> 'information_schema' AND n.nspname !~ '^pg_toast'
AND pg_catalog.pg_table_is_visible(c.oid)
ORDER BY c.relfrozenxid::text::bigint DESC;
Schema | Name | relfrozenxid
-----------+-------------------------+--------------
public | table_1 | 100002000
public | table_2 | 1846
pg_catalog | pg_database | 1827
pg_catalog | pg_user_mapping | 1821
pg_catalog | pg_largeobject | 1821
...
pg_catalog | pg_transform | 1821
(57 rows)
testdb=# SELECT datname, datfrozenxid FROM pg_database WHERE datname = 'testdb';
datname | datfrozenxid
--------+--------------
testdb | 1821
(1 row)
Примечание: опция Заморозки
Команда VACUUM с опцией Заморозки замораживает все txids в указанных таблицах. Это выполняется в eager mode, при этом freezeLimit устанавливается на OldestXmin (а не на 'OldestXmin - vacuum_freeze_min_age'). Например, когда команда VACUUM FULL выполняется по txid 5000 и нет других запущенных транзакций, OldesXmin = 5000, а txids, которые меньше 5000, замораживаются.
Заморозка в рамках Eager Mode
Eager mode в версии 9.5 и более ранних версиях неэффективен, поскольку всегда сканирует все страницы. Например, в примере из раздела 6.3.2 сканируется 0-я страница, хотя все кортежи на ней заморожены.
Для решения этой проблемы в версии 9.6 были усовершенствованы VM и процесс заморозки. Согласно информации, отображенной в разделе 6.2.1, обновленная VM содержит информацию о том, все ли кортежи были заморожены на каждой странице. Когда обработка заморозки выполняется в eager mode, страницы, содержащие только замороженные кортежи, могут быть пропущены.
Рис. 74 иллюстрирует конкретный пример. При заморозке таблицы 0-я страница пропускается при обращении к VM. После замораживания 1-й страницы обновляется связанная с ней информация о VM, поскольку все кортежи этой страницы были заморожены.







