Иногда вам нужно определить какому файлу на диске соответствует таблица. У вас имеется путь, полный цифр, такой как base/16499/19401 и вы хотите разобраться в нем. Вы можете смотреть на сообщение об ошибке, которое упоминает имя файла, например:
ERROR: could not read block 11857 of relation base/16396/3720450: read only 0 of 8192 bytes
В поисках пути отношения
Вы можете увидеть путь до таблицы используя:
SELECT pg_relation_filepath('tablename');
но что насчет обратного процесса, получения названия объекта из пути до него? Существует функция pg_filenode_relation, которая кажется подходящей для этого… но чтобы ее использовать, необходимо быть подключенным к конкретной базе данных, к которой относится этот файл, что подразумевает знание этой связи.
Структура пути до файла
Вот каким образом можно определить путь до таблиц и баз данных в современной версии PostgreSQL. (Более старые версии используют другой формат, про который можно почитать здесь).
Имеется 3 основных варианта пути:
- Для файлов в табличном пространстве по умолчанию, base/database_oid/filenode id for the relation
- Для файлов из прочих табличных пространств: pg_tblspc / tablespace_oid / tablespace_version_subdir / database_oid / filenode id отношения
- Для общих отношений: global/filenode id отношения
Общие отношения будут обсуждаться в конце. Для первых же двух вариантов, которые являются самыми распространенными, с которыми вы будете чаще всего сталкиваться, последняя часть пути идентична, oid базы и oid отношения.
Обратите внимание, я употребил формулировку "filenode id отношения", а не "oid отношения". Это связано с тем, что PostgreSQL имеет карту relfilenode в файле с именем pg_relfilenode.map для каждой базы данных/табличного пространства. Имена файлов таблиц вовсе не обязательно совпадают с их oid'ами из pg_class, и они могут измениться после запуска VACUUM FULL, TRUNCATE и прочих. К примеру:
test=> select pg_relation_filepath('a');
pg_relation_filepath
----------------------
base/16385/101565
(1 row)
test=> VACUUM FULL a;
VACUUM
test=> select pg_relation_filepath('a');
pg_relation_filepath
----------------------
base/16385/101577
(1 row)
Итак. Как превратить этот путь обратно в имя отношения?
Oid'ы базы данных и filenode ids отношения
Предположим, Вы получили ошибку из начала этой статьи. Ее можно разбить на несколько частей:
- base: в табличном пространстве по умолчанию
- 16396: в базе данных с oid'ом 16396
- 3720450 filenode id для таблицы с oid'ом 3720450
после чего рассмотреть что значит каждая из них.
Определение базы данных по oid
Во-первых, необходимо подключиться к любой базе данных в этом PostgreSQl процессе и выполнить:
select datname
from pg_database
where oid = 16396
(или любой другой oid базы, который вы имеете). Это вернет Вам имя базы данных.
После этого необходимо подключиться к этой базе.
Обратное преобразование relfilenodes на 9.4 версии
Если Вы используете версию 9.4, или более свежую, то для Вас следующая часть проста:
SELECT pg_filenode_relation(0, 3720450);
(0 означает «табличное пространство по умолчанию»)
Эта функция выполняет обратное преобразование relfilenode за вас. Таким образом, она просто покажет Вам имя таблицы. Для него не будет показана связь с какой-то схемой, если полученное имя таблицы принадлежит текущему search_path; Можно использовать SET search_path = ''; перед выполнением функции, для того, чтобы был указан путь вплоть до схемы.
Вы должны быть подключены к правильной базе данных, или будет получен неправильный ответ, либо вообще ответ не будет получен.
Обратное преобразование relfilenodes на 9.3 версии
Если вы используете версию 9.3, или старее, необходимо подключиться к базе данных, в которой находится таблица и выполнить следующий запрос к pg_class:
select
n.nspname AS tableschema,
c.relname AS tablename
from pg_class c
inner join pg_namespace n on (c.relnamespace = n.oid)
where c.relfilenode = 3720450;
(или любой другой полученный relfilenode id таблицы).
Это расскажет Вам о том, к какой таблице относится эта ошибка.
Нет результатов?
Что ж, обычно это помогает.
Relfilenode также может быть нулем, это в свою очередь означает, что файл расположен посредством pg_relfilenode.map. Это является типичным сценарием для общих и некоторых системных каталогов, их индексов, TOAST таблиц и т.д. К примеру, это могут быть pg_database, pg_class и pg_proc.
Что насчет схемы?
Вы обратили внимание, что схема (пространство имен) не фигурирует в пути?
В PostgreSQL схемы является только пространством имен внутри базы данных. Они не имеют никакого влияния на то, где физически хранятся таблицы на диске.
Другие пути табличных пространств
Недавний случай, с которым я столкнулся, был следующей ошибкой:
ERROR: could not truncate file "pg_tblspc / 16709 / PG_9.3_201306121 / 16499/19401" to 8 blocks: Permission denied
Это не табличное пространство по умолчанию, так как путь начинается с pg_tblspc.
Сам процесс нахождения таблицы на самом деле тот же. Можно проигнорировать pg_tblspc/nnn/PG_n.n_nnnnnn/ часть и сфокусироваться сразу на database_oid/relation_oid, как описано выше для случаев с табличным пространством по умолчанию. Для этого стоит понимать что означает путь.
Таким образом текст ошибки разбивается на следующие части:
- pg_tblspc: это не табличное пространство по умолчанию
- 16709: это табличное пространство с oid'ом 16709
- PG_9.3_201306121: используется PostgreSQL 9.3 с версией каталога 201306121.
- 16499: база данных с oid'ом 16499
- 19401 таблица с relfilenode id 19401
Мы уже обсуждали часть об oid'е базы данных и табличном relfilenode id. Они не отличаются от табличного пространства, только начинаются в другом месте.
Так что насчет части с табличным пространством?
pg_tblspc является директорией в директории данных PostgreSQL, которая содержит в себе символьные ссылки ко всем положениям табличных пространств (или на NTFS, точки соединения для них). Каждая символьная ссылка названа в честь oid табличного пространства. Именно так PostgreSQL находит табличные пространства. SQL команды к табличным пространствам оперируют этими ссылками.
Oid относится к pg_tablespace записи для табличного пространства, как видно из:
select spcname
from pg_tablespace
where oid = 16709;
Внутри директории табличного пространства, имеется еще одна директория, имеющая название, соответствующее версии PostgreSQL. Оно статично для этой версии и единственное применение этому — это множественный доступ нескольких PostgreSQL процессов к одному табличному пространству, например, во время pg_upgrade. Как правило, имеется только одна запись.
В целом же, структура та же как и для base/ путей — сначала oid базы данных, потом oid отношения.
Глобальные (общие) таблицы
Имеется еще третья категория ошибок, в случае если Вы ее наблюдаете, то Вы определенно в беде. PostgreSQL имеет общие каталоги — таблицы, которые имеют одинаковое содержимое в каждой базе данных. Они обитают в специальном табличном пространстве с relfilenode id 16709.
Пути к ним начинаются с global вместо base и у них отсутствует компонент с oid'ом базы данных.
Общие каталоги не отмечены relfilenode в pg_class. То есть Вы не сможете посмотреть, к примеру, pg_database из pg_class. pg_filenode_relation возвращает null, независимо от того, вызывать ли его с oid'ом табличного пространства по умолчанию, или же с oid'ом глобального табличного пространства 1664.
Выяснение этого является темой для последующей статьи с разобранными связями.
Конечно же, если вы испытываете проблемы с общими каталогами, вы, вероятнее всего, не сможете в принципе запустить базу данных.
Имея дело с повреждениями
Повреждения базы данных не должно случаться. Но оно может произойти в любом случае. Это могут быть проблемы с железом, баги ядра, или файловой системы, ССД, которые врут о совершении надежных дисковых приливах, глючные сети хранения данных, ну и конечно же баги самого PostgreSQL. Если Вы подозреваете повреждение базы данных, перед тем как что либо предпринять, прочтите и действуйте по советам с вики странички о повреждениях.
Внутренности
Чтобы увидеть как все это работает, запустите макрос relpathbackend в src/include/common/relpath.h. Он вызывает GetRelationPath в src/common/relpath.c.
В мануале рассматривается структура базы данных на диске. Ссылка.
Автор: the_unbridled_goose