Траблшутинг зависших двухфазных транзакций (2PC) в PostgreSQL
- Таблицы перестают очищаться автовакуумом (Autovacuum), вызывая стремительный рост Bloat и угрозу Wraparound.
- Таблицы остаются заблокированными, хотя в
pg_stat_activityнет активных выполняющихся запросов. - Использование менеджеров распределенных транзакций (XA, Bitronix, Narayana, Spring JTA).
1. Поиск зависших двухфазных транзакций
SELECT gid, prepared, owner, database,
age(transaction) AS age_in_xids,
now() - prepared AS duration
FROM pg_prepared_xacts
ORDER BY prepared ASC;2. Анализ удерживаемых блокировок подготовленной транзакцией
SELECT l.locktype, l.mode, l.granted, c.relname
FROM pg_locks l
LEFT JOIN pg_class c ON c.oid = l.relation
WHERE l.transactionid = (SELECT transaction FROM pg_prepared_xacts WHERE gid = 'target_gid');3. Принудительное завершение брошенной транзакции
В зависимости от бизнес-логики выполните откат или подтверждение транзакции:
-- Откат зависшей подготовленной транзакции
ROLLBACK PREPARED 'target_gid';
-- Или принудительное применение изменений:
-- COMMIT PREPARED 'target_gid';4. Отключение 2PC (если распределенные транзакции не используются)
В postgresql.conf установите:
max_prepared_transactions = 0systemctl restart postgresql Частые вопросы (FAQ)
Почему подготовленные транзакции (PREPARE TRANSACTION) не закрываются при разрыве TCP-соединения?
Такова специфика протокола 2PC (Two-Phase Commit). Подготовленная транзакция сохраняется на диске в WAL и удерживает блокировки бессрочно, пока внешний координатор транзакций явно не выполнит COMMIT PREPARED или ROLLBACK PREPARED.
Чем опасна забытая транзакция 2PC для PostgreSQL?
Она замораживает горизонт транзакций (xmin). Из-за этого Autovacuum не может удалить ни одну мертвую строку, созданную после старта этой транзакции, что приводит к разрастанию всей базы данных.