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

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

Я далеко не с первого раза понял начальные абзацы, мне кажется, что не хватает пояснений.

В первом абзаце есть вот такое:

Внутристраничная очистка (HOT cleanup) – это оптимизация, благодаря которой старые версии строк могут эффективно удаляться из блоков таблиц. Освобождённое место используется под размещение новой версии строки. Освобождается только место, занимаемое версиями строк, вышедшими за горизонт базы данных (xmin horizon)

Я так понимаю, что это чисто техническая активность по освобождению места в странице, не выполняемая в рамках каких-то транзакций. Причём удаляемые старые версии могли стать таковыми любым способом, например UPDATE создал новую версию строки.

А потом во втором абзаце уже идёт вот такое:

Алгоритм быстрой очистки табличной страницы реализован в файле pruneheap.c (heap page pruning and HOT-chain management code) исходного кода PostgreSQL. При удалении строки в блоке (странице) таблицы удаляющая транзакция проставляет свой номер в заголовок строки в поле xmax и пытается найти место под новую версию строки в том же самом блоке, при этом признак фиксации не проставляется.

Здесь уже, как я понял, речь совсем про другое: удаление строки из таблицы через DELETE в рамках пользовательской транзакции. Как это связано с "быстрой очисткой страницы" - непонятно и не объяснено.

Когда вы говорите, что бит LP_DEAD выставляется, когда index scan обнаружил, что "строка удалена", вы что имеете в виду? Какая-то транзакция могла пометить версию строки как "удалённая", но для параллельно выполняющихся repeatable read транзакций нужно возвращать эту строку, поэтому что-то сомнительно. А если имеется в виду запись, помеченная как "очищенная", тогда понятно.

HOT cleanup выполняется серверным процессом, имеющим назначенный номер транзакции и выполняется в рамках конкретной транзакции. Про транзакции лучше не думать, чтобы не запутаться. Удаляемые старые версии строк могли быть созданы любым способом: insert, update, delete.

Во второй цитате про UPDATE, так как DELETE не ищет место. Где написано «строка» читайте «версия строки», так как в блоках хранятся «версии строк». Актуальной может быть только одна из них или или ни одной. Обновляется или помечается удаленной только актуальная версия строки.

«Какая-то транзакция могла пометить..» В статье написано: «удалена и вышла за горизонт базы». Если вышла за горизонт базы, значит repeatable read транзакций нет (и любых других и снэпшотов), которым могла бы понадобиться удаленная (= очищенная = место которой освобождено) версия строки.

Если еще что-то непонятно - спрашивайте! 👍

Вы, с одной стороны, советуете не думать, что очистка записей в страницах выполняется в рамках транзакции, и я это понимаю, эта транзакция не имеет никакого отношения к версии строки, ей просто место нужно. Но вы сами довольно много подробностей про эту транзакцию написали: её номер сохраняется в очищенной строке, её статус впоследствии проверяется. Зачем? Строку почистили, потому что страница уже была загружена в память и было оптимально разместить новую запись на этой странице. Какое дело, кто почистил строку?

Про очистку строки и бит LP_DEAD - был неправ, мне причудилось, что написано "удалена или вышла за горизонт". Спасибо, что прояснили, что имеется в виду "очищенная" строка.

Мои непонятки относились к первым двум абзацам. Главную мысль статьи я понял: отдельные функции пытаются делать свою локальную пометку мусора в тех пространствах, в которые заходят. Зашли на страницу - пометили записи как очищенные. Прошли по указателю в индексе, но записи помечены как очищенные - пометим индексный указатель как очищенный, чтобы в следующий раз страницу с записями не загружать с диска.

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

В статье "записи" это "индексные записи". В блоках таблицы место, занимаемое версиями строк, очищается и метки ставить некуда. В заголовке блока таблицы есть "указатели" (line pointer, LP, на них указывают индексные записи). Индексная запись указывает на начало цепочки верий в блоке. При очистке версий строк указатели (каждый указатель занимает 4 байта) в заголовке блока не освобождаются и не меняют свой порядок, содержимое указателей обновляется. Место, занимаемое указателями, при быстрой очистке блока таблицы освободить нельзя, так как на них могут указывать индексные записи. И вот если процесс пришел по ссылке из индексной записи в блок и видит, что в блоке нет актуальной версии и все версии удалены или вышли за горизонт базы, то индексная запись помечается как DEAD, чтобы в следующий раз этот же или другой процесс не проверяли содержимое этого блока, где нет версий строк. Диск не играет роли, основное замедление происходит при доступе к буферам и разборе блоков, которые в них находятся.

Главная мысль в "Резюме" в конце. Если мысль про абзацы, то: даже один блок индекса может указывать на сотни версий строк в разных блоках таблицы. Чтобы добраться до актуальной версии, серверному процессу при каждом запросе приходится проверять много - в примере десятки тысяч строк (на картинке 226*407=91982). Такое происходит, если горизонт базы долго удерживается, а строки часто обновляются. Если горизонт сдвинется, то версии строк, вышедшие за горизонт очистятся серверными процессами и ссылки на них из индексов тоже, если был доступ был через индекс. То есть приложения, где часто выполняются команды UPDATE при наличии долгих запросов или транзакций начинают существенно тормозить. PostgreSQL имеет потенциал работать быстрее, если доработать логику работы индекса btree. Пока же - достаточно следить за транзакциями, запросами, репликами с обратной связью, удерживающими горизонт и по возможности стремиться, чтобы горизонт сдвигался. Как сдвинется - быстрая очистка блоков таблиц и индексов или автовакуум восстановят производительность выполнения команд и запросов к строкам, у которых удерживалось много версий. При проектировании приложений стремиться уменьшить частые и массированные UPDATE. INSERT более предпочтительны.

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