# relfilenode и форки отношения _Хранение и формат на диске · PostgreSQL Knowledge Base_ **TL;DR:** Каждая таблица и индекс лежит на диске как набор файлов - форков. Главный форк хранит сами данные, _fsm - карту свободного места, _vm - карту видимости, _init - шаблон для нежурналируемых таблиц. Имя файла - это relfilenode, и оно не равно OID после перезаписи таблицы. Таблица в PostgreSQL - это не один файл. Это несколько параллельных файлов, каждый со своей ролью. Их называют **форками** (forks). Главный форк хранит строки, остальные - служебные карты, которые ускоряют работу. Найти главный файл таблицы можно из SQL: ```sql SELECT pg_relation_filepath('flights'); -- base/16384/16503 ``` Путь читается так: `base/` - каталог обычных баз, `16384` - OID базы, `16503` - **relfilenode**, имя файла этой таблицы. Конкретные числа у тебя будут другими: OID назначаются по мере создания объектов. Подробнее про путь до базы - в [cluster-pgdata](/courses/postgres/kb/cluster-pgdata.md). ## Четыре форка | Форк | Суффикс файла | Что хранит | |---|---|---| | main | нет | сами строки таблицы (или записи индекса) | | fsm | `_fsm` | карта свободного места по страницам | | vm | `_vm` | карта видимости и заморозки | | init | `_init` | пустой шаблон для нежурналируемых таблиц | Главный форк есть всегда. Остальные появляются по мере надобности. Сразу после `CREATE TABLE` на диске только главный файл. Карты `_fsm` и `_vm` создаются позже - обычно при первом `VACUUM` или когда серверу понадобится свободное место под вставку. Про эти карты есть отдельные заметки: [free-space-map](/courses/postgres/kb/free-space-map.md) и [visibility-map](/courses/postgres/kb/visibility-map.md). ```sql -- сразу после CREATE TABLE: только главный форк SELECT pg_relation_filepath('t'); -- base/16384/24576 (число своё) -- файлов 24576_fsm и 24576_vm ещё нет VACUUM t; -- теперь карты появятся ``` ## relfilenode - это не OID У отношения есть OID (внутренний идентификатор в системном каталоге) и relfilenode (имя файла на диске). Поначалу они совпадают. Но команды, которые переписывают таблицу целиком - `VACUUM FULL`, `TRUNCATE`, `CLUSTER`, `ALTER TABLE`, меняющий тип колонки, - создают новый файл с новым relfilenode и удаляют старый. OID при этом остаётся прежним. Поэтому в коде, который должен пережить перезапись, ссылаются на OID, а не на имя файла. А `pg_relation_filepath()` всегда показывает актуальный relfilenode, считывая его из каталога. ## Сегменты по гигабайту Один форк не растёт одним бесконечным файлом. Когда главный форк переваливает за 1 ГБ, PostgreSQL продолжает в файле `relfilenode.1`, затем `relfilenode.2` и так далее. Это упрощает жизнь старым файловым системам с лимитом на размер файла и операциям копирования. Для тебя как читателя это значит: большая таблица на диске - это пачка файлов `16385`, `16385.1`, `16385.2`, а не один гигантский. ## Зачем это знать Когда видишь в `base/16384/` россыпь файлов с суффиксами и номерами, это не хаос, а форки и сегменты конкретных таблиц. Размер `_fsm` и `_vm` крошечный по сравнению с главным форком, но именно они позволяют вставке не сканировать таблицу в поисках места, а вакууму - пропускать страницы, где всё уже видимо. ## Команды ```sql SELECT pg_relation_filepath('flights'); ``` Путь к главному форку таблицы относительно PGDATA ```sql SELECT relname, relfilenode, oid FROM pg_class WHERE relname = 'flights'; ``` Сравнить OID и relfilenode отношения (после VACUUM FULL разойдутся) ```sql SELECT pg_relation_size('flights', 'fsm'); ``` Размер форка _fsm в байтах (0, если ещё не создан) ```sql SELECT pg_table_size('flights'), pg_total_relation_size('flights'); ``` Размер таблицы со всеми форками и с индексами ## См. также - [Кластер, PGDATA и каталоги](/courses/postgres/kb/cluster-pgdata.md) - [Free Space Map (карта свободного места)](/courses/postgres/kb/free-space-map.md) - [Visibility Map (карта видимости)](/courses/postgres/kb/visibility-map.md) - [Раскладка страницы 8 КБ](/courses/postgres/kb/page-layout.md)