mysql Innodb存储引擎的锁机制

ACID概念

在传统的E-R数据库事务中,我们经常提到ACID的概念,这里我们再简单的重温一下。

  • A(原子性):在数据库事务操作中,该操作是一个整体,要么整体成功或者失败。
  • C(一致性):一致性强调的是数据库中事务前后的数据是一致性,而事务中不做要求。
  • I(隔离性):隔离性是指不同的事务不会相互影响各自的执行。
  • D(持久性):事务操作一旦完成,则不会因为任何因素发生数据的丢失。

在ACID中,隔离性我们还可以进一步细分成不同的隔离级别,不同的隔离主要是数据库的隔离性与高性能中选均衡。

隔离级别 描述 脏读 不可重复读 幻读
读未提交 事务过程中可以读取其他事务未提交的数据 ✔️ ✔️ ✔️
读已提交 事务过程中可以读取其他已经提交的数据 ✔️ ✔️
可重复读 事务过程中多次读取相同的记录数据不变(Mysql默认级别) ✔️
串行化 最高的事务级别,串行化读取

而对于不同隔离实现的关键点,就引入了本文的主题:
Mysql innodb的锁机制与其他存储引擎相比,我认为其中最大的一个区别在与innodb实现了不同粒度的锁机制。

不同粒度的锁

针对不同的粒度,Mysql实现了行锁表锁。关于Mysql的行锁我们需要注意:行锁是基于索引实现的,如果查询记录中无法使用索引,则默认是表锁。
提到行锁和表锁之前,我们先普及一个基本概念:共享锁排他锁

共享锁和排他锁

  • 共享锁(S)

共享锁允许多个事务同时加锁,又称为读锁

  • 排他锁(X):

排他锁不允许多个事务同时加锁,又称为写锁

下面我们在描述一下共享锁和排他锁的兼容情况。

S锁 X锁
S锁 兼容 不兼容
X锁 不兼容 不兼容

共享锁和排他锁是一个非常基本的概念,该概念适用于行锁和表锁。
针对表锁和行锁的不同粒度,我们考虑下面的问题:何时我们可以加表锁?如何判断表中是否有行锁?
为了解决这个问题,innodb引入了意向锁:意向共享锁意向排他锁

意向锁

首先意向锁属于表锁,意向锁是当我们在对数据库记录进行加行锁的时候会同步给数据库表所加的锁。意向锁也可细分为共享意向锁排他意向锁
下面我们看一下共享意向锁、排他意向锁、共享锁和排他锁的兼容性。

IS锁 IX锁 S锁 X锁
IS锁 兼容 兼容 兼容 不兼容
IX锁 兼容 兼容 不兼容 不兼容
S锁 兼容 不兼容 兼容 不兼容
X锁 不兼容 不兼容 不兼容 不兼容

从上图可知,意向锁之间完全兼容,因为只有意向锁的时候真正的锁冲突需要延迟到行锁的维度上进行判断和检查。

一致性非锁定读

从锁之间的兼容可知,一旦记录或者表上加了X锁,会造成我们无法去读取数据库的数据,造成数据库读取性能的下降。
为了提高读取的性能,Innodb一般默认的读取采用的一致性非锁定读。

一致性非锁定读指InnoDB存储引擎通过多版本控制(MVCC)的方式来读取当前执行时间数据库中行的数据,如果读取的行正在执行DELETE或UPDATE操作,读取操作不会等待行上锁的释放。相反的,InnoDB会去读取行的一个快照数据 。

一致性非锁定读

每一个数据行可能会存在多个版本,Innodb的MVCC在不同的隔离级别对数据的快照存在不同:

  • 已提交读:事务能读取到的数据永远是最新的数据快照,所以存在不可重复读的问题。
  • 可重复读:事务能读取到的数据永远是事务开始时候的版本,故而解决了不可重复读的问题,但存在幻读的问题。

一致性锁定读

一致性非锁定读的反面就是锁定读,锁定读会对记录加S锁,它带来的最大的好处是可以锁定读取的数据行,从而避免在读取之后🈶事务对其进行更新。
下面我们介绍一种非常典型的应用场景:

