openGauss采用行级MVCC机制,历史版本集中存储,垃圾清理代价低。每个事务有一个单独的事务状态存储区域,记录了该事务的状态信息和CSN(Commit Sequence Number,提交顺序号)。CSN在openGauss内部使用一个全局自增的长整数来作为逻辑的时间戳,模拟数据库内部的时序。
举例来说:如图9-12所示,图中每个非只读事务在运行过程中会取得一个XID(事务号),在事务提交时会推进CSN,同时会将当前CSN与事务的XID映射关系保存起来。
图9-12 CSN-XID映射
因此当一个事务拿到的快照为CSN=3时,事务TX2、TX4、TX6、TX7、TX8的CSN分别为4、6、5、7、8,对于该事务的快照而言,这几个事务的修改都不可见。
MVCC解决的是读写并发冲突问题。更新数据的时候,原地更新,把老版本放到历史版本区页面里,同时维护新版本TUPLE(元组)到老TUPLE(元组)的指针。读TUPLE的时候, 根据快照SNPAHOST.CSN来判断应该读到哪个版本。
数据库在执行SQL的时候,首先会获取一个快照时间戳SNAPSHOT,当扫描数据页面的时候,根据SNAPSHOT.CSN和事务状态来判断哪个TUPLE版本可见或者都不可见。主要分以下3种场景:
- TUPLE(元组)的事务状态区里是回滚状态或者运行中,不可见。
- TUPLE(元组)的事务状态区里是提交状态,如果SNAPSHOT.CSN比事务区里的CSN小,当前TUPLE不可见,读取前一个版本继续比较CSN。反之可见。
- TUPLE(元组)的事务状态区里是待提交状态,需要等待提交。
CSN(Commit Sequence Number,提交顺序号)本身与XID(事务号)也会留存一个映射关系,以便将事务本身以及其对应的可见性进行关联,这个映射关系会留存在CSNLog中,如图9-13所示。
图9-13 CSNLog中映射关系
此映射机制类似于Clog本身,只不过不同的是,Clog记录的是事务ID的相关运行状态(运行中/提交/回滚),如图9-14所示。
图9-14 Clog记录的事务ID的相关运行状态
进一步结合前面讲过的行头的结构(其中的xmin、xmax)以及Clog、上述CSNLOG的映射机制,MVCC的大致判断流程如图9-15所示。
图9-15 MVCC判断流程
简单的总结来说:
如果当前事务ID小于一行的xmin,那么就需要检索xmin对应的clog,读取此事务状态,以此来判断此行数据是否对当前事务可见。
反之,如果当前事务ID大于一行中的xmax,那么说明此行数据的更新/删除发生于本事务开始之前,此行数据对本事务一定不可见(但不排除此行数据的新版本对本事务可见,因为新旧版本是单独进行判断的)
如果XID落在了Xmin、Xmax中间,就需要依据CSN来判断本事务的快照下,对应数据是否应该被看到,需要检索CSNLog来进行对比判断。