PostgreSQL的clog—从事务回滚速度谈起

本文探讨了PostgreSQL如何实现快速回滚,即使面对大规模事务。与MySQL不同,PostgreSQL的回滚是即时的,不受事务大小影响。这得益于其clog(或xact)日志系统,它记录事务状态,包括提交和中断。clog通过使用事务ID的比特位来高效地存储和检索事务状态,避免了在大量行上进行回滚的高昂成本。此外,文章还解释了事务可见性判断、clog的内部工作原理、事务提交和回滚的流程,以及数据行事务可见性判断与clog的关系。同时,文章提到了PostgreSQL在处理大表时的vacuum操作可能面临的挑战和未来的改进方向。
摘要由CSDN通过智能技术生成

如果是之前学习别的数据库的人,看PostgreSQL会感觉到有句话非常奇怪:“PostgreSQL的回滚是立即完成的,不会受到事务大小本身的影响”。


奇怪在哪里呢?比方我曾经遇到过一次MySQL的故障,一个开发给生产数据库导入数据,用的是Python脚本,但是,他没有注意一个事情,Python的MySQLdb默认情况下,是设置autocommit为0的,于是这哥们导数据(这里说的导入,不是普通那种load data,而是带有业务操作的SQL语句,所以需要脚本操作)脚本跑了一天之后,整个数据库的状况就变得极为糟糕了:他导入所用的,是一个业务的核心表,一堆业务操作都需要操作这个表,但随着这个导入动作跑了一天,占掉了大量的行锁(几百万行锁)之后,整个业务系统的对外服务都会处于一个无法求到锁的状况了(还掺和着MySQL间隙锁的坑坑洼洼),业务服务停摆,于是,作为DBA来说,最终的决策,只有杀掉这个”大”事务了。一个kill命令过去之后,我们当时俩DBA开始慢慢数—小蚂蚁慢慢爬——碰到—颗大豆芽——碰到两颗大豆芽——


最终在将近三个小时的rollback之后,这个事务完成回滚,业务系统恢复。


所以看到PostgreSQL的这个描述之后,我第一时间的反应是,why?how?what?
于是就有了这一篇文章,我从PG的事务可见性判断讲起,整理一下PG核心文件clog的机理与作用。


另注从pg 10以后,clog改名为xact,主要原因,是很多人习惯性地使用*log删除日志文件,总是会不小心删除掉原先的xlog与clog文件,导致数据库不可用,所以分别改名为wal与xact,后文依然以clog为讨论单词,需要注意。
 
clog简介



第一个问题,什么是clog?或者换个说法,PG到底有哪些日志,它们分别是干啥的?
除了理所当前的各路文本记录(比方数据库的运行报错日志之类),PG的二进制类日志文件主要有两个,一个就是对应传统数据库理论的redo日志,理论上,所有数据的修改操作都会被记录到这个日志,在事务提交的时候确保操作都记录到磁盘中,这样讲即便发生宕机,数据库也能以不丢数据的形态重新复活。


但是,各个数据库在这个点上都有不同的实现,比方MySQL会有一个binlog用于跨存储引擎的主从同步,而在PG中,主从同步已经通过redo日志(PG术语为XLOG)同步的情况下,为了处理没有undo带来的一系列问题,其中可见性判断这个功能,就是交给clog日志文件解决的。


Clog中记录了每一个事务相关的xid(记得之前曾吐槽过这个玩意的大小问题带来的freeze问题)以及xid对应的事务的提交状态。提交状态包括以下一些:执行中,已提交,已中断,已提交的子事务。看到这里,就可以明白,只要事务提交的时候,设置状态为已提交,而事务回滚的时候,设置状态为已中断,就可以达到目的,的确避免了操作数百万行的事务突然要回滚时候的巨大代价。


但我看到这里的时候,就产生一个疑惑,这样的话,我查数据的时候,见到一行的xid之后,需要马上确认其可见性,就需要去查clog,这个查询频率势必极高而且随机性很大,这个问题该怎么解决呢?

 
 
#define CLOG_BITS_PER_XACT    2	
#define CLOG_XACTS_PER_BYTE 4	
#define CLOG_XACTS_PER_PAGE (BLCKSZ * CLOG_XACTS_PER_BYTE)	
#define CLOG_XACT_BITMASK    ((1 << CLOG_BITS_PER_XACT) - 1)	
#define TransactionIdToPage(xid)    ((xid) / (TransactionId) CLOG_XACTS_PER_PAGE)	
#define TransactionIdToPgIndex(xid) ((xid) % (TransactionId) CLOG_XACTS_PER_PAGE)	
#define TransactionIdToByte(xid)    (TransactionIdToPgIndex(xid) / CLOG_XACTS_PER_BYTE)	
#define TransactionIdToBIndex(xid)    ((xid) % (TransactionId) CLOG_XACTS_PER_BYTE)


PG代码给了一个非常精彩的回答。


还记得之前vacuum那个里面,我大力吐槽PG对32位xid的执着,但这个32位id果真一无是处吗?看到这里才明白,还留着这么一笔思路。


一个简单的算术,每个事务标记占据2个比特位(无符号0 1 2 3对应前面提到的事务状态),也就是说,每个字节可以保存4个事务,每当PG需要确定当前事务状态的时候,就直接根据当前事务id计算得到对应的clog页位置(除每页clog之后的整数商是页数字,而余数则是在页中的具体位置)。真是把文件当hash表用的典范啊。


在32位xid的情况下,假设xid限制是20亿,每个8K的clog页存储32k事务位的情况下,clog最大也才五百来MB,这部分交给操作系统的文件缓存足以保障访问效率了。


真是一个绝妙的主意不是么?如果不考虑64位xid的情况下,clog大小完全不可控的情况的话。


还是把话题集中在clog,下面我们来探讨的是,当事务提交或者回滚的时候,其内部的运作机理又是如何呢?


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值