Один из важнейших аспектов, за которым должен следить каждый администратор баз данных PostgreSQL — процесс поддержания «здоровья» базы данных vacuum / autovacuum, удаляющий из памяти неактуальные версии табличных строк и сбрасывающий счётчик транзакций.
Однако не все советы и шаблоны действий, актуальные для «ванильного» PostgreSQL, в полной мере применимы для DWH с массивно-параллельной архитектурой (MPP — massive parallel processing) на основе ядра PostgreSQL, таких как Greenplum, Amazon Redshift и Citus. При этом с увеличением размера хранилища и нагрузки на БД растут и масштабы проблем с отслеживанием состояния счётчика транзакций, «раздувания» (bloat) таблиц и индексов за счёт «мёртвых» строк. В процессе подготовки к миграции с классического кластера PostgreSQL на распределённое хранилище данных DBA должны учитывать в т. ч. и отличия в подходе к очистке БД.
В этой статье я попытаюсь систематизировать особенности vacuum / autovacuum, с которыми сталкиваются администраторы MPP-РСУБД.
Best practices в PostgreSQL
В первом приближении, базовые инструкции для решения типичных проблем с wraparound (переполнение 32-битного счётчика транзакций) и «раздуванием» таблиц в немодифицированном PostgreSQL можно свести к нескольким простым пунктам:
-
Не отключать autovacuum.
-
В низконагруженных БД в часы наименьшей нагрузки стоит время от времени запускать пересборку таблиц через VACUUM FULL.
-
В высоконагруженных БД для уплотнения хранения таблиц вместо VACUUM FULL лучше использовать неблокирующие инструменты (pg_repack, pg_squeeze, pgcompacttable и т. п.).
-
Статичные таблицы (справочники и т. п.) полезно кластеризовать.
-
Не нарушать пункт 1 и следить, что ничего не мешает автовакууму – настройки репликации, длинные/зависшие транзакции, «битые» страницы на диске (при выключенных контрольных суммах).
Соблюдение этих пунктов позволит:
-
Никогда не задумываться о том, как спасать БД из single-user режима (защитная мера, которая включается при переполнении счётчика транзакций);
-
Эффективно использовать место на жёстком диске и буферный кэш.
Подробный обзор про инструментарий очистки PostgreSQL есть в этом цикле статей от PostgresPro – дальнейший технический обзор подразумевает, что читатель знаком с общей архитектурой PostgreSQL и механизмом работы VACUUM.
MPP-вариации PostgreSQL

Прежде чем переходить к особенностям VACUUM в MPP-РСУБД, проведём краткий обзор рассматриваемых систем, чтобы лучше понимать сценарии их использования и связь с базовым PostgreSQL:
Greenplum
-
open-source MPP-РСУБД;
-
доступна к standalone-установке на Unix-системах;
-
крупнейшие контрибьюторы – VMWare Tanzu (экс-Pivotal), Arenadata, Alibaba;
-
актуальная версия (6.22.1) базируется на основе PostgreSQL 9.4.26;
-
создана для масштабирования OLAP DWH.
Amazon Redshift
-
проприетарная MPP-РСУБД, часть Amazon Web Services;
-
актуальная версия (1.0.12103) базируется на основе PostgreSQL 8.0.2;
-
создана для масштабирования OLAP DWH и интеграции с BI-инструментарием Amazon.
Citus
-
open-source расширение, превращающее PostgreSQL в MPP-РСУБД;
-
доступно к установке на Unix-системах и в облаке Microsoft Azure;
-
крупнейший контрибьютор – Citus Data, с 2019 года принадлежит Microsoft;
-
актуальная версия (11.1.3) доступна к установке на любую версию PostgreSQL от 12.0;
-
создано для масштабирования OLTP БД и простой real-time аналитики.
Можно также вспомнить проект Postgres-XL – ещё одна ранее популярная open-source MPP-вариация PostgreSQL. Официальных объявлений о прекращении разработки не было, однако последний релиз вышел осенью 2018 года.

Vacuum в Greenplum
Обзор особенностей очистки MPP-РСУБД начнём с Greenplum, т.к. он более всего отличается от оригинального PostgreSQL.
Vacuum
В Greenplum можно выделить 3 основных способа хранения табличных данных:
-
Heap-таблицы со строковым хранением;
-
Append-Optimized (AO)-таблицы со строковым хранением;
-
AO-таблицы с колончатым хранением
|
В колоночных AO-таблицах каждый столбец хранится в отдельном файле. Основное техническое отличие таких таблиц от строковых AO-таблиц – наличие дополнительных метаданных, позволяющих сопоставлять сегментный файл с колонкой. |
|
В старых версиях Greenplum такие таблицы назывались Append-Only и не поддерживали операции UPDATE/DELETE. В актуальных версиях эти опции добавили с помощью вспомогательных таблиц, метод переименовали в Append-Optimized. |

