达梦数据库事务管理-MVCC

概念介绍

Multiversion Concurrency Control,简称 MVCC。是通过保存数据在某个时间点的快照来实现并发控制的。也就是说,不管事务执行多长时间,事务内部看到的数据是不受其它事务影响的,根据事务开始的时间不同,每个事务对同一张表,同一时刻看到的数据可能是不一样的。 主要有2大功能: 读不阻塞写,写不阻塞读,提供快照读(一致性读)的功能。

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,不可见。

SQL> select PARA_NAME,PARA_VALUE from v$dm_ini where para_name='TRX_VIEW_MODE';

行号     PARA_NAME     PARA_VALUE

---------- ------------- ----------

1          TRX_VIEW_MODE 1

已用时间: 6.417(毫秒). 执行号:646.

达梦数据库默认使用基于时间戳的MVCC.

MVCC可见性规则

实现多版本控制的关键是可见性判断,找到对当前事务可见的特定版本数据。DM 通过活动事务表,确定事务的可见性。根据事务隔离级的不同,在事务启动时(串行化),或者语句执行时(读提交),收集这一时刻所有活动事务,并记录系统中即将产生的事务号NEXT_TID。 DM 多版本可见性原则:

  1. 物理记录的 TRXID 等于当前事务号,说明是本事务修改的物理记录,物理记录可见;
  2. 物理记录的 TRXID 不在活动事务表中,并且 TRXID 小于 NEXT_TID,物理记录可见;
  3. 物理记录的 TRXID 包含在活动事务表中,或者 TRXID 大于等于 NEXT_TID,物理记录不可见;

Undo Log主要用于记录被修改之前的数据。在表信息修改时,会先把数据拷贝到Undo Log中。当一个事务需要读取行记录时,如果当前记录行不可见,可以通过回滚指针顺着Undo Log链找到满足其可见性条件的记录行版本;或者当事务进行回滚时,可以通过Undo Log里的日志还原数据。Undo Log一方面可用于MVCC读时构建记录,在MVCC多版本控制中,通过读取Undo Log的历史版本数据可以实现不同事务版本号都拥有自己独立的数据版本。另一方面,保证事务进行回滚时的原子性和一致性,当事务进行回滚时可以用Undo Log的数据进行恢复。

<会话1>

SQL> select isolation from v$trx;

行号     ISOLATION

---------- -----------

1          1

已用时间: 0.649(毫秒). 执行号:608.

当前隔离级别为读已提交。

SQL> CREATE TABLE TNAME (ID INT,NAME VARCHAR(20));

操作已执行

已用时间: 28.673(毫秒). 执行号:609.

SQL> INSERT INTO TNAME VALUES (1,'LOUXINRU');

影响行数 1

已用时间: 5.146(毫秒). 执行号:610.

SQL> COMMIT;

操作已执行

已用时间: 5.058(毫秒). 执行号:611.

SQL> SELECT TRXID,ID,NAME FROM TNAME;

行号     TRXID                ID          NAME

---------- -------------------- ----------- --------

1          30143                1           LOUXINRU

已用时间: 0.599(毫秒). 执行号:613.

<会话2>

SQL> select isolation from v$trx;

行号     ISOLATION

---------- -----------

1          1

2          1

3          1

4          1

5          1

已用时间: 10.685(毫秒). 执行号:900.

SQL>  SELECT TRXID,ID,NAME FROM TNAME;

行号     TRXID                ID          NAME

---------- -------------------- ----------- --------

1          30143                1           LOUXINRU

已用时间: 1.237(毫秒). 执行号:901.

<会话1>

执行更新语句不提交

SQL> UPDATE TNAME  SET NAME='RUBY LOU' WHERE ID=1;

影响行数 1

已用时间: 0.806(毫秒). 执行号:615.

SQL> SELECT TRXID,ID,NAME FROM TNAME;

行号     TRXID                ID          NAME

---------- -------------------- ----------- --------

1          30145                1           RUBY LOU

已用时间: 0.593(毫秒). 执行号:616.

<会话2>

SQL> SELECT TRXID,ID,NAME FROM TNAME;

