mysql innodb高并发基础-MVCC

mysql innodb能高效运行,支撑高并发原因就是基于MVCC实现。

本文仅是简单介绍下MVCC原理,介绍事务隔离级别RC,RR中原理。

MVCC

MVCC(全称Multi-Version Concurrency Control),多版本并发控制,解决数据库的并发访问高效问题。

维基百科中有更具体的描述,感兴趣可以看看。

MVCC到底解决什么问题

数据库中,读数据的同时,有另外的任务写入数据,在没有任何锁机制情况下,很有可能读到是『半写』或者不一致的数据。

当然解决这个问题最简单的方法,通过加锁,即Share lock,write lock,会有读读兼容,读写或写读阻塞问题,这种方式效率很低。实际中,在写某行数据同时,又另外任务使用mysql同时去读数据,读操作并没有感受到block,反而觉得效率很高。可以确定是mysql中没有最简单的机制来保证并发,官方文档上申明使用了MVCC。

当然,innodb是行锁,针对记录的更新会加排他锁,保证并发时资源时线程安全。
锁的介绍博文中有介绍,这里就不多介绍。

MVCC是怎么做到的

mysql文档:InnoDB是一个多版本的存储引擎,保存有关行已更改的old version的信息,以支持并发和回滚等事务功能。

简而言之:

MVCC使用了快照手段,每个连接到数据库的读者,在某个瞬间看到的是数据库的 某一个快照,写者写操作造成的变化在写操作完成之前(或者数据库事务提交之前)对于其他的读者来说是不可见的。

内部,InnoDB为数据库中存储的每一行j记录添加三个字段:

  • 6字节的事务ID(DB_TRX_ID ):表示插入或更新该行的最后一个事务的事务标识符
  • 7字节的回滚指针(DB_ROLL_PTR):指向写入回滚段的undo log记录。更新了行,则undo log记录包含在更新行之前重建行内容所需的信息。
  • 6字节的自增 (DB_ROW_ID):插入新行时单调增加的行ID。

undo log

回滚使用undo log,以rollback segment的数据结构(Oracle也是类似数据结构)为单位进行存储,存储在表空间中,可以说undo log被划分为多个段,具体某行的undo log就保存在某个段中 。因此InnoDB使用回滚段中的信息来执行事务回滚中所需的undo操作。磁盘上不存在单独的undo log文件,所有的undo log存放在存放于ibdata文件中。

具体操作:copy事务前的数据库内容(行)到undo buffer,在适合的时间把undo buffer中的内容刷新到磁盘。undo buffer是环形缓冲,但当缓冲满的时候,undo buffer中的内容会也会被刷新到磁盘undo log文件,所有的undo log均存放在主ibdata文件中(表空间),即使客户端设置了每表一个数据文件也是如此。

undo log分为insert 和update undo log类型,删除也是update undo log。

insert undo log:原始的数据并不存在,所以回滚时把insert undo log丢弃即可

流程如下:

  1. 初始数据行


    mysql innodb高并发基础-MVCC_第1张图片
    image.png

    原始的数据并不存在,回滚指针是null,回滚时直接丢弃改行。

  2. 事务1更修改
    获得记录更新锁后,把该行修改前的值Copy到undo log,记录事务ID 回滚ID。

mysql innodb高并发基础-MVCC_第2张图片
image.png

3.事务2修改


mysql innodb高并发基础-MVCC_第3张图片
image.png
  1. 事务提交
    当事务正常提交时,只需要更改事务状态为COMMIT即可,不需做其他额外的工作,而Rollback则稍微复杂点,需要根据当前回滚指针从undo log中找出事务修改前的版本并恢复。

上面的流程看来,undo log是不是越来越大,这点mysql也做了考虑,会启动purge进程以真实删除老的、过时的数据。

redo log

为了支持事务使用redo log。当客户端执行每条SQL(更新语句)时,redo log会被首先写入log buffer,执行COMMIT命令时,log buffer中的内容会被视情况刷新到磁盘。具体作用:保存执行的SQL语句到一个指定的Log文件,当Mysql服务因断电恢复或强行重启等时重新执行redo log记录的SQL操作,继续那些已经commit但数据尚未完全回写到磁盘的事务,保证操作不丢失。

与事务关系

  • READ_UNCOMMITTED
    读事务直接读取主记录,无论更新事务是否完成。

  • READ_COMMITTED
    优先读取本事务修改的值,未有修改的每次都读取undo log中最近的版本。两次对同一字段的读可能读到不同的数据,但能保证每次都读到最新的数据。使用行锁。

  • REPEATABLE_READ
    读该事务第一次读建立起来的数据快照,每次都读取指定的版本,如果没有做更新可能读不到最新的数据。这里使用行锁,gap锁,next-key锁,如果索引不是聚镞索引,使用next-key锁能保证不会产生幻读。

  • SERIALIZABLE
    读加共享锁,写加排他锁,读读不会阻塞,读写或写读出现阻塞,因此并发度大幅下降。

参考资料

https://en.wikipedia.org/wiki/Multiversion_concurrency_control
https://dev.mysql.com/doc/refman/5.7/en/innodb-multi-versioning.html
http://www.360doc.cn/article/12904276_403505950.html

你可能感兴趣的:(mysql innodb高并发基础-MVCC)