71.6. Макет страницы базы данных#

71.6. Макет страницы базы данных

71.6. Макет страницы базы данных

Этот раздел предоставляет обзор формата страниц, используемого в таблицах и индексах Tantor SE. [17] Последовательности и таблицы TOAST форматируются так же, как обычная таблица.

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

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

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

Таблица 71.2. Общая структура страницы

Элемент Описание
PageHeaderData24 байта длиной. Содержит общую информацию о странице, включая указатели на свободное пространство.
ItemIdDataМассив идентификаторов элементов, указывающих на фактические элементы. Каждая запись представляет собой пару (смещение, длина). 4 байта на каждый элемент.
Free spaceНераспределенное пространство. Новые идентификаторы элементов выделяются с начала этой области, новые элементы - с конца.
ItemsСами фактические элементы.
Special spaceСпецифические данные для метода доступа к индексу. Разные методы хранят разные данные. Кроме того, это пространство используется для хранения информации о 64-битных идентификаторах транзакций.

Первые 24 байта каждой страницы состоят из заголовка страницы (PageHeaderData). Его формат подробно описан в Таблица 71.3. Первое поле отслеживает самую последнюю запись WAL, связанную с этой страницей. Второе поле содержит контрольную сумму страницы, если data checksums включены. Далее следует 2-байтовое поле, содержащее флаговые биты. Затем идут три 2-байтовых целочисленных поля (pd_lower, pd_upper и pd_special). Они содержат смещения в байтах от начала страницы до начала неиспользованного пространства, до конца неиспользованного пространства и до начала специального пространства. Следующие 2 байта заголовка страницы, pd_pagesize_version, хранят размер страницы и индикатор версии. Начиная с PostgreSQL 8.3, номер версии равен 4; PostgreSQL 8.1 и 8.2 использовали номер версии 3; PostgreSQL 8.0 использовал номер версии 2; PostgreSQL 7.3 и 7.4 использовали номер версии 1; в предыдущих версиях использовался номер версии 0. (Основной макет страницы и формат заголовка не изменились в большинстве этих версий, но макет заголовков строк кучи изменился.) Размер страницы в основном присутствует только в качестве проверки; в установке нет поддержки нескольких размеров страниц. Последнее поле - это подсказка, которая показывает, вероятно ли, что обрезка страницы будет прибыльной: оно отслеживает самый старый непрореженный XMAX на странице.

Таблица 71.3. PageHeaderData Layout

ПолеТипДлинаОписание
pd_lsnPageXLogRecPtr8 байтLSN: следующий байт после последнего байта записи WAL для последнего изменения этой страницы
pd_checksumuint162 байтаКонтрольная сумма страницы
pd_flagsuint162 байтаФлаговые биты
pd_lowerLocationIndex2 байтаСмещение до начала свободного пространства
pd_upperLocationIndex2 байтаСмещение до конца свободного пространства
pd_specialLocationIndex2 байтаСмещение до начала специального пространства
pd_pagesize_versionuint162 байтаИнформация о версии размера страницы и расположения
pd_prune_xidTransactionId4 байтаСамый старый неочищенный XMAX на странице или ноль, если таковых нет

Все подробности можно найти в файле src/include/storage/bufpage.h.

После заголовка страницы следуют идентификаторы элементов (ItemIdData), каждый из которых требует четыре байта. Идентификатор элемента содержит смещение в байтах до начала элемента, его длину в байтах и несколько битов атрибутов, которые влияют на его интерпретацию. Новые идентификаторы элементов выделяются по мере необходимости из начала неаллоцированного пространства. Количество присутствующих идентификаторов элементов можно определить, посмотрев на pd_lower, который увеличивается для выделения нового идентификатора. Поскольку идентификатор элемента никогда не перемещается, пока он не будет освобожден, его индекс можно использовать на долгосрочной основе для ссылки на элемент, даже когда сам элемент перемещается по странице для компактного использования свободного пространства. Фактически, каждый указатель на элемент (ItemPointer), также известный как CTID), созданный Tantor SE, состоит из номера страницы и индекса идентификатора элемента.

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

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

