mysql中的各种锁把我搞糊涂啦~

大家好,我是公众号:java小杰要加油, 今天来分享一个关于mysql的知识点——mysql中的锁

  • 话不多说,直接开车

事务并发访问情况

读-读 情况

  • 并发事务读取相同的数据,并不会对数据造成影响,允许并发读

写-写 情况

  • 多事务并发写写时会发生脏写的情况,不过任何一个事务隔离级别都不允许此情况发生,通过加锁来杜绝脏写

    脏写

    • 事务T1 将数据改成了A,但是还未提交,可此时事务T2又将数据改成了B,覆盖了事务T1的更改,T1更新丢失,这种情况叫做脏写

    加锁

    • 例如,现在事务T1,T2对这条记录进行并发更改,刚才说是隔离级别是通过加锁来杜绝此脏写的,流程如下
      image

      这个锁结构中有两个比较关键的信息(其实还有很多信息,后面再聊)

    • trx信息:表示这个锁结构是和哪个事务所关联的

    • is_waiting信息:表示当前事务是否正在等待

Q: 能描述一下两个事务并发修改同一条数据时,mysql这个锁是怎么避免脏写的吗?

A :事务T1在更改这条数据前,就先内存中生成一把锁与此数据相关联(is_waiting为false,代表没有等待),然后咔咔一顿操作更改数据,这个时候,事务T2来了,发现此记录已经有一把锁与之相关联了(就是T1那一把锁),然后就开始等待(is_waiting为true代表正在等待),事务T1更改完数据提交事务后,就会把此事务对应的所结构释放掉,然后检测一下还有没有与此记录相关联的锁,结果发现T2还在苦苦的等待,就把T2的锁结构的(is_waiting为false,代表没有等待)然后把T2事务对应的线程唤醒,T2获取锁成功继续执行,总体流程如上。

读-写 /写-读 情况

在读-写 / 写 -读的情况下会出现脏读,不可重复读,幻读的现象,不同的隔离级别可以避免不同的问题,具体相关内容可以看小杰的这篇文章 京东面试官问我:“聊聊MySql事务,MVCC?”

不过贴心的我还是列出来了 注:√代表可能发生,×代表不可能发生

隔离级别 脏读 不可重复读 幻读
读未提交(read uncommitted RU)
读提交(read committed RC) ×
可重复读(repeatable read RR) × ×
串行化(serializable ) × × ×

但是 RR在某些程度上避免了幻读的发生

怎么避免脏读、不可重复读、幻读这些现象呢?其实有两种方案

  • 方案一 :读操作使用MVCC写操作进行加锁
  • mvcc里面最重要的莫过于ReadView了,它的存在保证了事务不可以读取到未提交的事务所作的更改,避免了脏读。
  • 在RC隔离级别下,每次select读操作都会生成ReadView
  • 在RR隔离级别下,只有第一次select读操作才会生成ReadView,之后的select读操作都复用这一个ReadView
  • 方案二:读写操作都用加锁

某些业务场景不允许读取旧记录的值,每次读取都要读取最新的值。 例如银行取款事务中,先把余额读取出来,再对余额进行操作。当这个事务在读取余额时,不允许其他事务对此余额进行访问读取,直到取款事务结束后才可以访问余额。所以在读数据的时候也要加锁

锁分类

当使用读写都加锁这个方案来避免并发事务写-写读-写写-读时而产生的脏读不可重复读幻读现象时,那么这个锁它就要做到,读读时不相互影响,上面三种情况时要相互阻塞,这时锁也分了好几类,我们继续往下看

锁定读

  • 共享锁(Shared Lock):简称S锁,在事务要读取一条记录时,需要先获取该记录的S锁
  • 独占锁(Exclusive Lock):简称X锁,也称排他锁,在事务要改动一条记录时,需要先获取该记录的X锁

他们之间兼容关系如下 √代表可以兼容,×代表不可兼容

兼容性 S锁 X锁
S锁 ×
X锁 × ×

