Всем привет! Перед вами заключительная, третья часть обзора изменений в Greenplum 7. Если вы по каким-то причинам пропустили прошлые публикации, то их можно найти в нашем блоге здесь и здесь. А сегодня мы поговорим об изменениях в партиционировании таблиц и ресурсных группах, JIT-компиляции выражений и многом другом. Давайте начнём!
Миграция на партиционирование из Postgres
Прошлые версии Greenplum использовали свою оригинальную схему партиционирования, которая была реализована для версий Postgres, не имеющих такой поддержки. Позднее и в Postgres 10 появилось своё партиционирование (f0e4475).
Greenplum 7 полностью мигрировал на инфраструктуру партиционирования Postgres. Это означает, что многие привычные инструменты были удалены и больше не доступны (например, представление-комбайн pg_partitions
, каталожные отношения pg_partition
, pg_partition_rule
и так далее). Синтаксис определения таблиц, знакомый по документации Postgres, считается основным. При дампе схемы именно он будет использован для её описания.
Однако поддержка классического синтаксиса, знакомого по прежним версиям Greenplum, по максимуму была сохранена, за исключением случаев, когда определяемые им выражения не имеют аналогов в варианте Postgres.
Например, партиции по диапазонам теперь всегда включают меньшую границу и исключают большую. Используя классический синтаксис, можно определить иной вариант, но лишь для тех типов данных, которые позволяют выразить нестрогое равенство через строгое:
Новые же возможности доступны только при использовании синтаксиса Postgres. Это и возможность разбить таблицу на диапазоны по нескольким столбцам или на основе остатка от деления хеш-значения, и неоднородное многоуровневое партиционирование. Другой вопрос, что работу с ними поддерживает только планировщик, унаследованный от Postgres, но не ORCA.
Row Level Security
Начиная с Postgres 9.5, в дополнение к определённой стандартом модели доступа на уровне типов операций над таблицей, появилась возможность задавать политики доступа к данным внутри неё. Каждая политика определяется с помощью условия. Условие определяет множество строк, которые может читать или изменять связанная с ней роль.
В примере ниже мы задаём для роли alice
политику, предоставляющую доступ только к строкам, содержащим чётное значение в столбце b
.
Выражение, указанное в определении политики, используется для фильтрации строк при сканировании или для проверки на соответствие при вставке:
Благодаря абстрагированной от формата хранения реализации данного функционала, применение политик доступно и для AO-таблиц. С другой стороны, планировщик ORCA в актуальных на момент написания статьи версиях Greenplum не поддерживает запросы к таблицам, для которых активированы политики доступа:
Разрешения по умолчанию для схемы и роли
Продолжая тему распределения прав доступа, отмечу привнесённую Postgres 10 возможность задавать разрешения по умолчанию для объектов, которые будут созданы позднее внутри схемы:
Или конкретной ролью:
WAL удерживаются только для последней контрольной точки
Postgres по 10-ю версию включительно (а значит, и Greenplum < 7) удерживал для аварийного восстановления сегменты журнала упреждающей записи с момента создания двух последних контрольных точек. Мотивировалось это повышением надёжности восстановления в случаях, когда самая последняя контрольная точка не была обнаружена в журнале упреждающей записи. Однако в некоторых случаях такое восстановление всё равно было бы невозможно, например из-за окончательного удаления каталогов табличных пространств в момент выполнения последней контрольной точки. Поэтому от данной практики решили отказаться (4b0d28d), попутно ощутимо сократив объём дискового пространства, занимаемого журналами упреждающей записи.
Just-in-Time (JIT) компиляция
На мой взгляд, ещё одно значительное новшество для аналитической нагрузки — это Just-in-Time компиляция универсальных участков кода под обработку конкретного пользовательского запроса. Процедуры проверки указанных пользователем условий, «расформирование» строк из представления на диске в представление для последующей обработки — эти операции выполняются для каждой извлекаемой строки, которых в уважающем себя запросе будут как минимум миллионы. Как следствие, затраты на преобразование интерпретируемого варианта выполнения в специализированный вариант будут перекрыты выигрышем в производительности.
По умолчанию jit
выключен, состояние управляется одноимённым GUC. Вывод статистики jit
для EXPLAIN ANALYZE
также закрыли отдельной конфигурационной переменной gp_explain_jit
. ORCA поддерживает jit
, однако, чтобы поддержать различия в cost-модели этого планировщика, введены дубликаты всех конфигурационных параметров с префиксом optimizer_
(например, optimizer_jit_above_cost
).
Генерируемые столбцы
Postgres 12 привнёс соответствующий стандарту синтаксис генерируемых столбцов. На текущий момент есть поддержка только хранимых генерируемых столбцов (fc22b66). Поддержку виртуальных, или вычисляемых при сканировании, столбцов планировалось реализовать позднее. От атрибутов со значением по умолчанию данный синтаксис отличает невозможность переопределения значения пользователем, а также возможность использования в выражении других атрибутов строки (кроме таких же генерируемых). Ниже продемонстрирован синтетический пример использования данного функционала:
В выражении можно использовать только immutable-функции (например, преобразование момента времени в строку не подходит, так как является стабильной функцией).
Значение генерируемого столбца не экспортируется и не импортируется с помощью
COPY
.В Greenplum генерируемый столбец нельзя использовать как ключ распределения, что логично, поскольку вычисление значения такого столбца происходит в узле
Modify Table
уже после потенциального перенаправления на сегмент, который будет хранить данную строку.
SQL/JSON
Вместе с коммитами Postgres 12 стала доступна поддержка языка JSON path. Вместе с функциями и операторами возможности по обработке JSON существенно расширились. Подробнее с возможностями можно ознакомиться на странице официальной документации. В качестве примера приведу простой запрос, позволяющий извлечь лишь те авиарейсы, где пунктом назначения выступает аэропорт Мурманска.
Autovacuum и autoanalyze
Очистка таблиц от удалённых строк и поддержание статистики в актуальном состоянии всегда были головной болью для администраторов Greenplum. Регламентные задания, не вписывающиеся в ограничения окон технического обслуживания, поиск критериев для запуска — вот это вот всё. Что изменилось в новой версии?
Увы, автоматического VACUUM
для пользовательских таблиц не случилось. Процессы autovacuum будут автоматически обслуживать только таблицы в схеме pg_catalog
и, опционально, вспомогательные таблицы Append Optimized-отношений (gp_autovacuum_scope). Также, судя по коду, vacuum должны подвергаться heap-таблицы, опасно приблизившиеся к wraparound, хотя в документации я этого не наблюдаю. К тому же процессы autovacuum действуют независимо на каждом сегменте, распределённый VACUUM
команда VMware отложила в долгий ящик.
А вот автоматический сбор статистики есть. Инициироваться он будет только с координатора — статистика на сегментах, как и раньше, не используется. Для принятия решения будет применяться подход из Postgres. Для этого был доработан сбор статистики после выполнения DML-операций с пишущих процессов на координатор.
Здесь anl_base_treshhold
— пороговое значение (по умолчанию — 50 кортежей), anl_scale_factor
— процент от числа кортежей, хранящихся в таблице (по умолчанию — 10%). Конкретные значения можно настроить для каждой таблицы индивидуально.
Впрочем, как это чудо работает в бою, ещё необходимо оценить. Утилита analyzedb
никуда из поставки не делась.
Оптимизация ANALYZE для AO-таблиц
Ещё одной проблемой, связанной со сбором статистики, была производительность ANALYZE
для AO-таблиц. Причина — невозможность использовать подход, применяемый для heap-таблиц, где мы, опираясь на фиксированный размер страницы, можем семплировать сначала страницы (функция scan_analyze_next_block
табличного метода доступа), а затем кортежи в них (scan_analyze_next_tuple
). В результате таблица сканировалась полностью. Это, в свою очередь, порождало попытки хранить число обновлений в таблице во внешних инструментах (analyzedb
), чтобы принимать решение о целесообразности очередного сбора статистики.
Наш коллега, @darthunix, после изучения вопроса предложил (ссылка на обсуждение канула в лету в момент закрытия исходного кода «ванили») переработать подход, внедрив виртуальные блоки семплирования, предполагающие фиксированное количество строк в них. Выигрыш по производительности достигался за счёт пропуска блоков произвольной длины без траты ресурсов на декомпрессию. Однако обсуждение патча заглохло.
В итоге в 2023 году разработчики выкатили свой патч (2939f9a), в котором отказались от двухуровневого семплирования, добавив оптимизацию для случая, когда по таблице есть хотя бы один индекс (а значит, и каталог блоков — Block Directory
) и получили 20-кратный прирост производительности. Для реализации этого подхода интерфейс табличных методов доступа был расширен функцией relation_acquire_sample_rows
.
Внешние таблицы
Поддержка протоколов внешних таблиц пережила обновление мажорной версии Greenplum, однако работа с ними теперь спрятана за FDW API. Для этой цели был реализован встроенный «удалённый» сервер gp_exttable_server
, который отвечает за работу с выбранным протоколом. Заявлена полная поддержка синтаксиса CREATE EXTERNAL TABLE
, однако выражение на лету конвертируется в определение foreign-таблицы. В плане запроса также будет Foreign Scan
:
Resource groups
Механизм ограничения ресурсов подвергся существенной переработке. Начать следует с того, что GUC gp_resource_manager
теперь имеет четыре значения: none
(по умолчанию), group
, group-v2
, queue
. И да, очереди живее всех живых.
В режиме group-v2
реализована поддержка cgroup v2, столь актуальная для современных дистрибутивов Linux. Контроллер io
, доступный для данной версии, позволил реализовать ограничение полосы пропускания при обращении к дисковым устройствам (параметр ресурсных групп IO_LIMIT
, чтение/запись, MB/s или IOPS). Оно может быть настроено индивидуально для различных табличных пространств. Стоит учитывать, что при ограничении на запись данный механизм будет хорошо работать только для Append Optimized-таблиц, поскольку сброс страниц heap-таблиц в норме производится отдельным фоновым процессом background writer
, который не подпадёт под ограничения роли.
Зато данный процесс подпадает под ограничения новой встроенной ресурсной группы system_group
. Изначально в эту группу помещается процесс postmaster и его дочерние процессы (например, рабочие процессы autovacuum), которым не была назначена иная группа или не было разрешено выполнение без ограничений.
Ограничения для разных версий cgroup реализованы с помощью разных контроллеров. Например, для ограничения по CPU для первой версии применяется cpu.cfs_quota_us
в сочетании с cpu.shares
, для второй — cpu.max
и cpu.weight
. Остался только вариант задания жёсткого потолка, похожий на сочетание CPU_RATE_LIMIT
и GUC gp_resource_group_cpu_ceiling_enforcement
, выставленного в истину в Greenplum 6. Упомянутый GUC был удалён. CPU_RATE_LIMIT
разделён на две отдельные опции CPU_MAX_PERCENT
и CPU_WEIGHT
, управляющие вышеупомянутыми параметрами cgroup.
Пожалуй, самые неоднозначные изменения произошли в подсистеме ограничения потребления памяти. Разработчики это преподносят как упрощение конфигурации. По факту, MEMORY_LIMIT
теперь влияет только на расчёт statement_mem
для запросов, выполняющихся в группе и может быть легко переопределён пользователем с помощью GUC gp_resgroup_memory_query_fixed_mem
. Дальнейший контроль за потреблением памяти в процессе выполнения со стороны ресурсных групп отсутствует. В тот момент, когда потребление памяти на сегменте превысит gp_vmem_protect_limit * runaway_detector_activation_percent
, Greenplum начнёт прерывать запросы начиная с самого тяжёлого на сегменте, а не в группе, как было раньше. Опции MEMORY_SPILL_RATIO
и MEMORY_SHARED_QUOTA
, GUC gp_resource_group_enable_recalculate_query_mem
и gp_resource_group_memory_limit
были удалены.
Подведём итоги
Как мы увидели, Greenplum 7 привносит множество новых возможностей для аналитической нагрузки. Для себя я отметил современную схему партиционирования, BRIN-индексы, JIT-компиляцию, расширение возможностей FDW, а также многочисленные оптимизации. С другой стороны, часть функционала Postgres 12 не будет доступна в исходном виде по объективным причинам. Также доработок потребует планировщик ORCA, чтобы обеспечить работу с новыми возможностями. Но так сложилось, что радикальные изменения требуют периода стабилизации. Надеюсь, что моя статья помогла вам ответить на вопрос — зачем нам новый Greenplum.
Благодарю за помощь в подготовке данной статьи наших дизайнеров — @kawaii_anya и @nastoika_lera.