Организация данных
Низкий уровень
16
Авторские права
© Postgres Professional, 2017–2024
Авторы: Егор Рогов, Павел Лузанов, Илья Баштанов, Алексей Береснев
Фото: Олег Бартунов (монастырь Пху и пик Бхрикути, Непал)
Использование материалов курса
Некоммерческое использование материалов курса (презентации,
демонстрации) разрешается без ограничений. Коммерческое
использование возможно только с письменного разрешения компании
Postgres Professional. Запрещается внесение изменений в материалы
курса.
Обратная связь
Отзывы, замечания и предложения направляйте по адресу:
Отказ от ответственности
Компания Postgres Professional не несет никакой ответственности за
любые повреждения и убытки, включая потерю дохода, нанесенные
прямым или непрямым, специальным или случайным использованием
материалов курса. Компания Postgres Professional не предоставляет
каких-либо гарантий на материалы курса. Материалы курса
предоставляются на основе принципа «как есть» и компания Postgres
Professional не обязана предоставлять сопровождение, поддержку,
обновления, расширения и изменения.
2
Темы
Файлы данных
Слои: данные, карты видимости и свободного пространства
Длинные версии строк и TOAST
3
Слои объекта
NNN_vm
NNN_fsm
сегмент
1 ГБ
main
fsm
vm
NNN_fsm
NNN_fsm.1
NNN
NNN.1
NNN.2
pg_relation_size
Обычно каждому объекту БД, хранящему данные (таблице, индексу,
последовательности, материализованному представлению),
соответствует несколько слоев (forks). Каждый слой содержит
определенный вид данных.
Вначале слой содержит одиндинственный файл. Имя файла состоит
из числового идентификатора, к которому может быть добавлено
окончание, соответствующее имени слоя.
Файл постепенно растет и, когда его размер доходит до 1 Гбайта,
создается следующий файл этого же слоя. Такие файлы иногда
называют сегментами. Порядковый номер сегмента добавляется
в конец имени файла. Общий размер любого слоя показывает функция
pg_relation_size.
Ограничение размера файла в 1 Гбайт возникло исторически для
поддержки различных файловых систем, некоторые из которых не
умеют работать с файлами большого размера. Установить другой
размер можно только при сборке сервера из исходных кодов
(--with-segsize).
Таким образом, одному объекту БД на диске может соответствовать
несколько файлов. Для небольшой таблицы их будет 3, для индекса —
два. Все файлы объектов, принадлежащих одному табличному
пространству и одной БД, будут помещены в один каталог. Это
необходимо учитывать, потому что файловые системы могут не очень
хорошо работать с большим количеством файлов в каталоге.
4
Слои
Основной слой
собственно данные (версии строк)
существует для всех объектов
Слой инициализации (init)
«пустышка» для основного слоя
используется при сбое; только для нежурналируемых таблиц
Карта видимости (vm)
существует только для таблиц
Карта свободного пространства (fsm)
существует и для таблиц, и для индексов
Посмотрим теперь на типы слоев.
Основной слой — это собственно данные: версии строк таблиц или
индексные записи. Имена файлов основного слоя совпадают
с идентификатором. Основной слой существует для любых объектов.
Имена файлов слоя инициализации оканчиваются на «_init». Этот слой
существует только для нежурналируемых таблиц (созданных
с указанием UNLOGGED) и их индексов. Такие объекты ничем не
отличаются от обычных, но действия с ними не записываются в журнал
упреждающей записи. За счет этого работа с ними происходит быстрее,
но в случае сбоя их содержимое невозможно восстановить. При
восстановлении PostgreSQL просто удаляет все слои таких объектов и
записывает слой инициализации на место основного слоя. В результате
получается пустая таблица.
Слой vm (visibility map) — битовая карта видимости. Имена файлов
этого слоя оканчиваются на «_vm». Слой существует только для
таблиц; для индексов не поддерживается отдельная версионность.
Слой fsm (free space map) — карта свободного пространства. Имена
файлов этого слоя оканчиваются на «_fsm». Этот слой существует и
для таблиц, и для индексов.
Про две эти карты рассказывалось в модуле «Архитектура».
6
TOAST
Версия строки должна помещаться на одну страницу
можно сжать часть полей
можно вынести часть полей в отдельную toast-таблицу
можно сжать и вынести одновременно
Toast-таблица
находится в схеме pg_toast (pg_toast_temp_N)
поддержана собственным индексом
содержит фрагменты «длинных» значений размером меньше страницы
читается только при обращении к «длинному» полю
имеет собственную версионность
используется прозрачно для приложения
Любая версия строки в PostgreSQL должна целиком помещаться на
одну страницу. Для «длинных» версий строк применяется технология
TOAST — The Oversized Attributes Storage Technique. Она
подразумевает несколько стратегий работы с «длинными» полями.
Значение поля может быть сжато так, чтобы версия строки поместилась
на страницу. Значение может быть убрано из версии и перемещено
в отдельную служебную таблицу. Могут применяться и оба подхода:
какие-то значения будут сжаты, какие-то — перемещены, какие-то —
сжаты и перемещены одновременно.
Для каждой основной таблицы при необходимости создается отдельная
toast-таблица (и к ней специальный индекс). Такие таблицы и индексы
располагаются в отдельной схеме pg_toast и поэтому обычно не видны
(для временных таблиц используется схема pg_toast_temp_N
аналогично обычной pg_temp_N).
Версии строк в toast-таблице тоже должны помещаться на одну
страницу, поэтому длинные значения хранятся порезанными на
фрагменты. Из этих фрагментов PostgreSQL прозрачно для приложения
«склеивает» необходимое значение.
Toast-таблица используется только при обращении к длинному
значению. Кроме того, для toast-таблицы поддерживается своя
версионность: если обновление данных не затрагивает «длинное»
значение, новая версия строки будет ссылаться на то же самое
значение в toast-таблице — это экономит место.
8
Размер таблицы
pg_total_relation_size
pg_indexes_sizepg_table_size
Таблица TOAST Индексы
Как уже говорилось, размер отдельного слоя можно получить функцией
pg_relation_size. Чтобы не суммировать размеры отдельных слоев, есть
несколько функций, показывающих размеры таблицы:
pg_table_size показывает размер таблицы и ее toast-части (toast-
таблицы и обслуживающего ее индекса), но без обычных индексов.
Эту же функцию можно использовать для вычисления размера
отдельного индекса: и таблицы, и индексы являются отношениями,
и, несмотря на название, функция принимает любое отношение.
pg_indexes_size суммирует размеры всех индексов таблицы, кроме
индекса toast-таблицы.
pg_total_relation_size показывает полный размер таблицы, вместе
со всеми ее индексами.
10
Итоги
Объект представлен несколькими слоями
Слой состоит из одного или нескольких файлов-сегментов
Для «длинных» версий строк используется TOAST
11
Практика
1. Создайте нежурналируемую таблицу в пользовательском
табличном пространстве и убедитесь, что для таблицы
существует слой init.
Удалите созданное табличное пространство.
2. Создайте таблицу со столбцом типа text.
Какая стратегия хранения применяется для этого столбца?
Измените стратегию на external и вставьте в таблицу
короткую и длинную строки.
Проверьте, попали ли строки в toast-таблицу, выполнив
прямой запрос к ней. Объясните, почему.
12
Практика+
1. Создайте базу данных.
Сравните размер базы данных, возвращаемый функцией
pg_database_size, с общим размеров всех таблиц в этой базе.
Объясните полученный результат.
2. В TOAST поддерживаются два метода сжатия: pglz и lz4.
Проверьте средствами SQL, был ли PostgreSQL
скомпилирован с поддержкой этих методов.
3. Создайте текстовый файл размером больше 10 Мбайт.
Загрузите его содержимое в таблицу с текстовым полем,
сначала без сжатия, а затем используя каждый из
алгоритмов. Сравните итоговый размер таблицы и время
загрузки данных для трех вариантов.
1. Список таблиц базы данных можно получить из таблицы pg_class
системного каталога.
2. С помощью представления pg_config можно узнать, какие параметры
были установлены скрипту configure при сборке ПО сервера. Строка,
содержащая список параметров, велика; выделить необходимые
параметры можно функцией string_to_table().
3. Чтобы получить текст для эксперимента, можно взять достаточно
большой двоичный файл (например, исполняемый файл postgres)
и преобразовать его в текст. Для преобразования можно использовать
алгоритм Base32 (ключ -w0 отменяет переносы строк):
base32 -w0 < двоичный-файл > текстовый-файл