事务T1获取某记录的S锁后,

  • 事务T2也可以获取此记录的S锁,(兼容)
  • 事务T2不可以获取此记录的X锁,直到T1提交后将S锁释放 (不兼容)

事务T1获取某记录的X锁后,

  • 事务T2不可以获取此记录的S锁,直到T1提交后将X锁释放 (不兼容)
  • 事务T2不可以获取此记录的X锁,直到T1提交后将X锁释放 (不兼容)

锁定读语句

<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">`SELECT .. LOCK IN SHARE MODE # 对读取的记录添加S锁

SELECT .. FOR UPDATE # 对读取的记录添加X锁` </pre>

多粒度锁

前面提到的锁都是针对记录的,其实一个事务也可以在表级进行加锁(S锁、X锁)

  • T1给表加了S锁,那么

    • T2可以继续获取此表的S锁
    • T2可以继续获取此表中的某些记录的S锁
    • T2不可以继续获取此表的X锁
    • T2不可以继续获取此表中的某些记录的X锁
  • T1给表加了X锁,那么

    • T2不可以继续获取此表的S锁
    • T2不可以继续获取此表中的某些记录的S锁
    • T2不可以继续获取此表的X锁
    • T2不可以继续获取此表中的某些记录的X锁

可是怎么可能平白无故的就给表加锁呢,难道没什么条件吗?答案是肯定有条件的

  • 若想给表加S锁,得先确保表中记录没有X锁
  • 若想给表加X锁,得先确保表中记录没有X锁和S锁

但是这个怎么确保呢?难道要一行一行的遍历表中的所有数据吗?当然不是啦,聪明的大佬们想出了下面这两把锁

  • 意向共享锁(Intention Shared Lock):简称IS锁,当事务准备在某记录上加S锁时,需要先在表级别加上一个IS锁
  • 意向独占锁(Intention Exclusive Lock):简称IX锁,当事务准备在某记录上加X锁时,需要先在表级别加上一个IX锁

让我们来看下加上这两把锁之后的效果是什么样子的

  • 当想给记录加S锁时,先给表加一个IS锁,然后再给记录加S锁
image
  • 当想给记录加X锁时,先给表加IX锁,然后再给记录加X锁
image

然后 经过上面的操作之后

  • 如果想给表加S锁,先看下表加没加IX锁,如果有的话,则表明此表中的记录有X锁,则需要等到IX锁释放掉后才可以加S锁
image
  • 如果想给表加X锁,先看下表加没加IS锁或者IX锁,如果有的话,则表明此表中的记录有S锁或者X锁,则需要等到IS锁或者IX锁释放掉后才可以加X锁
image

这几种锁的兼容性如下表

兼容性 IS锁(表级锁) S锁 IX锁(表级锁) X锁
IS锁(表级锁) ×
S锁 × ×
IX锁(表级锁) × ×
X锁 × × × ×
  • IS、IX锁都是表级锁,他们可以共存。
  • 他们的提出仅仅是为了在之后加表级别的S锁或者X锁时可以快速判断表中的记录是否被上锁,避免用遍历的方式来查看一行一行的去查看而已

InnoDB中的行级锁

Record Lock(记录锁)

  • 官方名字 LOCK_REC_NOT_GAP
  • 仅仅锁住一条记录
  • 有S型和X型之分

Gap Lock(间隙锁)

  • 官方名字 LOCK_GAP
  • 给某记录加此锁后,阻塞数据在此记录和上一个记录的间隙插入,但是不锁定此记录
  • 有S型和X型之分,可是并没有什么区别他们的作用是相同的,gap锁的作用仅仅是为了防止插入幻影记录而已,如果对一条记录加了gap锁(无论S/X型)并不会限制其他事务对这条记录加Record Lock或者Gap Lock

Next-Key Lock(记录锁+间隙锁)

  • 官方名字 LOCK_ORDINARY
  • 既可以锁住某条记录,又可以组织其他事务在该记录面前插入新记录

