Макет страницы базы данных
Эта страница переведена нейросетью GigaChat.
Этот раздел предоставляет обзор формата страниц, используемого в таблицах и индексах PostgreSQL. Последовательности и таблицы TOAST отформатированы так же, как обычная таблица.
В следующем объяснении предполагается, что байт содержит 8 бит. Кроме того, термин элемент относится к отдельному значению данных, которое хранится на странице. В таблице элемент представляет собой строку; в индексе элемент является записью индекса.
Каждая таблица и индекс хранятся в виде массива страниц фиксированного размера (обычно 8 КБ, хотя при компиляции сервера можно выбрать другой размер страницы). В таблице все страницы логически эквивалентны, поэтому конкретный элемент (строка) может храниться на любой странице. В индексах первая страница обычно зарезервирована для метастраницы, содержащей управляющую информацию, а внутри индекса могут быть разные типы страниц в зависимости от метода доступа к индексу.
Таблица 73.2 показывает общую структуру страницы. На каждой странице есть пять частей.
Таблица 73.2. Общая компоновка страницы
Элемент | Описание |
---|---|
Заголовок страницы | Длина 24 байта. Содержит общую информацию о странице, включая указатели свободного пространства. |
Данные элемента | Массив идентификаторов элементов, указывающих на фактические элементы. Каждая запись представляет собой пару (смещение, длина). 4 байта на элемент. |
Свободное пространство | Не распределенное пространство. Новые идентификаторы элементов распределяются с начала этой области, новые элементы - с конца. |
Элементы | Фактические элементы сами по себе. |
Специальное пространство | Метод доступа к индексу специфичен для конкретных данных. Разные методы хранят разные данные. Пусто в обычных таблицах. |
Первые 24 байта каждой страницы состоят из заголовка страницы (PageHeaderData
). Первое поле отслеживает последнюю запись WAL, связанную с этой страницей. Второе поле содержит контрольную сумму страницы, если включены контрольные суммы данных. Далее следует двухбайтовое поле, содержащее флаговые биты. За этим следуют три двухбайтовых целых поля (pd_lower
, pd_upper
и pd_special
). Они содержат смещения в байтах от начала страницы до начала неразмеченного пространства, до конца неразмеченного пространства и до начала специального пространства. Следующие два байта заголовка страницы, 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 на странице.
Макет PageHeaderData:
Поле | Тип | Длина | Описание |
---|---|---|---|
pd_lsn | PageXLogRecPtr | 8 байт | LSN: следующий байт после последнего байта записи WAL для последнего изменения этой страницы |
pd_checksum | uint16 | 2 байта | Контрольная сумма страницы |
pd_flags | uint16 | 2 байта | Флаги битов |
pd_lower | Индекс местоположения | 2 байта | Смещение начала свободного пространства |
pd_upper | Индекс местоположения | 2 байта | Смещение до конца свободного пространства |
pd_специальный | Индекс местоположения | 2 байта | Смещение начала специального пространства |
pd_размер_страницы_версия | uint16 | 2 байта | Информация о версии размера и макета страницы |
pd_prune_xid | Идентификатор транзакции | 4 байта | Самый старый непроходимый XMAX на странице или ноль, если его нет |
Все подробности можно найти в src/include/storage/bufpage.h
.
После заголовка страницы следуют идентификаторы элементов (ItemIdData
), каждый из которых требует четыре байта. Идентификатор элемента содержит смещение в байтах до начала элемента, его длину в байтах и несколько бит атрибутов, которые влияют на его интерпретацию. Новые идентификаторы элементов выделяются по мере необходимости с начала неразмеченного пространства. Количество присутствующих идентификаторов элементов может быть определено путем просмотра pd_lower
, который увеличивается для выделения нового идентификатора. Поскольку идентификатор элемента никогда не перемещается до тех пор, пока он не будет освобожден, его индекс может использоваться в долгосрочной перспективе для ссылки на элемент, даже когда сам элемент перемещается по странице для компактного свободного места. Фактически, каждая ссылка на элемент (ItemPointer
, также известная как CTID
) создается PostgreSQL и состоит из номера страницы и индекса идентификатора элемента.
Сами элементы хранятся в пространстве, выделенном с конца неразмеченного пространства. Точная структура варьируется в зависимости от того, что должна содержать таблица. Таблицы и последовательности используют структуру под названием HeapTupleHeaderData
, которая описана ниже.
Последний раздел - это «специальный разде л», который может содержать все, что метод доступа хочет сохранить. Например, индексы b-дерева хранят ссылки на левую и правую соседние страницы, а также некоторые другие данные, относящиеся к структуре индекса. Обычные таблицы вообще не используют специальный раздел (это указывается установкой pd_special
равной размеру страницы).
На рисунке 73.1 показано, как эти части расположены на странице.
Рисунок 73.1. Макет страницы
Макет строки таблицы
Все строки таблиц структурированы одинаково. Существует заголовок фиксированного размера (занимающий 23 байта на большинстве машин), за которым следует необязательная карта нулей, необязательное поле идентификатора объекта и пользовательские данные. Фактические пользовательские данные (столбцы строки) начинаются с смещения, указанного в t_hoff
, которое всегда должно быть кратным расстоянию MAXALIGN для платформы. Карта нулей присутствует только в том случае, если бит HEAP_HASNULL установлен в t_infomask
. Если она присутствует, то начинается сразу после фиксированного заголовка и занимает достаточно байтов, чтобы иметь один бит на каждый столбец данных (то есть количество битов, равное количеству атрибутов в t_infomask2
). В этом списке битов бит 1 указывает не-нулевое значение, а бит 0 - нулевое. Когда карта нулей отсутствует, предполагается, что все столбцы не являются нулевыми. Идентификатор объекта присутствует только в том случае, если бит HEAP_HASOID_OLD установлен в t_infomask
. Если он присутствует, он появляется прямо перед границей t_hoff
. Любая необходимая подгонка для того, чтобы сделать t_hoff
кратной MAXALIGN, появится между картой нулей и идентификатором объекта. (Это, в свою очередь, гарантирует, что идентификатор объекта правильно выровнен.)
Макет HeapTupleHeaderData:
Поле | Тип | Длина | Описание |
---|---|---|---|
t_xmin | Идентификатор транзакции | 4 байта | вставить штамп XID |
t_xmax | Идентификатор транзакции | 4 байта | удалить штамп XID |
t_cid | Идентификатор команды | 4 байта | вставка и/или удаление штампа CID (перекрывается с t_xvac) |
t_xvac | Идентификатор транзакции | 4 байта | XID для операции VACUUM перемещения версии строки |
t_ctid | ItemPointerData | 6 байт | текущий TID этой или более новой версии строки |
t_infomask2 | uint16 | 2 байта | количество атрибутов плюс различные флаги |
t_infomask | uint16 | 2 байта | различные флаги бит |
t_hoff | uint8 | 1 байт | смещение до пользовательских данных |
Все подробности можно найти в src/include/access/htup_details.h
.
Интерпретация фактических данных может быть выполнена только с использованием информации, полученной из других таблиц, в основном pg_attribute
. Ключевые значения, необходимые для идентификации расположения полей, это attlen
и attalign
. Нет прямого способа получить конкретный атрибут, за исключением случаев, когда используются поля фиксированной ширины и отсутствуют нулевые значения. Все эти уловки заключены в функции heap_getattr, fastgetattr и heap_getsysattr.
Чтобы прочитать данные, нужно проверить каждый атрибут по очереди. Сначала проверьте, является ли поле NULL согласно битовой карте нулевых значений. Если это так, перейдите к следующему. Затем убедитесь, что у вас правильное выравнивание. Если поле имеет фиксированную ширину, то все байты просто размещаются. Если это поле переменной длины (attlen = -1), то это немного сложнее. Все типы данных переменной длины имеют общую структуру заголовка struct varlena
, которая включает общую длину хранимого значения и некоторые флаги. В зависимости от флагов данные могут находиться либо внутри, либо в таблице TOAST; они также могут быть сжаты (см. раздел «TOAST»).