Прямой ответ
TOAST — механизм PostgreSQL для хранения значений, превышающих размер страницы. Большое значение сжимается и либо остаётся в строке, либо переносится в отдельную таблицу, при этом обращение к нему прозрачно.
| Режим хранения | Сжатие | Вынос за строку |
|---|---|---|
PLAIN |
Нет | Нет |
EXTENDED |
Да | Да |
EXTERNAL |
Нет | Да |
MAIN |
Да | Только при необходимости |
-- Режим хранения задаётся для столбца
ALTER TABLE documents ALTER COLUMN body SET STORAGE EXTERNAL;
-- Размер значения и таблицы
SELECT pg_column_size(body) AS value_bytes,
pg_size_pretty(pg_total_relation_size('documents')) AS table_size
FROM documents LIMIT 5;
Режим EXTENDED применяется по умолчанию для типов, допускающих сжатие и вынос. Режим EXTERNAL исключает сжатие, что ускоряет доступ к подстрокам, поскольку значение не требуется распаковывать целиком.
Сжатие pglz и lz4, влияние на чтение
PostgreSQL поддерживает два метода сжатия для TOAST, и выбор влияет на соотношение объёма и времени.
pglz — метод по умолчанию, реализованный в самой базе. Не требует внешних зависимостей, поэтому доступен в любой установке.
lz4 — доступен при сборке базы с поддержкой соответствующей библиотеки. Сжатие и распаковка выполняются быстрее, коэффициент может отличаться.
Практическое следствие: метод сжатия выбирается по тому, что ограничивает — объём хранения либо время доступа к большим значениям.
Отдельный аспект — доступ к части значения. Сжатое значение распаковывается целиком, поэтому чтение подстроки из сжатого столбца требует распаковки всего значения. Режим EXTERNAL исключает сжатие, поэтому доступ к подстроке выполняется без распаковки.
Три признака того, что режим хранения выбран неверно.
Частое чтение подстрок из сжатого столбца. Каждое обращение распаковывает значение целиком, поэтому время запроса растёт.
Значения чуть больше страницы. Вынос за строку добавляет обращение к отдельной таблице, поэтому чтение строки требует двух обращений.
Данные уже сжаты. Сжатие бинарных данных без избыточности расходует время без уменьшения объёма.
О влиянии выноса значений на очистку таблицы — в статье про vacuum и bloat: строки TOAST-таблицы также требуют обслуживания.
О хранении документов в формате JSON — в статье про jsonb: значения JSON также проходят через TOAST при превышении размера страницы.
Диагностика объёма выполняется системными функциями, которые возвращают размер значения и размер отношения; о разделении больших таблиц — в статье про секционирование.
Практика: настройка хранения больших значений
1. Определите характер доступа. Чтение значения целиком либо обращение к подстрокам определяет режим хранения.
2. Используйте EXTERNAL при доступе к подстрокам. Отсутствие сжатия исключает распаковку всего значения.
3. Используйте EXTENDED при чтении целиком. Сжатие сокращает объём, а распаковка выполняется один раз на обращение.
4. Не сжимайте уже сжатые данные. Бинарные значения без избыточности не дают выигрыша при сжатии.
5. Измеряйте размер значения. Функция размера столбца показывает фактический объём, занимаемый значением.
6. Учитывайте обслуживание TOAST-таблицы. Строки вынесенных значений также требуют очистки.
7. Проверяйте метод сжатия в установке. Доступность lz4 зависит от сборки базы данных.
Рекомендации:
- Различайте сжатие и вынос за строку. Режим хранения задаёт оба свойства независимо, поэтому выбираются по характеру доступа.
- Учитывайте двойное обращение при выносе. Строка и вынесенное значение хранятся отдельно, поэтому чтение требует двух обращений.
- Не меняйте режим без измерения. Изменение режима влияет на объём и время доступа, поэтому проверяется на фактических данных.
- Проверяйте размер до изменения схемы. Значения, не превышающие страницу, не проходят через TOAST, поэтому изменение режима не влияет на них.
- Фиксируйте метод сжатия в документации. Доступность метода зависит от сборки, поэтому перенос базы может изменить поведение.
Практический сценарий: запросы к документам выполнялись медленно
Контекст: запросы, читающие фрагмент текста из документов, выполнялись заметно дольше ожидаемого.
- Диагностика. Столбец с текстом хранился в режиме со сжатием. Каждый запрос читал подстроку, поэтому значение распаковывалось целиком, хотя требовалась небольшая часть.
- Причина. Режим хранения выбран без учёта характера доступа. Сжатие сокращало объём, но распаковка всего значения при каждом обращении к подстроке увеличивала время запроса.
- Действия. (а) Измерили размер значений и долю запросов, читающих подстроки; (б) изменили режим хранения на вариант без сжатия для столбцов с частым доступом к подстрокам; (в) оставили сжатие для столбцов, значения которых читаются целиком; (г) проверили изменение объёма таблицы после изменения режима; (д) задокументировали правило выбора режима по характеру доступа.
- Результат. Время запросов к подстрокам сократилось, поскольку распаковка значения не выполняется.
Вывод: режим хранения TOAST выбирается по характеру доступа к значению. Сжатие выгодно при чтении значения целиком и увеличивает время доступа к подстрокам из-за распаковки.
FAQ
Когда значение проходит через TOAST? Когда размер значения превышает размер страницы базы. Значения меньшего размера хранятся в строке без выноса.
Чем EXTERNAL отличается от EXTENDED?
EXTERNAL выносит значение за строку без сжатия, EXTENDED выносит со сжатием. Доступ к подстрокам быстрее в режиме без сжатия.
Какой метод сжатия используется по умолчанию? pglz, реализованный в самой базе. lz4 доступен при сборке с поддержкой соответствующей библиотеки.
Влияет ли сжатие TOAST на объём резервной копии? Да, сжатые значения занимают меньше места, поэтому объём данных, включаемых в копию, сокращается.
Источники: PostgreSQL TOAST, ALTER TABLE, admin functions — ссылки в шапке.