zoukankan      html  css  js  c++  java
  • InnoDB一致性非锁定读

    多版本并发控制仅仅是一种技术概念,并没有统一的实现标准, 其的核心理念就是数据快照,不同的事务访问不同版本的数据快照,从而实现不同的事务隔离级别。虽然字面上是说具有多个版本的数据快照,但这并不意味着数据库必须拷贝数据,保存多份数据文件,这样会浪费大量的存储空间。InnoDB通过事务的undo日志巧妙地实现了多版本的数据快照。

    数据库的事务有时需要进行回滚操作,这时就需要对之前的操作进行undo。因此,在对数据进行修改时,InnoDB会产生undo log。当事务需要进行回滚时,InnoDB可以利用这些undo log将数据回滚到修改之前的样子。

    隐藏列

    在分析MVCC原理之前,先看下InnoDB中数据行的结构:

    img

    在InnoDB中,每一行都有2个隐藏列DATA_TRX_ID和DATA_ROLL_PTR(如果没有定义主键,则还有个隐藏主键列):

    1. DATA_TRX_ID表示最近修改该行数据的事务ID
    2. DATA_ROLL_PTR则表示指向该行回滚段的指针,该行上所有旧的版本,在undo中都通过链表的形式组织,而该值,正式指向undo中该行的历史记录链表

    整个MVCC的关键就是通过DATA_TRX_ID和DATA_ROLL_PTR这两个隐藏列来实现的。

    事务链表

    MySQL中的事务在开始到提交这段过程中,都会被保存到一个叫trx_sys的事务链表中,这是一个基本的链表结构:

    trx_list

    事务链表中保存的都是还未提交的事务,事务一旦被提交,则会被从事务链表中摘除。

    MVCC在MySQL的InnoDB中的实现

    在InnoDB中,会在每行数据后添加两个额外的隐藏的值来实现MVCC,这两个值一个记录这行数据何时被创建,另外一个记录这行数据何时过期(或者被删除)。 在实际操作中,存储的并不是时间,而是事务的版本号,每开启一个新事务,事务的版本号就会递增。 在可重读Repeatable reads事务隔离级别下:

    • SELECT时,读取创建版本号<=当前事务版本号,删除版本号为空或>当前事务版本号。
    • INSERT时,保存当前事务版本号为行的创建版本号
    • DELETE时,保存当前事务版本号为行的删除版本号
    • UPDATE时,插入一条新纪录,保存当前事务版本号为行创建版本号,同时保存当前事务版本号到原来删除的行
      通过MVCC,虽然每行记录都需要额外的存储空间,更多的行检查工作以及一些额外的维护工作,但可以减少锁的使用,大多数读操作都不用加锁,读数据操作很简单,性能很好,并且也能保证只会读取到符合标准的行,也只锁住必要行。

    “读”与“读”的区别

    我们且看,在RR级别中,通过MVCC机制,虽然让数据变得可重复读,但我们读到的数据可能是历史数据,是不及时的数据,不是数据库当前的数据!这在一些对于数据的时效特别敏感的业务中,就很可能出问题。
    对于这种读取历史数据的方式,我们叫它快照读 (snapshot read),而读取数据库当前版本数据的方式,叫当前读 (current read)。很显然,在MVCC中:

    • 快照读:就是select
      • select * from table ….;
    • 当前读:特殊的读操作,插入/更新/删除操作,属于当前读,处理的都是当前的数据,需要加锁。
      • select * from table where ? lock in share mode;
      • select * from table where ? for update;
      • insert;
      • update ;
      • delete;

    事务的隔离级别实际上都是定义了当前读的级别,MySQL为了减少锁处理(包括等待其它锁)的时间,提升并发能力,引入了快照读的概念,使得select不用加锁。而update、insert这些“当前读”,就需要另外的模块来解决了。

    undo日志实现多版本并发控制(MVCC)

    根据行为的不同 undo log 分为两种 insert undo log和update undo log。
     insert undo log 是在 insert 操作中产生的 undo log。因为 insert 操作的记录只对事务本身可见,对于其它事务此记录是不可见的,所以 insert undo log 可以在事务提交后直接删除而不需要进行 purge 操作。

    update undo log 是 update 或 delete 操作中产生的 undo log,因为会对已经存在的记录产生影响,为了提供 MVCC机制,因此 update undo log 不能在事务提交时就进行删除,而是将事务提交时放到入 history list 上,等待 purge 线程进行最后的删除操作。

    为了保证事务并发操作时,在写各自的undo log时不产生冲突,InnoDB采用回滚段的方式来维护undo log的并发写入和持久化。回滚段实际上是一种 Undo 文件组织方式。

    InnoDB行记录有三个隐藏字段:分别对应该行的rowid、事务号db_trx_id和回滚指针db_roll_ptr,其中db_trx_id表示最近修改的事务的id,db_roll_ptr指向回滚段中的undo log。如下图所示。

    初始状态

    当事务2使用UPDATE语句修改该行数据时,会首先使用排他锁锁定改行,将该行当前的值复制到undo log中,然后再真正地修改当前行的值,最后填写事务ID,使用回滚指针指向undo log中修改前的行。如下图所示。

    第一次修改

    当事务3进行修改与事务2的处理过程类似,如下图所示。

    第二次修改

    REPEATABLE READ隔离级别下事务开始后使用MVVC机制进行读取时,会将当时活动的事务id记录下来,记录到Read View中。READ COMMITTED隔离级别下则是每次读取时都创建一个新的Read View。
     Read View是InnoDB中用于判断记录可见性的数据结构,记录了一些用于判断可见性的属性。

    • low_limit_id:某行记录的db_trx_id < 该值,则该行对于当前Read View是一定可见的
    • up_limit_id:某行记录的db_trx_id >= 该值,则该行对于当前read view是一定不可见的
    • low_limit_no:用于purge操作的判断
    • rw_trx_ids:读写事务数组

    Read View创建后,事务再次进行读操作时比较记录的db_trx_id和Read View中的low_limit_id,up_limit_id和读写事务数组来判断可见性。

    如果该行中的db_trx_id等于当前事务id,说明是事务内部发生的更改,直接返回该行数据。否则的话,如果db_trx_id小于up_limit_id,说明是事务开始前的修改,则该记录对当前Read View是可见的,直接返回该行数据。

    如果db_trx_id大于或者等于low_limit_id,则该记录对于该Read View一定是不可见的。如果db_trx_id位于[up_limit_id, low_limit_id)范围内,需要在活跃读写事务数组(rw_trx_ids)中查找db_trx_id是否存在,如果存在,记录对于当前Read View是不可见的。
     如果记录对于Read View不可见,需要通过记录的DB_ROLL_PTR指针遍历undo log,构造对当前Read View可见版本数据。
     简单来说,Read View记录读开始时及其之后,所有的活动事务,这些事务所做的修改对于Read View是不可见的。除此之外,所有其他的小于创建Read View的事务号的所有记录均可见。

    参考

    MySQL探秘(六):InnoDB一致性非锁定读

    理解 MySQL 一致性非锁定读原理

    MySQL MVCC实现

  • 相关阅读:
    LiveNVS实现摄像头RTSP无插件播放,并集中化管理
    liveplayer免费网页直播_点播播放器-页面动态多播放器添加代码示例
    JavaScript之图片滚动
    JavaScript之图片轮换
    DOS命令
    jquery之音乐均衡器
    JavaScript之可运行按钮
    jQuery之点击弹出图标环形菜单
    Android之View方法
    Android之所有权限
  • 原文地址:https://www.cnblogs.com/hongdada/p/12505896.html
Copyright © 2011-2022 走看看