MVCC理解

MVCC (Multiversion Concurrency Control),即多版本并发控制技术,它使得大部分支持行锁的事务引擎,不再单纯的使用行锁来进行数据库的并发控制,取而代之的是,把数据库的行锁与行的多个版本结合起来,只需要很小的开销,就可以实现非锁定读,从而大大提高数据库系统的并发性能.

 

解决问题:

 

  1. 读写不冲突,提升读写并发数
  2. 是隔离级别 RR 和RC实现隔离性的手段

实现原理:

 

  1. 隐式字段中的DB_TRX_ID(事务ID)
  2. 隐式字段中的DB_ROLL_PTR(回滚指针)
  3. 隐式字段中的删除flag
  4. 隐式字段中的删除flag的事物版本
  5. undolog
  6. readview

mvcc  流程:

  1. 当更新时会将更新行数据copy一份放入undolog,且更新行的DB_TRX_ID(事务ID)递增+1,DB_ROLL_PTR(回滚指针)指向undolog
  2. 当删除时会将隐式字段中的删除flag设置为删除
  3. 查询时要根据事务ID查询可看到的记录版本,比如RR事务级别,查询时可看到版本号小于当前事物版本号的记录(如删除flag为true,要判断删除事务版本与当前查询事物版本的大小)

purge线程:

  事务在进行update或delete时产生的undo log; 不仅在事务回滚时需要,在快照读时也需要;

所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被purge线程统一清除。

 

purge线程原理:purge线程自己也维护了一个read view(这个read view相当于系统中最老活跃事务的read view),所以purge能看到被标记delete flag的记录直接删除是不会影响其他事物的。

 

 

Read View(读视图)

核心作用: 当前事务能够看到哪个版本的数据,因为开启事务A时,系统中可能运行了很多事物,事物A能看到这些事物的哪些版本呢? 这就是由READ VIEW视图快照记录了下来。

 

什么是Read View,说白了Read View就是事务进行快照读操作的时候生产的读视图(Read View),在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的ID(当每个事务开启时,都会被分配一个ID, 这个ID是递增的,所以最新的事务,ID值越大)

所以我们知道 Read View主要是用来做可见性判断的, 即当我们某个事务执行快照读的时候,对该记录创建一个Read View读视图,把它比作条件用来判断当前事务能够看到哪个版本的数据,既可能是当前最新的数据,也有可能是该行记录的undo log里面的某个版本的数据。

Read View遵循一个可见性算法,主要是将要被修改的数据的最新记录中的DB_TRX_ID(即当前事务ID)取出来,与系统当前其他活跃事务的ID去对比(由Read View维护),如果DB_TRX_ID跟Read View的属性做了某些比较,不符合可见性,那就通过DB_ROLL_PTR回滚指针去取出Undo Log中的DB_TRX_ID再比较,即遍历链表的DB_TRX_ID(从链首到链尾,即从最近的一次修改查起),直到找到满足特定条件的DB_TRX_ID, 那么这个DB_TRX_ID所在的旧记录就是当前事务能看见的最新老版本



 


 

READ VIEW中的   up_limit_id,low_limit_id :

MVCC Multi-version Concurrency Control

  • 怎么个MultiVersion法?
    • 每个开启的事务,会有一个tx_id,是不断自增长的,后面开的事务比前面开的事务大
    • 在默认的隔离级别:RepeatableRead,可重复读,需要做成,在事务中,看不到别人的修改,怎么做到呢?
      • 先引入一个概念:ReadView。ReadView里包含:
        • 当前活跃的事务id列表
        • 创建者的事务id
        • up_limit_id , low_limit_id 上下限,小于up_limit_id 可见, 大于low_limit_id 不可见。
          • 那么这个两个值是怎么赋值的呢。
          • low_limit_id 是系统最大的事务id值。up_limit_id是活跃事务列表的最小事务id
            • /* No future transactions should be visible in the view */
            • 349  view->low_limit_no = trx_sys->max_trx_id;
            • 350  view->low_limit_id = view->low_limit_no;
            • /* The last active transaction has the smallest id: */
            • view->up_limit_id = view->trx_ids[view->n_trx_ids - 1];
        • 这样理解Read View :保存了不应该让这个事务看到的其他的事务 ID 列表
      • 再引入关于可见性的规则
        • 读某一行数据的时候,如果发现他的事务id < up_limit_id (活跃id列表最小值),可见。
        • 如果发现数据的事务id>low_limit_id(ReadView时系统中最大的事务id) ,不可见。
        • 如果发现数据的事务id在列表范围内
          • 如果是id集合中的,不可见
          • 如果不在id集合中,可见
      • 根据上面的规则,以RepeatableRead举个例子吧,要求读一个值,一直读都是同一个值。
        • 这种隔离级别下,开启事务的时候开启一个ReadView,在整个事务过程中都用这个ReadView。
        • 我开启是事务是10,ReadView是 (4,8, 10),【我自己也活跃】,up_limit_id=11,low_limit_id=4
        • 如果我读到一个数据的事务id是1,小于活跃列表的最小值,可见。
          • 为什么?事务开启的时候生成的ReadView,除了4,8,10,其他都已经提交了,所以其实版本<4,以及5、6、7、9,都是肯定在我开启的时候已经提交了。这些版本的的数据,再怎么读都不会变。放心读吧。
        • 如果我读到一个数据的事务id是12,说明他在我创建ReadView之后提交的,我不应该看见这个值,应该去undolog里找这个数据的前面的版本,如果找到<4,或者5、6、7、9都是安全的,可以读。
        • 如果我读到一个数据事务id在活跃列表的范围内
          • 如果就是活跃的事务之一,比如说是8,说明这个数据在我开启事务之后提交了,这个我不能看见,应该去undo log中找上一个版本来读,假设说是7。
          • 7也是在这个活跃范围里,但是并不是活跃事务之一,这个版本是在开启事务之前提交的,这个我可见。
      • 再举个读已提交的例子。
        • 这个隔离级别是每次读都采用新的ReadView。
        • 开启的事务是10。
        • 读一个数据,事务id是9,ReadView活跃id是(4,8,10),按照规则,可见。
        • 过一会再读这个数据,发现事务id变成了11,活跃的id还是(4,8,10),但是因为开启了新的ReadView,当前系统最大事务id>11(因为我们已经读到11了嘛),判断规则是:不在活跃id列表,所以可见。这回就读到了这个数据的新版本了

 

参考

https://www.jianshu.com/p/8845ddca3b23

https://blog.csdn.net/waltonhuang/article/details/105850745

 

 

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值