MySQLInnoDB锁介绍及加锁死锁分析个人文章

数据库事务需要满足ACID四个原则,"I"即隔离性,它要求两个事务互不影响,不能看到对方尚未提交的数据。数据库有4中隔离级别(isolationlevel),按照隔离性从弱到强(相应地,性能和并发性从强到弱)分别是:

"I"隔离性正是通过锁机制来实现的。提到锁就会涉及到死锁,需要明确的是死锁的可能性并不受隔离级别影响,因为隔离级别改变的是读操作的行为,而死锁是由写操作产生的。

MySQL的隔离级别比标准隔离级别提前了一个级别,具体如下:

准确地说,MySQL的InnoDB引擎在提已交读(READ-COMMITED)级别通过MVCC解决了不可重复读的问题,在可重复读(REPEATABLE-READ)级别通过间隙锁解决了幻读问题。也就是说MySQL的事务隔离级别比对应的标准事务隔离级别更为严谨,也即:

InnoDB有两种不同的SELECT,即普通SELECT和锁定读SELECT。锁定读SELECT又有两种,即SELECT...FORSHARE和SELECT...FORUPDATE;锁定读SELECT之外的则是普通SELECT。

不同的SELECT是否都需要加锁呢?

FORSHARE语法是MySQL8.0时加入的,FORSHARE和LOCKINSHAREMODE是等价的,即,FORSHARE用于替代LOCKINSHAREMODE,不过,为了向后兼容,LOCKINSHAREMODE依然可用。

隔离级别为RU和Serializable时不需要MVCC,因此,只有RC和RR时,才存在MVCC,才存在一致性非锁定读。

这两种锁定读在搜索时所遇到的(注意:不是最终结果集中的)每一条索引记录(indexrecord)上设置排它锁或共享锁。此外,如果当前隔离级别是RR,它还会在每个索引记录前面的间隙上设置排它的或共享的gaplock(排它的和共享的gaplock没有任何区别,二者等价)。

看完背景介绍,我们再来看一下InnoDB提供的各种锁。

InnoDB一共有8种锁类型,其中,意向锁(IntentionLocks)和自增锁(AUTO-INCLocks)是表级锁,剩余全部都是行级锁。此外,共享锁或排它锁(SharedandExclusiveLocks)尽管也作为8种锁类型之一,它却并不是具体的锁,它是锁的模式,用来“修饰”其他各种类型的锁。

MySQL5.7及之前,可以通过information_schema.innodb_locks查看事务的锁情况,但是,只能看到阻塞事务的锁;如果事务并未被阻塞,则在该表中看不到该事务的锁情况。

MySQL8.0删除了information_schema.innodb_locks,添加了performance_schema.data_locks,可以通过performance_schema.data_locks查看事务的锁情况,和MySQL5.7及之前不同,performance_schema.data_locks不但可以看到阻塞该事务的锁,还可以看到该事务所持有的锁,也就是说即使事务并未被阻塞,依然可以看到事务所持有的锁(不过,正如文中最后一段所说,performance_schema.data_locks并不总是能看到全部的锁)。表名的变化其实还反映了8.0的performance_schema.data_locks更为通用了,即使你使用InnoDB之外的存储引擎,你依然可以从performance_schema.data_locks看到事务的锁情况。

performance_schema.data_locks的列LOCK_MODE表明了锁的类型,下面在介绍各种锁时,我们同时指出锁的LOCK_MODE。

它并不是一种锁的类型,而是其他各种锁的模式,每种锁都有shard或exclusive两种模式。

数据行r上共享锁(S锁)和排它锁(X锁)的兼容性如下:

假设T1持有数据行r上的S锁,则当T2请求r上的锁时:

假设T1持有数据行r上的X锁,则当T2请求r上的锁时:

T2请求r上的任何类型的锁时,T2都无法获得锁,此时,T2必须要等待直到T1释放r上的X锁。

意向锁是表锁。含义是已经持有了表锁,稍候将获取该表上某个/些行的行锁。有shard或exclusive两种模式。

LOCK_MODE分别是:IS或IX。