Метод доступа Heap перекочевал в Greenplum из PostgreSQL практически без изменений (разве что размер страницы увеличен с 8 до 32 кбайт), и по сей день остаётся дефолтным методом при создании новых таблиц в Greenplum. Логично, что и VACUUM для heap-таблиц работает ровно так же, как в и PostgreSQL. Если взять актуальный Greenplum 6, то команда VACUUM имеет ровно тот же набор опций, что и в PostgreSQL 9.4 (т.е. только VERBOSE, FREEZE, ANALYZE и FULL). Все настройки управления vacuum по кластеру и по конкретным таблицам соответствуют своим аналогам из PG.
AO-таблицы состоят из страниц произвольной длины — примерно как TOAST — таблицы в базовом PostgreSQL; только AO-таблицы могут иметь ещё и собственные TOAST — таблицы. Все метаданные, обеспечивающие транзакционность, хранятся в своеобразной вариации карты видимости (в отличие от PG, это не просто файл на диске, а доступная heap — таблица с именем вида pg_aoseg.pg_aovisimap_<oid_родительской_AO-таблицы>).
Сегментные файлы, в которых хранятся страницы AO — таблиц, не поддерживают удаление данных. При выполнении команд UPDATE/DELETE старая версия строки помечается в карте видимости как удалённая, а новая версия (если есть) пишется в конец текущего сегментного файла. Соответственно, и VACUUM не может вычищать «мёртвые» строки из этих файлов – вместо этого все активные версии строк копируются в конец нового сегментного файла, а старый файл удаляется целиком (на этой стадии на таблицу на короткое время навешивается Access Exclusive Lock).
При этом идентификаторы кортежей (ctid) меняются и приходится перезаписывать ссылки на них во всех индексах таблицы – с одной стороны, это дополнительная работа, которая не требуется для heap-таблиц; с другой стороны, в MPP-БД использование индексов сведено к минимуму, и эта особенность редко действительно влияет на время выполнения VACUUM. Другой незначительный побочный эффект – даже для обычного VACUUM требуется свободное место на диске, чтобы разместить новый сегмент.
VACUUM для AO-таблиц управляется отдельным набором настроек:
-
gp_appendonly_compaction – включает/отключает VACUUM для AO-таблиц;
-
gp_appendonly_compaction_threshold – пороговое количество «мёртвых» кортежей в сегментном файле, после которого VACUUM начнёт чистить этот файл (по умолчанию, 10%).
Autovacuum
Если кратко – автовакуума нет.
Одна из первых проблем, с которыми исторически столкнулись разработчики всех MPP-РСУБД на базе PostgreSQL – что делать с счётчиками транзакций на каждом отдельном узле? Количество транзакций на разных сегментах, как правило, отличается друг от друга, в зависимости от нагрузки, и не предусмотрен никакой механизм для синхронизации/объединения счётчиков между серверами.
В Greenplum 4 в своё время autovacuum работал точно так же, как и в «ванильном» PostgreSQL – каждый сегмент самостоятельно запускал autovacuum, как того требовали настройки. В результате, из-за этого проседала производительность всего кластера Greenplum (по известному закону «производительность запроса в Greenplum = производительность самого медленного сегмента, на котором выполняется запрос»).
Начиная с Greenplum 5 это поведение изменили — процесс autovacuum теперь есть только для БД template0 (чистая БД с дефолтными настройками для клонирования, как в PostgreSQL). Во всех остальных БД автовакуума нет совсем и включить его нельзя. Кстати, любимые многими PostgreSQL DBA инструменты для автоматизированного и неблокирующего vacuum full (pg_repack, pgcompacttable, pg_squeeze) в Greenplum также недоступны. Подразумевается, что администраторы Greenplum должны самостоятельно настраивать и планировать ручной vacuum на каждом сегменте, исходя из профиля нагрузки на сегменты и кластер в целом, а также ETL-процессов, чтобы минимизировать просадки производительности сегментов.
|
16 декабря 2022 вышла первая бета-версия Greenplum 7. Среди её ключевых особенностей в Release Notes упомянуто, что autovacuum теперь будет работать по всему кластеру, а не только в шаблонных БД. Остаётся только надеяться, что этот функционал доживёт до релиза. |
Vacuum full / reindex / cluster
CLUSTER для heap-таблиц работает аналогично «ванильному» PostgreSQL, а для AO-таблиц не поддерживается (у них много сложностей с индексированием, проще просто забыть про индексы для AO-таблиц).
REINDEX также полностью идентичен команде из PostgreSQL, только нельзя забывать про версию ядра – в 9.4.26 ещё не было опций CONCURRENTLY (без блокировки таблицы) и TABLESPACE (перенос в другое табличное пространство).
Про VACUUM FULL в разделе «Best Practices» на сайте VMWare есть лаконичная рекомендация никогда его не использовать:

