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

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

Егор, спасибо за статью. Особенно интересна рекомендация про fulfill фактор как возможность оптимизации производительности. Надеюсь, выпадет случай применить ее на практике.

Так как Вам удобнее отвечать на вопросы в отдельных комментариях — напишу их также отдельно.

Внутристраничная очистка убирает версии строк, не видимые ни в одном снимке (находящиеся за «горизонтом событий» базы данных, об этом мы говорили в прошлый раз), но работает строго в пределах одной табличной страницы.


Пусть изменилось поле text, которое настолько велико, что хранится в TOAST. Пусть его изменили несколько раз. Началась внутристраничная очистка. Полагаю, что для TOAST она не работает? Потому что TOAST — это размещение данных на нескольких страницах.

применяется ли MVCC к TOAST? Вероятно, это будет раскрыто в последующих статьях и вопрос преждевременный.
Владимир, спасибо. Отвечать более или менее все равно как, а читать потом легче, когда ответы стоят рядом с вопросами, как мне кажется.

Потому что TOAST — это размещение данных на нескольких страницах.

Это не совсем так. TOAST — это размещение данных, которые не помещаются на одной странице, но как они размещаются? Они нарезаются на фрагменты, каждый из которых — помещается. Так что TOAST-таблица практически ничем не отличается от обычной и к ней все сказанное тоже применимо, включая и MVCC.

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

TOAST-таблица используется только при обращении к «длинному» значению. Кроме того, для toast-таблицы поддерживается своя версионность: если обновление данных не затрагивает «длинное» значение, новая версия строки будет ссылаться на то же самое значение в TOAST-таблице — это экономит место.


А если происходит обратная ситуация — меняется только «длинное значение»? Что происходит? Создается новая версия строки, по сути, копия уже существующей с той лишь разницей, что ссылка будет указывать на новую версию TOAST-таблицы?

Если так, то понятно, как будет работать HOT в данном случае. Если нет — поясните, пожалуйста, механизм с TOAST.
Ага, все так.
Все неактуальные версии строк (0,1), (0,2) и (0,3) очищены; после этого на освободившееся место добавлена новая версия строки (0,5).


Ради интереса я выполнил UPDATE в транзакции и потом откатил транзакцию. Строки все равно остались очищенными. То есть внутристраничная очистка от транзакции, видимо, никак не зависит. Как и проставление битов статусов транзакций. То есть, такие операции нетранзакционны, видимо, в силу того, что такая транзакционность никогда не требуется.
Да, очистка от границ транзакций напрямую не зависит. А зависит от горизонта событий.
(Другое дело, что пока транзакция работает, горизонт не даст удалить версии строк, которые в этой транзакции меняются.)
Внутристраничная очистка и VACUUM

Получается, что последующая процесс AUTO VACUUM почистит индексные страницы и уберет unused указатели? А также удалит цепочку.
Цепочку не надо удалять, она хорошая. А в остальном — да, именно так. Об этом в следующий раз (:
Вероятно, следующий вопрос слишком низкоуровневый — а как фоновый AUTO VACUUM и внутристраничная очистка «делят между собой» процесс очистки? Пусть автовакуум хочет удалить цепочку, а начавшаяся внутристраничная очистка хочет цепочку продолжить.

Что будет происходить? Вероятно, ситуация решается физическими блокировками страниц?
Да, чтобы что-то сделать со страницей, очистка должна ее заблокировать в буферном кеше. Поэтому они друг другу не помешают.

Егор, спасибо вам большое за ваш титанический труд!

У меня вопрос по очистке версий строк:

Итак, при следующем обращении к странице должна произойти внутристраничная очистка. Проверим это.

=> UPDATE hot SET s = 'E';
=> SELECT * FROM heap_page('hot',0);
 ctid  | state  |   xmin   | xmax  | hhu | hot | t_ctid
-------+--------+----------+-------+-----+-----+--------
(0,1) | dead   |          |       |     |     |
(0,2) | dead   |          |       |     |     |
(0,3) | dead   |          |       |     |     |
(0,4) | normal | 3982 (c) | 3983  |     |     | (0,5)
(0,5) | normal | 3983     | 0 (a) |     |     | (0,5)
(5 rows)

Все неактуальные версии строк (0,1), (0,2) и (0,3) очищены; после этого на освободившееся место добавлена новая версия строки (0,5).

Указатели на удаленные версии строк освободить нельзя, поскольку на них существует ссылки из индексной страницы.

А что будет с этими указателями? Они остаются в странице навсегда? Или в конечном итоге они тоже будут удалены? Если да, то каким образом? Спасибо

Ответ описан в первых пару абзацах продолжения)

Благодарю и возьму на заметку!

когда на работе используется postgres 10, то статьи, возможно, ещё и с опережением идут)

Пора, пора обновляться! Раз в пять лет-то можно себе позволить (:

Зарегистрируйтесь на Хабре, чтобы оставить комментарий