意向锁用来锁定层级数据结构,获取子层级的锁之前,必须先获取到父层级的锁。可以这么看InnoB的层级结构:InnoDB所有数据是schema的集合,schema是表的集合,表是行的集合。意向锁就是获取子层级(数据行)的锁之前,需要首先获取到父层级(表)的锁。

意向锁的目的是告知其他事务,某事务已经锁定了或即将锁定某个/些数据行。事务在获取行锁之前,首先要获取到意向锁,即:

事务请求锁时,如果所请求的锁与已存在的锁兼容,则该事务可以成功获得所请求的锁;如果所请求的锁与已存在的锁冲突,则该事务无法获得所请求的锁。

表级锁(table-levellock)的兼容性矩阵如下:

对于上面的兼容性矩阵,一定注意两点:

所以,意向锁只会阻塞全表请求(例如:LOCKTABLES...WRITE|READ),不会阻塞其他任何东西。因为LOCKTABLES...WRITE|READ需要设置X|S表锁,这会被意向锁IX或IS所阻塞。

查看表锁语句:showopentableswherein_use>0;

InnoDB允许表锁和行锁共存,使用意向锁来支持多粒度锁(multiplegranularitylocking)。意向锁如何支持多粒度锁呢,我们举例如下

T1:SELECT*FROMt1WHEREi=1FORUPDATE;

T2:LOCKTABLEt1WRITE;

T1执行时,需要获取i=1的行的X锁,但T1获取行锁前,T1必须先要获取t1表的IX锁,不存在冲突,于是T1成功获得了t1表的IX锁,然后,又成功获得了i=1的行的X锁;T2执行时,需要获取t1表的X锁,但T2发现,t1表上已经被设置了IX锁,因此,T2被阻塞(因为表的X锁和表的IX锁不兼容)。

假设不存在意向锁,则:

T1执行时,需要获取i=1的行的X锁(不需要获取t1表的意向锁了);T2执行时,需要获取t1表的X锁,T2能否获取到T1表的X锁呢?T2无法立即知道,T2不得不遍历表t1的每一个数据行以检查,是否某个行上已存在的锁和自己即将设置的t1表的X锁冲突,这种的判断方法效率实在不高,因为需要遍历整个表。

所以,使用意向锁,实现了“表锁是否冲突”的快速判断。意向锁就是协调行锁和表锁之间的关系的,或者也可以说,意向锁是协调表上面的读写锁和行上面的读写锁(也就是不同粒度的锁)之间的关系的。

在隔离级别为RR模式下加锁的默认锁类型,会根据条件进行锁退化,退化成索引记录锁(RecordLocks)、间隙锁(GapLocks)、或者两者都存在。

假设表中数据存在id=1,5,10三条数据,以排它锁(X锁)为例:

也就是所谓的行锁,锁定的是索引记录。行锁就是索引记录锁,所谓的“锁定某个行”或“在某个行上设置锁”,其实就是在某个索引的特定索引记录(或称索引条目、索引项、索引入口)上设置锁。有shard或exclusive两种模式。

LOCK_MODE分别是:S,REC_NOT_GAP或X,REC_NOT_GAP。

行锁就是索引记录锁,索引记录锁总是锁定索引记录,即使表上并未定义索引。表未定义索引时,InnoDB自动创建隐藏的聚集索引(索引名字是GEN_CLUST_INDEX),使用该索引执行recordlock。

举个例子(MySQL8.0下),假设有如下表结构及数据:

LOCK_MODE分别是:S,GAP或X,GAP。

gaplock可以共存(co-exist)。事务T1持有某个间隙上的gaplock并不能阻止事务T2同时持有同一个间隙上的gaplock。sharedgaplock和exclusivegaplock并没有任何的不同,它俩并不冲突,它俩执行同样的功能。

gaplock锁住的间隙可以是第一个索引记录前面的间隙,或相邻两条索引记录之间的间隙,或最后一个索引记录后面的间隙。

索引是B+树组织的,因此索引是从小到大按序排列的,在索引记录上查找给定记录时,InnoDB会在第一个不满足查询条件的记录上加gaplock,防止新的满足条件的记录插入。

举个例子(MySQL8.0下),表结构及数据同上:

一种特殊的gaplock。INSERT操作插入成功后,会在新插入的行上设置indexrecordlock,但,在插入行之前,INSERT操作会首先在索引记录之间的间隙上设置insertintentionlock,该锁的范围是(插入值,向下的一个索引值)。有shard或exclusive两种模式,但,两种模式没有任何区别,二者等价。

LOCK_MODE分别是:S,GAP,INSERT_INTENTION或X,GAP,INSERT_INTENTION。

insertintentionlock发出按此方式进行插入的意图:多个事务向同一个indexgap并发进行插入时,多个事务无需相互等待。

假设已存在值为4和7的索引记录,事务T1和T2各自尝试插入索引值5和6,在得到被插入行上的indexrecordlock前,俩事务都首先设置insertintentionlock,于是,T1insertintentionlock(5,7),T2insertintentionlock(6,7),尽管这两个insertintentionlock重叠了,T1和T2并不互相阻塞。

如果gaplock或next-keylock与insertintentionlock的范围重叠了,则gaplock或next-keylock会阻塞insertintentionlock。隔离级别为RR时正是利用此特性来解决幻读问题;尽管insertintentionlock也是一种特殊的gaplock,但它和普通的gaplock不同,insertintentionlock相互不会阻塞,这极大的提供了插入时的并发性。总结如下:

INSERT插入行之前,首先在索引记录之间的间隙上设置insertintentionlock,操作插入成功后,会在新插入的行上设置indexrecordlock,也就是在不发生锁冲突的情况下在瞬间LOCK_MODE由X,GAP,INSERT_INTENTION变为X,REC_NOT_GAP。

在最简单的情况下,如果一个事务正在向表中插入记录,则任何其他事务必须等待对该表执行自己的插入操作,以便使第一个事务插入的行的值是连续的。

通过对它的设置可以达到性能与安全(主从的数据一致性)的平衡。

我们先对insert做一下分类

首先insert大致上可以分成三类:

innodb_autoinc_lock_mode的说明

innodb_autoinc_lock_mode有三个取值:

总结:

semi-consistentread(半一致性读)是什么简单来说,semi-consistentread是readcommitted与consistentread两者的结合。一个update语句(semi-consistentread只针对update),如果读到一行已经加锁的记录,此时InnoDB返回记录最近提交的版本,由MySQL上层(Server层)判断此版本是否满足update的where条件。若满足(需要更新),则MySQL会重新发起一次读操作,此时会读取行的最新版本(并加锁)。(对于updatescan返回的不满足条件的记录,会提前放锁)

使用限制

优点

缺点

这种类型的死锁十分好理解,跟各种语言中的死锁基本一致,即线程1和线程2都需要获取A、B两把锁,但是他们获取锁的顺序相反:线程1先获取A、再获取B,而线程2先获取B再获取A,两者获取锁的顺序相反产生相互等待的情况,产生了死锁,在Java语言中就有自检测这种死锁的机制,JVM堆栈会报发现deadlock异常,同样MySQL也会有这样的自检测机制,一旦出现死锁,引擎就会报deadlock异常。下面是一个这样的典型例子:这是一个由于争抢两个Gap锁导致的死锁

