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

Вилки и файлы

Информация, связанная с отношениями, организована в виде нескольких ветвей (слоев) разных типов. Каждая вилка содержит определенный тип данных.

С самого начала ответвление физически отражается как один файл в файловой системе. Имя файла состоит из числового идентификатора (iod) и суффикса, отражающего тип ответвления.

Со временем файл растет, и когда его размер достигает 1 ГБ, создается еще один файл для той же ветки (файлы одной и той же вилки обычно называются сегментами) . Порядковый номер сегмента добавляется в конец файла в виде суффикса.

Ограничение размера файла в 1 ГБ существует из-за исторических причин поддержки разных файловых систем (некоторые из них не могут работать с файлами размером более 1 ГБ). Его можно изменить только в процессе компиляции и сборки PostgreSQL с помощью специального свойства (./configure — with-segsize).

Согласно сказанному выше, одно отношение имеет несколько файлов в файловой системе. Например, даже в небольшой таблице без индексов есть три файла, по одному на ответвление.

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

Есть несколько типов вилок по умолчанию.

Основная вилка

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

Файлы основного ответвления состоят только из числового идентификатора (он равен значению, хранящемуся в столбце relfilenode в таблице pg_class).

Давайте рассмотрим пример:

public> CREATE UNLOGGED TABLE t
                 (
                     a integer,
                     b numeric,
                     c text,
                     d json
                 );
public> INSERT INTO t
        VALUES (1, 2.0, 'foo', '{}');
public> SELECT pg_relation_filepath('t')

> base/5/16407

Каталог с именем base принадлежит табличному пространству pg_default, следующий подкаталог (5) принадлежит базе данных, а внутри него мы видим файл таблицы (16407):

public> SELECT oid FROM pg_database WHERE datname = 'postgres';

> 5

public> SELECT relfilenode FROM pg_class WHERE relname = 't';

> 16407

И мы можем проверить размер файла в файловой системе:

public> SELECT size FROM pg_stat_file('base/5/16407');

> 8192

Вилка инициализации

Эта вилка существует только для незарегистрированных таблиц (созданных с помощью ключевого слова UNLOGGED) и их индексов. Такие объекты абсолютно такие же, как и остальные, за исключением одного факта — любые действия над такими объектами не записываются в WAL (журнал упреждающей записи). Так, с такими объектами рабочий процесс происходит быстрее, но в случае сбоя данные не могут быть восстановлены непротиворечивым образом. PostgreSQL просто удаляет все форки таких объектов, в случае восстановления, и записывает init-форки в основной форк. В результате процесса восстановления у нас есть пустой объект.

Таблица t из приведенного выше примера создается без регистрации. Итак, у него есть форк инициализации. У него то же имя, что и у основного ответвления, но с суффиксом _init:

public> SELECT size FROM pg_stat_file('base/5/16407_init')

>0

Свободная космическая карта

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

Имена файлов карты свободного пространства имеют суффикс _fsm. Но файлы создаются только тогда, когда это необходимо. Самый простой способ инициировать создание файла — просто очистить таблицу:

public> VACUUM t;
public> SELECT size FROM pg_stat_file('base/5/16407_fsm')

>24576

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

Для индексов также существует карта свободного пространства. Но нельзя добавить строку индекса на случайную страницу (например, для B-дерева страница вычисляется по методу сортировки). Таким образом, карта свободного пространства содержит абсолютно чистые страницы, которые можно использовать только повторно.

Карта видимости

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

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

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

Файл ветвления имеет имя, содержащее суффикс _vm. Такие форки занимают мало места на диске:

public> SELECT size FROM pg_stat_file('base/5/16407_vm');

>8192

Карта видимости существует для таблиц, но не для индексов.

В этой статье мы рассказали о вилках. Как они хранятся и как они организованы. В следующей статье мы рассмотрим страницы и технологию TOAST.

Спасибо за чтение!

Рекомендации

PostgreSQL 15 изнутри. — М.: ДМК Пресс, 2023. — 662 с. ISBN 978–5–93700–178–8