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

Функции осмотра страниц

OrioleDB предоставляет SQL-функции для осмотра страниц B-дерева, цепочек отката и внутренних системных деревьев. Они являются частью расширения orioledb (а не отдельного модуля contrib PostgreSQL pageinspect) и возвращают читаемые строки вместо сырых байтов страниц.

Данные функции предназначены для отладки и разработки: понимания раскладки дерева, истории отката MVCC, хранения TOAST, мостовых индексов и системных деревьев каталогов.

Требования​

Для использования функций осмотра страниц должны выполняться следующие условия:

  • Расширение orioledb должно быть создано с помощью команды CREATE EXTENSION orioledb.
  • Пользовательские таблицы и индексы, передаваемые функциям осмотра, должны использовать метод доступа OrioleDB.

Страницы могут считываться как из разделяемой памяти, так и из дискового хранилища. При вытеснении дочерней страницы функция read_page_from_disk() считывает ее с использованием нижней ссылки родительской страницы. При наличии активной нижней ссылки операция ввода-вывода завершается перед продолжением обхода.

Логические номера страниц​

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

Страницы нумеруются в порядке обхода в ширину (по уровням):

  1. Корневая страница всегда имеет pageno = 0.
  2. Дочерние страницы обнаруживаются через нижние ссылки на внутренних страницах, слева направо в пределах каждой страницы.
  3. Каждый уровень B-дерева посещается сверху вниз.
  4. Правые ссылки (сibling-страницы после разделения) не используются для обхода или нумерации.

Данная схема стабильна для заданной формы дерева в памяти и соответствует тому, как ссылки нижних связей отображаются в выводе *_page_items.

Пример: после создания небольшой таблицы корень соответствует странице 0, а листовые страницы имеют level = 0:

SELECT pageno, level, is_leaf
FROM orioledb_bt_pages('my_table_pkey')
ORDER BY pageno;

Семейства функций​

Функции сгруппированы по осматриваемому дереву. Большинство семейств имеют одинаковую структуру возврата.

ПрефиксЦельПервый аргумент
orioledb_bt_*Любой индекс OrioleDB (первичный, вторичный или мостовой индекс)regclass — имя индекса
orioledb_table_*Дерево первичного индекса таблицы/материализованного представления OrioleDBregclass — имя таблицы/материализованного представления
orioledb_toast_*Дерево TOAST таблицы OrioleDBregclass — имя таблицы
orioledb_bridge_*Дерево мостового индекса (таблицы с index_bridging)regclass — имя таблицы
orioledb_row_undo / orioledb_page_undoЦепочки журнала отката (undo)regclass — таблица или индекс
orioledb_systree_*Внутренние каталогные деревья OrioleDBtext — имя дерева

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

Перечисление страниц​

  • orioledb_bt_pages(relid) — перечислить все страницы в дереве индекса;
  • orioledb_table_pk_pages(relid) — перечислить все страницы в дереве первичного ключа таблицы;
  • orioledb_systree_pages(tree_name) — аналогично для системного дерева.

Возвращается по одной строке на страницу:

СтолбецТипОписание
pagenoint8Логический номер страницы (корень = 0)
levelint4Уровень B-дерева (0 = листовой)
is_leafboolЯвляется ли страница листовой
undo_locationint8Начало цепочки отката на уровне страницы, если присутствует
is_on_diskbooltrue, если изображение страницы считано с диска, а не из разделяемой памяти

Мета-страница​

orioledb_bt_meta_page, orioledb_table_meta_page, orioledb_toast_meta_page, orioledb_bridge_meta_page, orioledb_systree_meta_page и orioledb_systree_toast_meta_page возвращают единственную строку с описанием мета-страницы дерева.

СтолбецОписание
meta_loadedВалидна ли мета-страница
root_page_pagenoЛогический номер корневой страницы (обычно 0)
root_page_change_countСчетчик изменений корневой страницы
checkpoint_numНомер контрольной точки, записанный на мета-странице
page_change_countСчетчик изменений мета-страницы
num_free_blocksКоличество свободных блоков в дереве свободного пространства
datafile_length_0, datafile_length_1Длины файлов данных на диске (двойная буферизация)
ctidТекущий счетчик CTID
bridge_ctidСчетчик CTID моста
leaf_pages_numКоличество листовых страниц
num_seq_scansАктивные последовательные сканирования
to_be_freed_on_seq_scan_releaseСтраницы, ожидающие освобождения после завершения последовательного сканирования
dirty_flag1, dirty_flag2Флаги измененности
punch_holes_chkp_numНомер контрольной точки для операций создания отверстий
free_buf_location, free_buf_cur_page_num, free_buf_file_page_numСостояние свободного буфера

orioledb_bridge_meta_page возвращает ошибку, если у таблицы отсутствует мостовой индекс.

Заголовок страницы​

