关键字:
KingbaseES、MVCC、数据快照、快照过期、人大金仓
1、基本概念
1.1、什么是MVCC
MVCC(Multi-Version Concurrency Control),即:多版本并发控制,是一种数据库处理并发操作的机制。
在提出MVCC概念前,处理并发控制场景时,常见的做法是资源锁,通过锁定来确保某一指定时刻只有一个事务可以修改/读取数据,避免数据不一致或者冲突,。然而锁的本质是以牺牲DB的性能来保障数据的一致性,在高并发访问时,继续使用锁的话,将会导致整个DB的性能瓶颈,尤其是并发性能的下降。
MVCC的核心思想:对于每个变更数据的事务操作对象,不再是原始数据,而是基于事务创建的数据版本。当其他事务仍然可以访问到该数据的已提交版本,而不受正在进行的修改事务影响,从而实现数据的一致性。
1.2、用途与目的
MVCC通过版本号、时间戳或者类似的标识来管理数据版本,通过一定的判断规则来决定不同版本对于不同事务的可见性。
MVCC中,每个事务在操作数据时会得到一个特定的数据版本,从而使不同事务在对同一数据可以进行同时操作。每个事务仅操作属于自己的数据版本,事务间互不干扰,从而提高了并发性,使整个DB展现更好的性能。
MVCC被提出的最根本目的——数据库并发性能提升,具体实现可分为如下场景:
- 无锁读取,读操作事务不再对数据产生锁闭,多个事务同时进行读操作,不再需要等待资源锁释放,不会互相干扰,避免读冲突(写冲突);
- 无干扰写入,不同事物的写操作均在数据版本上发生,避免了写冲突;
2、实现方式
2.1、核心组件
MVCC的核心组件包括:版本链(Version Chain)、trx_id(即,事务id)、可见性规则(Visibility Rules)、数据版本、可回滚段。
版本链:每个变更数据的事务操作对象,不是原始数据,而是为该事务创建的数据版本,复数个有关联的数据版本就会产生一个链接于该原始数据行的链表;
版本链简图
事务ID:标识事务的唯一标识符,具有全局唯一性,事务提交后递增,可用来跟踪每个事务的操作;
数据版本/数据快照:每一个更新数据的事务启动时,在DB中的数据都有与之对应的状态记录拷贝(事务的操作也是针对这个记录,而不是针对原始数据),以便确定数据拷贝对该事务是否可见,从而实现事务间的数据隔离;
可见性规则:DB用于决定哪些版本对于特定事务是可见的判定规则;
可回滚段(undo log):为了支持事务的回滚操作,DB提出了可回滚段这个概念。当一个事务需要回滚时,根据undo log中的记录,撤销事务对应修改,使数据恢复到事务发生前的状态。 在版本链中,回滚操作依赖回滚指针(roll_pt)和rwo_id来实现;
rwo_id(索引id):数据版本的隐藏列,产生聚集索引树,与更新一个数据版本的回滚指针对应。
回滚指针(roll_pt):数据版本中的隐藏列,用以标识出回滚动作的旧数据版本的rwo_id。
在执行更改数据操作时,每次操作都将在undo log中生成一条详细记录,其中包含着全局唯一的roll_pointer,用以指出这条记录有关联关系的前一条undo-log记录。一条数据的多个undo-log记录通过roll_pointer就可以形成针对该数据的版本链,当有事务对该数据进行操作时,将操作记录连接到版本链的头部。
MVCC版本链及数据版本结构
2.2、MVCC中的事务ID
当一个版本链生成时,会创建一个读视图,包括整个版本链涉及的所有数据版本的事务ID可调用的最小事务ID(min_trx_id)、最大事务ID(max_trx_id)、
xmin_id:数据的insert操作发生时,占用的事务ID;
xmax_id:数据的delete操作发生时,占用的事务ID,未执行删除操作时为0,删除数据的事务发生时,变更为非0数组;
cmin_id:更新数据元组的操作在insert事务中的标识符;
cmax_id:放弃数据元组的操作在insert事务中的标识符;
3、MVCC的应用
3.1、事务隔离与可见规则
MVCC的引入导致数据经常处于多事务同时处理的状态,这时就需要事务隔离级别的规定来保证事务间的互不干扰与可见性界定
RC(Read Committed),读写时只有committed的数据能被操作,但是会引入不可重复读;
RR(Repeatable Read),读写时即使已committed,但是事务ID还在版本链区间,也不可操作,确保数据一致性读。
可见性规则举例:
①数据记录的事务ID<最小事务ID时,表示该数据版本已提交,可以被读取;
②数据记录的事务ID在最小事务ID和最大事务ID之间,且>cmin_id时,表示该版本对应的事务未提交,不可见;
③数据记录的事务ID在最小事务ID和最大事务ID之间,且<cmin_id时,表示该版本对应的事务已提交,可见;
④数据记录的事务ID>最大事务ID时,表示该数据版本该视图生成后创建,不可见;
3.2、读操作(select)执行过程
1)获取事务指定的数据快照:开始执行select事务时,生成一个数据快照,以确定当前数据版本的状态;
2)查询可见的数据版本:基于数据快照,检查每个被查询的数据行的版本链,并根据DB的可见性规则确定每个数据行当前状态为可见的数据版本;
3)展示被读取到的信息:根据上一步查询的结果,生成需要放回的视图信息;
3.3、写操作(insert、update、delete)执行过程
1)开始事物:写操作事务开始时,获取唯一的事务ID(xmin_id),并记录当前的数据快照;
2)生成数据版本:写操作在未被提交前,不会直接操作相关数据表,而是在undo log中创建一个数据版本;
3)更新版本链:将新生成的版本插入到对应的版本链的链头,成为版本链中最新的数据版本;
4)事物提交:事务在提交后,更新为最新的可见版本。
3.4、其他
3.4.1、MVCC与不可重复读
不可重复读:当事务A对数据进行读取后(未提交),该数据被被另一个事务修改(已提交),事务A再次对数据进行读取,获取结果为修改后的数据。
MVCC处理机制:针对同一事务的第一次查询会获取数据快照,该事物后续的查询都基于该快照去判断,这样每次查询的结果都在同一版本链的可见数据版本内,从而解决不可重复读的问题。
3.4.2、MVCC与幻读
幻读:当事务A对数据进行读取后(未提交),该数据被被另一个事务修改(未提交),事务A再次对数据进行读取,获取结果为修改后的数据
幻读的问题是无法通过MVCC单独解决,但是通过使用范围锁(range locking)去锁定一定范围的数据,以便确保其他事务不能再范围内进行数据操作。因为指定事务能够锁定整个范围锁内全部数据,从而保持了数据的一致性。
4. 快照过期
4.1 数据快照的缺陷
当一个数据版本被一个长事务占用时,将导致相关存储资源和内存资源无法回收。此种情况在数据库server大量发生的话,将会导致数据库内数据严重膨胀,进而影响数据库的性能和稳定性。
数据膨胀产生的简化逻辑流程见下图:
4.2 快照过期业务逻辑
为解决数据膨胀的问题,引入了一个新的特性:snapshot too old(快照过旧),在数据库server增加了一个参数old_snapshot_threshold。通过这个参数,数据库server可以为数据快照设定一个过期时间,对于过期的数据快照,数据库server可以直接清理,此时占用此数据快照的长事务结束并报错提示:snapshot too old。
增加快照过旧特性的数据版本管理流程简化逻辑,见下图
5 应用演示
在人大金仓的数据库产品中(单机的KingbaseES,读写分离的RWC,共享存储的RAC),也引入了MVCC和快照过期的特性。
在kingbase.conf中搜索关键字可以找到old_snapshot_threshold,根据需要配置参数的值,配置区间:-1~86400;-1代表不过期;0代表立刻过期;1~86400,过期时间,默认单位为分钟,也可以配置为Xh或Xd这样的格式,按小时或天为计量单位,最大过期时间不大于60天即可。
同时根据实际使用,还需要配置参数autovacuum,开启自动回收垃圾数据,才能确保环境中的长事物占用的数据不会造成数据库数据膨胀。
特性生效验证:
- 修改kingbase.conf文件,开启old_snapshot_threshold;
- 重启数据库;
- 查看相关配置是否生效;
(执行语句“show old_snapshot_threshold;”,可以查看当前数据库的快照过期配置)
- 数据库里创建表t1,并插入数据;
- 执行一个长查询事物
(示例中通过查询中加入sys_sleep()参数实现放慢业务处理速度的效果)
- 另起一个连接,清除t1的数据
- 手动回收t1表的死元组数据(需要多次执行)
(等待快照过期配置的时间生效后,可以看到数据被回收,长查询事物失败,生成error级报错,ERROR:snapshot too old)
6 应用细节
验证功能时发现,长事务失败报错的时间和old_snapshot_threshold参数配置时间不一致,要比快照过期时间大一些?
这是因为查询类长事务在执行时会对数据进行分页,一次性获取一整页的数据缓存,当缓存数据处理完毕时,才会再次获取下一页数据的缓存,此时事务获取数据失败,数据快照早已按照过期配置清理。
案例1:
在第二张图中,可以明显看到600条数据的长查询中的数据被分为了3页,其中第一页因为缓冲区,在回收数据时被跳过,快照过旧发生在长查询完成对第一页数据完成处理,调用第二页数据时(此时被查询数据已经过期,且被回收)。
案例2:
在第二张图中,可以明显看到100条数据的长查询中的数据仅占用1页,其中第一页因为缓冲区,在回收数据时被跳过,长查询完成对第一页数据处理,打印事物结果,快照过旧无法发生(被查询数据已经过期,且被回收,但是缓存确保了事物的完成)。