Но совсем без него обойтись, конечно, не получится. Самый распространённый сценарий использования Greenplum – OLAP DWH на сотни/тысячи таблиц, зачастую с десятками/сотнями партиций, и примерно таким же количеством представлений-витрин. В результате, системный каталог на каждом сегменте кластера может очень сильно распухать, и в первую очередь – таблицы pg_class (список объектов в БД) и pg_attribute (список столбцов в таблицах).
Дополнительные проблемы может также доставить утилита gpload. Это один из встроенных инструментов Greenplum для импорта данных из внешних источников, процедура загрузки конфигурируется с помощью простого YAML-файла. По умолчанию, в процессе создаётся промежуточная внешняя таблица (external table), из которой данные потом перекладываются в целевую таблицу; внешняя таблица при этом удаляется. При импорте больших объёмов данных микропорциями можно получить раздутый системный каталог ещё до начала работы с БД. Методы решения: опция REUSE_TABLES: true в конфигурации gpload, использовать другие инструменты импорта (gpfdist, pxf, copy и т. д.)
Чтобы производительность чтения метаданных не проседала, необходимо время от времени запускать VACUUM FULL для таблиц системного каталога, держа в уме несколько важных моментов:
-
Чистка системного каталога на сегменте кластера фактически эквивалентна остановке этого сегмента.
-
Никогда, ни при каких обстоятельствах нельзя прерывать VACUUM FULL системного каталога, если он уже запущен. Это может полностью сломать сервис Greenplum на сегменте – известен кейс, когда после остановки очистки таблица pg_class осталась в несогласованном состоянии, и на сегмент больше нельзя было добавлять новые объекты.
-
Надо помнить про жёсткий лимит на объём «мёртвых» строк для VACUUM – не более 1 ГБ указателей строк на процесс. Эта величина «прибита гвоздями» в исходниках ядра PostgreSQL, и в Greenplum её перенесли без изменения. Вот только в гигантских DWH на базе Greenplum гораздо проще добраться до этого предельного значения, если своевременно не выполнять VACUUM. Например, если в pg_attributes накопилось свыше 1 ГБ указателей на «мёртвые» строки, то VACUUM FULL этой таблицы упадёт с ошибкой, что может привести к последствиям из п. 2.
Для AO-таблиц большой пользы от выполнения VACUUM FULL нет, т.к. он полностью аналогичен обычному VACUUM с параметром gp_appendonly_compaction_threshold равным 0 (т.е. пересобираются все сегментные файлы, независимо от количества «мёртвых» кортежей). Кроме того, VACUUM FULL для AO-таблиц не сжимает связанные индексы как в случае с heap-таблицами.

Vacuum в Amazon Redshift
Хотя Redshift и построен на базе доисторического ядра PostgreSQL 8.0, на сегодняшний день их пути разошлись уже достаточно далеко. В Redshift не реализованы или реализованы иначе ряд ключевых особенностей даже опорной для него версии PostgreSQL, т.к. они не требуются для архитектуры данной РСУБД.
Главные отличительные особенности хранения данных в Amazon Redshift – колоночное хранение (блоки по 1 МБ) и принудительная сортировка значений (подобные принципы используются также в Snowflake). У каждой таблицы обязательно должен быть определён набор ключей сортировки, причём сделать это можно двумя методами – compound (с иерархией ключей) и interleaved (все ключи равноправны; не более 8 ключей сортировки).
Vacuum
Исторически сложилось, что процесс обязательной автоматической сортировки разработчики Amazon интегрировали именно в операцию VACUUM. Синтаксис вызова очистки и происходящие процессы заметно отличаются от стандарта PostgreSQL:
VACUUM [ FULL | SORT ONLY | DELETE ONLY | REINDEX | RECLUSTER ]
[ [ table_name ] [ TO threshold PERCENT ] [ BOOST ] ]
Дефолтный вариант — полная очистка, т.е. команды VACUUM и VACUUM FULL эквивалентны. Процедура сначала вычищает «мёртвые» кортежи и сжимает таблицу, кратковременно блокируя её, а затем сортирует строки (уже без блокировки), если в таблице отсортированы менее 95% строк. Можно указать опцию TO threshold PERCENT, чтобы изменить данный порог на другое значение в процентах.

