У всех, кто эксплуатирует Postgres под нагрузкой, есть один и тот же неприятный ритуал. Таблица за годы UPDATE/DELETE распухла, физический размер в разы больше реальных данных, индексы раздулись, диск кончается. Лечится это перепаковкой — но VACUUM FULL берёт ACCESS EXCLUSIVE lock на всю таблицу, то есть кладёт по ней любые чтения и записи на всё время операции. На терабайтной таблице это окно простоя, которое согласовывают неделями.

Стандартный обходной путь — внешнее расширение pg_repack: оно перепаковывает таблицу почти без блокировки, ловя изменения через триггеры и применяя их на копию. Работает, но это сторонняя зависимость, со своими нюансами по правам, индексам и внешним ключам.

4 июня 2026 вышла PostgreSQL 19 Beta 1, и там эта боль наконец закрывается штатно: в ядро завезли команду REPACK с неблокирующей опцией CONCURRENTLY. Это, на мой взгляд, главная операционная фича релиза — и про неё стоит говорить раньше, чем про распиаренные графовые запросы. Разберём обе, по официальному анонсу и release notes, без «я проверил в проде» — beta в прод не ставят.

REPACK (CONCURRENTLY): перепаковка без окна простоя

Идея та же, что у pg_repack, но теперь это часть СУБД, а не внешний бинарник. Опции, как и у других команд, идут в скобках:

-- блокирующая перепаковка (как VACUUM FULL — берёт ACCESS EXCLUSIVE)
REPACK orders;

-- неблокирующая: таблица остаётся доступной на чтение и запись
REPACK (CONCURRENTLY) orders;

REPACK (CONCURRENTLY) пересобирает таблицу и её индексы в новые файлы, освобождая раздутое место, и не держит эксклюзивную блокировку всё время работы. Изменения, прилетевшие в таблицу во время операции, захватываются через logical decoding (а не триггерами, как у pg_repack) и накатываются на новую копию; короткий тяжёлый lock остаётся только на финальную atomic-подмену — как у REINDEX CONCURRENTLY. Отсюда и новая серверная переменная max_repack_replication_slots: каждый одновременный repack забирает дополнительный слот репликации, а верхняя граница параллелизма задаётся этим параметром и общими ресурсными лимитами.

И вот тут — самое важное для тех, кто собрался катить это на прод. REPACK (CONCURRENTLY) не является MVCC-safe: документация прямо предупреждает, что транзакция со старым снапшотом в некоторых сценариях может увидеть перепакованную таблицу пустой. Для операционной команды это не мелкая сноска, а то, что нужно держать в голове, планируя окно.

Documented-ограничения беты, которые отсекают часть кейсов сразу — CONCURRENTLY нельзя применить:

  • к UNLOGGED-таблицам, секционированным таблицам, системным каталогам и TOAST;

  • к таблицам без primary key или index-based replica identity (логично: иначе нечем сопоставлять строки при логическом декодировании);

  • внутри transaction block;

  • а ещё операция может не завершиться, если по таблице параллельно пойдёт DDL.

Что прогнать на staging до GA:

  • Дисковый оверхед. Перепаковка строит новую копию таблицы и индексов: нужно место минимум под таблицу + индексы, а на пике (seq scan + сортировка) — до двойного размера таблицы плюс индексы. Для терабайтника это не мелочь.

  • Бюджет слотов и лаг репликации. Если уже есть физические/логические реплики — посчитайте слоты заранее, иначе упрётесь.

  • Поведение под нагрузкой на копии самых проблемных таблиц, с поправкой на MVCC-оговорку выше.

Заменит ли он pg_repack полностью? Для типового «раздуло таблицу, нужно вернуть место без даунтайма» — во многом да, и без внешней зависимости. Но с оглядкой на список ограничений выше и не раньше, чем прогоните на своих данных.

SQL/PGQ: графовые запросы без отдельной графовой БД

Вторая большая фича — нативные property-graph запросы по стандарту SQL/PGQ (часть SQL:2023, GRAPH_TABLE). Ключевое, что многие пропускают: это не новое хранилище. Граф описывается как метаданные поверх таблиц, которые у вас уже есть — фактически view. Данные не дублируются и не синхронизируются, Postgres переписывает graph-паттерн в обычные джойны на этапе планирования.

-- граф — это метаданные над существующими users/posts/follows
CREATE PROPERTY GRAPH social_graph
  VERTEX TABLES (
    users LABEL person PROPERTIES (id, name),
    posts LABEL post   PROPERTIES (id, title)
  )
  EDGE TABLES (
    follows
      SOURCE KEY (follower_id) REFERENCES users (id)
      DESTINATION KEY (followed_id) REFERENCES users (id)
      LABEL follows
  );

-- на кого подписана Alice
SELECT followed_name
FROM GRAPH_TABLE (social_graph
  MATCH (a IS person WHERE a.name = 'Alice')
        -[IS follows]->
        (b IS person)
  COLUMNS (b.name AS followed_name)
);

То, что раньше было лесенкой из самоджойнов или рекурсивного CTE, становится читаемым паттерном (a)-[follows]->(b). Для follower-графов, деревьев зависимостей, тегов и прочих связей средней глубины это снимает реальную боль — и убирает из стека отдельный Neo4j, который держали только ради двух графовых запросов.

Но честная граница Beta 1: переменной длины пути пока нет. То есть «найди всех, до кого можно дойти за 1…N переходов» (классический обход графа неизвестной глубины) этим синтаксисом ещё не выразить — это всё ещё территория рекурсивных CTE или специализированных графовых движков. Так что SQL/PGQ закрывает паттерны фиксированной глубины, а не заменяет графовую БД целиком.

Что ещё приехало

Коротко, чтобы было видно вектор релиза:

  • Параллельный autovacuum (autovacuum_max_parallel_workers) и новая система приоритизации таблиц для вакуума — давняя боль больших баз.

  • Автоскейлинг I/O-воркеров поверх асинхронного I/O из 18-й версии (io_min_workers / io_max_workers).

  • pg_plan_advice / pg_stash_advice — стабилизация и контроль решений планировщика без хаков с pg_hint_plan.

  • До 2x быстрее INSERT при наличии проверок внешних ключей, плюс пачка оптимизаций планировщика (anti-join, инкрементальные сортировки, eager aggregation).

Вывод

Графовые запросы — красивый заголовок, но для большинства из нас новость релиза в другом: рутинная перепаковка раздутых таблиц перестаёт требовать окна простоя и внешнего расширения. REPACK (CONCURRENTLY) — это та фича, ради которой стоит поднять PostgreSQL 19 на staging уже сейчас и прогнать на копии своих самых проблемных таблиц: проверить дисковый оверхед, бюджет слотов и documented-ограничения (включая то, что команда не MVCC-safe) до того, как версия дойдёт до GA.

SQL/PGQ берите как приятный бонус: для связей фиксированной глубины он реально убирает отдельную графовую БД из стека, но обходы переменной глубины пока мимо.

А вы чем сейчас перепаковываете раздутые таблицы — pg_repack, VACUUM FULL в окно, pg_squeeze или живёте с bloat’ом до мажорного обновления?

Комментарии (1)


  1. OlegIct
    17.06.2026 06:25

    А вы чем сейчас перепаковываете раздутые таблицы — pg_repackVACUUM FULL в окно, pg_squeeze

    pgcompacttable. Он требует меньше места