Диаграмма 71.1 иллюстрирует, как эти части располагаются на странице.

Диаграмма 71.1. Макет страницы


71.6.1. Разметка строки таблицы

Все строки таблицы структурированы одинаково. Существует заголовок фиксированного размера (занимающий 23 байта на большинстве машин), за которым следует необязательная битовая карта нулевых значений, необязательное поле идентификатора объекта и пользовательские данные. Заголовок подробно описан в Таблица 71.4. Фактические пользовательские данные (столбцы строки) начинаются с смещения, указанного в t_hoff, которое всегда должно быть кратно максимальному расстоянию MAXALIGN для данной платформы. Битовая карта нулевых значений присутствует только в том случае, если установлен бит HEAP_HASNULL в t_infomask. Если она присутствует, она начинается сразу после фиксированного заголовка и занимает достаточное количество байтов для наличия одного бита на каждый столбец данных (то есть количество битов, равное количеству атрибутов в t_infomask2). В этом списке битов 1 бит указывает на ненулевое значение, а 0 бит - на нулевое значение. Когда битовая карта отсутствует, предполагается, что все столбцы имеют ненулевые значения. Идентификатор объекта присутствует только в том случае, если установлен бит HEAP_HASOID_OLD в t_infomask. Если он присутствует, он появляется непосредственно перед границей t_hoff. Любое заполнение, необходимое для того, чтобы сделать t_hoff кратным MAXALIGN, появляется между битовой картой нулевых значений и идентификатором объекта. (Это, в свою очередь, гарантирует, что идентификатор объекта имеет подходящее выравнивание).

Таблица 71.4. HeapTupleHeaderData Layout

ПолеТипДлинаОписание
t_xminTransactionId4 байташтамп вставки XID
t_xmaxTransactionId4 байтаметка удаления XID
t_cidCommandId4 байташтамп команды insert и/или delete (перекрывается с t_xvac)
t_xvacTransactionId4 байтаXID для операции VACUUM, перемещающей версию строки
t_ctidItemPointerData6 байттекущий TID этой или более новой версии строки
t_infomask2uint162 байтаколичество атрибутов, плюс различные флаги
t_infomaskuint162 байтаразличные флаги
t_hoffuint81 байтсмещение к пользовательским данным

Все подробности можно найти в файле src/include/access/htup_details.h.

Саму информацию о данных можно интерпретировать только с помощью информации, полученной из других таблиц, в основном из таблицы pg_attribute. Ключевые значения, необходимые для определения местоположения полей, - это attlen и attalign. Нет прямого способа получить конкретный атрибут, за исключением случаев, когда есть только поля фиксированной ширины и нет значений null. Все эти трюки реализованы в функциях heap_getattr, fastgetattr и heap_getsysattr.

Чтобы прочитать данные, вам нужно исследовать каждый атрибут по очереди. Сначала проверьте, является ли поле NULL согласно битовой карты NULL. Если это так, перейдите к следующему. Затем убедитесь, что у вас правильное выравнивание. Если поле имеет фиксированную ширину, то все байты просто размещаются. Если это поле переменной длины (attlen = -1), то все немного сложнее. Все типы данных переменной длины имеют общую структуру заголовка struct varlena, которая включает общую длину сохраненного значения и некоторые флаговые биты. В зависимости от флагов данные могут быть встроенными или находиться в таблице TOAST; они также могут быть сжатыми (см. Раздел 71.2).



[17] Фактически, использование этого формата страницы не требуется ни для таблиц, ни для методов доступа к индексам. Метод доступа к таблице heap всегда использует этот формат. Все существующие методы индексов также используют базовый формат, но данные, хранящиеся на метастраницах индексов, обычно не соответствуют правилам размещения элементов.