今天我们来学习一下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'释放这个锁,才能继续它的当前读。

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

小结

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

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

读提交

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

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

参考资料

MySQL事务(二)事务隔离的实现原理:一致性读的更多相关文章

  1. MySQL事务和事务隔离级别

    1.概述 事务就是对数据库数据进行更改(包括insert.update.delete等)操作的一个执行单元,通常有一条或多条更改语句组成.在同一个事务中的更改操作要么同时成功,要么同时失败. 事务具有 ...

  2. MySQL学习(三)MySQL锁与事务

    本章我们着重讨论MySQL锁机制的特点,常见的锁问题,以及解决MySQL锁问题的一些方法或建议. 一.MySQL锁概述 相对其他数据库而言,MySQL的锁机制比较简单,其最显著的特点是不同的存储引擎支 ...

  3. MySQL中的事务和MVCC

    本篇博客参考掘金小册--MySQL 是怎样运行的:从根儿上理解 MySQL 以及极客时间--MySQL实战45讲. 虽然我们不是DBA,可能对数据库没那么了解,但是对于数据库中的索引.事务.锁,我们还 ...

  4. MySQL 一致性读 深入研究

    一致性读,又称为快照读.使用的是MVCC机制读取undo中的已经提交的数据.所以它的读取是非阻塞的. 相关文档:http://dev.mysql.com/doc/refman/5.6/en/innod ...

  5. MySQL 一致性读 深入研究 digdeep博客学习

    http://www.cnblogs.com/digdeep/p/4947694.html 一致性读,又称为快照读.使用的是MVCC机制读取undo中的已经提交的数据.所以它的读取是非阻塞的. 相关文 ...

  6. [MySQL] 一致性读分析

    MySQL MVCC MySQL InnoDB存储引起实现的是基于多版本的并发控制协议---MVCC(Multi-Version Concurrency Control),基于锁的并发控制,Lock- ...

  7. 谈谈MySQL支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景?

    在日常开发中,尤其是业务开发,少不了利用 Java 对数据库进行基本的增删改查等数据操作,这也是 Java 工程师的必备技能之一.做好数据操作,不仅仅需要对 Java 语言相关框架的掌握,更需要对各种 ...

  8. 第36讲 谈谈MySQL支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景

    在日常开发中,尤其是业务开发,少不了利用 Java 对数据库进行基本的增删改查等数据操作,这也是 Java 工程师的必备技能之一.做好数据操作,不仅仅需要对 Java 语言相关框架的掌握,更需要对各种 ...

  9. MySQL 温故知心(二) 事务的隔离级别

    事务的隔离级别 A事务做了操作 没有提交 对B事务来说 就等于没做 获取的都是之前的数据但是 在A事务中查询的话 查到的都是操作之后的数据没有提交的数据只有自己看得到,并没有update到数据库 查看 ...

随机推荐

  1. 练习启动各种浏览器的同时加载插件:Firefox, Chrome, IE

    # -*- coding:utf-8 -*-import osimport seleniumfrom selenium import webdriverfrom selenium.webdriver. ...

  2. Leetcode 1329. 将矩阵按对角线排序 题解

    首先遍历对角线元素,顺序为: 先从第一列的最后一行到第一行 然后从第一行的第一列到最后一列 遍历的同时记录坐标和数值,对数值进行排序,然后坐标顺序放回. class Solution: def dia ...

  3. java开发两年,连Spring的依赖注入的方式都搞不清楚,你工作可能有点悬!

    Spring依赖注入 常的java开发中,程序员在某个类中需要依赖其它类的方法,则通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的类实例不好统一管理,spring提出了依赖注入的 ...

  4. 细说FL Studio中的Wasp合成器功能

    FL Studio 简称FL,因其Logo像水果,故国人亲切的叫他"水果"本章节采用图文结合的方式给大家讲解FL Studio中的Wasp合成器功能.感兴趣的朋友可以一起来交流哦. ...

  5. 牛逼哄哄的PageHelper分页插件到底是怎么实现的?网友:给我10分钟,给你写一个~

    Hi,各位读者们 PageHelper是一款好用的开源免费的Mybatis第三方物理分页插件,其实我并不想加上好用两个字,但是为了表扬插件作者开源免费的崇高精神,我毫不犹豫的加上了好用一词作为赞美. ...

  6. 简单的 通过ID获取文件名称

    模型中的方法class 模型名{ /** * 通过ID获取文件名称 */ public static function getNameById($id) { $model = self::findOn ...

  7. 【CF600E】Lomsat gelral——树上启发式合并

    (题面来自luogu) 题意翻译 一棵树有n个结点,每个结点都是一种颜色,每个颜色有一个编号,求树中每个子树的最多的颜色编号的和. ci <= n <= 1e5 裸题.统计时先扫一遍得到出 ...

  8. 跨域共享CORS详解及Gin配置跨域

    跨域简介 当两个域具有相同的协议(如http), 相同的端口(如80),相同的host,那么我们就可以认为它们是相同的域(协议,域名,端口都必须相同). 跨域就指着协议,域名,端口不一致,出于安全考虑 ...

  9. 【坑爹的mybtis plus】wrapper.in击垮了我们的数据库!

    mybatis plus让我们从很大程度上实现了用"java去写sql",但是有些很隐晦的使用方式,如果不注意的话,也会引起错误: 如果.in的时候给了一个null,这个时候并不会 ...

  10. moviepy音视频剪辑:视频剪辑基类VideoClip的__init__构造方法参数has_constant_size的作用

    ☞ ░ 前往老猿Python博文目录 ░ moviepy音视频剪辑模块的视频剪辑基类VideoClip构造方法: __init__(self, make_frame=None, ismask=Fals ...