MySQL事务隔离性,从基本概念深入到实现

1.事务隔离性的基本概念

1.1是什么ACID中的Isolation,隔离性
Isolation,隔离性,也有人称之为并发控制(concurrency control)。事务的隔离性要求每个事务读写的对象对其他事务都是相互隔离的,也就是这个事务提交前,这个事务的修改内容对其他事务都是不可见的。事务的隔离性,主要是解决不同事物之间的相互读写影响。
读写影响:

  • 脏读:读到了别的事务尚未提交(commit)的变更,别人没提交,我读到了。
  • 不可重复读:别的事务提交了变更,被当前事务读到了。然后导致本事务多次select的结果不一样,读到了别的事务提交的内容。
  • 幻读:也是读到了别的事务提交的内容,但是跟上面的不同之处在于,读到了原本不存在的记录。
    注意,不可重复读,主要是读到了别的事务update的内容。而幻读,是读到了别的事务insert的内容。

1.2隔离性的隔离级别
为了解决事务的隔离性的问题,数据库一般会与有不同的隔离级别来解决相应的读写影响。

  • 读未提交:一个事务B还没提交,他的修改就被事务A读到了。
  • 读已提交:事务B提交了,他的修改被事务A读到了
  • 可重复读:一个事务B提交前和提交后,事务A都无法读到事务B的变更
  • 串行化:对同一行记录,当出现不同的事务的读写冲突时,是通过串行化的方式解决的,后一个事务必须等待前一个事务完成后才能执行。
    不同隔离级别解决不同的隔离性问题。
    在MySQL的innodb引擎中,在可重复读级别下,通过mvcc解决了幻读的问题。

2.事务的隔离性的实现

要实现事务的隔离性,需要了解两个方面的内容,一个是锁,一个是多版本并发控制(MVCC)。
2.1事务的行锁
InnoDB中,实现了两种标准的行级锁:

  • 共享锁(S lock),也叫读锁,允许事务读取一行数据。
  • 排它锁(X lock),也叫写锁,允许事务删除或者更新一条数据(插入涉及到幻读)。

普通的select语句不会有任何锁,如何获得共享锁和排它锁呢?

  • select ...lock in share mode 语句能够获得一行数据
  • select ...for update (特殊的select,用mysql简单实现分布式锁经常应用它)、update、delete语句能够获得排它锁
    当一个事务A已经获得了行r的共享锁,那么另一个事务B可以立刻获得行r的共享锁,因为不会改变r的数值,这种叫做锁兼容。
    如果这时候有事务C希望获得行r的排它锁,那么就必须等待事务A和事务B释放行r的共享锁之后,才能获得排它锁,这种叫做锁不兼容。

普通的select不会对行上锁,而select...lock in share mode会上共享锁,select ... for update会上排它锁、

  • 对于普通的select 读取方式,称为"快照读",也叫作"一致性非锁定读";
  • 对于带锁的select读取,或者update tb set a = a +1,称为"当前读",也叫作"一致性锁定读"。
    如果在update、insert的时候,不能进行select,那么服务的并发访问性能就太差了。因此,我们日常的查询,都是“快照读”,不会上锁,只有在update\insert\“当前读”的时候,才会上锁。而为了解决“快照读”的并发访问问题,就引入了MVCC。

2.2 多版本并发控制器MVCC
如果说上面的行锁是一种悲观锁,那么MVCC就是一种乐观锁的实现方式,而且是一种很常用的乐观锁实现方式。
所谓多版本,就是一行记录在数据库中存储了多个版本,每个版本以事务ID作为版本号。InnoDB 里面每个事务有一个唯一的事务 ID,是在事务开始的时候向InnoDB的事务系统申请的,并且按照申请顺序严格递增的。假如一行记录被多个事务更新,那么,就会产生多个版本的记录。

我们要区分MySQL里面的两个”视图”概念:

  • 一个是view,通过语法create view … 实现,主要创建一个虚拟表,用来执行查询语句。

  • 一个是InnoDB用来实现mvcc的一致性视图(consistent read view),纯逻辑概念,没有物理结构,定义了在事务期间,你能看到哪些版本的数据

  • “读未提及”级别下,没有一致性视图

  • “读已提交”级别下,会在 每个SQL开始执行的时候 创建一致性视图

  • “可重复读”级别下,会在 每个事务开始的时候 创建一致性视图

  • “串行化”级别下,直接通过加锁避免并发问题

