PostgreSQL VACUUM

В PostgreSQL есть так называемый MVCC (Multiversion Concurrency Control), который обеспечивает то, что каждая транзакция видит свой слепок данных, и транзакции чтения данных никогда не блокируют транзакции записи данных. а также запись никогда не блокирует чтение.

Для этого при выполнении SQL-команд UPDATE и DELETE фактическое удаление и изменение строки никогда не происходит сразу. На самом деле при выполнении UPDATE каждый раз создаётся новая версия строки, но и старая продолжает существовать. А при DELETE версия строки только помечается, как удалённая конкретной транзакцией, но фактически не удаляется.

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

У команды VACUUM есть два варианта: обычный и FULL.

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

Второй вариант создаёт новую копию таблицы без неиспользуемых областей. Этот вариант требует гораздо больше дискового пространства и более активно использует операции чтения/записи:

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

MVCC полагается на возможность сравнения ID (XID) версий транзакции, которая вставила строку с XID текущей транзакции. Строка, у которой XID транзакции, которая её вставила, больше XID текущей транзакции была вставлена в будущем, а значит она не должна быть видна из текущей транзакции. Для ID транзакций используется 32 бита, поэтому при выполнении достаточно большого количества транзакций (более четырёх миллиардов) произойдёт так называемый transaction ID wraparound, то есть ID Транзакции снова станет 0 и внезапно все транзакции, которые были в прошлом окажутся в будущем, что приведёт к тому, что их изменения станут видимыми.

Для того чтобы избежать transaction ID wraparound нужно обязательно выполнять VACUUM для каждой базы данных как минимум раз в 2 миллиарда транзакций.

Каким же образом команда VACUUM исправляет проблему? В PostgreSQL есть специальный XID транзакции FrozenXID. Этот XID не следует обычным правилам сравнения ID и считается всегда старше, чем любой нормальный XID. Обычные XID сравниваются не как простые числа, а как циклические. Для каждого XID есть два миллиарда XID-ов, которые старше и два миллиарда XID-ов, которые новее, то есть пространство XID-ов замкнуто в кольцо. После создания строки с каким-нибудь обычным XID строка становится «в прошлом» для двух миллиардов последующих транзакций, независимо от числа в их XID. Если эта строка до сих пор существует после двух миллиардов транзакций, то она случайно оказывается в будущем. Для того чтобы предотвратить это, старым версиям строк в XID нужно прописать FrozenXID до того, как мы достигнем рубежа в 2 миллиарда транзакций. После присвоения этого специального XID они окажутся «в прошлом» для всех нормальных транзакций, а поэтому они будут корректными вплоть до удаления, независимо от того, через какое время оно произойдёт. Присвоение FrozenXID осуществляет команда VACUUM, именно поэтому так важно, чтобы она периодически выполнялась.


Поделиться:
1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (Пока оценок нет)
Загрузка...

PostgreSQL VACUUM: 2 комментария

  1. Употребление слова «биллион» тут неуместно (видимо, оно осталось от перевода), следует употреблять слово «миллиард» (ведь речь идёт о 2 в степени 32).
    Биллион вообще для русского языка слово неоднозначное.
    https://ru.wikipedia.org/wiki/Биллион
    Будучи грамматиком, но не информатиком, тем не менее замечу, что 2 млрд это много и вряд ли какая база его достигнет.
    Доводилось ли автору встречать достижение базой этого числа? Если приходилось, предполагаю, что нужно смотреть в сторону 64-битных чисел (нужно исправлять PostgresQL)

Добавить комментарий

Ваш e-mail не будет опубликован.

*