linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
Intro
Lessons
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Главы
  • How it worksскоро
  • Уроки
  • База знаний
  • Собеседование
home/postgres/lessons/pg-lab-14-1-vacuum-horizon

lesson ── postgres-labs ── ~25 мин ── 6 шагов

Заставь долгую транзакцию держать мусор всей базы

Перед тобой две psql-сессии: вкладка psql-a и вкладка psql-b, обе подключены к одной базе lab. В psql-a мы откроем долгую транзакцию и забудем её закрыть. В psql-b будем плодить мёртвые версии и запускать VACUUM - и увидим, что он бессилен, пока psql-a держит горизонт.

Это главный сценарий раздувания в реальных базах. Перед шагами с VACUUM сначала предскажи: упадёт ли число мёртвых версий?

▶ интерактивный sandbox

Поднимется контейнер postgreslab/postgres-base с PostgreSQL 17 и psql. В браузере откроется терминал, база lab уже настроена. Каждый шаг проверяется автоматически. Сеть air-gapped, наружу контейнер не ходит.

запустить sandbox →

stack ── PostgreSQL 17 · psql · 1 GB RAM · air-gapped · самоуничтожается через 45 мин простоя

Шаги

  1. 01

    psql-a: открой долгую транзакцию

    Переключись на вкладку psql-a и зафиксируй снимок:

    sql
    BEGIN ISOLATION LEVEL REPEATABLE READ;
    SELECT 1;   -- здесь берётся снимок, горизонт встаёт на эту транзакцию

    Не коммить и не закрывай эту вкладку. Транзакция перешла в состояние idle in transaction - она ничего не делает, но держит горизонт.

    ✓ psql-a висит в idle in transaction — горизонт зафиксирован.

  2. 02

    psql-b: наплоди мёртвые версии

    Переключись на psql-b и обнови раздутую демо-таблицу:

    sql
    UPDATE bloat_demo SET payload = repeat('z', 200);
    SELECT dead_tuple_count, dead_tuple_percent
    FROM pgstattuple('bloat_demo');

    Каждая строка получила новую версию, старые стали мёртвыми. pgstattuple считает их живым сканом, без задержки статистики.

    ✓ Мёртвые версии появились — есть что убирать.

  3. 03

    psql-b: запусти VACUUM — и предскажи результат

    sql
    VACUUM bloat_demo;
    SELECT dead_tuple_count FROM pgstattuple('bloat_demo');

    Предскажи до запуска: упадёт ли dead_tuple_count до нуля? VACUUM отработает честно, но горизонт держит psql-a - её снимок ещё может потребовать эти версии.

    подсказка

    Версия удаляема, только если её xmax старше горизонта. Горизонт стоит на psql-a.

    ✓ VACUUM прошёл, но мёртвые версии на месте — горизонт не пустил.

  4. 04

    psql-b: найди, кто держит горизонт

    sql
    SELECT pid, state, now() - xact_start AS xact_age, query
    FROM pg_stat_activity
    WHERE state = 'idle in transaction'
    ORDER BY xact_start;

    Вот он - бэкенд psql-a, висящий в idle in transaction. В проде именно так ищут виновника раздувания, а защищаются idle_in_transaction_session_timeout.

    ✓ Виновник найден — долгая транзакция psql-a.

  5. 05

    psql-a: закрой транзакцию

    Вернись на вкладку psql-a и заверши транзакцию:

    sql
    COMMIT;

    Горизонт освобождён - больше ни один снимок не держит старые версии.

    ✓ Транзакция закрыта — горизонт сдвинулся вперёд.

  6. 06

    psql-b: повтори VACUUM — теперь сработает

    sql
    VACUUM bloat_demo;
    SELECT dead_tuple_count FROM pgstattuple('bloat_demo');

    Тот же VACUUM, та же таблица - но горизонт ушёл вперёд, и мёртвые версии стали удаляемыми. Предскажи dead_tuple_count до запуска.

    ✓ Мусор убран. Одна транзакция держала его всё это время.

Что ты узнал

Горизонт транзакции - минимум по активным снимкам. VACUUM удаляет только версии старше горизонта. Долгая или idle-in-transaction транзакция держит горизонт и запрещает уборку мусора во всей базе.

команды

  • SELECT * FROM pg_stat_activity WHERE state='idle in transaction';найти держателя горизонта
  • SELECT dead_tuple_count FROM pgstattuple('bloat_demo');живой замер мёртвых версий
  • SET idle_in_transaction_session_timeout='5min';защита от забытых транзакций

концепции

  • · VACUUM удаляет версию, только если её xmax старше горизонта
  • · одна долгая транзакция держит горизонт и мусор всей базы
  • · idle in transaction — главный антипаттерн раздувания

← предыдущая

Поймай HOT-обновление и поиграй с fillfactor

следующая →

Прочитай отчёт VACUUM VERBOSE построчно

Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.