今天我们来学习一下MySQL的事务隔离是如何实现的。如果你对事务以及事务隔离级别还不太了解的话,这里左转

好的,下面正式进入主题。事务隔离级别有4种:读未提交、读提交、可重复读和串行化。首先我们来说一下读未提交和串行化。

  • 读未提交:性能最好,因为不加锁,所以可以理解为没有隔离。
  • 串行化:读加共享锁,其他事务可并发读,但不能写;写加排他锁,其他事务不能并发写也不能并发读。

这两种方式要么啥都不管,并发性能最好,但也最多问题;要么管得很严,无法并发处理,实现简单。

另外两种,读提交和可重复读,的实现方式就有考究了。

可重复读

首先我们来看一下可重复读是如何实现的。

在可重复读隔离级别下,事务在启动的时候就“拍了个快照”,并且这个快照是基于整个库的。而“快照”在计算机里是拷贝了一份当前的副本文件,但在数据库并发访问场景下,不可能真的拷贝一份数据副本。

实际上,这个快照是基于InnoDB在实现MVCC时用到的一致性读视图来实现的。

MVCC的全称是“多版本并发控制”。这项技术使得InnoDB的事务隔离级别下执行一致性读操作有了保证,换言之,就是为了查询一些正在被另一个事务更新的行,并且可以看到它们被更新之前的值。这是一个可以用来增强并发性的强大的技术,因为这样的一来的话查询就不用等待另一个事务释放锁。这项技术在数据库领域并不是普遍使用的。一些其它的数据库产品,以及MySQL其它的存储引擎并不支持它。

如何实现“快照”

InnoDB里面每个事务有一个唯一的事务ID,叫作transaction id。它是在事务开始的时候向InnoDB的事务系统申请的,是按申请顺序严格递增的。

数据表中的一行记录,其实可能有多个版本(row),每个版本都有自己的row trx_id。如图1所示:




图1 行状态变更图

图中虚线框里是同一行数据的4个版本,当前最新版本是V4,k的值是22,它是被transaction id为25的事务更新的,因此它的row trx_id是25。

其实除了最新版本V4外,其他三个版本实际上是不存在的,它们是由undo log和最新版本数据计算得到的。其中undo段就是图中的虚线箭头的U1、U2、U3,例如V1版本就是根据V4依次执行U3、U2、U1计算得到的。

undo log是回滚日志,保存的是逻辑格式的日志,可用于事务回滚,也可以用于MVCC。

按照可重复读的定义,一个事务启动的时候,能够看到所有已经提交的事务结果。但之后在这个事务执行期间,其他事务的更新对它不可见。

InnoDB为每个事务构成一个数组,用来保存这个事务启动瞬间,当前正在“活跃”的所有事务ID。“活跃”指启动了但还没有提交。

数组里事务ID的最小值记为低水位,当前系统里面已经创建过的事务ID的最大值加1记为高水位。这个视图数组加高水位就组成了当前事务的一致性视图(read-view)。

而数据版本的可见性规则,就是基于数据的row trx_id和这个一致性视图的对比结果得到的。




图2 数据可见性规则

对于当前事务的启动瞬间来说,一个数据版本的row trx_id有以下几种可能:

  • 如果落在绿色部分,表示这个版本是已经提交的事务或者是当前事务自己生成的,这个数据是可见的。
  • 如果落在红色部分,表示这个版本是由将来启动的事务生成的,不可见。
  • 如果落在橙色部分,就有两种情况:
    • 若row trx_id在数组中,表示这个版本是由还没提交的事务生成的,不可见。
    • 若row trx_id不在数组中,表示这个版本是已经提交了的事务生成的,可见。

比如图1中的数据来说,如果有一个事务,它的低水位是18,那么当它访问这一行数据时,就会从V4通过U3计算出V3,所以在它看来,这一行的值是11。

在更新时如何使用一致性读




图3 示例1

我们来看示例1,如果事务B在事务C更新之前查询,这个查询返回值是1。但是当它要去更新数据时,就不能在历史版本上更新了,否则事务事务C的更新就会丢失。

这里就用到一条规则:更新数据都是先读后写,而这个读只能是读当前的值,称为“当前读”(current read)。

因此事务B更新时,当前读拿到的数据是(1, 2),更新后是(1, 3),并且row trx_id是101。

事务B后续查询时,看到最新数据的版本号是101,而自己也是101,就直接返回,得到的k值是3。




图4 示例2

我们再来看示例2,示例2与示例1的区别在于,事务C'在事务B的写读操作后提交。

事务C'在提交前对行加写锁。而事务B是当前读,而且必须要加锁,因此被锁住了,必须等到事务C'释放这个锁,才能继续它的当前读。

到这里,把一致性读、当前读和行锁串起来了。

小结

本节问题,事务的可重复读隔离级别是如何实现的?

可重复读的核心就是一致性读;而事务更新数据的时候,只能用当前读。如果当前的记得的行锁被其他事务占用的话,就需要进入锁等待。

读提交

读提交的实现方式跟可重复读类似,它们最主要的区别是:

  • 在可重复读隔离级别下,只需要在事务开始的时候创建一致性视图,之后事务里的其他查询都共用这个一致性视图;
  • 在读提交隔离级别下,每个语句执行前都会重新算出一个新的视图。

参考资料

最新文章

  1. 实现了一个百度首页的彩蛋——CSS3 Animation简介
  2. tyvj1125 JR's chop
  3. H5移动端知识点总结
  4. DOM基础3
  5. Visual Studio 2010 更新NuGet Package Manager出错解决办法
  6. SQL Server DATEDIFF() 函数
  7. Memcached source code analysis (threading model)--reference
  8. (ASP.net)利用Application对象制作简单聊天室
  9. java中关于static的小知识
  10. IE6/IE7不识别display:inline-block属性怎么办
  11. 移动端日历控件 mobiscroll 的简单使用、参数设置
  12. Android ListView中Item点击事件失效解决方案
  13. mysql数据库NO CONNECTION问题分析以及解决方案
  14. nginx之安装、多虚拟主机、反向代理和负载均衡
  15. 常用MySQL操作(一)
  16. PS教程:大神教你用PS制作《大鱼海棠》海报
  17. [20180828]exadata--豆腐渣系统的保护神.txt
  18. C语言 统计一篇英文短文中单词的个数
  19. C#语句从MySQL中简单的读取数据库信息
  20. nginx——Nginx 防爬虫优化

热门文章

  1. rpm命令介绍
  2. REDHAT 7.5beta 新推出的VDO功能
  3. 加速OSD的启动
  4. kettle连接报错
  5. char 和 byte 区别
  6. 看阿里P7讲MyBatis:从MyBatis的理解以及配置和实现全帮你搞懂
  7. 教你用Camtasia制作精美片头
  8. FL Studio CPU面板讲解
  9. vue 2.9.6升级到3X版本
  10. chrome浏览器查看当前页面cookie