Отсутствие полной блокировки таблицы на всё время VACUUM FULL в Redshift достигается за счёт добавления новых блоков к каждому столбцы таблицы на каждом сегменте в кластере, где хранится данная таблица – подобно Append-Optimized-таблицам в Greenplum, описанным ранее. Если столбцов и сегментов в кластере много, то могли бы возникать ситуации, когда очистка добавляла бы больше новых блоков, чем сжимала. Поэтому в Redshift выполняется предварительный расчёт обработки блоков памяти, и если оверхед будет больше потенциального сжатия, то операция очистки «мёртвых» кортежей пропускается.
Опции SORT ONLY и DELETE ONLY ограничивают работу VACUUM только сортировкой или только очисткой и сжатием, соответственно (практически никогда не применяется на практике).
В режиме RECLUSTER происходит только сортировка неотсортированной части таблицы, удаление «мёртвых» кортежей не выполняется. Этот режим подходит для быстрой обработки новых строк после INSERT в большие таблицы.
Самая требовательная к ресурсам операция — VACUUM REINDEX. Эта процедура применяется только к таблицам с методом сортировки interleaved (чередующиеся ключи) —сначала производится полное сканирование таблицы с статистическим анализом распределения всех ключей сортировки, а затем выполняется стандартный для Redshift VACUUM FULL.
Опция BOOST блокирует таблицу от записи и выделяет дополнительные системные ресурсы для очистки, ускоряя её. BOOST не работает с REINDEX и игнорируется с DELETE ONLY.
Autovacuum
Достаточно долгое время (2012 — 2019) в Redshift отсутствовали инструменты автоматизации очистки. Затем в течение 2019 года были последовательно добавлены фоновые процессы для VACUUM DELETE ONLY, ANALYZE и VACUUM SORT ONLY. Можно только догадываться о том, как именно они настроены и реализованы, т. к. описание этих процессов в документации отсутствует, и нет способов их отключить или изменить параметры. Позиция Amazon заключается в том, что обслуживание БД теперь считается полностью автоматизированным, и администраторам/разработчикам не следует в него вмешиваться; для оперативного сглаживания аномалий оставили ручной VACUUM. Можно отметить, что в Redshift отсутствует проблема с wraparound, т.к. используются 64-битные номера транзакций.

Vacuum в Citus
Как упоминалось в начале статьи, Citus является расширением к «ванильному» PostgreSQL, поэтому в нём отсутствуют какие бы то ни было глубокие переработки ядра. Шардирование таблиц между сегментами кластера реализовано с помощью нативной потоковой репликации PostgreSQL. Также добавлен новый метод доступа для колоночного хранения данных.
Очистка распределённых таблиц не отличается от стандартных процессов в PostgreSQL, но запускается параллельно сразу на всех сегментах кластера, где располагается данная таблица. В случае autovacuum на каждый сегмент выделяется отдельный worker из пула. Проблему с рассинхронизацией транзакций на сегментах кластера в Citus частично решают с помощью двухфазного коммита (через prepared transactions и обратную связь между сегментами). В результате получилось сохранить работу autovacuum (в отличие от Greenplum) с общим счётчиком транзакций на всех сегментах кластера, но не обеспечивается консистентность чтения в распараллеленных транзакциях с изменением данных.
Колоночные таблицы в Citus имеют целый ряд ограничений, по сравнению со стандартными heap-таблицами или колоночными-таблицами в Greenplum или Redshift. Тем не менее, с их помощью всё равно можно добиваться хороших уровней компрессии данных и оптимизации запросов. С точки зрения очистки данных нужно выделить несколько моментов:
-
Колоночные таблицы работают только в режиме append-only – «мёртвые» кортежи в нормальных условиях не генерируются, т.к. нет операций UPDATE и DELETE.
-
Побочный эффект append-only – «мёртвые» кортежи всё равно могут появляться, если транзакция с INSERT откатилась.
-
Обычный VACUUM (без FULL) не сканирует данные в колоночной таблице, только метаданные (карту видимости, карту свободного пространства).
-
Колоночные таблицы не поддерживают индексы.
По этим причинам VACUUM в колоночных таблицах в Citus работает значительно быстрее, чем для heap-таблиц.
Заключение
Перенос хранилища с Single-Node на распределённую архитектуру может оказаться нетривиальной задачей, даже если выполняется внутри одной экосистемы. В этой статье был рассмотрен один из аспектов этого процесса, а именно некоторые разложенные грабли и другие особенности при обслуживании таблиц в Greenplum, Amazon Redshift и Citus по сравнению с базовой версией PostgreSQL.
Автор: Николай Налбантов@NickNal
ссылка на оригинал статьи https://habr.com/ru/company/axenix/blog/710806/
Добавить комментарий