Insert Intention Lock(插入意向锁锁)

  • 官方名字 LOCK_INSERT_INTENTION
  • 事务在插入记录时,如果插入的地方加了gap锁,那么此事务需要等待,此时此事务在等待时也需要生成一个锁结构,就是插入意向锁

锁内存结构

  • 我们难道锁一条记录就要生成一个锁结构吗?

当然不是!

一个锁结构

如果被加锁的记录符合下面四条状态的话,那么这些记录的锁则会合到一个锁结构

  • 在同一个事务中进行加锁操作
  • 被加锁的记录在同一个页面中
  • 加锁的类型是一样的
  • 等待的状态是一样的
image

锁结构信息

然后我们再来依此看下这个所结构每个部分的信息都是什么意思

image
  • 锁所在的事务信息:无论是表级锁还是行级锁,一个锁属于一个事务,这里记载着该锁对应的信息

  • 索引信息:对于行级锁来说,需要记录一下加锁的记录属于哪个索引

  • 表锁/行锁信息:行级锁

    • Space_ID:记录所在的表空间 *** Page Number**:记录所在的页号
    • n_bits:一条记录对应着一个比特;一个页面包含多条记录,用不同的比特来区分到底是那一条记录加了锁,有个计算公式如下(公式中是取商)n_bits = (1+(n_recs+LOCK_PAGE_BITMAP_MARGIN)/ 8)x 8LOCK_PAGE_BITMAP_MARGIN是固定的值为64,n_recs指当前界面一共有多少条记录(包含伪记录以及在垃圾链表中的记录),
  • type_mode:32比特的数

image
  • lock_mode(锁模式):低4比特位表示

    • LOCK_AUTO_INC(十进制的4):表示AUTO-INC锁
    • LOCK_IS(十进制的0):表示共享意向锁,IS锁
    • LOCK_IX(十进制的1):表示独占意向锁,IX锁
    • LOCK_S(十进制的2):表示共享锁,也就是S锁
    • LOCK_X(十进制的3):表示独占锁,也就是X锁
  • lock_type(锁类型):第5~8比特位表示

    • LOCK_TABLE(十进制的1):当第5比特位设置为1时,表示表级锁
    • LOCK_REC(十进制的32):当第6比特位设置为1时,表示行级锁
  • rec_lock_type(行锁的具体类型):其余的比特位表示

    • LOCK_ORDINARY(十进制的0):表示next-key锁

    • LOCK_GAP(十进制的512):当第10比特位是1时,表示gap锁

    • LOCK_REC_NOT_GAP(十进制的1024):也就是当第11比特设置为1时,表示Record Lock(记录锁)

    • LOCK_INSERT_INTENTION(十进制的2048):也就是当第12比特设置为1时,表示Insert Intention Lock(插入意向锁)

    • LOCK_WAIT(十进制的256):也就是当

      • 第9比特设置为1时,表示is_waiting为true,即当前事务获取锁失败,处于等待状态
      • 第9比特设置为0时,表示is_waiting为false,即当前事务获取锁成功
  • 其他信息:此文章不讨论

  • 一堆比特位:此文章不讨论

举个例子

事务T1 要给user表中的记录加锁,假设这些记录存储在表空间号为20,页号为21的页面上,T1给id=1的记录加S型Record Lock锁,假如当前页面一共有5条记录(3条用户记录和2条伪记录)

过程:先给表加IS锁,不过我们现在不关心,只关心行级锁, 具体生成的所结构如下图所示

image

最后

  • 快过年啦,小杰可能也需要休息一下下,因为最近都周更(虽然上周有点事没更,打脸),周末完全没有其余时间了
  • 感觉和朋友家人们联系有点少了,过年回家巩固下感情和朋友们聊聊天吹吹牛逼,顺便维护下峡谷的治安
  • 最后祝关注java小杰要加油的宝贝儿们
  • 脱单暴富事事顺,升职加薪牛哄哄!

好文推荐

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

推荐阅读更多精彩内容