PostgreSQL的并发控制机制需要以下维护过程。
- 删除死元组及指向死元组的索引元组
- 移除提交日志(clog) 中非必需的部分
- 冻结旧的事务标识(txid)
- 更新FSM,VM,以及统计信息
第 5.3.2 节和 5.4.3 节分别解释了为什么需要第一个和第二个过程。第三个过程与事务标识回卷问题有关,本小节将概述事务标识回卷(txid wrap around) 问题。
在PostgreSQL中,清理过程(VACUUM
)负责这些过程。清理过程(VACUUM) 在 第六章 清理过程(VACUUM) 中描述。
5.10.1 冻结处理
接下来将介绍事务标识回卷(txid wrap around) 问题。
假设元组Tuple_1
是由txid = 100
事务创建的,即Tuple_1
的t_xmin = 100
。服务器运行了很长时间,但Tuple_1
一直未曾被修改。假设txid
已经前进到了$ 2^{31}+100$,这时候正好执行了一条SELECT
命令。此时,因为对当前事务而言txid = 100
的事务属于过去的事务,因而Tuple_1
对当前事务可见。然后再执行相同的SELECT
命令,此时txid
步进至$2^{31}+101$。但因对当前事务而言,txid = 100
的事务是属于未来的,因此Tuple_1
不再可见(图5.20)。这就是PostgreSQL中所谓的事务回卷问题。
图5.20 回卷问题
为了解决这个问题,PostgreSQL引入了一个冻结事务标识(Frozen txid) 的概念,并实现了一个名为FREEZE
的过程。
在PostgreSQL中定义了一个冻结的txid
,它是一个特殊的保留值txid = 2
,在参与事务标识大小比较时,它总是比所有其他txid
都旧。换句话说,冻结的txid
始终处于非活跃状态 ,且其结果对其他事务始终可见。
清理过程(VACUUM
) 会调用冻结过程(FREEZE
)。冻结过程将扫描所有表文件,如果元组的t_xmin
比当前txid - vacuum_freeze_min_age
(默认值为5000万)更老,则将该元组的t_xmin
重写为冻结事务标识。在 第六章 清理过程(VACUUM) 中会有更详细的解释。
举个例子,如图5.21(a)所示,当前txid
为5000万,此时通过VACUUM
命令调用冻结过程。在这种情况下,Tuple_1
和Tuple_2
的t_xmin
都被重写为2。
在版本9.4或更高版本中使用元组t_infomask
字段中的XMIN_FROZEN
标记位来标识冻结元组,而不是将元组的t_xmin
重写为冻结的txid
,如图5.21(b)所示。
图5.21 冻结过程