PG的事务ID回卷逻辑

PG到目前为止使用的事务ID仍然是32位的,在内存计算时虽然已经使用64位事务ID,但是存储在页中tuple仍然使用32位事务ID,这就是说,事务ID回卷仍然是必须处理的问题。

所谓PG事务ID回卷,简单地说,就是在数据库频繁运行中,事务ID不断增加,32位的事务ID不够用了,怎么办?PG的处理方法,简单的说,就是在事务ID还没用完以前,把数据库中所有的tuple处理一遍,将以前的事务(不活跃的事务)修改的(包括插入)tuple中的事务ID改为2(或设置infomask),表示这个tuple对于以后的事务(不管是多少的事务ID)都是可见的,即freeze。

这样,当前事务达到2^32后,再从3开始(0、1、2保留做特殊事务ID),就不会有什么问题了,即就不会导致很旧的事务修改过的tuple不可见了。

这里有个隐含背景知识,就是PG内部读写tuple时,tuple中的事务ID,在当前事务ID之前  ,这个tuple对于当前事务才是可见的。

网上有一张图片,来说明“之前”、“之后”和回卷的概念,但是我觉得都没说明白,我来尝试理解一下,我觉得问题的关键是PG源码中比较事务ID的函数:

TransactionIdPrecedes() 和 TransactionIdFollows()

按照上面代码的算法,对于任何事物ID,例如100,如果另一个事务ID比它大,但是没有超过这个事务ID后的半圆,例如2^31+100,就认为是在它的后面,那么事务100的tuple,对事务2^31+100就是可见的。

按照上面代码的算法,对于任何事物ID,例如100,如果另一个事务ID比它大,还超过了事务ID后的半圆,例如2^31+101,就认为是在它前面,那么事务100的数据,对事务2^31+101就是不可见的。

这导致对任何事务ID,它只能看到它之前的 2^31 个事务的数据,而不是 2^32 个事务的数据,而且不同事务的可见事务集合也是不同的。

这种算法叫做 “模算数” (modulo-2^32 arithmetic)

这个和freeze有什么关系呢?

想象事务ID一直在增加,穿过圆心的线顺时针转动,每转动一个事务ID,就有一个事务ID变的不可见,要想不丢失这个事务ID的数据,就要对它的tuples做freeze,这一岂不是freeze太频繁了吗?

实际上,PG对于past部分的tuple,早就做了freeze了,例如下图,一般X之前的tuple已经freeze过了,而当前事务ID不断增加,每隔一段时间做一次freeze,将与当前事务ID有一定差值(5千万)的past tuple,做freeze。

并不是在穿过圆心的直线另一端,快到达某个事物ID时(未做freeze的tuple事务ID与当前事务ID差值已经非常大了),才对这个事物ID做freeze。

总结一下vacuum,最近看pgvector索引代码时,对vacuum有了新的理解,pgvector有个接口:hnswbulkdelete,是删除了表的一些记录后,做vacuum时,删除索引中对应条目用的。

PG删除记录时,不是真的删除,而是在tuple上标记删除,此时也不会删除对应的索引项,真的删除是vacuum时做的。

vacuum是以表为单位进行的,就是说最小可以只对一个表进行vacuum,它主要有两个工作:

1、删除 dead tuple

2、将记录设置frozen xid

这两个工作都有,lazy和eager两种模式,它们的触发条件不同,对系统的影响也不同。

1、lazy模式,删除dead tuple,跳过没有dead tuple的page(参考vm),对于有dead tuple的page,物理删除dead tuple和对应索引项,页内碎片整理。

2、eager模式,删除dead tuple,不会参考vm,直接重建一份表数据,从旧的表数据的page中逐个tuple拷贝,重建索引,表内碎片整理。它的触发条件就是用FULL VACUUM命令。

3、lazy模式,freeze,跳过没有dead tuple和所有tuple都已经freeze的page(参考vm),对其他page中的tuple设置frozen txid,但是又不是每个tuple都设置,只有xmin小于特定值的tuple才设置。

4、eager模式,freeze,跳过所有tuple都已经freeze的page(参考vm),对其他page中的tuple设置frozen txid,但是又不是每个tuple都设置,只有xmin小于特定值的tuple才设置。这里面还有个特殊情况,就是vacuum时有FREEZE选项,则用eager模式,但是并不是只有xmin小于特定值的tuple才设置frozen,而是所有小于当前事务ID的tuple都设置frozen。

触发条件:粗糙的说,当前事务ID与数据库中最小的(未freeze的)事务ID,的差达到一定值后,就会触发。这个“当前事务ID”是指OldestXmin ,“数据库中最小的事务ID”是指pg_database.datfrozenxid,差值是Vacuum_freeze_table_age。

5、autovacuum触发条件,既可以是事务ID的改变,也可以是dead tuple的比例的改变,也可以是新插入的tuple的比例达到一定阈值。

参考:

6.3. Freeze Processing :: Hironobu SUZUKI @ InterDB

postgresql - About "Transaction ID Wraparound" - Database Administrators Stack Exchange

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值