orioledb_bt_page_header, orioledb_table_page_header, orioledb_toast_page_header, orioledb_bridge_page_header, orioledb_systree_page_header и orioledb_systree_toast_page_header принимают (relid_or_tree_name, pageno) и возвращают одну строку:

СтолбецОписание
pagenoЛогический номер страницы
levelУровень B-дерева
flagsФлаги страницы
is_leaf, is_leftmost, is_rightmostФлаги положения страницы
csnПоследовательный номер фиксации (текст)
undo_locationНачало цепочки отката на уровне страницы
page_change_countСчетчик изменений страницы
checkpoint_numНомер контрольной точки
items_countКоличество элементов индекса
chunks_countКоличество фрагментов страницы
free_spaceСвободное место на странице
hikeyРазделительный ключ в формате JSON (null на самых правых страницах)

Элементы страницы​

orioledb_bt_page_items, orioledb_table_page_items, orioledb_toast_page_items, orioledb_bridge_page_items, orioledb_systree_page_items и orioledb_systree_toast_page_items принимают (relid_or_tree_name, pageno) и возвращают по строке на каждый элемент или фрагмент на странице.

СтолбецОписание
pagenoЛогический номер страницы
itemoffsetСмещение элемента на странице
chunk_offsetСмещение фрагмента в пределах страницы
is_leafЯвляется ли содержащая страница листовой
tup_csnCSN кортежа (для листовых элементов)
oxidИдентификатор транзакции OrioleDB
deletedСтатус удаления: none, deleted, moved_partitions, pk_changed
format_flagsФлаги формата кортежа
chain_has_locksИмеются ли блокировки в цепочке отката
prev_undo_locationПредыдущая запись отката для данной строки
tupleЛистовой кортеж в формате JSON (имена столбцов и значения)
keyКлюч индекса в формате JSON
downlink_pagenoЛогический номер дочерней страницы (внутренние элементы, нижние ссылки в памяти и на диске)
downlink_typeТип нижней ссылки: memory, io или disk

На внутренних страницах разделительные ключи отображаются в key; на листовых — заполняются tuple и key. Для системных деревьев JSON кортежа и ключа используют поле repr с представительной формой вывода дерева.

orioledb_systree_toast_page_items фильтрует только элементы TOAST/фрагменты. Допустимо для системных кешей O_TABLES, O_INDICES и нативного TOAST; для других деревьев возвращается ошибка systree "<name>" has no toast data.

Осмотр отката​

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

Откат на уровне строк​

orioledb_row_undo(relid, undo_location) следует по цепочке отката модификации строки (от новой к старой через ссылки prev_undo_location).

СтолбецОписание
chain_orderПозиция в цепочке (0 = начальная запись)
undo_locationПозиция данной записи отката
prev_undo_locationСсылка на предыдущую версию
record_typeТип данных: modify, lock или other
actionДействие: insert, update, delete или lock
pagenoЛогическая страница, к которой применено изменение
page_change_countСчетчик изменений страницы на момент модификации
tup_csn, oxid, deleted, format_flags, chain_has_locksМетаданные транзакции и кортежа
tupleПредыдущее изображение кортежа в формате JSON (при наличии)

Пример — отслеживание версий после обновлений:

CREATE TABLE t (i int PRIMARY KEY, v text) USING orioledb;
INSERT INTO t VALUES (1, 'a');
UPDATE t SET v = 'b' WHERE i = 1;
UPDATE t SET v = 'c' WHERE i = 1;

-- Найти prev_undo_location из текущей строки
SELECT prev_undo_location
FROM orioledb_table_page_items('t', (
SELECT pageno FROM orioledb_bt_pages('t_pkey') WHERE level = 0 LIMIT 1
))
WHERE tuple->>'v' = 'c';

-- Обойти цепочку (использовать значение, полученное выше)
SELECT chain_order, action, tuple
FROM orioledb_row_undo('t', <prev_undo_location>)
ORDER BY chain_order;

Откат на уровне страниц​

orioledb_page_undo(relid, undo_location) следует по цепочке отката изображения страницы из undo_location страницы (обычно устанавливается после массовых обновлений или разделений/слияний страниц).

СтолбецОписание
chain_orderПозиция в цепочке
undo_locationПозиция данной записи изображения страницы
prev_undo_locationПредыдущее изображение страницы
image_typecompact, split, merge или invalid
csnCSN изображения страницы
levelУровень B-дерева сохраненной страницы

Системные деревья​

Функции для системных деревьев принимают имя дерева (регистр не учитывается) вместо regclass. Допустимые имена:

