Как стать автором
Обновить

Комментарии 20

НЛО прилетело и опубликовало эту надпись здесь
Вы изобрели VACUUM FULL. Удерживая access exclusive прочитать всю таблицу, записать живые данные в новый relfilenode, подменить старый на новый.
Увы, нет.
Параллельно выполняющийся запрос нам мешает — ведь он когда-то может захотеть обратиться к этим версиям (а вдруг?), и они должны быть ему доступны. И поэтому даже VACUUM FULL нам не поможет.
Вы изобрели именно vacuum full.
Если параллельный запрос вам мешает — вы не сможете взять access exclusive.
Нет, он мешает VACUUM FULL не наложить блокировку на таблицу, а считать предыдущие версии записей «полностью мертвыми» и не перенести их в новый relfilenode.

VACUUM FULL все же накладывает эксклюзивную блокировку (ожидает завершения транзакций на таблице). Проверить просто — открыть два окна, в одном начать транзакцию и выбрать что-то из таблицы, в другом выполнитьVACUUM FULL. Он не начнется пока не будет завершена транзакция в другом окне.


Соглашусь с предыдущим комментарием — выглядит так, как будто можно заменить на


SET statement_timeout = '1s';
VACUUM FULL table_name;
Безусловно, и VACUUM FULL, и способ выше блокировку накладывают. Только VF не чистит:
VACUUM FULL VERBOSE tbl;

INFO:  vacuuming "public.tbl"
INFO:  "tbl": found 0 removable, 10026 nonremovable row versions in 45 pages
DETAIL:  10000 dead row versions cannot be removed yet.

А так — да, логически они полностью идентичны, кроме момента, что VF сохраняет MVCC и не может быть выполнен внутри транзакции.

Ого. Интересно.Заставило почитать исходный код. Вы ведь используете репликацию с hot_standby_feedback? Как я понял standby вычисляет и посылает в этом случае ид транзакции с мастера (на standby нет своих номеров транзакций), которая еще хранит нужные ему строки и мастер не очищает строки чтобы не удалить строки которые нужны на standby.


При этом truncate таким не страдает и спокойно очищает строки. Интересно, vacuum full это по сути запрос cluster, но в нем все равно проверяется какие строки должны оставаться видимы.


https://github.com/postgres/postgres/blob/8ce3aa9b5914d1ac45ed3f9bc484f66b3c4850c7/src/backend/commands/cluster.c#L864


egorov, звучит как тема для еще одной статьи — как работает host_standby_feedback и на что он влияет на мастере)

Все эти тесты гонялись на изолированном мастере v11, никаких реплик. Там еще кучка отдельных граблей была бы в придачу.

Спасибо за статью, узнал про запрос TABLE.
Возник вопрос — чем не подошел pg_repack?


И еще дополнение — в приведенном запросе имена индексов не сохранятся, добавится префикс swap. И так каждый раз.


Заголовок спойлера
CREATE TABLE _swap_%table(LIKE %table INCLUDING ALL);
INSERT INTO _swap_%table TABLE %table;
DROP TABLE %table;
ALTER TABLE _swap_%table RENAME TO %table;
pg_repack — хороший инструмент для больших таблиц. А для своей «маленькой и быстрой» очереди хочется контролировать поведение от и до непосредственно из приложения.
А имена индексов… на моей памяти они сами по себе большой ценности никогда не представляли.

Полезное дело делаете!
Пара уточнений.


Да и последующие запросы по этой таблице пойдут у нас по «горячему кэшу»

Vacuum использует буферное кольцо в кеше, чтобы не вымывать из него полезные данные. Обратная сторона медали — в кеше ничего (почти ничего) не останется после его работы.


Для этого нам необходимо включить SERIALIZABLE-изоляцию для нашей транзакции

Достаточно и Read Committed, вы же вручную ставите блокировку.

Спасибо за инфу, про кольцо VACUUM был не в курсе, учту на будущее. А кольцо — общее на все экземпляры? То есть пройдет ли VACUUM FULL быстрее, если перед ним выполнить VACUUM по той же таблице? Опыт подсказывает, что таки да.

С READ COMMITED возникала, насколько помню, проблема при обращении параллельной транзакции. Вот только последний раз проверял такой кейс под реальной нагрузкой чуть ли не на 9.1 — возможно, был какой-то баг.

Кольцо — одно на операцию. По сути, это просто кусочек общего кеша, который из него временно «откушен».
Думаю, ускорение там из-за того, что vacuum full ведь тоже чистит ненужные версии строк, ну и если перед этим vacuum уже прошелся, то и ему легче.


Насчёт Read Committed — скорее всего в чем-то другом дело было. Против Access Exclusive не попрешь.
(К тому же Serializable работает только если и остальные транзакции используют тот же уровень. Иначе все это выражается в Repeatable Read.)

Я так понимаю, это кусочек shared buffers, но в pagecache операционки эти страницы будут доступны с очень большой вероятностью?

Это да.

Про кольца нигде особо не написано, но погрепайте buffer ring в исходниках.

Об очень похожем метода мы рассказывали совсем недавно на YaTalks — https://youtu.be/hXH_tRBxFnA 05:02:53 (извиняюсь не смог с телефона получить точную ссылку — секция про очереди, доклад "Как Толока росла вместе с кластером PostgreSQL").

Спасибо, отличная статья!
Не знал, что есть способ обойти MVCC. )
Нашел в документации соответствующее предупреждение:
«Команда TRUNCATE небезопасна с точки зрения MVCC. После опустошения таблицы она будет выглядеть пустой для параллельных транзакций, если они работают со снимком, полученным до опустошения.»
На первом же примере из wiki.postgresql.org/wiki/MVCC_violations соседний клиент в параллельной транзакции словит дичь в виде пустой таблицы.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий