前阿里数据库专家总结的MySQL里的各类锁(下篇)

在上篇中,咱们介绍了MySQL中的全局锁和表锁。git

今天,咱们专一于介绍一下行锁,这个在平常开发和面试中经常困扰咱们的问题。github

1.行锁基础

因为全局锁和表锁对增删改查的性能都会有较大影响,因此,咱们天然会想到,面试

只须要对有修改的行加锁就好了,这就是行锁。数据库

在事务中,事务1更新了一行主键为1的数据行,那么,在这个事务释放锁以前,事务2是不能操做的。并发

 

前阿里数据库专家总结的MySQL里的各类锁(下篇)

 

另外,有一个不少人容易混淆的概念,就是行锁何时释放?高并发

搞清这个事情,须要了解什么叫做 两阶段锁。性能

什么是两阶段锁呢?举个例子你就明白了。优化

前阿里数据库专家总结的MySQL里的各类锁(下篇)

 这里事务2执行后会是什么结果呢?阿里云

若是你明白了两阶段锁的含义,你就会知道,事务2的updat语句会阻塞,直到事务1提交之后才能继续执行。日志

因此,这里再次强调一下,两阶段锁的含义。

在 InnoDB 事务中,行锁是在须要的时候才加上的,但并不是语句执行完了了就马上释放, 而是要等到事务结束时才释放。

注意,除了update语句能加写锁外,另外,还有一种对select语句加写锁的方式,就是

当前读:Select …. for update

2.行锁进阶

2.1 什么是幻读

面试的时候,面试官常常会喜欢问数据库的事务隔离级别。

你们要能回答出四种隔离级别,四种隔离级别的含义。

再多问一点,会问你什么是脏读,什么是幻读,哪一个隔离级别会解决什么问题。

首先明确一下,什么是幻读?

一样是一个事务,在事务中先后两次查询,出现了不一样的结果。

不一样之处在于,脏读是针对update,也就是同一行的数据出现了不一致。

注意,幻读出现的场景

第一:事务的隔离级别为可重复读,且是当前读

第二:幻读仅专指新插入的行,在范围查询中,后一次查询出现了新的数据行。

 

2.2 怎么解决幻读

这些若是你都能答上,面试官可能会继续追问,幻读是怎么产生的,又是怎么被解决的。

即便咱们给全部update涉及的行都加上了行锁,仍是没法解决新插入的记录,由于这些记录本来不存在,天然没法加上行锁。

那怎么办呢?为了解决这个问题,innodb只好引入新的锁,间隙锁(Gap Lock)。

“间隙锁,锁的是两个值之间的空隙”。

举个例子:

在四条记录,ID=0,10,20,30中,会产生以下的五个间隙范围

前阿里数据库专家总结的MySQL里的各类锁(下篇)

 间隙锁就是对这五个间隙范围加锁,防止新的记录插入。

注意,行锁的冲突是行与行之间的冲突,是行锁与行锁之间的。与间隙锁冲突的是往“间隙中插入数据”这个操做,间隙锁自己不会产生冲突。

间隙锁和行锁合称为next-key lock。

每一个next-key lock是前开后闭的。间隙锁自己是前开后开的。

小tips

标准的事务隔离级别中,可重复读只解决脏读问题,没法解决幻读问题。可是在innodb中,用next-key lock解决了幻读的问题。

3.关于行锁的优化应用

3.1 两阶段锁的优化应用

上面的基础知识中,解释了什么是 两阶段锁。

那么,对咱们业务开发中有什么借鉴意义呢?

既然咱们知道了,行锁必须在整个事务彻底提交后才会释放,那么,若是咱们的事物中须要锁住多行,就要把最可能形成锁冲突,或者是锁住最多行的语句尽量地日后放。

举个例子,小A在线上购买了商家B的一个产品,这个购买的动做能够简化为3个操做:

1)小A的银行帐户余额扣款x;

2)商家B的银行帐户余额增长x;

3)添加一条交易记录;

这里,涉及到两个update操做,和一个insert操做。为了保证交易的原子性,将三个动做放在了一个事务中。

那怎么安排三个语句的前后顺序呢?若是不仔细考虑,那么就多是随意选个123或者213的顺序了。

仔细想一想呢?

显然,这里最容易形成冲突的是步骤2),可能同时有多个用户购买商家B的产品,而后须要给商家B的余额作update操做。

另外,步骤3)是insert操做,最不容易出现锁冲突。

因此,最好的步骤顺序是3)-> 1) -> 2),将最容易产生冲突的操做放在最后执行,那么会比2)->1) ->3)的顺序,大大提升并发度。

 

3.2 间隙锁的问题与优化

间隙锁的引入也带来了一些新的问题,好比:下降并发度,可能致使死锁。

由于间隙锁的引入,可能会致使一样的语句锁住了更大的范围。

那怎么办呢?

注意,间隙锁在可重复读级别下才是有效的。

因此,只要咱们的业务不须要可重复读的保证,咱们就能够把隔离级别设置为读提交(也是阿里云rds数据库的默认隔离级别),就没有间隙锁了。

而后,为了解决可能的数据和日志不一致的问题,须要把binlog格式设置为row。

读提交级别 + binlog的row格式,也是通常公司数据库的标准配置。

如今,你知道缘由了吧:)

 

参考:

丁奇《MySQL 实战45讲》

 

看到这里了,原创不易,点个关注、点个赞吧,你最好看了~

知识碎片从新梳理,构建Java知识图谱:https://github.com/saigu/JavaKnowledgeGraph(历史文章查阅很是方便)

扫码关注个人公众号“阿丸笔记”,第一时间获取最新更新。同时能够免费获取海量Java技术栈电子书、各个大厂面试题。

阿丸笔记

相关文章
相关标签/搜索