ИмяНазначение
SHARED_ROOT_INFOРазделяемые метаданные корня для загруженных деревьев
O_TABLESДескрипторы таблиц OrioleDB
O_INDICESДескрипторы индексов OrioleDB
OPCLASS_CACHEКеш классов операторов
ENUM_CACHE, ENUMOID_CACHEКеши перечислимых типов
RANGE_CACHE, MULTIRANGE_CACHEКеши типов диапазонов
CLASS_CACHEКеш pg_class
EXTENTS_OFF_LEN, EXTENTS_LEN_OFFДеревья экстентов свободного пространства
PROC_CACHE, TYPE_CACHE, AGG_CACHE, OPER_CACHEКеши каталогов
AMOP_CACHE, AMPROC_CACHE, AMOP_STRAT_CACHEКеши методов доступа
COLLATION_CACHE, DATABASE_CACHEКеши правил сортировки и баз данных
EVICTED_DATAВытесненные данные страниц
CHKP_NUMНомера контрольных точек
CATALOG_XID_UNDO_LOCATIONПозиции отката XID каталога

Пример:

SELECT pageno, level FROM orioledb_systree_pages('SHARED_ROOT_INFO') ORDER BY pageno;
SELECT * FROM orioledb_systree_meta_page('O_TABLES');
SELECT chunks_count FROM orioledb_systree_page_header('CLASS_CACHE', 0);

Типичные рабочие процессы​

Осмотр индекса после загрузки данных​

CREATE TABLE o_test (id int PRIMARY KEY, val text) USING orioledb;
CREATE INDEX o_test_val_idx ON o_test (val);
INSERT INTO o_test SELECT g, 'val' FROM generate_series(0, 10000) g;

SELECT * FROM orioledb_bt_meta_page('o_test_pkey');
SELECT * FROM orioledb_bt_pages('o_test_pkey');
SELECT * FROM orioledb_bt_page_header('o_test_pkey', 0);
SELECT * FROM orioledb_bt_page_items('o_test_pkey', 1);

В функции orioledb_bt_* можно передавать regclass таблицы или индекса.

Сравнение таблицы и первичного индекса​

SELECT * FROM orioledb_table_page_items('o_test', 1);
-- Эквивалентно:
SELECT * FROM orioledb_bt_page_items('o_test_pkey', 1);

Осмотр вытесненных (дисковых) страниц​

После вытеснения листовые страницы могут отсутствовать в разделяемой памяти. Функции pageinspect по-прежнему перечисляют и декодируют их:

INSERT INTO big SELECT g, g FROM generate_series(1, 5000) g;
SELECT orioledb_evict_pages('big'::regclass, 0);

SELECT pageno, is_on_disk
FROM orioledb_bt_pages('big_pkey')
WHERE is_on_disk
ORDER BY pageno;

SELECT count(*) FROM orioledb_table_page_items('big', <disk_leaf_pageno>);

Осмотр хранения TOAST​

CREATE TABLE big (i int PRIMARY KEY, v text) USING orioledb;
INSERT INTO big4 (i, v)
VALUES (
1,
(
SELECT string_agg(md5(random()::text), '')
FROM generate_series(1, 2000)
)
);

SELECT * FROM orioledb_toast_page_items('big', 0);

Осмотр мостового индекса​

Требует таблицы, созданной с USING orioledb WITH (index_bridging), и мостового индекса:

CREATE TABLE bridged (i int NOT NULL, j int)
USING orioledb WITH (index_bridging);
CREATE INDEX bridged_heap_j ON bridged (j) WITH (orioledb_index = off);
CREATE INDEX bridged_j ON bridged (j);

SELECT * FROM orioledb_bridge_page_items('bridged', 0);

Сравнение с pageinspect PostgreSQL​

PostgreSQL pageinspectФункции pageinspect OrioleDB
РасширениеОтдельный модуль contrib pageinspectВстроено в orioledb
ВводФизический номер блока (blkno)Логический pageno (корень = 0)
ВыводСырые страницы bytea и бинарные поляКортежи/ключи JSON, декодированные метаданные
ОбластьКуча, btree, gin, gist, hash, brinДеревья btree OrioleDB, откат, системные деревья
Модель храненияРазделяемые буферы PostgreSQL / файлыРазделяемая память OrioleDB и файлы деревьев на диске

Таблицы OrioleDB организованы по индексу; отдельного потока кучи для осмотра через get_raw_page не существует. Вместо этого использовать orioledb_table_* или orioledb_bt_* на первичном индексе.

Карта страниц перестраивается при каждом вызове функции и хранит только легкие метаданные (логический номер страницы, номер блока в памяти с pageChangeCount или дисковую нижнюю ссылку, а также кешированные поля заголовка для перечисления страниц). Дисковые и хранящиеся в памяти страницы копируются по одной в временный буфер размером 8 КБ с помощью o_btree_try_read_page(); буферы не удерживаются для всего дерева. Если pageChangeCount больше не совпадает (например, из-за вытеснения страницы и изменения нижней ссылки родительской страницы), функции pageinspect повторно обходят дерево для обновления позиции и повторяют попытку (до 100 раз). Одновременные разделения или операции DDL все еще могут изменить форму дерева настолько, что осмотр завершится ошибкой.