Перейти к основному содержимому

Уровень 2.0

Предусловие:

  • Изучен модуль «Очистка» данного курса

В этом задании вы освоите:

  • Внутристраничную очистку
  • Оптимизацию HOT

Автоочистка​

Автоочистка обеспечивает функционирование механизма многоверсионности MVCC за счет удаления устаревших версий строк, находящихся за горизонтом очистки, и выполнения других сопутствующих задач.

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

В связи с этим в Pangolin, в дополнение к автоочистке используются оптимизации, повышающие производительность автоочистки и механизма MVCC в целом.

К ним относятся внутристраничная очистка (или pruning-очистка) и HOT-обновления (Heap Only Tuple Update — «обновления только в куче»).

Внутристраничная очистка​

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

При внутристраничной очистке табличной страницы удаляются «мертвые» версии строк, находящиеся за горизонтом очистки.

При внутристраничной очистке индексной страницы удаляются индексные строки, ссылающиеся на «мертвые» версии строк.

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

Внутристраничная очистка табличных страниц​

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

Внутристраничная очистка табличной страницы срабатывает в следующих случаях:

  • Страница заполнена более чем на 90 %
  • Страница заполнена более чем на fillfactor %
  • В заголовке страницы выставлен флаг PD_PAGE_FULL

Если при обращении к табличной странице обнаружится, что она заполнена более чем на 90 % или на значение параметра хранения fillfactor, то будет выполнена ее внутристраничная очистка.

Параметр хранения fillfactor используется для резервирования места в страницах для выполнения обновлений. Например, при значении fillfactor=70 30 % объема страницы резервируется для создания новых версий строк при выполнении операции обновления, но не вставки. Если страница заполнена более чем на 70 %, вставка новых строк в нее невозможна.

Параметр fillfactor используется для настройки оптимизации HOT, речь о которой пойдет ниже в данной лекции.

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

При срабатывании внутристраничной очистки в табличной странице очищаются «мертвые» версии строк, находящиеся за горизонтом очистки.

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

Однако меняется статус указателей с normal на dead. Если при индексном доступе потребуется очищенная строка, то по статусу указателя будет понятно, что ее уже нет.

При этом в индексной строке при обнаружении «мертвой» версии строки также проставится статус dead для ctid соответствующей строки. Это делается для исключения дальнейших обращений к удаленной версии строки и возможности внутристраничной очистки уже индексной строки.

Помимо карты видимости, также с целью экономии ресурсов не обновляется и карта свободного пространства.

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

Пример состояния табличной и индексной страниц после выполнения внутристраничной очистки табличной страницы представлен на следующем рисунке:

Внутристраничная очистка

В данном случае при внутристраничной очистке была удалена строка с ctid, равным (5, 35).

При этом ее указатель не освободился. Однако его статус поменялся на dead.

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

Внутристраничная очистка индексных страниц​

Также внутристраничной очистке подвергаются индексные страницы.

Это связано с известной проблемой расцепления страниц в индексах типа B-дерево.

Дело в том, что индексы типа B-дерево устроены таким образом, что если в странице нет места для вставки новой индексной строки, то она расщепляется на две с перераспределением индексных строк между ними.

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

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

Склеивание происходит только в случае полного перестроения индексов с использованием таких команд, как VACUUM FULL или REINDEX.

Внутристраничная очистка индексной страницы срабатывает в момент вставки новой индексной строки в случае недостатка свободного пространства в странице для этого.

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

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

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

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

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

Однако это оказывается менее ресурсозатратным, чем выполнять расщепление страницы.

Оптимизация HOT​

Необходимость оптимизации HOT​

Структура хранения данных, рассматривавшаяся до этого, предполагает хранение для каждой версии табличной строки соответствующей индексной строки.

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

Это, в свою очередь, может приводить к разрастанию индекса с расщеплением страниц и, соответственно, снижению эффективности индексного доступа и увеличению накладных расходов при очистке.

