DM数据库为用户提供了两种判断MVCC事务可见性的模式,使用参数TRX_VIEW_MODE控制。这两种方式分别是:
TRX_VIEW_MODE=0:事务启动前收集当前时刻的活动事务 ID 构造可见性视图,以确定某一特定事务是否对当前事务可见;
TRX_VIEW_MODE=1: 系统维护事务提交历史,以此判断事务可见性。
其不同在于,一种是基于回滚记录的,一种是基于时间戳的。
对于基于回滚记录的MVCC并发机制,DM采用了常见的设计,即在物理记录上,包含事务TID和版本指针RPTR,使得记录间单向连接,形成链式版本。
这种MVCC机制,要求事务根据当前正在活动事务的视图,依据上述的链式版本,构成可见的记录集合。这个视图根据设置的事务隔离级别的不同,在不同的时间形成,如下表。
读已提交级别 | 在SQL执行前收集一次活动事务信息 |
可串行化级别 | 在事务启动时收集一次活动事务信息 |
简要来说,对于每一个新事务,这个活动事务视图包含了当前还没有结束的事务的事务ID,也就是说,对于每一个新事务,都维护了一个数组。
进行多版本读时,这个视图的使用方式是这样的:
在形成活动事务视图TRX_VIEW时,一个事务还收集当前最下活动事务号MIN_ACTIVE_ID,以及全局下一个事务号NEXT_ID。这样,一个事务在读一条记录时,就可以进行如下的比对:
记录事务ID大于(等于)下一个事务ID | 记录不可见 |
记录事务ID等于自身事务ID | 记录可见 |
记录事务ID小于当前最小已激活事务ID | 记录可见 |
记录事务ID不在事务视图中 | 记录可见 |
记录事务ID在事务视图中 | 记录不可见 |
对于可见的记录,则取这条记录为最终版本;对于不可见的记录,则通过链式版本继续向这条记录的过往版本进行上述的比对,直到找到可见的版本(或不取这条记录)。
这样的MVCC实现方式在主流的数据库上较为多见。但这样的问题是,当全局事务量较为庞大时,因为每个事务都需要维护一个数组,可能会导致收集、扫描、保存数组的代价过于高昂。
为此,DM数据库提供给用户第二种MVCC方式,也是当前版本DM数据库的默认MVCC方式,即基于时间戳的MVCC。
这种MVCC的实现方式是,全局只维护一个大数组CMTARR(长度一般在千万级);DM数据库令事务在启动时,将这个数组中对应位置(事务号)上的数据CMTSEQ置为0,而在事务提交时,则将这个位置上的数据CMTSEQ置为当前的时间。
这样的话,事务或SQL启动时,只需要获取一个当前的时间戳SNAP_CMTSEQ,再与记录ID在CMTARR对应位置上的时间戳比对,就可得出可见与否了。具体比对方式如下:
SNAP_CMTSEQ >= CMTSEQ | 可见 |
SNAP_CMTSEQ < CMTSEQ | 不可见 |
这个大数组的长度,可以由静态参数TRX_CMTARR_SIZE控制(单位:百万)。这个参数设置越大,内存消耗越多;单位时间内事务完成数量大时,可将此参数值设大。
DM在这种MVCC策略下,对于“长事务”,也有对应的处理方案。
所谓“长事务”,就是事务号<下一个事务号-(数组长度/2)的事务,即当前的下个事务(即全局最新的事务)与这个长事务间隔超过了CMTARR数组长度的一半,也就是执行时间超长,以至于在其执行期间由大量新事务启动的事务。
DM对这些长事务,采用专用线程定期收集,等级到全局LONGARR内;每个事务开始时都拷贝长事务信息,令这些长事务一律不可见。
达梦在线服务平台地址:达梦数据库 - 新一代大型通用关系型数据库 | 达梦在线服务平台