THE END
1.Tongass已获得主机厂认可和量产项目定点汽车电子razordcx金融界9月30日消息,有投资者在互动平台向中科创达提问:请问畅行智驾研发的单SoC舱驾融合域控——RazorDCX Tarkine,目前已实现项目定点了没? 公司回答表示:座舱域控制器产品RazorDCX Tongass(SA8255P)已经赢得了主机厂认可,获得了量产项目定点。其他域控产品的项目定点还在稳步推进中。 本文源自:金融界https://www.163.com/dy/article/JDBJA0CQ0519QIKK.html
2.MySQL锁的使用数据库自带锁如何使用1、意向锁并不是直接锁定资源,而是为通知其他事务,以防止它们在资源上设置不兼容锁。 2、意向锁并不是直接由用户请求的,而是由 MySQL 管理的。 意向共享锁 IS:事务打算设置共享锁(读锁),此时不希望其他事务设置排他锁 意向排他锁 IX 在触发意向锁的事务提交或者回滚后释放 2)AUTO_INC 锁 在表主键设置ID自增https://blog.csdn.net/xcg340123/article/details/136744426
3.Twobox方案‘ESC+eBooster’功能安全之安全概念设计其中,EHB 以传统的液压制动系统为基础,用电子器件替代了部分机械部件的功能,使用制动液作为动力传递媒介,同时具备液压备份制动系统,是目前的主流技术方案。而EHB根据集成度的高低,EHB 可以分为Two-box 和One-box 两种技术方案。 随着新能源汽车市场的扩张,“eBooster+ ESC”组合成为了目前市场上最主流的Two-box方案https://www.dongchedi.com/article/7160867801518244383
4.宝马3.0CSL前门中控锁电机来电的西装暴徒,加装外挂电机的宝马还是你的“情人”吗? 林海读车 2024年11月07日 新款宝马X5的48V轻混是ISG电机还是BSG电机呢? 赵老师聊车 2023年08月30日 2025常州电机展 展会发布 2024年09月04日 2.0T+电机,120万,买吗? BMWsky宝马杂志 2024年04月23日 2024款智己LS6,21000rpm镁合金电机 选车侦探 20https://news.yiche.com/tag/15233076.html
5.AUTOINC锁和AUTOINCREMENT在InnoDB中处理方式AUTO-INC锁是当向使用含有AUTO_INCREMENT列的表中插入数据时需要获取的一种特殊的表级锁 在最简单的情况下,如果一个事务正在向表中插入值,则任何其他事务必须等待对该表执行自己的插入操作,以便第一个事务插入的行的值是连续的。 innodb_autoinc_lock_mode配置选项控制用于自动增量锁定的算法。 它允许您选择如何在https://www.jianshu.com/p/68b581481831
6.innodb中的autoincrementubuntuer5, AUTO_INC 虽然AUTO_INC是在语句级,即insert完毕,计数器对像上的这个特殊表锁马上释放;但mysql的实现是 两个事务不能同时占用计数器上的AUTO_INC锁, 这意味着在有auto_increment列的表上大量并发insert(不同的事务)会很容易引起AUTO_INC锁争用, 严重影响并发性能; http://blog.chinaunix.net/uid-9950859-id-99120.html
7.MySQL那些“锁”事,你听烦了吗?3. 共享读锁:MDL允许多个事务同时获取读锁,因为读操作之间不会相互冲突。 4. 锁的级别:MDL的锁级别是语句级别的,而不是表级别或行级别的。这意味着对于同一表的不同语句,可以同时持有读锁和写锁,因为它们不会互相冲突。 自增锁Auto-inc Locks 是特殊的表级别锁,专门针对事务插入AUTO_INCREMENT类型的列。 https://www.51cto.com/article/785345.html
8.MySQL自增锁模式innodbautoinclockmode参数详解表级的auto_inc锁,在语句结束的时候才释放这把锁,注意呀,这里说的是语句级而不是事务级的,一个事务可能包涵有一个或多个语句。 3、它能保证值分配的可预见性,与连续性,可重复性,这个也就保证了insert语句在复制到slave 的时候还能生成和master那边一样的值(它保证了基于语句复制的安全)。 http://chengdu.cdxwcx.cn/article/jhjsej.html
9.innodbautoinclockmode参数解读在这种锁模式中,所有“类似INSERT”的语句都会获得一个特殊的表级AUTO-INC锁,用于插入到具有AUTO_INCREMENT列的表中。此锁通常保持到语句的末尾(而不是事务的末尾),以确保自动增量值以可预测和可重复的顺序分配给给定的INSERT语句序列,并确保任何给定语句分配的自动增量值是连续的。 https://blog.itpub.net/29654823/viewspace-3015933/
10.赵慧勇mode convertion from FWS to 4WS without problem of rear overhang swing. in 8th International Conference on Mechanical and Intelligent Manufacturing Technologies, ICMIMT 2017, February 3, 2017 - February 6, 2017. 2017. Cape Town, South africa: Institute of Electrical and Electronics Engineers Inc.https://auto.huat.edu.cn/info/1153/5291.htm