行号     TRXID                ID          NAME

---------- -------------------- ----------- --------

1          30143                1           LOUXINRU

已用时间: 0.935(毫秒). 执行号:902.

SQL> select max(id) from v$trx;

行号     MAX(ID)

---------- --------------------

1          30146

已用时间: 0.775(毫秒). 执行号:618.

会话1:TRXID为30145

会话2:TRXID为30143

因为会话一物理记录的TRXID:30145小于等于当前最大事务号:30146, 所以在能在本事务里看见,其他事务里看不见。

会话二物理记录的TRXID:30143不在当前事务中,并且小于NEXT_TID,所以其物理记录可见;

<会话1>

SQL>  SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

操作已执行

已用时间: 0.615(毫秒). 执行号:626.

SQL> SELECT TRXID,ID,NAME FROM TNAME;

行号     TRXID                ID          NAME

---------- -------------------- ----------- --------

1          30145                1           RUBY LOU

已用时间: 0.421(毫秒). 执行号:627.

在会话1中插入数据不提交:

SQL> insert into tname values (2,'LOUXINRU');

影响行数 1

已用时间: 0.502(毫秒). 执行号:628.

SQL> SELECT TRXID,ID,NAME FROM TNAME;

行号     TRXID                ID          NAME

---------- -------------------- ----------- --------

1          30145                1           RUBY LOU

2          30148                2           LOUXINRU

已用时间: 0.324(毫秒). 执行号:629.

<会话2>

SQL>  SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

操作已执行

已用时间: 0.192(毫秒). 执行号:908.

SQL> SELECT TRXID,ID,NAME FROM TNAME;

行号     TRXID                ID          NAME

---------- -------------------- ----------- --------

1          30145                1           RUBY LOU

2          30148                2           LOUXINRU

已用时间: 0.363(毫秒). 执行号:910.

在读未提交隔离级别下,所有事务都是可见的。

<会话1>

SQL> SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

操作已执行

已用时间: 0.307(毫秒). 执行号:632.

SQL> insert into tname values (3,'LOU');

影响行数 1

已用时间: 0.575(毫秒). 执行号:633.

SQL> COMMIT;

操作已执行

已用时间: 0.290(毫秒). 执行号:637.

SQL> SELECT TRXID,ID,NAME FROM TNAME;

行号     TRXID                ID          NAME

---------- -------------------- ----------- --------

1          30145                1           RUBY LOU

2          30148                2           LOUXINRU

3          30150                3           LOU

已用时间: 0.314(毫秒). 执行号:634.

<会话2>

SQL> SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

操作已执行

已用时间: 0.238(毫秒). 执行号:912.

SQL> SELECT TRXID,ID,NAME FROM TNAME;

行号     TRXID                ID          NAME

---------- -------------------- ----------- --------

1          30145                1           RUBY LOU

2          30148                2           LOUXINRU

已用时间: 0.354(毫秒). 执行号:913.

SQL> EXIT

[dmdba@DSC-CLUSTER-01 bin]$ ./disql

disql V8

用户名:

密码:

服务器[LOCALHOST:5236]:处于普通打开状态

登录使用时间 : 2.551(ms)

SQL> select isolation from v$trx;

行号     ISOLATION

---------- -----------

1          1

2          1

3          1

4          1

5          1

SQL> SELECT TRXID,ID,NAME FROM TNAME;

行号     TRXID                ID          NAME

---------- -------------------- ----------- --------

1          30145                1           RUBY LOU

2          30148                2           LOUXINRU

3          30150                3           LOU

已用时间: 0.693(毫秒). 执行号:1100.

隔离级别为可串行话时,会话2无法看到会话1中已提交的数据,只有结束会话后,重新登录,隔离级别默认为已提交读,才可以查看到已提交的数据

总结

Read Uncommitted隔离级别总是读取最新的记录行,不需要MVCC的支持;Serializable 则会对所有读取的记录行都加锁,单靠MVCC无法完成;

MVCC在不加锁的情况下解决了脏读、不可重复读和快照读下的幻读问题;

MVCC通过对比事务ID 的大小对数据进行展示。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值