使用子事务
本页目录
TL;DR
如无必要,否则不要使用子事务。
什么是子事务?
子事务,也称为"嵌套事务",是指在已启动的事务范围内通过指令启动的事务 (来源:Wikipedia)。这一特性允许用户部分回滚事务,这在许多情况下很有帮助:如果发生错误,重新执行的步骤会减少。
SQL 标准定义了描述这种机制的两个基本指令:SAVEPOINT 和扩展的 ROLLBACK 语句 — ROLLBACK TO SAVEPOINT。PostgreSQL 实现了这些指令,但允许在语法上稍有不同,例如可以在 RELEASE 和 ROLLBACK 语句中省略 SAVEPOINT 关键字。
你可能已经在使用子事务,例如:
- 在 Django 中,使用嵌套的 "atomic()" blocks
- 隐式使用:在 PL/pgSQL 函数中使用
BEGIN / EXCEPTION WHEN ... / END块。
如何使用 (如果你真的想用)
语法:
SAVEPOINT savepoint_name(SAVEPOINT)ROLLBACK [ WORK | TRANSACTION ] TO [ SAVEPOINT ] savepoint_name(ROLLBACK TO)RELEASE [ SAVEPOINT ] savepoint_name(RELEASE SAVEPOINT)
一个示例:

推荐
对于任何计划增长的 OLTP 类型负载 (如 Web 和移动应用),我唯一的实际建议是:
尽可能避免子事务
如果你不想有一天发生以下情况:

或者类似情况:

你可以在这篇文章中找到对子事务四大危险的详细分析:PostgreSQL Subtransactions Considered Harmful。
截至 2023 年 (PG16 版本),这些问题尚未解决,尽管一些优化工作正在进行中:
- More scalable multixacts buffers and locking
- suboverflowed subtransactions concurrency performance optimize (不幸的是,补丁已被撤回)
底线是
- 如果可以,不要使用子事务。
- 关注与子事务相关的 pgsql-hackers 邮件讨论,并参与测试和改进。
- 如果绝对需要使用子事务,请参考 Problem 3: unexpected use of Multixact IDs 以及:
- 仅在低 TPS 系统中使用它们
- 避免深度嵌套
- 在包含子事务的事务中谨慎使用
SELECT ... FOR UPDATE。 - 监控
pg_stat_slru数值 (PG13+,Monitoring stats),以便迅速发现并解决 SLRU 溢出问题。
我见
在 17 版本中,已经针对 SLRU 的问题进行了极大优化,并且可以配置 SLRU 相关参数 — multixact_member_buffers、multixact_offset_buffers 和 subtransaction_buffers。
SLRU optimization
- Make the cache size configurable
- Divide SLRU cache into small associative banks
- Implement bankwise lock to remove the centralized control lock contention
- Remove centralize LRU counter
Performance
- 2-3 x performance gain when there is huge load on SLRU