zoukankan      html  css  js  c++  java
  • mvcc多版本并发控制

    mvcc全称是,多版本并发控制: multi version concurrency control

    并发问题

    我们注意到一个关键词是concurrency,表明了mvcc的初衷就是为了并发问题而设计的。既然mvcc是为了并发而生,那么是为了什么并发问题呢?

    我们看这么一个例子,以下有一条数据

    name age
    lay 28

    有两个并发事务想要修改它

    1)事务A:set age = age + 1 where name = lay

    2) 事务B:set age = age + 1 where name = lay

    在没有任何并发控制的情况下,结果可能是:

    1)事务A:age = 28 + 1 = 29

    2) 事务B:age = 28 + 1 = 29

    我们希望的结果是每个事务将 age + 1,最终结果是age = 30,但由于并发修改问题,导致了数据的不一致

    互斥锁X

    并发问题,我们最简单的方式就是加上互斥锁,让两个事务在访问这条数据的时候有序执行。那么结果就变成

    1)事务A:age = 28 + 1 = 29

    2) 事务B:age = 29 + 1 = 30

    互斥锁固然是一个最简单的方式让数据一致了,它让数据在访问的时候是互斥的。这也就导致一个问题,数据访问会有很多select,而较少的update等操作。如果都互斥的话,那么我们就会因此损失很大的性能。比如:事务A在读取数据的时候,事务B只能等待事务A读取完毕,事务B才可以读取

    共享锁S

    互斥锁的问题显而易见,我们希望读和读之间不互斥,只要控制读写、写写之间的互斥即可。这样,我们就能够支持并发的数据读取了。实现这个特性,我们需要引入共享锁

    读的时候用共享锁,写的时候用互斥锁,那么情况是:

    1)读读不互斥

    2)读写互斥

    3)写写互斥

    共享锁提高了读读的并发性,但是我们还希望在这个基础上再优化一下。为此,我们想想读写是不是也可以并发执行呢?

    MVCC

    version

    为了优化掉读写互斥,我们再引入mvcc机制。mvcc既然号称多版本,那么我们就给数据增加一个版本字段,来构造多版本

    name age version
    lay 28 01

     如果我们修改了数据,那么同时增加一条新版本的数据,这时候数据就有两个版本了

    name age version
    lay 28 01
    name age version
    lay 29 02

    两个版本,两份数据同时存在。这有什么好处呢?如:

    1)事务A:set age = 29 where name = lay

    2) 事务B:select age

    事务A在修改01版本数据的时候加上了互斥锁,控制了并发修改。但我们注意,事务A不直接修改01版本数据,而是产生02版本的新数据。

    事务B在读取01版本数据的时候不加锁,不与事务A互斥,从而读读并发、读写并发,当然写写依旧互斥。

    我们思考一个问题。当一条数据产生很多个版本的时候,事务select这条数据,怎么确定select哪个版本呢?最新版本?我们怎么知道哪个最新?

    rollback pointer

    到这里,我们需要把同一份数据的不同版本按顺序串联起来,所以我们决定再增加一个字段,rollback pointer用来指向上一个版本

    name age version rollback pointer
    lay 28 01 null
    name age version rollback pointer
    lay 29 02 01

    我们看到 rollback pointer这个字段把02版本的上一个版本只想了01,最终会串联出一个链表

    02 -> 01 -> null

    这时候我们就知道了,噢,02是最新的版本,我select的时候应该读取02这个版本的数据

    到这里mvcc的基本内容就完了,其实就是一个增加了version + rollback pointer,把一份数据不同版本通过链表的方式串联起来

    Innodb的mvcc实现

    我们再接着看看innodb对mvcc的主要实现,首先把version和rollback pointer换一下名字

    name age data_trx_id data_roll_ptr
    lay 28 事务ID 上一版本的指针

    1)version -> data_trx_id:修改该记录的事务ID

    2)rollback pointer -> data_roll_ptr:上一个版本的指针

    除了换个名字,大体和我们上面的逻辑一致。不过data_roll_prt不是指向data_trx_id而是而外生成的一个值,这里我们想成一个数据各个版本的唯一标识即可。

    基本的多版本结构有了,那么innodb是如何确定select哪个版本的呢?

    readView

    innodb设计了一个readView,它包含了当前活跃事务的ID,比如

    1)事务A,txId = tx_a,已提交

    2) 事务B,txId = tx_b,未提交

    2) 事务C,txId = tx_c,未提交

    当前活跃事务为:事务B、事务C。因此,如果生成一个readView,那么将会是

    readView = [tx_b, tx_c]

    如何根据readView确定版本?

    明白了readView是什么,再看看如何根据readView来确定版本,如下数据:

    name age data_trx_id data_roll_ptr
    lay 28 tx_a 上一版本的指针

    1)事务B执行select这条数据,这时候生成:

    readView = [tx_b, tx_c]

    2)获取到这条最新数据当前的data_trx_id = tx_a

    3)tx_a 不在 readView当中,意味着事务A已经提交了。事务B读取该版本数据。

    那么,如何在readView中呢?比如

    name age data_trx_id data_roll_ptr
    lay 28 tx_b 上一版本的指针

    1)事务C执行select这条数据,这时候生成:

    readView = [tx_b, tx_c]

    2)获取到这条最新数据当前的data_trx_id = tx_b

    3)tx_b 在 readView当中,意味着事务B未提交了。

    4) 获取data_roll_ptr找到上一个版本 data_trx_id = tx_a

    5)tx_a不在readView当中,事务C读取该版本数据。

    注意:在这两个例子当中,我们只假设事务会读取已经提交的数据修改,不考虑隔离级别问题。

    所以,通过readView来判断当前版本的事务是否已经提交,如果在readView中意味着事务未提交,如果不在readView中意味着事务已经提交,则已经提交的版本将被选中。

    隔离级别

    接下来把隔离级别考虑进去

    1)如果是read uncommitted级别,我们不需要readView,因为每次读取到最新数据即可

    2)如果是serializable级别,事务都是串行化的,甚至连mvcc都不需要了

    3)如果是read committed级别,也就是事务提交以后的数据都可见。换句话说,也就是不在readView中的tx_id版本都可以被读取到,和上面的例子一致。

    4)如果是read repeatable级别,也就是一个事务中读取到数据一直保持一致。换句话说,一个事务只生成一次readView,这样每次读取的就是同一个版本的数据了。

    那么read committed和read repeatable生成readView有什么区别呢?

    1)read committed级别是每次select都生成一遍readView,因此committed的数据都看得到

    2) read repeatalbe级别是第一次select的时候生成readView,因此可以重复读

    总结

    mvcc的本质就是构造一分数据的多个版本,把不同版本通过指针的方式变成一个链表的结构。通过链表就可以知道最新版本,并顺着链表回溯历史版本。

    而innodb在mvcc的基础上增加了readView,通过readView判断哪些事务已经提交。然后根据不同的隔离级别来确定哪个版本是可读的。

  • 相关阅读:
    linux开机启动服务配置
    流媒体服务器配置安装SRS及nginx+rtmp
    WEBRTC配置安装
    linux操作20200825
    转载流媒体服务器相关收藏
    RabbitMQ中间件使用
    如何查找删除指定进程
    硬件接口,串行比并行快的原因
    JavaBean+jsp开发模式 --结合form表单 实例
    session会话
  • 原文地址:https://www.cnblogs.com/lay2017/p/13221991.html
Copyright © 2011-2022 走看看