/*
 * 第一步:从数据库中读取用户的好友计数
 * 第二部:从改计数的值+count来更新好友计数
1. select follow from Music_UserInfo where userId=1 lock in share mode;
2. update Music_UserInfo set follow=a+b

延伸说明: lock in share modeselect for update的区别

lock in share mode: 对数据库行记录加S锁,容易造成死锁
select for update:对数据库行记录加X锁

锁的算法

下面我们会主要介绍行锁、间隙锁和Next-key锁,这些不同的锁算法,都是针对数据库的行记录的排他锁来说的,而表锁不在这里讨论。

行锁(Record Lock)

锁直接加在索引的记录上面,锁住的是索引Key。

间隙锁(Gap Lock)

锁定索引的一个间隙范围,确保这个间隙范围的一致性,只有在可重复读的隔离级别中有效。

Next-key Lock

行锁和间隙锁组合在一起,形成Next-key Lock。

假设有表如下,同时Innodb的隔离级别是可重复读。
create table Students(
    sid int,
    name varchar(256),
    age int,
    primary key(id),
  key `idx_age`(`age`)) Engine=InnoDB DEFAULT CHARSET=UTF8;

假设存在如下的索引:
uk(sid) - 唯一的主键索引
index_age(age) - 非唯一索引

假设表存在如下的数据:
(1, "Lucy", 1)
(2, "Kate", 3)
(3, "John", 4)
(4, "Keith", 6)
(6, "Messy", 6)
(7, "Lili", 8)
(8, "Nancy", 13)
(9, "Smith", 15)
(10, "April", 16)

假设有如下SQL语句:
1. update Students set name='Lucy H' where id=1;(行级锁)
2. update Students set name='Kate W' where age=5;(行级锁,间隙锁,Next Key Lock)
3. update Students set name="Macy" where age>7;(间隙锁)

问题1:如何保证不能插入age=6的记录?
控制age<6的记录、age>6的记录、age=6的记录的记录间不插入数据,这里引入的是间隙锁

问题2:如何理解间隙锁?
根据age字段,可以划分为如下的区间(-∞,1)(1,3)(3,4)(4,6)(6,8)(8,13)(13,15)(15,16)(16,+∞)
比如区间(6,8)的记录之间可以插入更多的记录,则认为存在间隙。

问题3:间隙锁解决了什么问题?
间隙锁解决了缓存问题,所以Mysql可以在可重复读的隔离级别下解决幻读问题。

问题4:间隙锁在什么条件下生效?
a. 必须是在可重复隔离级别下
b. 检索条件中必须包含有索引,这个与行锁的实现方式有关

实际案例:
session 1:
start transaction;
select * from Students where age=6 for update; //加next-key, 锁age=6的记录和(4,8)的区间

session 2:
start transaction;
insert into Students(14,'johnson',5); //阻塞
insert into Students(15,'johnson',4); //阻塞
insert into Students(5,'johnson',10); //成功

这里的案例还是比较简答的,这里我们再稍微扩展一下。

问题1:对于联合索引的情况,间隙应该怎么计算?
问题2:当查询出的记录是唯一索引时,是否还存在间隙锁?

死锁问题

在介绍mysql死锁之前,先来温习一下死锁产生是需要4个必要条件:

  • 互斥条件
  • 不可剥夺条件
  • 请求和保持条件
  • 循环等待条件

在mysql中,所谓的死锁我们可以简单的理解是多个事务在执行过程中,因争夺锁资源而造成的互相等待的情况。而mysql中解决死锁的常见方式有:

  • 超时:超时需要其中的部分事务进行回滚,另外一部分事务则顺利执行。超时后mysql会按照FIFO的方式进行回滚,可能会导致大事务进行回滚,影响性能。
  • 等待图:等待图可以进行死锁的检测,死锁检测是一种主动的发现死锁的方式,当发现有死锁产生时,可以选择主动回滚,mysql一般会回滚undo最少的事务。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容