Функции осмотра страниц
OrioleDB предоставляет SQL-функции для осмотра страниц B-дерева, цепочек отката и внутренних системных деревьев. Они являются частью расширения orioledb (а не отдельного модуля contrib PostgreSQL pageinspect) и возвращают читаемые строки вместо сырых байтов страниц.
Данные функции предназначены для отладки и разработки: понимания раскладки дерева, истории отката MVCC, хранения TOAST, мостовых индексов и системных деревьев каталогов.
Требования
Для использования функций осмотра страниц должны выполняться следующие условия:
- Расширение
orioledbдолжно быть создано с помощью командыCREATE EXTENSION orioledb. - Пользовательские таблицы и индексы, передаваемые функциям осмотра, должны использовать метод доступа OrioleDB.
Страницы могут считываться как из разделяемой памяти, так и из дискового хранилища. При вытеснении дочерней страницы функция read_page_from_disk() считывает ее с использованием нижней ссылки родительской страницы. При наличии активной нижней ссылки операция ввода-вывода завершается перед продолжением обхода.
Логические номера страниц
Все ориентированные на страницы функции используют логические номера страниц (pageno), а не физические номера блоков или идентификаторы блоков в памяти.
Страницы нумеруются в порядке обхода в ширину (по уровням):
- Корневая страница всегда имеет
pageno = 0. - Дочерние страницы обнаруживаются через нижние ссылки на внутренних страницах, слева направо в пределах каждой страницы.
- Каждый уровень B-дерева посещается сверху вниз.
- Правые ссылки (с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_* | Дерево первичного индекса таблицы/материализованного представления OrioleDB | regclass — имя таблицы/материализованного представления |
orioledb_toast_* | Дерево TOAST таблицы OrioleDB | regclass — имя таблицы |
orioledb_bridge_* | Дерево мостового индекса (таблицы с index_bridging) | regclass — имя таблицы |
orioledb_row_undo / orioledb_page_undo | Цепочки журнала отката (undo) | regclass — таблица или индекс |
orioledb_systree_* | Внутренние каталогные деревья OrioleDB | text — имя дерева |
Для организованных по индексу таблиц orioledb_table_page_items возвращает те же строки, что и orioledb_bt_page_items на первичном индексе для той же логической страницы.
Перечисление страниц
orioledb_bt_pages(relid)— перечислить все страницы в дереве индекса;orioledb_table_pk_pages(relid)— перечислить все страницы в дереве первичного ключа таблицы;orioledb_systree_pages(tree_name)— аналогично для системного дерева.
Возвращается по одной строке на страницу:
| Столбец | Тип | Описание |
|---|---|---|
pageno | int8 | Логический номер страницы (корень = 0) |
level | int4 | Уровень B-дерева (0 = листовой) |
is_leaf | bool | Является ли страница листовой |
undo_location | int8 | Начало цепочки отката на уровне страницы, если присутствует |
is_on_disk | bool | true, если изображение страницы считано с диска, а не из разделяемой памяти |
Мета-страница
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_csn | CSN кортежа (для листовых элементов) |
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_type | compact, split, merge или invalid |
csn | CSN изображения страницы |
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 все еще могут изменить форму дерева настолько, что осмотр завершится ошибкой.