1. 背景
在数据库系统中,并发控制是一个重要的组成部分,它用于处理多个事务同时对数据进行访问和修改的情况。如果没有正确的并发控制,可能会导致数据的不一致性和不可预测的结果。而在并发控制中,事务快照是一种常用的技术,它通过对事务执行过程中的数据状态进行快照,保证了事务的原子性和一致性。具体来说,事务快照可以在事务执行过程中,将数据状态记录下来,并且保证在事务执行过程中不会被其他事务干扰。
本文将主要介绍AntDB数据库内事务快照的设计方案,包括其在集中式和分布式架构之间联系和差异。
2. 事务快照的含义
快照(Snapshot),一个从摄影中借用而来的词,本意是表示系统内某个时间点的状态,就好像是对系统拍了张“照片”。那么事务快照,顾名思义,就是事务状态的“照片”,有固定的样式,如下:
xmin:xmax:xip_list
● xmin:最早的活跃的事务的txid,比它更早的事务(txid < xmin),要么已提交,要么已回滚并成为死元组。
● xmax:第一个尚未分配的txid,txid ≥ xmax的事务在获取快照时尚未启动。
● xip_list:获取快照时点的活跃事务的txid列表,仅包括xmin与xmax之间的txid。
由此可见,通过事务快照,可以得知某一个时刻数据库内事务的活跃状态。
3. 事务快照的用途
一般来说,事务快照(后简称快照)对用户是透明的,即用户感知不到快照的存在。但是在数据库内部,快照却是时刻都在发挥作用的,每条SQL语句都会获取快照,获取的频率与事务的隔离级别有关。
读已提交(RC):事务内每条SQL执行时都会获取快照。
可重复读(RR):仅在事务内的第一条SQL执行时会获取快照,后续的SQL都会使用第一次获取的快照。
根据事务的隔离级别,SQL语句获取快照的目的就是为了实现可见性的判断。所谓可见性判断,是AntDB的一种并发控制的手段,不通过锁机制就可以实现读写操作的并发执行,从而提高数据库的吞吐性能的机制。(关于可见性判断的内容,笔者会在后续的文章中进一步介绍)
4. SQL如何获取事务快照
4.1 获取快照函数的设计
在SQL进入语法解析阶段之前就会获取一次快照,同时标记为ActiveSnapshot,后续进入执行阶段之前,会判断快照是否已经失效,如果失效的话,会重新获取一次。
AntDB内用于获取快照的函数是GetSnapshotData(),内部按照如下的逻辑执行。当然,完整的代码(src/backend/storage/ipc/procarray.c)处理逻辑要复杂的多,比如还有更新GlobalXmin,子事务相关处理等等,笔者在此简化了很多:
1. share buffer内加上共享读锁(ProcArrayLock),用于访问所有的会话对象;
2. 遍历所有的Proc对象(每个用户连接在数据库内核里面会对应一个Proc对象);
3. 从每个Proc对象中读取该连接的事务号信息;
4. 最终获取所有连接中的xmin,xmax以及xid列表,生成一份快照数据;
5. 解除共享读锁;
6. 返回快照数据。
4.2 分布式架构下的差异
上面的流程是集中式架构下的流程,对于分布式架构而言,又有所不同。主要区别在于,在分布式架构内,用户是连接各个CN节点执行SQL,每个CN都会申请事务号,即属于分布式事务模型(关于分布式事务的内容,笔者会在后续的文章中进一步介绍)。
为了简化设计,同时又满足快照需求,AntDB通过GTM节点维护了一个全局的事务活跃列表,每个节点上都有一份,且内容相同,里面保存了当前整个分布式集群中处于活跃状态的事务号。
因此,获取快照同样是上面的流程,首先可以从这个列表里面直接取出全局的活跃事务号,然后再遍历节点本地的Proc对象(正常情况不需要再遍历了,但为了一些特殊场景保留了这个逻辑),最后融合二者之后就可以得到最终的事务快照,AntDB内称之为全局一致性事务快照。
所谓的全局一致性,目的就是保证不管在哪个节点取快照,看到的都是同样的事务号,结合可见性判断,就可以保证分布式下的并发,不存在数据不一致的情况。
5. 总结
如上所述,快照的获取,每个SQL都要执行,而且可能是多次,里面既要遍历,还要有加锁的行为,整体上看是个相当消耗资源的动作。基于此,新版的AntDB考虑会添加复用快照等等的优化,进一步提高并发性能,欢迎读者朋友们持续关注。
关于亚信安慧AntDB数据库
AntDB数据库始于2008年,在运营商的核心系统上,为全国24个省份的10亿多用户提供在线服务,具备高性能、弹性扩展、高可靠等产品特性,峰值每秒可处理百万笔通信核心交易,保障系统持续稳定运行近十年,并在通信、金融、交通、能源、物联网等行业成功商用落地。