我们简单介绍一下一致性视图的逻辑,以"可重复读"级别为例:
1、当一个事务开启的时候,会向系统申请一个新事务id
2、此时,可能还有多个正在进行的其他事务没有提交,因此在瞬时时刻,是有多个活跃的未提交事务id
3、将这些未提交的事务id组成一个数组,数组里面最小的事务id记录为低水位,当前系统创建过的事务id的最大值+1记录为高水位
4、这个数组array 和 高水位,就组成了“一致性视图”
有了一致性视图后,我们就可以判断一行数据的多版本可见性了,无论是“读已提交”还是“可重复读”级别,可见性判断规则是一样的
在当前事务中,读取其他某一行的记录,对其中的版本号的可见性判断有五种情况:
1、如果版本号小于“低水位”,说明事务已经提交,那肯定可见;
2、如果版本号大于“高水位”,说明这行数据的这个事务id版本是在快照后产生的,那肯定不可见;
3、如果版本号在事务数组array中,说明这个事务还没提交,所以不可见;
4、如果版本号不在事务数组array中,且低于高水位,说明这个事务已经提交,所以可见;
5、无论什么时候,自己的事务id中的任何变化,都是可见的;
举个例子:
系统创建过的事务id:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
事务A启动,产生快照,发现未提交的事务id:7,8,9,放入数组array[]中,此时的一致性视图array[7,8,9]+高水位16组成一致性视图。
那么对于任意一行数据的可见性判断:

  • 事务id小于7的,可见
  • 大于16的,快照后产生的,不可见
  • 10~15,不在array中,说明已经提交了,可见
  • 7,8,9在array中,说明未提交,不可见

结论:

  • InnoDB 利用了“所有数据都有多个版本”的这个特性,实现了“秒级创建快照”的能力。
  • MVCC的实现,就是根据当前事务的事务id为依据创建“一致性视图”,利用一致性视图来判断数据版本的可见性。

隔离性实战

1)select和update案例
id=1的value初始值为1

微信图片_20200418170416.png

在不同隔离情况下Time5,Time7,Time9事务A查询到的value为多少?

  • "读未提交":1,2,3,2
  • "读已提交":1,1,2,2
  • "可重复读":1,1,1,2
  • "串行化":1,1,1,2
    2)并发update案例
    id=1的value初始值为1,可重复读级别:
    微信图片_20200418171918.png

    事务A和事务B读取的value是多少?答案 1和3
    可能会产生困惑,事务A在启动后快照,所以读到了1是正常的,但是事务2在启动的时候快照了,然后在自己的事务中+1,怎么会读到3而不是2呢?
    原因:在可重复读的级别,事务更新数据的时候,只能用当前读
    如果当前的记录的行锁被其他事务占用的话,就需要进入锁等待。而读提交的逻辑和可重复读的逻辑类似,它们最主要的区别是:在可重复读隔离级别下,只需要在事务开始的时候创建一致性视图,之后事务里的其他查询都共用这个一致性视图;在读提交隔离级别下,每一个语句执行前都会重新算出一个新的视图
    我们需要注意的是事务的启动时机:
  • begin/start transaction 命令并不是一个事务的起点,在执行到它们之后的第一个操作 InnoDB 表的语句,事务才真正启动。一致性视图是在执行第一个快照读语句时创建的。
  • 如果你想要马上启动一个事务,可以使用 start transaction with consistent snapshot 这个命令,一致性视图也是在执行 start transaction with consistent snapshot 时创建的。

幻读

对于普通数据库,需要到串行化的隔离级别才能解决幻读问题
而对于InnoDB存储引擎来说,在可重复读级别下就能解决幻读问题,InnoDB存储引擎有三种行锁算法:

  • 行锁:单个行记录上的锁
  • 间隙锁:Gap Lock,锁定一个范围,但不包括本身。
  • Next-Key Lock:行锁+间隙锁,同时锁上一个范围并且锁定记录本身。
    (转)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351