Для сглаживания описанного эффекта в Pangolin предусмотрена оптимизация под названием HOT (Heap Only Tuple — «кортеж только в куче»).

Оптимизация HOT предотвращает создание избыточных индексных строк при обновлениях в определенных случаях, которые будут рассмотрены ниже.

Принцип работы оптимизации HOT​

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

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

Под описанные условия подпадает и вырожденный случай полного отсутствия индексов у таблицы.

Под задействованием столбца в индексе понимается либо его участие в ключе индексирования, либо участие в качестве неключевого столбца include-индекса.

Индексы типа BRIN являются исключением из описанного правила, поскольку в них не хранятся ссылки на табличные версии строк.

При HOT-обновлении в заголовке старой версии строки в поле ctid проставляется ссылка на новую версию строки, а также проставляется информационный бит heap hot updated. При этом в новой версии строки проставляется информационный бит heap only tuple.

В случае считывания версии строки с проставленным битом heap hot updated выполняется переход к версии строки, идентификатор которой указан в поле ctid.

Таким образом осуществляется обход цепочки версий строк на странице с проверкой их видимости и выдачей обслуживающему процессу при необходимости.

Пример состояния табличной и индексной страниц после HOT-обновлений представлен на следующем рисунке:

HOT-обновление

В данном случае одна строка имеет три версии в табличной странице.

Однако в индексной странице только одна строка, ссылающаяся на первую версию строки с ctid=(5,20) в табличной странице.

У первой версии строки проставлен бит heap hot updated, что означает наличие следующей версии в цепочке, идентификатор которой указан в поле ctid(next) = (5,35).

Во второй версии строки с ctid = (5,35) проставлен бит heap only tuple, показывающий, что ссылок из индексов на данную версию нет.

Помимо этого также проставлен бит heap hot updated, и идентификатор следующей версии строки ctid(next) = (5,40).

Третья версия строки является последней, поэтому в поле ctid(next) она ссылается сама на себя и не проставлен информационный бит heap hot updated.

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

Снизить вероятность разрыва цепочек можно использованием ранее рассмотренного параметра хранения fillfactor.

При частых обновлениях столбцов таблицы, не задействованных в индексах, может оказаться целесообразным зарезервировать больше пространства для HOT-обновлений путем снижения значения fillfactor.

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

Внутристраничная очистка при HOT-обновлениях​

Наличие оптимизации HOT требует уточнения процедуры внутристраничной очистки.

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

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

Для этой цели используется статус указателя redirect to.

Пример состояния табличной и индексной страниц после выполнения внутристраничной очистки табличной страницы с HOT-обновлениями представлен на следующем рисунке:

Внутристраничная очистка с HOT-обновлением

В результате внутристраничной очистки удалены версии строк с ctid, равными (5, 20) и (5, 35).

Указатель на версию строки (5, 35) освобожден, о чем говорит статус unused.

При этом указатель на версию строки (5, 20) нельзя освободить, поскольку он является началом цепочки и на него есть ссылка из индекса.

Однако статус указателя на эту строку изменен на redirect to 40, для того чтобы была возможность при индексном доступе проследовать по цепочке к невычищенной версии строки (5, 40).

Итоги​

  • Внутристраничная очистка помогает автоочистке и работает в пределах одной страницы
  • Внутристраничная очистка срабатывает при обращении к странице в случае недостатка свободного пространства на ней
  • Оптимизация HOT помогает избежать избыточности в индексах
  • Параметр хранения fillfactor позволяет зарезервировать место для HOT-обновлений

Самопроверка​

Вопрос 1

В каких случаях срабатывает внутристраничная очистка табличной страницы? Выберите все верные варианты ответа:

Вопрос 2

В каких случаях срабатывает внутристраничная очистка индексной страницы?

Вопрос 3

В каких случаях выполняется HOT-обновление? Выберите все верные варианты ответа:

Вопрос 4

Значение параметра хранения fillfactor равно 75. Что это означает?