目录
MySQL 锁机制完全指南:5.6、5.7、8.0 版本对比与优化
一、MySQL 锁机制概述
1.1 锁的基本概念与作用
在数据库系统中,锁是协调多个会话对共享资源并发访问的基本机制。MySQL 作为一个关系型数据库管理系统,提供了多种锁类型来保证数据的一致性和完整性,同时尽可能提高并发性能(31)。锁机制的核心作用是在多个事务并发执行时,通过加锁的方式避免数据不一致问题,确保事务的 ACID 特性(原子性、一致性、隔离性、持久性)得到满足。
锁的基本原理是当一个事务访问某个资源(如一行数据、一张表)时,会在该资源上加锁,阻止其他事务同时对该资源进行可能导致冲突的操作。不同类型的锁允许不同程度的并发访问,例如共享锁允许其他事务读取但不允许修改,而排他锁则完全禁止其他事务的访问(31)。
MySQL 的锁机制可以从多个维度进行分类:按锁的粒度划分、按锁的模式划分、按锁的实现方式划分等(31)。理解这些不同类型的锁及其特性,对于优化数据库性能、解决死锁问题以及提高系统稳定性至关重要。
1.2 MySQL 锁的分类体系
MySQL 的锁可以按照多种方式进行分类,这有助于全面理解锁机制的设计与实现:
按锁的粒度划分:
-
全局锁:锁定整个 MySQL 实例,一旦施加全局锁,整个实例下的所有数据库、所有表都将处于只读状态(1)
-
表级锁:锁定整个表,其锁定粒度介于全局锁和行锁之间
-
行级锁:锁定表中的某一行或多行数据,是 MySQL 中粒度最细的锁(1)
按锁的模式划分:
-
共享锁 (S 锁):允许多个事务同时读取同一资源,但不允许修改(31)
-
排他锁 (X 锁):只允许一个事务对资源进行读写,其他事务在获得排他锁之前无法访问该资源(31)
-
意向锁:一种表锁,用于表示某个事务对某行数据加锁的意图,分为意向共享锁 (IS)和意向排他锁 (IX)(31)
按锁的实现方式划分:
-
悲观锁:总是假设最坏情况,先加锁再访问资源
-
乐观锁:不加锁,通过版本号或时间戳实现数据一致性控制(31)
此外,MySQL 还提供了一些特殊类型的锁,如元数据锁 (MDL)、间隙锁、临键锁等,这些锁在特定场景下发挥重要作用(31)。
1.3 不同存储引擎的锁支持差异
MySQL 的锁机制与存储引擎密切相关,不同的存储引擎对锁的支持存在显著差异:
MyISAM 存储引擎:
-
仅支持表级锁,读操作加表级共享锁(S 锁),写操作加表级排他锁(X 锁)
-
不支持事务,因此也不支持行级锁和事务相关的锁机制
-
表级锁的特点是开销小,加锁快;不会出现死锁;但锁定粒度大,发生锁冲突概率高,并发度最低(7)
InnoDB 存储引擎:
-
支持行级锁和事务,通过多版本并发控制(MVCC)实现高并发
-
同时支持表级意向锁(IS/IX)和元数据锁(MDL)
-
行级锁的特点是开销大,加锁慢;会出现死锁;但锁定粒度最小,发生锁冲突的概率最低,并发度最高(7)
-
InnoDB 的行锁实现在索引上,没有索引的查询会导致行锁退化为表锁,大大降低并发性能(1)
其他存储引擎:
-
Berkeley DB(DBD):默认使用页级锁,锁定粒度介于表锁和行锁之间,并发度一般(29)
-
Memory:支持表级锁,与 MyISAM 类似
了解不同存储引擎的锁支持情况,有助于根据应用场景选择合适的存储引擎,优化数据库性能。在互联网高并发场景下,InnoDB 因其行级锁支持和事务特性成为首选存储引擎(25)。
二、MySQL 锁机制深度解析
2.1 全局锁:锁定整个数据库实例
2.1.1 全局锁的实现原理与应用场景
全局锁是 MySQL 中粒度最粗的锁,它会锁定整个 MySQL 实例。一旦施加全局锁,整个实例下的所有数据库、所有表都将处于只读状态,此时任何对数据的写操作(包括插入、更新、删除)以及对表结构的修改操作(如创建表、修改表结构、删除表)都将被阻塞(1)。
MySQL 中实现全局锁的主要命令是 FLUSH TABLES WITH READ LOCK (FTWRL)。当执行该命令后,MySQL 会经历两个关键步骤:第一步,MySQL 会关闭所有正在打开的表,确保后续对表的访问都需要重新获取表资源;第二步,MySQL 会对所有表施加读锁,此时只有读操作能够正常执行,写操作和表结构修改操作都会被拒绝,直到全局锁被释放(1)。
全局锁的释放方式有两种:一种是主动执行 UNLOCK TABLES 命令释放全局锁;另一种是当持有全局锁的会话断开连接时,MySQL 会自动释放该全局锁,以避免因会话异常导致全局锁长期占用,影响整个实例的可用性(1)。
全局锁的主要应用场景是数据库备份。在进行全量备份时,如果不施加全局锁,可能会导致备份过程中数据出现不一致的情况。例如,在备份过程中,某张表的数据正在被更新,可能会导致备份出来的数据一部分是更新前的,一部分是更新后的,从而破坏数据的一致性。而通过施加全局锁,将整个实例设置为只读状态,能够确保在备份期间所有数据都不会发生变化,从而保证备份数据的完整性和一致性(1)。
2.1.2 全局锁与 SET GLOBAL read_only=1 的区别
虽然全局锁和 SET GLOBAL read_only=1 都能让数据库实例进入只读状态,但它们之间存在关键差异:
| 特性 | FLUSH TABLES WITH READ LOCK (FTWRL) | SET GLOBAL read_only=1 |
|---|---|---|
| 超级用户权限影响 | 不影响(超级用户可写) | 影响(所有用户只读) |
| 表结构修改(DDL) | 完全阻塞 | 仅普通用户阻塞,超级用户可执行 |
| 释放机制 | 会话断开或 UNLOCK TABLES 自动释放 | 需手动设置 read_only=0 释放 |
| 应用场景 | 数据库备份,确保一致性 | 主从复制中的从库设置 |
在备份场景中,虽然可以使用 SET GLOBAL read_only=1 的方式让全库进入只读模式,但更推荐使用 FTWRL 命令,主要有两个原因:一是在有些系统中,read_only 的值有业务逻辑,用于判断是主库还是从库;二是在备份过程中,如果客户端发生异常,FTWRL 会自动释放锁,而设置数据库为 read_only 后,数据库会一直处于只读状态,使整个数据库处于不可用状态(1)。
2.2 表级锁:锁定整张表的锁机制
2.2.1 表级锁的类型与实现原理
表级锁是锁定整个表的锁,其锁定粒度介于全局锁和行锁之间。MySQL 中表级锁主要有两种类型:表共享读锁(Table Shared Read Lock,简称 IS 锁)和表独占写锁(Table Exclusive Write Lock,简称 IX 锁),此外还有一些特殊用途的表级锁,如表结构修改锁(Metadata Lock,MDL)等(1)。
表共享读锁(IS 锁):
当事务对某张表施加表共享读锁后,其他事务可以对该表施加读锁或行级读锁,从而实现多个事务同时读取该表的数据,但不允许任何事务对该表施加写锁或进行写操作。实现上,MySQL 会在表的元数据结构中记录该表已被施加读锁以及持有读锁的事务数量,当有新的事务请求读锁时,只需增加读锁计数即可;当事务释放读锁时,减少读锁计数,当读锁计数为 0 时,表明该表上的读锁已全部释放(1)。
表独占写锁(IX 锁):
当事务对某张表施加表独占写锁后,其他事务既不能对该表施加读锁,也不能施加写锁,只有持有写锁的事务能够对该表进行读写操作。在实现时,MySQL 会在表的元数据结构中标记该表已被施加写锁,并且确保同一时间只有一个事务能够持有该表的写锁。当持有写锁的事务释放锁后,其他事务才能竞争该表的锁资源(1)。
意向锁:
意向锁是表级锁的一种特殊形式,分为意向共享锁(IS 锁)和意向排他锁(IX 锁)。意向锁的作用是表示事务对表中某些行数据加锁的意图,当事务想要对表中的某些行数据加共享锁时,会先申请意向共享锁;当事务想要对表中的某些行数据加排他锁时,会先申请意向排他锁。意向锁的引入主要是为了协调行级锁与表级锁的关系,支持多粒度锁的共存(29)。
2.2.2 表级锁的应用场景与优化策略
表级锁的应用场景主要包括以下几类:
批量数据更新场景:
当需要对表中的大量数据进行更新(如批量更新某一字段的值)或删除操作时,如果使用行锁,会对每一行数据都施加锁,导致锁的数量急剧增加,不仅会消耗大量的系统资源,还可能引发锁等待和死锁问题。而使用表级写锁,只需对整个表施加一次锁,就能完成所有数据的更新或删除操作,大大减少了锁的开销和锁竞争,提高了操作的效率。例如,在数据仓库中,定期对历史数据进行批量清理或数据转换时,使用表级锁是一个合适的选择(1)。
表结构频繁修改场景:
在一些业务场景中,可能需要频繁地对表结构进行修改,如增加字段、修改字段类型等。由于表结构修改操作需要施加 MDL 锁,而 MDL 锁会阻塞其他事务对表的读写操作,如果表结构修改操作频繁发生,使用表级锁能够确保每次表结构修改操作的顺利执行,避免因锁竞争导致表结构修改操作长时间等待。不过,需要注意的是,频繁的表结构修改本身会对数据库性能产生影响,应尽量避免在生产环境的核心业务表上频繁进行表结构修改(1)。
非 InnoDB 存储引擎场景:
对于一些不支持行级锁的存储引擎(如 MyISAM 存储引擎),表级锁是其主要的并发控制手段。在使用这些存储引擎的数据库中,所有的读写操作都依赖于表级锁来保证数据的一致性。例如,在一些使用 MyISAM 存储引擎的小型应用中,由于数据量较小,并发访问量不高,表级锁能够满足业务对数据一致性和性能的需求(1)。
表级锁的优化策略主要包括:
-
合理设计表结构,减少不必要的表级锁操作
-
在批量操作前,考虑使用表级锁替代行级锁
-
对于支持行级锁的存储引擎(如 InnoDB),应优先使用行级锁,仅在必要时使用表级锁
-
对于 MyISAM 等仅支持表级锁的存储引擎,应尽量避免在高并发场景下使用,或通过优化业务逻辑减少锁冲突(7)
2.3 行级锁:精细化控制的锁机制
2.3.1 行级锁的类型与实现原理
行级锁是 MySQL 中粒度最细的锁,它只锁定表中的某一行或多行数据,能够最大限度地支持并发访问,减少锁竞争。行锁主要由支持事务的存储引擎实现,其中最典型的是 InnoDB 存储引擎,MyISAM 等不支持事务的存储引擎不支持行锁(1)。
InnoDB 存储引擎的行锁实现基于索引,它通过在索引记录上施加锁来实现对行数据的锁定。具体来说,当事务执行 UPDATE、DELETE 或 SELECT … FOR UPDATE 等语句时,InnoDB 会根据语句中使用的索引,找到对应的索引记录,并在这些索引记录上施加行锁。如果语句中没有使用索引,InnoDB 将无法确定要锁定的具体行,此时会退化为表级锁,这会大大降低数据库的并发性能,因此在实际应用中,应尽量确保更新、删除等语句使用索引(1)。
InnoDB 的行锁主要有两种类型:行共享锁(Row Shared Lock,简称 S 锁)和行排他锁(Row Exclusive Lock,简称 X 锁):
行共享锁(S 锁):
当事务对某一行数据施加 S 锁后,其他事务可以对该行数据施加 S 锁(即多个事务可以同时读取该行数据),但不允许施加 X 锁(即不允许其他事务修改该行数据)。只有当所有持有 S 锁的事务都释放锁后,其他事务才能对该行数据施加 X 锁(1)。
行排他锁(X 锁):
当事务对某一行数据施加 X 锁后,其他事务既不能对该行数据施加 S 锁,也不能施加 X 锁,只有持有 X 锁的事务能够对该行数据进行读写操作。当持有 X 锁的事务释放锁后,其他事务才能竞争该行数据的锁资源(1)。
此外,InnoDB 还提供了几种特殊类型的行级锁,这些锁在特定场景下发挥重要作用:
记录锁(Record Lock):
锁定索引中的具体记录,是最基本的行锁类型。例如,当执行 SELECT * FROM user WHERE id=1 FOR UPDATE 语句时,会在 id=1 的索引记录上施加记录锁(25)。
间隙锁(Gap Lock):
锁定索引记录之间的间隙,用于防止其他事务在这个间隙中插入新记录,以避免幻读。间隙锁不锁定具体行,而是锁定行与行之间的空间。例如,如果表中有 id 为 1、3、5 的记录,那么间隙锁可以锁定 (-∞,1)、(1,3)、(3,5)、(5,+∞) 等区间(29)。
临键锁(Next-Key Lock):
是记录锁和间隙锁的组合,锁定具体行和其前面的间隙,确保在一个范围内不会出现幻读。临键锁是个左开右闭的区间,如 (16,18]。InnoDB 默认的行锁就是临键锁,这也是在可重复读隔离级别下避免幻读的关键机制(29)。
插入意向锁(Insert Intention Lock):
是一种等待间隙的锁,用于指示事务打算在某个间隙中插入记录,允许其他事务进行共享锁,但在插入时会阻止其他的排他锁。插入意向锁是一种特殊的间隙锁,它之间互不排斥,所以即使多个事务在同一区间插入多条记录,只要记录本身(主键、唯一索引)不冲突,那么事务之间就不会出现冲突等待(29)。
2.3.2 行级锁的应用场景与优化策略
行级锁的应用场景主要包括以下几类:
高并发的在线交易场景:
在电商平台、金融交易系统等在线交易场景中,存在大量的并发读写操作。例如,在电商平台的秒杀活动中,大量用户同时抢购商品,需要同时更新商品的库存数量;在金融交易系统中,多个用户同时进行转账、支付等操作,需要修改账户余额等数据。这些场景下,使用行锁能够确保每个事务只锁定自己需要修改的行数据,不会影响其他事务对其他行数据的操作,从而支持高并发的交易请求,保证数据的一致性和交易的准确性(1)。
数据更新频率高且更新范围小的场景:
当业务中某张表的数据更新频率较高,但每次更新的只是表中的少量行数据时,使用行锁能够有效减少锁竞争,提高数据库的并发性能。例如,在社交应用中,用户频繁更新自己的个人信息(如昵称、头像等),每次更新只涉及到用户表中的一行数据,使用行锁可以确保多个用户同时更新个人信息时不会相互干扰,提高应用的响应速度和并发处理能力(1)。
对数据一致性要求高的事务场景:
在一些对数据一致性要求极高的事务场景中,如银行的转账事务、订单的创建与支付事务等,需要确保事务的原子性、一致性、隔离性和持久性(ACID 特性)。行锁能够与 InnoDB 的事务机制和 MVCC 机制相结合,实现事务的隔离级别,避免脏读、不可重复读和幻读等问题,从而保证事务处理的准确性和数据的一致性。例如,在订单创建事务中,需要锁定订单表中的特定行数据,确保在事务执行期间,其他事务不能修改该订单的状态和相关信息,直到事务提交或回滚(1)。
行级锁的优化策略主要包括:
索引优化:
确保更新、删除等操作使用索引,避免全表扫描导致的表级锁。通过添加合适的索引,减少锁的范围,提高并发性能(13)。
事务优化:
保持事务简短,按固定顺序访问资源。例如在电商下单场景,将复杂操作拆分为独立事务,并统一按表名或主键 ID 顺序访问资源(13)。
隔离级别选择:
根据业务需求选择合适的隔离级别。对于非金融类高并发业务,可考虑使用 READ COMMITTED 隔离级别;仅在强一致性场景(如金融转账)使用 SERIALIZABLE 隔离级别(13)。
锁类型与粒度优化:
使用乐观锁替代悲观锁,通过版本号实现数据更新的并发控制;优先使用行锁替代表锁,减少锁争用(13)。
死锁处理:
开启详细死锁日志,在配置文件中设置 innodb_print_all_deadlocks = 1 和 innodb_deadlock_detect = 1,永久开启死锁日志记录;也可通过 SET GLOBAL innodb_print_all_deadlocks = ON 临时开启(13)。
2.4 元数据锁 (MDL):保护数据库对象结构
2.4.1 元数据锁的实现原理与特性
元数据锁(Metadata Lock,简称 MDL)是 MySQL5.5 引入的一种表级锁,属于表锁范畴(29)。MDL 的作用是保证读写的正确性,特别是在 DDL(数据定义语言)操作和 DML(数据操作语言)操作同时执行时,防止表结构的不一致问题。例如,如果一个查询正在遍历一个表中的数据,而执行期间另一个线程对这个表结构做变更,增加了一列,那么查询线程拿到的结果跟表结构对不上,肯定是不行的(29)。
MDL 的工作原理是,当对一个表做增删改查操作的时候,加 MDL 读锁;当要对表做结构变更操作的时候,加 MDL 写锁。读锁之间不互斥,因此可以有多个线程同时对一张表增删改查。读锁和写锁之间、写锁和写锁之间是互斥的,用来保证变更表结构操作的安全性,解决了 DML 和 DDL 操作之间的一致性问题(29)。
MDL 锁不需要显式使用,在访问一个表的时候会被自动加上。当事务开始访问表时,系统会自动获取 MDL 读锁;当执行 DDL 操作时,系统会自动获取 MDL 写锁。MDL 锁的释放时机是在事务提交或回滚时自动释放(39)。
MDL 锁的主要特性包括:
锁模式:
MDL 锁分为读锁(共享锁)和写锁(排他锁)两种模式。读锁之间不互斥,可以同时存在多个读锁;读锁和写锁之间、写锁和写锁之间是互斥的(39)。
锁粒度:
MDL 锁是表级锁,锁定的是整个表的元数据,而不是具体的数据行(39)。
锁获取方式:
MDL 锁由数据库自动管理,不需要用户显式调用。当执行 SELECT、INSERT、UPDATE、DELETE 等 DML 操作时,会自动获取 MDL 读锁;当执行 ALTER TABLE、CREATE INDEX、DROP TABLE 等 DDL 操作时,会自动获取 MDL 写锁(39)。
锁释放时机:
MDL 锁在事务提交或回滚时自动释放,而不是在语句执行完毕后立即释放。这一点需要特别注意,因为如果在事务中执行了 DDL 操作,那么 MDL 写锁会一直持有到事务结束,可能会阻塞其他事务对该表的访问(39)。
2.4.2 MDL 锁的应用场景与版本差异
MDL 锁的主要应用场景是确保 DML 和 DDL 操作的一致性,防止在表结构变更过程中出现数据不一致的情况。例如,当多个事务同时对同一个表进行操作时,MDL 锁可以保证:
-
当一个事务正在对表进行结构变更(如添加列)时,其他事务无法同时对该表进行 DML 操作,避免数据不一致(39)。
-
当多个事务同时对表进行 DML 操作时,它们之间不会互相阻塞,因为 MDL 读锁之间是兼容的(39)。
-
当一个事务正在对表进行 DML 操作时,其他事务无法对该表进行结构变更,直到 DML 事务提交或回滚(39)。
MDL 锁在不同 MySQL 版本中的实现和特性存在一些差异:
MySQL 5.6 版本:
-
引入了 MDL 锁机制,但功能相对基础
-
MDL 锁的管理相对简单,主要用于保证 DML 和 DDL 操作的基本一致性
-
存在一些已知的 MDL 锁问题,如在某些情况下可能导致长时间的锁等待(6)
MySQL 5.7 版本:
-
对 MDL 锁进行了增强和优化,提高了并发性能
-
引入了共享排他锁(SX 锁),主要是解决 SMO(在线 DDL)带来的问题
-
改进了 MDL 锁的管理机制,减少了锁冲突和锁等待的可能性(6)
MySQL 8.0 版本:
-
进一步增强了 MDL 锁的功能和性能
-
改进了 MDL 锁的加锁策略,减少了不必要的锁获取
-
提供了更完善的 MDL 锁监控和管理工具,如 performance_schema.metadata_locks 表(39)
在 MySQL 8.0 中,MDL 锁的增强主要体现在以下几个方面:
-
锁模式的扩展:
MySQL 8.0 引入了更多的 MDL 锁模式,如 SHARED_READ_ONLY 锁,提供了更细粒度的锁控制(8)。
-
锁等待超时机制:
MySQL 8.0 引入了 lock_wait_timeout 系统变量,用于控制 MDL 锁的等待时间,默认值为 31536000 秒(约 1 年),而 MySQL 5.x 版本中该参数的默认值为 50 秒(40)。
-
锁管理的优化:
MySQL 8.0 改进了 MDL 锁的管理机制,减少了锁冲突和锁等待的可能性,提高了并发性能(39)。
-
锁监控的增强:
MySQL 8.0 在 performance_schema 中提供了 metadata_locks 表,用于监控 MDL 锁的持有和等待情况,方便 DBA 进行问题排查和性能优化(39)。
2.5 间隙锁与临键锁:解决幻读问题的关键机制
2.5.1 间隙锁与临键锁的实现原理
间隙锁(Gap Lock)和临键锁(Next-Key Lock)是 InnoDB 存储引擎中用于解决幻读问题的两种特殊锁机制,它们主要在可重复读(RR)隔离级别下发挥作用(7)。
间隙锁(Gap Lock):
间隙锁是针对索引中两个记录之间的间隙加锁,防止其他事务在这个间隙中插入新记录,以避免幻读。间隙锁不锁定具体行,而是锁定行与行之间的空间(31)。例如,如果表中有 id 为 1、3、5 的记录,那么间隙锁可以锁定以下区间:(-∞,1)、(1,3)、(3,5)、(5,+∞)。当事务在某个间隙上加上间隙锁后,其他事务不能在这个间隙中插入新的记录(7)。
间隙锁的特点包括:
-
间隙锁是表级锁的一种,但它只在可重复读隔离级别下有效
-
间隙锁可以是共享锁或排他锁,但它们的作用是相同的,即阻止其他事务在间隙中插入记录
-
间隙锁之间是兼容的,多个事务可以在同一个间隙上持有间隙锁
-
间隙锁不会阻塞除插入操作之外的其他操作,如 SELECT、UPDATE、DELETE 等(7)
临键锁(Next-Key Lock):
临键锁是记录锁和间隙锁的组合,锁定具体行和其前面的间隙,确保在一个范围内不会出现幻读。临键锁是个左开右闭的区间,如 (16,18](31)。当事务对某个索引记录加临键锁时,它实际上锁定了该记录本身以及该记录前面的间隙。例如,如果事务对 id=3 的记录加临键锁,那么它实际上锁定了 (1,3] 这个区间(假设表中还有 id=1 和 id=5 的记录)(7)。
临键锁的特点包括:
-
临键锁是 InnoDB 默认的行锁类型,在可重复读隔离级别下会自动使用
-
临键锁可以是共享锁或排他锁,分别对应行共享锁和行排他锁
-
临键锁会阻塞其他事务在锁定区间内插入记录,以及对锁定记录的修改操作
-
临键锁的锁定范围是左开右闭区间,这意味着它包含锁定记录本身,但不包含区间起点(7)
插入意向锁(Insert Intention Lock):
插入意向锁是一种特殊的间隙锁,它表示事务打算在某个间隙中插入记录。插入意向锁是在插入操作被阻塞时生成的,用于指示事务的插入意图。插入意向锁的特点是它们之间互不排斥,即使多个事务在同一间隙中插入记录,只要这些记录不冲突(即主键或唯一索引不重复),它们的插入意向锁之间就不会相互阻塞(29)。
2.5.2 间隙锁与临键锁的应用场景与优化策略
间隙锁和临键锁主要应用于以下场景:
可重复读隔离级别下的范围查询:
在可重复读隔离级别下,为了防止幻读,InnoDB 会自动使用临键锁。例如,当执行 SELECT * FROM orders WHERE amount > 100 FOR UPDATE 语句时,InnoDB 会在满足条件的记录上加上临键锁,同时锁定这些记录前面的间隙,防止其他事务在这个范围内插入新的记录(7)。
唯一性约束检查:
当插入一条新记录时,InnoDB 会检查该记录是否违反唯一性约束。如果表中已经存在相同的主键或唯一索引值,插入操作会被阻塞。在检查唯一性约束时,InnoDB 会使用间隙锁或临键锁来锁定相关的间隙,防止其他事务在这个间隙中插入冲突的记录(7)。
范围更新和删除:
当执行 UPDATE 或 DELETE 语句时,如果条件是一个范围(如 WHERE id BETWEEN 10 AND 20),InnoDB 会使用临键锁来锁定这个范围内的记录和间隙,确保在事务执行期间,其他事务不能在这个范围内插入新的记录或修改已有的记录(7)。
针对间隙锁和临键锁的优化策略主要包括:
合理选择事务隔离级别:
如果应用对幻读不敏感,可以考虑将事务隔离级别从可重复读(RR)降低到读已提交(RC)。在 RC 隔离级别下,InnoDB 默认不会使用间隙锁和临键锁,从而减少锁的持有时间和锁冲突的可能性(13)。
优化查询语句:
确保查询语句使用精确的索引,避免不必要的范围扫描。例如,使用具体的主键或唯一索引进行查询,而不是使用范围条件。这样可以减少间隙锁和临键锁的使用,提高并发性能(13)。
控制事务大小:
尽量缩短事务的执行时间,减少间隙锁和临键锁的持有时间。将大事务拆分为多个小事务,避免长时间持有锁资源(13)。
使用乐观锁替代悲观锁:
对于一些特定的业务场景,可以考虑使用乐观锁(如版本号机制)替代悲观锁,从而避免使用间隙锁和临键锁,提高并发性能(13)。
避免使用不必要的锁:
只有在必要时才使用 SELECT … FOR UPDATE 或 SELECT … LOCK IN SHARE MODE 等加锁语句。如果不需要对数据进行排他访问,可以考虑使用普通的 SELECT 语句(13)。
优化索引设计:
确保查询条件使用的索引是合适的,避免全表扫描或索引失效的情况。通过合理设计索引,可以减少间隙锁和临键锁的使用范围,提高数据库的并发性能(13)。
三、MySQL 锁机制的版本差异分析
3.1 MySQL 5.6 锁机制特点与局限性
3.1.1 MySQL 5.6 锁机制的核心特点
MySQL 5.6 是一个广泛使用的版本,其锁机制具有以下核心特点:
锁类型支持:
-
支持全局锁、表级锁(包括表共享读锁、表独占写锁、意向锁)和行级锁
-
行级锁仅在 InnoDB 存储引擎中支持,MyISAM 等存储引擎仅支持表级锁
-
支持共享锁(S 锁)和排他锁(X 锁)两种基本锁模式(22)
行锁实现:
-
InnoDB 的行锁基于索引实现,通过在索引记录上施加锁来锁定行数据
-
如果查询没有使用索引,InnoDB 会退化为表级锁,大大降低并发性能
-
行锁包括行共享锁(S 锁)和行排他锁(X 锁)两种类型(22)
死锁检测:
-
InnoDB 使用 wait-for graph 算法检测死锁
-
只能查看最近一次死锁的信息,无法获取历史死锁记录
-
死锁检测由系统自动完成,当检测到死锁时,会选择回滚代价最小的事务(通常以 undo 日志量为判断标准)(14)
元数据锁(MDL):
-
MySQL 5.5 引入了元数据锁(MDL),5.6 版本继续支持
-
MDL 锁用于保护表的元数据,确保 DML 和 DDL 操作的一致性
-
MDL 锁分为读锁(共享锁)和写锁(排他锁)两种类型(6)
锁等待超时:
-
行锁等待超时时间由 innodb_lock_wait_timeout 参数控制,默认值为 50 秒
-
MDL 锁等待超时时间由 lock_wait_timeout 参数控制,默认值也为 50 秒(40)
锁升级策略:
-
当事务持有大量行锁时,系统可能会触发锁升级,将行锁转换为表锁,以减少内存占用和锁管理的开销
-
锁升级的阈值没有固定值,通常认为当锁定的行数超过 5000 行时,可能会触发锁升级(71)
3.1.2 MySQL 5.6 锁机制的局限性
尽管 MySQL 5.6 的锁机制已经相对成熟,但仍存在一些局限性:
死锁检测的局限性:
-
只能查看最近一次死锁的信息,无法获取历史死锁记录,不利于死锁问题的分析和排查
-
死锁检测算法的性能在高并发场景下可能成为瓶颈
-
无法检测到涉及表锁或其他存储引擎锁的死锁情况(58)
锁升级策略的不足:
-
锁升级的阈值不明确,容易导致在某些情况下不必要的锁升级
-
锁升级可能导致并发性能急剧下降,因为表锁的粒度比行锁大得多
-
没有提供控制锁升级的配置参数,用户无法根据实际需求调整锁升级策略(71)
MDL 锁的局限性:
-
MDL 写锁在事务提交或回滚时才会释放,可能导致长时间的锁等待
-
在某些情况下,MDL 锁可能导致 DDL 操作长时间阻塞,影响数据库的可用性
-
没有提供有效的 MDL 锁监控和管理工具,排查 MDL 锁冲突问题较为困难(6)
行锁实现的限制:
-
行锁必须基于索引,如果查询没有使用索引,会退化为表级锁
-
行锁的实现依赖于存储引擎,不同存储引擎的行锁实现存在差异
-
在高并发场景下,行锁的管理可能成为性能瓶颈(22)
锁等待超时的问题:
-
行锁和 MDL 锁的默认等待超时时间较长(50 秒),在高并发场景下可能导致大量的锁等待
-
锁等待超时时间的配置不够灵活,无法针对不同类型的锁设置不同的超时时间(40)
3.2 MySQL 5.7 锁机制的改进与增强
3.2.1 MySQL 5.7 锁机制的主要改进
MySQL 5.7 在 5.6 的基础上对锁机制进行了多项改进和增强,主要包括:
死锁检测的改进:
-
支持开启 innodb_print_all_deadlocks 参数,将所有死锁信息记录到错误日志中,而不仅仅是最近一次死锁
-
改进了死锁检测算法的性能,减少了高并发场景下死锁检测的开销
-
可以通过设置 innodb_deadlock_detect 参数禁用死锁检测,在某些高并发场景下可以提高性能(14)
元数据锁(MDL)的增强:
-
引入了共享排他锁(SX 锁),主要用于解决在线 DDL(SMO)带来的问题
-
改进了 MDL 锁的管理机制,减少了锁冲突和锁等待的可能性
-
提供了 performance_schema.metadata_locks 表,用于监控 MDL 锁的持有和等待情况(6)
行锁实现的优化:
-
改进了行锁的实现,减少了锁的内存占用和管理开销
-
优化了行锁的加锁和释放过程,提高了并发性能
-
增强了行锁的诊断功能,可以更方便地查看行锁的持有和等待情况(22)
锁升级策略的优化:
-
优化了锁升级的触发条件,减少了不必要的锁升级
-
提供了更灵活的锁升级控制机制,虽然没有直接的配置参数,但通过其他参数可以间接控制
-
改进了锁升级的性能,减少了锁升级带来的性能影响(71)
锁等待超时的改进:
-
仍然使用 innodb_lock_wait_timeout 参数控制行锁等待超时时间,默认值为 50 秒
-
引入了 lock_wait_timeout 参数专门控制 MDL 锁的等待超时时间,默认值为 50 秒
-
可以分别设置行锁和 MDL 锁的等待超时时间,提高了配置的灵活性(40)
行锁粒度粗化(锁膨胀):
-
当内存中存储锁对象的空间满了后,为了节省内存空间,行锁会粗化为表锁
-
当范围行写操作需要加的行锁较多时,也可能会粗化成表锁
-
提供了 innodb_max_undo_log_size 参数,可以控制 undo 日志的大小,间接影响锁的管理(32)
3.2.2 MySQL 5.7 中的新锁类型:共享排他锁(SX 锁)
MySQL 5.7 引入了一种新的锁类型:共享排他锁(Shared Exclusive Lock,简称 SX 锁),它主要用于解决在线 DDL(SMO)过程中出现的问题(6)。
SX 锁的实现原理:
-
SX 锁是一种特殊的锁类型,它允许持有锁的事务读取数据,但阻止其他事务对数据进行修改
-
SX 锁不会阻塞共享锁(S 锁),但会阻塞排他锁(X 锁)和其他 SX 锁
-
SX 锁主要用于在线 DDL 操作,允许在表结构变更的同时进行数据读取,但阻止数据修改(32)
SX 锁的应用场景:
-
在线 DDL 操作:当执行 ALTER TABLE 等 DDL 操作时,使用 SX 锁可以允许其他事务继续读取表数据,但阻止修改操作
-
表结构变更与数据读取的并发控制:在表结构变更过程中,允许读取操作继续进行,提高系统的可用性
-
备份和恢复场景:在备份过程中,使用 SX 锁可以允许其他事务继续读取数据,但阻止修改操作,确保备份数据的一致性(32)
SX 锁与其他锁的兼容性:
-
SX 锁与 S 锁兼容,可以同时存在
-
SX 锁与 X 锁不兼容,不能同时存在
-
SX 锁与其他 SX 锁不兼容,不能同时存在
-
这一兼容性特点使得 SX 锁能够在保证数据一致性的同时,提高并发性能(32)
SX 锁的优势:
-
提高在线 DDL 操作的并发性能:在 DDL 操作期间,允许其他事务继续读取数据,减少锁冲突
-
减少表结构变更的停机时间:通过允许并发读取,缩短了 DDL 操作对应用的影响时间
-
提高系统的可用性:在表结构变更期间,系统仍然可以处理读请求,提高了整体可用性(32)
3.3 MySQL 8.0 锁机制的重大改进
3.3.1 MySQL 8.0 锁机制的核心变化
MySQL 8.0 在锁机制方面进行了多项重大改进,这些改进显著提高了数据库的性能和可靠性:
元数据锁(MDL)的重大增强:
-
改进了 MDL 锁的管理机制,减少了锁冲突和锁等待的可能性
-
引入了新的 MDL 锁模式,如 SHARED_READ_ONLY 锁,提供了更细粒度的锁控制
-
改进了 MDL 锁的加锁策略,减少了不必要的锁获取
-
提供了更完善的 MDL 锁监控和管理工具,如 performance_schema.metadata_locks 表(39)
死锁检测的优化:
-
改进了死锁检测算法的性能,提高了高并发场景下的处理能力
-
增强了死锁日志记录功能,提供了更详细的死锁信息
-
提供了 lock_order 工具,用于检测和预防锁获取顺序导致的死锁(从 MySQL 8.0.17 开始支持)(54)
锁等待超时的改进:
-
行锁等待超时时间仍然由 innodb_lock_wait_timeout 参数控制,默认值为 50 秒
-
MDL 锁等待超时时间由 lock_wait_timeout 参数控制,默认值为 31536000 秒(约 1 年)
-
可以分别设置行锁和 MDL 锁的等待超时时间,提高了配置的灵活性
-
MDL 锁的默认等待超时时间大幅延长,减少了因 MDL 锁等待超时导致的应用中断(40)
行锁实现的优化:
-
改进了行锁的实现,减少了锁的内存占用和管理开销
-
优化了行锁的加锁和释放过程,提高了并发性能
-
增强了行锁的诊断功能,提供了更详细的锁信息和诊断工具(22)
备份锁的改进:
-
引入了实例级备份锁(LOCK INSTANCE FOR BACKUP)和新的 MDL 锁(LOCK TABLES FOR BACKUP)
-
支持在不影响 DML 操作的情况下阻塞 DDL 和非事务表更新
-
提高了备份过程的并发性和性能(42)
在线 DDL 的改进:
-
支持更多类型的在线 DDL 操作,如添加 / 删除列、重命名列等
-
改进了在线 DDL 的锁机制,减少了对业务的影响
-
提供了更灵活的锁模式选择,如 LOCK=NONE、LOCK=SHARED、LOCK=EXCLUSIVE 等(42)
性能监控和诊断的增强:
-
在 performance_schema 中提供了更多与锁相关的表,如 data_locks、data_lock_waits 等
-
增强了 SHOW ENGINE INNODB STATUS 命令的输出,提供了更详细的锁信息
-
提供了 sys.innodb_lock_waits 视图,简化了锁等待信息的查询(12)
3.3.2 MySQL 8.0 中移除的锁相关功能
MySQL 8.0 在改进锁机制的同时,也移除了一些不再推荐使用的功能:
InnoDB 锁相关的 INFORMATION_SCHEMA 表:
-
移除了 INFORMATION_SCHEMA.INNODB_LOCKS 和 INFORMATION_SCHEMA.INNODB_LOCK_WAITS 表
-
这些表在 MySQL 5.7 中已被标记为废弃,在 8.0 中被彻底移除
-
建议使用 performance_schema.data_locks 和 performance_schema.data_lock_waits 表替代(11)
某些 MDL 锁相关的功能:
-
移除了一些不再使用的 MDL 锁类型和状态
-
简化了 MDL 锁的管理机制,提高了性能和稳定性(39)
某些锁模式的兼容性:
-
改变了某些锁模式的兼容性规则,提高了并发性和安全性
-
移除了一些不再需要的锁模式组合(39)
3.3.3 MySQL 8.0 的锁管理工具改进
MySQL 8.0 提供了一系列改进的锁管理工具,帮助 DBA 和开发人员更好地监控和管理锁机制:
performance_schema 中的锁相关表:
-
data_locks 表:提供了当前持有和等待的锁的详细信息,包括锁的类型、模式、状态等
-
data_lock_waits 表:提供了锁等待关系的详细信息,包括等待线程和阻塞线程的信息
-
metadata_locks 表:提供了 MDL 锁的详细信息,包括锁的类型、状态、持有线程等(12)
sys schema 中的锁相关视图:
-
sys.innodb_lock_waits 视图:简化了锁等待信息的查询,提供了更直观的锁等待关系
-
sys.schema_table_lock_waits 视图:提供了表级锁等待的详细信息
-
sys.schema_object_lock_waits 视图:提供了数据库对象锁等待的详细信息(12)
SHOW ENGINE INNODB STATUS 命令:
-
增强了输出内容,提供了更详细的锁信息和死锁信息
-
在 LATEST DETECTED DEADLOCK 部分提供了更详细的死锁信息,包括死锁事务的 SQL 语句、锁类型及回滚信息(12)
新的锁监控和诊断工具:
-
lock_order 工具:用于检测和预防锁获取顺序导致的死锁(从 MySQL 8.0.17 开始支持)
-
lock_wait_timeout 参数:控制 MDL 锁的等待超时时间,默认值为 31536000 秒
-
innodb_deadlock_detect 参数:控制死锁检测的启用和禁用,提高了配置的灵活性(58)
3.4 三个版本锁机制的对比分析
下表对 MySQL 5.6、5.7 和 8.0 三个版本的锁机制进行了全面对比:
| 特性 | MySQL 5.6 | MySQL 5.7 | MySQL 8.0 |
|---|---|---|---|
| 行锁实现 | 基于索引的行锁,无索引时退化为表锁 | 基于索引的行锁,优化了加锁和释放过程 | 基于索引的行锁,进一步优化,减少内存占用 |
| MDL 锁 | 基础支持,读锁和写锁 | 增强,引入 SX 锁 | 重大增强,更多锁模式,更灵活的管理 |
| 死锁检测 | 仅最近一次死锁信息 | 支持记录所有死锁,改进算法 | 进一步优化算法,增强日志记录 |
| 锁等待超时 | 行锁和 MDL 锁均为 50 秒 | 行锁 50 秒,MDL 锁 50 秒 | 行锁 50 秒,MDL 锁 31536000 秒 |
| 在线 DDL 支持 | 有限 | 增强,支持更多操作 | 显著增强,支持更多操作,更灵活的锁模式 |
| 锁监控工具 | 有限 | 增强,performance_schema | 全面增强,更多表和视图 |
| 备份锁机制 | 全局锁 | 改进的备份锁 | 实例级备份锁,更灵活的备份支持 |
| 移除的功能 | 无 | 无 | INFORMATION_SCHEMA.INNODB_LOCKS 等表 |
| 新特性 | 无 | SX 锁,改进的死锁检测 | LOCK INSTANCE FOR BACKUP,lock_order 工具等 |
通过对比可以看出,MySQL 8.0 在锁机制方面进行了最全面的改进,尤其是在 MDL 锁、在线 DDL 和锁监控方面。这些改进使得 MySQL 8.0 在高并发场景下的性能和稳定性有了显著提升。MySQL 5.7 则是在 5.6 的基础上进行了一些关键改进,如引入 SX 锁和改进的死锁检测。而 MySQL 5.6 虽然功能相对基础,但仍是许多生产环境中使用的版本。
四、死锁分析与处理
4.1 死锁的概念与形成条件
4.1.1 死锁的定义与特征
死锁是指两个或多个事务在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。在数据库系统中,死锁是一种严重的并发问题,它会导致事务无法完成,占用系统资源,甚至可能导致整个系统性能下降(13)。
死锁的特征主要包括:
-
相互等待:两个或多个事务彼此等待对方释放资源
-
资源独占:每个事务都持有对方需要的资源,并且不愿意释放
-
循环等待:事务之间形成了一个资源等待的环路
-
无法自行解除:若无外部干预,死锁将一直存在(16)
在 MySQL 中,死锁通常发生在使用行级锁的 InnoDB 存储引擎中,因为 MyISAM 等存储引擎仅支持表级锁,而表级锁不会产生死锁。死锁的发生与事务隔离级别无关,任何隔离级别下都可能发生死锁(70)。
4.1.2 死锁形成的必要条件
死锁的形成必须同时满足以下四个必要条件,这四个条件被称为死锁的 “四个必要条件”:
互斥条件:
资源一次只能由一个事务持有,即资源具有排他性。例如,行排他锁(X 锁)在同一时间只能由一个事务持有(16)。
占有并等待条件:
事务已经持有了至少一个资源,但又提出了新的资源请求,而该资源已被其他事务持有,导致事务等待(16)。
非抢占条件:
事务持有的资源不能被其他事务强行抢占,只能由持有资源的事务自己释放(16)。
循环等待条件:
多个事务形成了一个资源等待的环路,即事务 T1 等待事务 T2 持有的资源,事务 T2 等待事务 T3 持有的资源,依此类推,最后事务 Tn 等待事务 T1 持有的资源(16)。
这四个条件必须同时满足,死锁才会发生。因此,要预防死锁,可以通过破坏其中任何一个条件来实现。
4.1.3 死锁的典型场景与案例分析
在 MySQL 中,死锁通常出现在以下几种典型场景中:
交叉锁导致的死锁(适用 MySQL 5.5 及以上版本):
两个事务以不同顺序访问相同资源时,容易产生交叉锁。例如事务 A 先锁定资源 X,再尝试锁定资源 Y;事务 B 先锁定资源 Y,再尝试锁定资源 X,此时双方相互等待,形成死锁(13)。
并发插入导致的死锁(适用 MySQL 5.6 及以上版本):
多个事务同时向有唯一索引的表插入数据时,若插入的数据违反唯一约束,且事务持有锁的顺序不一致,可能引发死锁。比如两个事务同时插入相同主键值的记录(13)。
事务嵌套导致的死锁(适用 MySQL 5.7 及以上版本):
子事务与父事务之间的锁冲突也可能导致死锁。当子事务获取的锁与父事务后续需要的锁产生依赖循环时,死锁便会出现(13)。
长事务导致的死锁(适用所有主流 MySQL 版本):
长时间运行的事务持续持有锁资源,其他事务无法获取所需锁,可能导致多个事务相互等待,进而引发死锁(13)。
间隙锁导致的死锁(适用 MySQL 8.0 及以上版本,在 RR 隔离级别下):
在可重复读(RR)隔离级别下,间隙锁会锁定记录之间的间隙,防止幻读。但当多个事务同时对同一间隙进行操作时,可能产生死锁(13)。
以下是一个交叉锁导致死锁的具体案例:
测试数据准备:
CREATE TABLE t\_order (
  id INT PRIMARY KEY,
  status INT
) ENGINE=InnoDB;
INSERT INTO t\_order (id, status) VALUES (100, 0), (200, 0);
事务 A:
BEGIN;
UPDATE t\_order SET status=1 WHERE id=100; -- 锁定id=100
UPDATE t\_order SET status=1 WHERE id=200; -- 尝试锁定id=200
事务 B:
BEGIN;
UPDATE t\_order SET status=1 WHERE id=200; -- 锁定id=200
UPDATE t\_order SET status=1 WHERE id=100; -- 尝试锁定id=100
如果事务 A 和事务 B 同时执行,事务 A 先锁定 id=100,事务 B 先锁定 id=200,然后各自尝试锁定对方已锁定的行,就会形成死锁(1)。
4.2 死锁检测机制与实现原理
4.2.1 InnoDB 的死锁检测算法
InnoDB 存储引擎使用等待图(Wait-For Graph)算法来检测死锁,这是一种经典的死锁检测算法(52)。该算法的基本思想是通过维护一个事务等待图,实时记录事务间的资源依赖关系,然后定期检查这个图是否存在环路,如果存在环路,则判定为死锁(77)。
等待图构建:
InnoDB 通过维护一个事务等待图,实时记录事务间的资源依赖关系。每个事务作为图节点,当事务 T1 等待事务 T2 持有的锁时,建立 T1 → T2 的有向边(77)。
环路检测:
系统周期性地检查等待图是否存在环路。发现环路时立即判定为死锁,检测频率由参数 innodb_lock_wait_timeout 部分控制(77)。
死锁检测的具体步骤:
-
当一个事务需要阻塞等待锁时触发死锁检测
-
以该事务为起点,从锁的信息链表中,查找对应锁的持有者(事务)信息
-
根据锁的持有者信息,去事务等待链表中,查找持有者是否在等待获取其他锁
-
若持有者在等待获取其他锁,则以持有者为起点继续查找,直到出现闭环或查找到未阻塞事务
-
若出现闭环,则介入破坏闭环,选择一个事务进行回滚操作(32)
死锁检测的触发时机:
-
当事务尝试获取锁而无法立即获得时,触发死锁检测
-
当持有锁的事务释放锁时,检查是否有等待的事务可以获得锁,并触发死锁检测
-
定期检查(由参数控制)是否存在死锁(52)
4.2.2 不同版本死锁检测的差异
不同版本的 MySQL 在死锁检测机制上存在一些差异:
MySQL 5.6:
-
只能查看最近一次死锁的信息
-
死锁检测由系统自动完成,无法禁用
-
死锁信息通过 SHOW ENGINE INNODB STATUS 命令查看(14)
MySQL 5.7:
-
支持开启 innodb_print_all_deadlocks 参数,将所有死锁信息记录到错误日志中
-
支持禁用死锁检测(通过设置 innodb_deadlock_detect=OFF)
-
提供了更详细的死锁信息,包括死锁事务的 SQL 语句(14)
MySQL 8.0:
-
进一步优化了死锁检测算法的性能
-
增强了死锁日志记录功能,提供了更详细的死锁信息
-
提供了 lock_order 工具,用于检测和预防锁获取顺序导致的死锁(从 MySQL 8.0.17 开始支持)
-
在 performance_schema 中提供了更多与死锁相关的表(51)
死锁检测的配置参数:
-
innodb_deadlock_detect:控制是否启用死锁检测,默认值为 ON
-
innodb_print_all_deadlocks:控制是否将所有死锁信息记录到错误日志中,默认值为 OFF
-
innodb_lock_wait_timeout:控制事务等待锁的超时时间,默认值为 50 秒(51)
4.2.3 死锁检测的性能影响与优化
死锁检测虽然是数据库系统中必不可少的功能,但在高并发场景下可能会带来一定的性能开销。以下是一些优化死锁检测性能的策略:
禁用死锁检测:
在某些高并发场景下,如果应用能够确保死锁极少发生,可以考虑禁用死锁检测,转而依赖 innodb_lock_wait_timeout 参数来处理死锁。这可以通过设置 innodb_deadlock_detect=OFF 来实现(57)。
调整死锁检测频率:
虽然 MySQL 没有直接提供控制死锁检测频率的参数,但可以通过调整 innodb_lock_wait_timeout 参数来间接影响检测频率。较小的值会导致更频繁的死锁检测,但也会增加锁等待超时的可能性(57)。
优化事务设计:
通过优化事务设计,可以减少死锁的发生概率,从而降低死锁检测的需求。具体策略包括:
-
保持事务简短,减少锁持有时间
-
按固定顺序访问资源
-
避免在事务中执行耗时操作(13)
使用乐观锁替代悲观锁:
对于一些特定的业务场景,可以考虑使用乐观锁(如版本号机制)替代悲观锁,从而避免死锁的发生(13)。
监控和分析死锁:
通过监控和分析死锁信息,可以发现死锁的规律和模式,进而针对性地优化应用和数据库设计。可以通过以下方式监控死锁:
-
开启 innodb_print_all_deadlocks 参数,记录所有死锁信息
-
定期查看错误日志中的死锁记录
-
使用 SHOW ENGINE INNODB STATUS 命令查看最新的死锁信息(13)
4.3 死锁处理与恢复策略
4.3.1 死锁发生后的处理流程
当 InnoDB 检测到死锁时,会自动选择一个事务进行回滚,以打破死锁环路。这个过程称为死锁的自动处理(16)。以下是死锁发生后的处理流程:
死锁检测:
InnoDB 通过等待图算法检测到死锁后,会确定死锁环中的事务(52)。
选择回滚事务:
InnoDB 选择回滚代价最小的事务来打破死锁。回滚代价通常以事务生成的 undo 日志量为判断标准,undo 日志量越小的事务,回滚代价越小。如果两个事务的 undo 日志量相同,则选择回滚触发死锁的事务(52)。
回滚事务:
InnoDB 回滚选定的事务,释放该事务持有的所有锁资源(52)。
通知应用:
被回滚的事务会收到一个错误代码(通常是 1213,即 ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction),应用程序可以捕获这个错误并进行相应的处理(13)。
重新执行事务:
应用程序在捕获到死锁错误后,可以选择重新执行被回滚的事务。为了避免再次发生死锁,通常需要在重新执行前等待一段时间(如指数退避)(13)。
4.3.2 死锁信息的查看与分析
在 MySQL 中,可以通过多种方式查看和分析死锁信息:
使用 SHOW ENGINE INNODB STATUS 命令:
该命令可以显示 InnoDB 存储引擎的状态信息,包括最近一次死锁的详细信息。在输出结果中,LATEST DETECTED DEADLOCK 部分包含了死锁的详细信息,如死锁发生的时间、涉及的事务、持有的锁和等待的锁等(12)。
开启 innodb_print_all_deadlocks 参数:
在 MySQL 5.6 及以上版本中,可以通过设置 innodb_print_all_deadlocks=ON,将所有死锁信息记录到错误日志中,而不仅仅是最近一次死锁的信息。这对于分析频繁发生的死锁非常有帮助(14)。
使用 performance_schema 中的表:
在 MySQL 8.0 中,可以使用 performance_schema.data_locks 和 performance_schema.data_lock_waits 表来查看当前的锁和锁等待信息(12)。
使用 sys.innodb_lock_waits 视图:
在 MySQL 8.0 中,sys.innodb_lock_waits 视图提供了锁等待关系的简洁表示,包括阻塞线程和等待线程的信息、等待时间、SQL 语句等(12)。
以下是 SHOW ENGINE INNODB STATUS 输出中死锁信息的示例:
\------------------------
LATEST DETECTED DEADLOCK
\------------------------
2020-04-23 14:57:39 0x7f1e04377700
\*\*\* (1) TRANSACTION:
TRANSACTION 2638716, ACTIVE 164 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 4 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 1
MySQL thread id 4909, OS thread handle 139767434131200, query id 1231199 localhost root update
insert into test\_innodb value(17, '11',111,111, '1212121212')
\*\*\* (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 410 page no 3 n bits 80 index PRImARY of table \`test\`.\`test\_innodb\` trx id 2638716 lock mode x locks gap before rec insert intention waiting
Record lock, heap no 11 PHYSICAL RECORD: n fields 7; compact format; info bits 0
 0: len 4; hex 80000014; asc ;;
 1: len 6; hex 000000284377; asc (CW;;
 2: len 7; hex cf000001c4011d; asc ;;
 3: len 2; hex 3131; asc 11;;
 4: len 3; hex 313131; asc 111;;
 5: len 4; hex 8000006f; asc o;;
 6: len 10; hex 31323132313231323132; asc 1212121212;;
\*\*\* (2) TRANSACTION:
TRANSACTION 2638711, ACTIVE 957 sec inserting
mysql tables in use 1, locked 1
4 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 3
MyS0L thread id 4908, OS thread handle 139766896490240, query id 1231200 localhost root update
insert into test\_innodb value(17,'11',111,111,'1212121212')
\*\*\* (2) HOLDS THE LOCK(S):
RECORD LOCKS space 1d 410 page no 3 n bits 80 index PRIMARY of table \`test\`.\`test\_innodb\` trx id 2638711 lock mode X locks gap before rec
Record lock, heap no 1l PHYSICAL RECORD: n fields 7; compact format; info bits θ
 0: len 4; hex 80000014; asc ;;
 1: len 6; hex 000000284377; asc (CW;;
 2: len 7; hex cf000001c4011d; asc ;;
 3: len 2; hex 3131; asc 11;;
 4: len 3; hex 313131; asc 111;;
 5: len 4; hex 8000006f; asc o;;
 6: len 10; hex 31323132313231323132; asc 1212121212;;
\*\*\* (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 410 page no 3 n bits 80 index PRimARY of table \`test\`.\`test\_innodb\` trx id 2638711 lock mode X locks gap before rec insert intention waiting
Record lock, heap no 11 PHYSICAL RECORD: n fields 7; compact format; info bits θ
 0: len 4; hex 80000014; asc ;;
 1: len 6; hex 000000284377; asc (CW;;
 2: len 7; hex cf000001c4011d; asc ;;
 3: len 2; hex 3131; asc 11;;
 4: len 3; hex 313131; asc 111;;
在这段输出中,可以看到两个事务相互等待对方持有的锁,形成了死锁。InnoDB 选择回滚其中一个事务来打破死锁(14)。
4.3.3 死锁预防策略与最佳实践
预防死锁比处理死锁更为重要,以下是一些有效的死锁预防策略:
保持事务简短:
将不必要的操作移出事务,仅在必要时使用事务。例如,在更新用户余额场景中,先完成其他耗时操作,再开启事务执行更新操作(13)。
保持事务中 SQL 语句的顺序一致性:
确保所有事务以相同顺序访问资源,避免交叉锁的产生。例如,在电商下单场景,统一按表名或主键 ID 顺序访问资源(13)。
使用短事务代替长事务:
将大事务拆分成多个小事务,降低死锁发生概率。例如,将批量数据插入操作分批次进行,每批次处理一部分数据并提交事务(13)。
合理设计索引:
确保查询使用索引,避免全表扫描。全表扫描会导致行锁退化为表锁,增加锁冲突的可能性(13)。
避免在事务中执行耗时操作:
如远程调用、文件操作、大量计算等,这些操作会延长事务持有锁的时间,增加死锁风险(13)。
使用较低的事务隔离级别:
在保证数据一致性的前提下,选择较低的事务隔离级别(如 READ COMMITTED),可以减少锁的持有时间和范围(13)。
使用乐观锁替代悲观锁:
对于一些特定的业务场景,如更新库存、版本控制等,可以使用乐观锁替代悲观锁,从而避免死锁(13)。
实现重试机制:
在应用层实现死锁重试逻辑,捕获死锁异常后自动重试,并采用指数退避策略设置重试间隔(13)。
以下是一个 Java 代码中实现死锁重试的示例:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class MysqlDeadlockRetry {
  private static final String URL = "jdbc:mysql://localhost:3306/your\_database";
  private static final String USER = "your\_username";
  private static final String PASSWORD = "your\_password";
  public static void executeWithRetry(String query, int maxRetries, double backoffFactor) {
  int retries = 0;
  while (retries < maxRetries) {
  try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
  Statement statement = connection.createStatement()) {
  connection.setAutoCommit(false);
  statement.executeUpdate(query);
  connection.commit();
  System.out.println("Query executed successfully.");
  return;
  } catch (SQLException e) {
  if (isDeadlockException(e)) {
  retries++;
  double waitTime = backoffFactor \* Math.pow(2, retries - 1);
  try {
  Thread.sleep((long) (waitTime \* 1000));
  } catch (InterruptedException interruptedException) {
  Thread.currentThread().interrupt();
  }
  } else {
  throw new RuntimeException("Database operation failed", e);
  }
  }
  }
  throw new RuntimeException("Max retries reached for deadlock retry");
  }
  private static boolean isDeadlockException(SQLException e) {
  return e.getErrorCode() == 1213; // MySQL死锁错误码
  }
  public static void main(String\[] args) {
  String updateQuery = "UPDATE your\_table SET column = value WHERE condition";
  executeWithRetry(updateQuery, 3, 0.5);
  }
}
4.4 死锁监控与日志记录
4.4.1 死锁相关参数配置
MySQL 提供了多个与死锁相关的参数,可以通过配置这些参数来控制死锁检测和日志记录行为:
innodb_deadlock_detect:
-
作用:控制是否启用死锁检测
-
取值:ON(启用,默认值)或 OFF(禁用)
-
说明:在高并发场景下,如果应用能够确保死锁极少发生,可以考虑禁用死锁检测,转而依赖 innodb_lock_wait_timeout 参数来处理死锁(57)。
innodb_print_all_deadlocks:
-
作用:控制是否将所有死锁信息记录到错误日志中
-
取值:1(启用)或 0(禁用,默认值)
-
说明:启用后,所有死锁信息都会被记录到错误日志中,而不仅仅是最近一次死锁的信息,这对于分析死锁非常有帮助(14)。
innodb_lock_wait_timeout:
-
作用:控制事务等待行锁的超时时间
-
取值:整数,单位为秒,默认值为 50
-
说明:如果一个事务在等待行锁时超过了这个时间,就会被回滚,并返回错误。在禁用死锁检测的情况下,可以通过设置较小的值来快速处理死锁(57)。
lock_wait_timeout:
-
作用:控制 MDL 锁的等待超时时间
-
取值:整数,单位为秒,默认值在 MySQL 5.x 中为 50,在 MySQL 8.0 中为 31536000(约 1 年)
-
说明:在 MySQL 8.0 中,该参数的默认值较大,可能导致 MDL 锁等待时间过长,建议根据实际情况调整(40)。
innodb_rollback_on_timeout:
-
作用:控制当事务等待锁超时时是否自动回滚
-
取值:ON(自动回滚,默认值)或 OFF(不自动回滚,仅返回错误)
-
说明:设置为 ON 时,当事务等待锁超时后会自动回滚;设置为 OFF 时,仅返回错误,事务需要手动回滚(57)。
4.4.2 死锁日志分析工具与方法
分析死锁日志是诊断和解决死锁问题的关键步骤。以下是一些常用的死锁日志分析工具和方法:
错误日志分析:
当开启 innodb_print_all_deadlocks 参数后,所有死锁信息都会被记录到 MySQL 的错误日志中。可以通过查看错误日志来分析死锁的原因和规律(14)。
SHOW ENGINE INNODB STATUS 命令:
该命令可以显示 InnoDB 存储引擎的状态信息,包括最近一次死锁的详细信息。在输出结果中,LATEST DETECTED DEADLOCK 部分包含了死锁的详细信息,如死锁发生的时间、涉及的事务、持有的锁和等待的锁等(12)。
performance_schema 中的表:
在 MySQL 8.0 中,performance_schema 提供了 data_locks 和 data_lock_waits 表,可以用于查看当前的锁和锁等待信息(12)。
sys.innodb_lock_waits 视图:
该视图提供了锁等待关系的简洁表示,包括阻塞线程和等待线程的信息、等待时间、SQL 语句等,便于快速分析锁等待问题(12)。
pt-deadlock-logger 工具:
Percona Toolkit 中的 pt-deadlock-logger 工具可以对 MySQL 错误日志中的死锁信息进行专业分析,适用于各个版本的 MySQL 数据库(13)。
死锁分析步骤:
-
确定死锁发生的时间:通过查看错误日志或 SHOW ENGINE INNODB STATUS 的输出,确定死锁发生的时间(13)。
-
获取死锁相关的 SQL 语句:从死锁日志中提取涉及的 SQL 语句,分析这些语句的执行顺序和锁获取情况(13)。
-
分析锁的持有和等待关系:确定哪些事务持有哪些锁,以及它们等待哪些锁,形成死锁环路(13)。
-
找出死锁的原因:根据锁的持有和等待关系,分析死锁的原因,如交叉锁、并发插入、事务嵌套等(13)。
-
制定解决方案:根据死锁的原因,制定相应的解决方案,如调整事务顺序、优化索引、缩短事务等(13)。
4.4.3 死锁处理的版本差异与注意事项
不同版本的 MySQL 在死锁处理方面存在一些差异和注意事项:
MySQL 5.6:
-
只能查看最近一次死锁的信息
-
不支持禁用死锁检测
-
死锁信息通过 SHOW ENGINE INNODB STATUS 命令查看(14)
MySQL 5.7:
-
支持记录所有死锁信息(通过 innodb_print_all_deadlocks 参数)
-
支持禁用死锁检测(通过 innodb_deadlock_detect 参数)
-
提供了更详细的死锁信息,包括死锁事务的 SQL 语句(14)
MySQL 8.0:
-
移除了 INFORMATION_SCHEMA.INNODB_LOCKS 和 INFORMATION_SCHEMA.INNODB_LOCK_WAITS 表
-
在 performance_schema 中提供了 data_locks 和 data_lock_waits 表,用于查看锁信息
-
提供了 sys.innodb_lock_waits 视图,简化了锁等待信息的查询(12)
注意事项:
-
禁用死锁检测的风险:禁用死锁检测后,死锁将无法被自动检测,只能通过锁等待超时来处理。这可能导致事务长时间等待,占用系统资源(57)。
-
锁等待超时的设置:在禁用死锁检测的情况下,应将 innodb_lock_wait_timeout 参数设置为较小的值(如 5-10 秒),以便快速处理死锁(57)。
-
死锁重试机制:应用程序应实现死锁重试机制,在捕获到死锁异常后自动重试,提高系统的容错性(13)。
-
死锁日志的保留:应保留足够的死锁日志,以便分析死锁的原因和规律。可以通过设置合适的日志保留策略来实现(13)。
五、锁机制优化策略与最佳实践
5.1 锁机制优化的基本原则
在优化 MySQL 锁机制时,应遵循以下基本原则:
一致性与并发性能的平衡:
锁机制的核心是在保证数据一致性的前提下,尽可能提高数据库的并发性能。因此,优化锁机制的关键在于找到一致性和并发性能之间的平衡点(1)。
锁粒度与性能的权衡:
锁的粒度越细(如行锁),并发性能越高,但锁管理的开销也越大;锁的粒度越粗(如表锁、全局锁),并发性能越低,但锁管理的开销也越小。应根据业务场景选择合适的锁粒度(1)。
锁持有时间与性能的关系:
锁持有时间越长,锁冲突的可能性越大,并发性能越低。因此,应尽可能缩短事务持有锁的时间,减少锁冲突(1)。
索引与锁性能的关系:
索引是行锁的基础,没有索引的查询会导致行锁退化为表锁,大大降低并发性能。因此,应确保查询语句使用合适的索引(1)。
事务隔离级别与锁的关系:
较高的事务隔离级别(如可重复读)需要更多的锁和更长的锁持有时间,而较低的隔离级别(如读已提交)则可以减少锁的使用。应根据业务需求选择合适的事务隔离级别(13)。
5.2 不同场景下的锁优化策略
5.2.1 高并发写入场景的锁优化
高并发写入场景是最容易出现锁冲突和性能问题的场景,以下是针对这类场景的锁优化策略:
批量操作优化:
对于批量插入、更新、删除等操作,应将大批次拆分为多个小批次,每批次处理一部分数据并提交事务。这样可以减少锁持有时间,降低锁冲突的可能性(71)。
示例代码(Java):
@Transactional
public void batchInsert(List\<Data> dataList) {
  int batchSize = 1000;
  for (int i = 0; i < dataList.size(); i += batchSize) {
  List\<Data> batch = dataList.subList(i, Math.min(i + batchSize, dataList.size()));
  dataRepository.saveAll(batch);
  // 手动清除Hibernate一级缓存,避免内存溢出
  entityManager.clear();
  }
}
索引优化:
确保频繁更新的列上有合适的索引,避免全表扫描。全表扫描会导致行锁退化为表锁,大大降低并发性能(13)。
使用行锁而非表锁:
在 InnoDB 存储引擎中,应尽量使用行锁而非表锁。可以通过以下方式实现:
-
使用具体的条件查询,避免全表扫描
-
确保查询条件使用索引
-
避免在事务中执行 LOCK TABLES 等显式表锁操作(13)。
优化事务设计:
-
保持事务简短,仅包含必要的操作
-
避免在事务中执行耗时操作
-
将不相关的操作移出事务
-
按固定顺序访问资源(13)。
使用乐观锁:
对于库存更新、版本控制等特定业务场景,可以使用乐观锁替代悲观锁,避免锁冲突(13)。
示例 SQL(乐观锁):
UPDATE products SET stock = stock - 1, version = version + 1 WHERE id = 1 AND version = 0;
数据库连接池优化:
调整数据库连接池的配置,合理设置最大连接数、最小连接数、获取连接超时等参数,避免连接池耗尽导致的性能问题(13)。
5.2.2 读多写少场景的锁优化
读多写少场景是数据库中最常见的场景之一,以下是针对这类场景的锁优化策略:
使用合适的事务隔离级别:
在保证数据一致性的前提下,选择较低的事务隔离级别(如 READ COMMITTED),可以减少锁的使用和持有时间(13)。
优化共享锁的使用:
在需要读取数据并确保数据在事务期间不被修改的场景中,可以使用 SELECT … LOCK IN SHARE MODE 语句获取共享锁。共享锁之间是兼容的,可以提高并发性能(25)。
缓存热点数据:
对于频繁读取但很少更新的数据,可以使用缓存(如 Redis)来缓存数据,减少数据库的读压力和锁冲突(13)。
读写分离:
对于读多写少的系统,可以考虑实施读写分离架构,将读操作分发到多个从库,减轻主库的压力。在这种架构下,写操作在主库执行,读操作在从库执行(13)。
批量读取优化:
对于需要多次读取同一数据的场景,可以一次性读取所有需要的数据,避免多次查询和加锁。例如,使用 IN 子句一次性查询多个 ID 的数据(13)。
索引优化:
确保查询语句使用合适的索引,提高查询效率,减少锁冲突。对于读多写少的表,可以考虑添加更多的索引,即使这可能会增加写入的开销(13)。
5.2.3 在线 DDL 操作的锁优化
在线 DDL(Online DDL)操作是指在不锁定表或对业务影响较小的情况下执行表结构变更。以下是在线 DDL 操作的锁优化策略:
选择合适的在线 DDL 算法:
MySQL 支持多种在线 DDL 算法,包括 COPY、INPLACE 和 INSTANT。不同的算法对锁的要求不同,应根据具体操作选择合适的算法(42)。
LOCK 选项的使用:
在执行 ALTER TABLE 语句时,可以使用 LOCK 选项来控制锁的行为:
-
LOCK=NONE:不使用任何锁,允许并发 DML 和 DDL 操作(仅适用于某些特定操作)
-
LOCK=SHARED:使用共享锁,允许并发 DML 操作,但不允许并发 DDL 操作
-
LOCK=EXCLUSIVE:使用排他锁,不允许任何并发 DML 或 DDL 操作(默认值)(42)。
支持的 INSTANT 操作:
MySQL 8.0 支持以下 INSTANT 操作,这些操作可以在不锁定表的情况下执行:
-
添加列
-
删除列
-
重命名列
-
修改列默认值(42)。
示例 DDL 语句:
\-- 使用INSTANT算法添加列
ALTER TABLE orders ADD COLUMN discount DECIMAL(10,2) DEFAULT 0, ALGORITHM=INSTANT;
\-- 使用SHARED锁进行INPLACE操作
ALTER TABLE orders ADD INDEX idx\_order\_date(order\_date), ALGORITHM=INPLACE, LOCK=SHARED;
避免在事务中执行 DDL 操作:
DDL 操作会自动获取 MDL 写锁,并且这个锁会一直持有到事务结束。因此,应避免在事务中执行 DDL 操作,除非必要(42)。
低峰期执行 DDL 操作:
对于不支持 INSTANT 或 INPLACE 的 DDL 操作,应在业务低峰期执行,减少对业务的影响(42)。
5.2.4 备份与恢复场景的锁优化
数据库备份与恢复是数据库管理中的重要操作,以下是针对备份与恢复场景的锁优化策略:
使用热备份工具:
对于 InnoDB 存储引擎,应使用热备份工具(如 Percona XtraBackup)进行备份,而不是使用 FLUSH TABLES WITH READ LOCK(FTWRL)等全局锁。热备份工具利用 InnoDB 的事务日志和多版本并发控制(MVCC)机制,能够在不锁定整个实例的情况下实现数据的一致性备份(1)。
备份锁的使用:
MySQL 8.0 引入了实例级备份锁(LOCK INSTANCE FOR BACKUP)和新的 MDL 锁(LOCK TABLES FOR BACKUP),支持在不影响 DML 操作的情况下阻塞 DDL 和非事务表更新。可以使用这些锁来提高备份过程的并发性和性能(42)。
事务一致性备份:
对于支持事务的存储引擎(如 InnoDB),可以使用事务一致性备份方法,如 mysqldump 的 --single-transaction 选项。该选项会启动一个事务,获取一致性视图,确保备份过程中数据的一致性,同时允许其他事务继续执行更新操作(1)。
分库分表备份:
对于大型数据库,可以考虑分库分表备份,每次备份一部分数据,减少锁的持有时间和范围(1)。
备份策略优化:
-
对于静态数据(很少更新的数据),可以增加备份频率,减少恢复时间
-
对于动态数据(频繁更新的数据),可以减少备份频率,增加增量备份的使用
-
结合全量备份和增量备份,平衡备份时间和恢复时间(1)。
5.3 基于锁监控的性能优化方法
5.3.1 锁监控工具与技术
有效的锁监控是优化锁机制的前提,以下是一些常用的锁监控工具和技术:
SHOW ENGINE INNODB STATUS 命令:
该命令可以显示 InnoDB 存储引擎的状态信息,包括锁的持有和等待情况、死锁信息等。在输出结果中,LATEST DETECTED DEADLOCK 部分包含了最近一次死锁的详细信息(12)。
performance_schema 中的锁相关表:
-
data_locks 表:提供了当前持有和等待的锁的详细信息
-
data_lock_waits 表:提供了锁等待关系的详细信息
-
metadata_locks 表:提供了 MDL 锁的详细信息(12)。
sys schema 中的锁相关视图:
-
sys.innodb_lock_waits 视图:简化了锁等待信息的查询,提供了更直观的锁等待关系
-
sys.schema_table_lock_waits 视图:提供了表级锁等待的详细信息
-
sys.schema_object_lock_waits 视图:提供了数据库对象锁等待的详细信息(12)。
慢查询日志:
开启慢查询日志(slow_query_log=1),设置 long_query_time=1,可以捕获执行时间较长的 SQL 语句,这些语句可能持有锁的时间也较长,是锁优化的重点对象(12)。
InnoDB 状态监控:
可以通过以下状态变量监控 InnoDB 的锁状态:
-
Innodb_row_lock_current_waits:当前正在等待的行锁数量
-
Innodb_row_lock_time:等待行锁的总时间
-
Innodb_row_lock_time_avg:平均等待行锁的时间
-
Innodb_row_lock_time_max:最长等待行锁的时间
-
Innodb_row_lock_waits:等待行锁的总次数(12)。
5.3.2 锁性能分析与瓶颈识别
通过锁监控获取数据后,需要对数据进行分析,识别锁性能瓶颈。以下是一些锁性能分析的方法:
锁等待时间分析:
通过分析 Innodb_row_lock_time 和 Innodb_row_lock_time_avg 等状态变量,可以了解行锁等待的严重程度。如果这些值持续增长,说明存在严重的锁竞争问题(12)。
锁等待频率分析:
通过分析 Innodb_row_lock_waits 状态变量,可以了解锁等待的频率。如果锁等待频繁发生,说明数据库的并发性能可能受到影响(12)。
锁等待最长时间分析:
通过分析 Innodb_row_lock_time_max 状态变量,可以了解最长的锁等待时间。如果这个值很大,说明存在长时间的锁等待,可能导致应用响应变慢(12)。
锁等待分布分析:
通过分析不同表、不同索引、不同 SQL 语句的锁等待情况,可以找出锁等待的热点区域,进而针对性地优化(12)。
死锁分析:
通过分析死锁日志和死锁信息,可以了解死锁的原因和规律,进而优化应用和数据库设计,减少死锁的发生(13)。
5.3.3 锁优化的具体实施步骤
基于锁监控和分析的结果,可以采取以下步骤优化锁机制:
步骤一:确定优化目标:
根据锁监控和分析的结果,确定需要优化的锁相关问题,如高锁等待时间、频繁死锁、长时间锁持有等(13)。
步骤二:制定优化策略:
根据优化目标,制定相应的优化策略,如调整事务隔离级别、优化索引、缩短事务等(13)。
步骤三:实施优化措施:
按照优化策略,实施具体的优化措施。在实施过程中,应注意以下几点:
-
一次只实施一项优化措施,便于评估效果
-
对重要的优化措施进行测试,确保不会引入新的问题
-
记录优化过程和结果,便于后续分析和回顾(13)。
步骤四:监控优化效果:
实施优化措施后,应继续监控锁的状态和性能指标,评估优化效果。如果优化效果不明显,应重新分析问题,调整优化策略(13)。
步骤五:持续优化:
锁优化是一个持续的过程,应根据业务负载的变化和系统性能的变化,不断调整优化策略,确保系统性能始终处于最佳状态(13)。
5.4 MySQL 8.0 锁机制的优化建议
MySQL 8.0 在锁机制方面进行了多项改进,以下是针对 MySQL 8.0 的锁优化建议:
充分利用新的 MDL 锁功能:
-
使用新的 MDL 锁模式(如 SHARED_READ_ONLY)提高并发性能
-
利用实例级备份锁(LOCK INSTANCE FOR BACKUP)和新的 MDL 锁(LOCK TABLES FOR BACKUP)提高备份的并发性
-
合理设置 lock_wait_timeout 参数,控制 MDL 锁的等待超时时间,默认值为 31536000 秒(约 1 年)可能过长,建议根据实际情况调整(42)。
优化在线 DDL 操作:
-
尽可能使用 INSTANT 算法执行在线 DDL 操作,如添加 / 删除列、重命名列等
-
根据业务需求选择合适的 LOCK 选项,如 LOCK=NONE、LOCK=SHARED、LOCK=EXCLUSIVE
-
在非高峰期执行可能影响性能的 DDL 操作(42)。
使用 performance_schema 监控锁:
-
使用 data_locks 表监控当前的锁持有和等待情况
-
使用 data_lock_waits 表监控锁等待关系
-
使用 sys.innodb_lock_waits 视图简化锁等待信息的查询(12)。
优化死锁处理:
-
开启 innodb_print_all_deadlocks 参数,记录所有死锁信息
-
分析死锁日志,找出死锁的原因和规律
-
在高并发场景下,可以考虑禁用死锁检测(innodb_deadlock_detect=OFF),转而依赖 innodb_lock_wait_timeout 参数来处理死锁,但需要谨慎使用(57)。
索引优化:
-
确保查询语句使用合适的索引,避免全表扫描
-
定期分析索引使用情况,删除不再使用的索引
-
对于频繁更新的表,平衡索引数量和更新性能(13)。
事务优化:
-
保持事务简短,减少锁持有时间
-
避免在事务中执行耗时操作
-
按固定顺序访问资源,避免交叉锁
-
使用较低的事务隔离级别(如 READ COMMITTED)减少锁的使用(13)。
六、总结与展望
6.1 锁机制在数据库系统中的地位与作用
锁机制是数据库系统中协调多个会话对共享资源并发访问的基本机制,它在保证数据一致性和完整性的同时,尽可能提高数据库的并发性能(31)。锁机制的核心作用包括:
保证事务的隔离性:
通过加锁机制,数据库可以实现不同的事务隔离级别,确保事务之间互不干扰,避免脏读、不可重复读和幻读等问题(13)。
实现数据的一致性:
锁机制确保在多个事务并发执行时,数据的一致性得到维护。例如,在转账事务中,锁机制确保转出账户和转入账户的余额变化是原子性的,不会出现数据不一致的情况(13)。
协调资源访问:
锁机制通过协调多个事务对共享资源的访问,避免资源竞争和冲突,确保系统的稳定性和可靠性(31)。
支持高并发访问:
通过合理设计锁机制,数据库可以支持高并发访问,提高系统的吞吐量和响应速度。例如,InnoDB 的行锁机制允许在同一时间对不同行数据进行并发修改,大大提高了并发性能(1)。
6.2 三个版本锁机制的演进与改进
从 MySQL 5.6 到 5.7 再到 8.0,锁机制经历了一系列重要的演进与改进:
MySQL 5.6:
-
基础的锁机制,包括全局锁、表级锁和行锁
-
基本的死锁检测机制,只能查看最近一次死锁的信息
-
基本的 MDL 锁支持,确保 DML 和 DDL 操作的一致性(22)。
MySQL 5.7:
-
引入共享排他锁(SX 锁),解决在线 DDL(SMO)带来的问题
-
支持记录所有死锁信息(通过 innodb_print_all_deadlocks 参数)
-
支持禁用死锁检测(通过 innodb_deadlock_detect 参数)
-
改进了锁升级策略,减少不必要的锁升级(6)。
MySQL 8.0:
-
重大增强 MDL 锁功能,引入新的锁模式和备份锁
-
改进在线 DDL 的锁机制,支持更多类型的在线 DDL 操作
-
增强锁监控和诊断工具,提供更多与锁相关的 performance_schema 表和视图
-
移除了不再推荐使用的锁相关功能(如 INFORMATION_SCHEMA.INNODB_LOCKS 表)(42)。
6.3 锁机制优化的未来发展方向
随着数据库技术的不断发展,锁机制优化也面临着新的挑战和机遇。以下是锁机制优化的未来发展方向:
更智能的死锁检测与处理:
未来的死锁检测算法可能会更加智能,能够预测死锁的发生并提前预防,而不仅仅是在死锁发生后进行处理。例如,基于机器学习的死锁预测和预防技术可能会被应用到数据库系统中(70)。
更细粒度的锁机制:
未来的锁机制可能会支持更细粒度的锁定,如页级锁、列级锁等,进一步提高并发性能。例如,PostgreSQL 数据库已经支持列级锁,这可能是 MySQL 未来的一个发展方向(70)。
无锁数据结构:
随着硬件性能的提高和算法的发展,无锁数据结构可能会在数据库系统中得到更广泛的应用,从而避免锁竞争和死锁问题。例如,一些内存数据库已经开始采用无锁数据结构提高性能(70)。
分布式锁管理:
随着分布式数据库的普及,分布式锁管理将成为一个重要的研究方向。如何在分布式环境中高效地管理锁,确保数据的一致性和可用性,将是未来的研究重点(70)。
自适应锁机制:
未来的锁机制可能会根据系统负载和业务模式自动调整锁策略,实现自适应优化。例如,根据并发访问模式自动调整锁的粒度、锁的持有时间等参数,以获得最佳的性能(70)。
6.4 锁机制在数据库发展中的趋势
随着数据库技术的不断演进,锁机制在数据库发展中呈现出以下趋势:
从悲观锁向乐观锁转变:
乐观锁因其无需显式加锁、减少锁竞争等优势,在高并发场景下越来越受到青睐。未来,乐观锁可能会在更多的业务场景中替代悲观锁,尤其是在对数据一致性要求不是特别高的场景(13)。
从表级锁向行级锁转变:
随着硬件性能的提高和应用需求的变化,行级锁因其细粒度和高并发性能优势,逐渐成为主流的锁机制。未来,更多的存储引擎可能会支持行级锁,提高数据库的并发性能(1)。
从集中式锁管理向分布式锁管理转变:
随着分布式数据库的发展,锁管理也将从集中式向分布式转变。分布式锁管理需要解决分布式环境中的一致性、可用性和性能问题,是一个具有挑战性的研究方向(70)。
从手动锁管理向自动锁管理转变:
未来的数据库系统可能会提供更智能的自动锁管理功能,减少人工干预的需求。例如,数据库可以根据查询语句的特点和业务模式自动选择合适的锁策略,无需用户显式指定(70)。
从单一锁模式向混合锁模式转变:
未来的数据库系统可能会支持多种锁模式的混合使用,根据不同的业务场景和数据访问模式选择最合适的锁策略。例如,在同一个事务中,可以根据不同的操作自动选择共享锁、排他锁、乐观锁等不同的锁模式(70)。
七、附录:MySQL 锁机制常用命令与技巧
7.1 锁状态查看命令
以下是查看 MySQL 锁状态的常用命令:
查看 InnoDB 引擎状态:
SHOW ENGINE INNODB STATUS\G;
该命令可以显示 InnoDB 存储引擎的状态信息,包括锁的持有和等待情况、死锁信息等。在输出结果中,LATEST DETECTED DEADLOCK 部分包含了最近一次死锁的详细信息(12)。
查看当前锁等待情况:
在 MySQL 8.0 中,可以使用以下语句查看当前的锁等待情况:
SELECT 
  r.trx\_id AS waiting\_trx\_id,
  r.trx\_mysql\_thread\_id AS waiting\_thread,
  r.trx\_query AS waiting\_sql,
  b.trx\_id AS blocking\_trx\_id,
  b.trx\_mysql\_thread\_id AS blocking\_thread,
  b.trx\_query AS blocking\_sql
FROM performance\_schema.data\_lock\_waits w
JOIN information\_schema.innodb\_trx b ON b.trx\_id = w.blocking\_engine\_transaction\_id
JOIN information\_schema.innodb\_trx r ON r.trx\_id = w.requesting\_engine\_transaction\_id;
该语句可以显示等待事务和阻塞事务的详细信息,包括事务 ID、线程 ID、执行的 SQL 语句等(12)。
使用 sys.innodb_lock_waits 视图:
在 MySQL 8.0 中,可以使用 sys.innodb_lock_waits 视图简化锁等待信息的查询:
SELECT \* FROM sys.innodb\_lock\_waits;
该视图整合了锁等待的关键信息,包括阻塞线程 ID、等待时间、SQL 语句等(12)。
查看 MDL 锁状态:
可以使用以下语句查看元数据锁(MDL)的状态:
SELECT 
  OBJECT\_SCHEMA,
  OBJECT\_NAME,
  LOCK\_TYPE,
  LOCK\_STATUS,
  OWNER\_THREAD\_ID
FROM performance\_schema.metadata\_locks
WHERE OBJECT\_NAME = '目标表名';
该语句可以显示指定表的 MDL 锁持有和等待情况(12)。
查看行锁状态:
可以使用以下语句查看行锁的持有和等待情况:
SELECT 
  ENGINE\_LOCK\_ID,
  OBJECT\_SCHEMA,
  OBJECT\_NAME,
  LOCK\_TYPE,
  LOCK\_MODE,
  LOCK\_STATUS,
  THREAD\_ID,
  LOCK\_DATA
FROM performance\_schema.data\_locks
WHERE ENGINE = 'INNODB';
该语句可以显示 InnoDB 行锁的详细信息(12)。
7.2 锁操作与控制命令
以下是控制 MySQL 锁的常用命令:
手动加锁:
- 共享锁(S 锁):
SELECT ... LOCK IN SHARE MODE;
该语句用于手动获取共享锁,允许多个事务同时读取数据,但阻止其他事务获取排他锁(25)。
- 排他锁(X 锁):
SELECT ... FOR UPDATE;
该语句用于手动获取排他锁,阻止其他事务获取共享锁或排他锁(25)。
手动释放锁:
- 提交事务:
COMMIT;
提交事务会释放该事务持有的所有锁(1)。
- 回滚事务:
ROLLBACK;
回滚事务也会释放该事务持有的所有锁(1)。
锁定表:
- 读锁(共享锁):
LOCK TABLES table\_name READ;
该命令用于对表施加读锁,允许其他事务读取表数据,但阻止其他事务写入表数据(1)。
- 写锁(排他锁):
LOCK TABLES table\_name WRITE;
该命令用于对表施加写锁,阻止其他事务读取或写入表数据(1)。
- 释放表锁:
UNLOCK TABLES;
该命令用于释放通过 LOCK TABLES 命令获取的表锁(1)。
全局锁:
- 施加全局锁:
FLUSH TABLES WITH READ LOCK;
该命令用于施加全局锁,将整个数据库实例设置为只读状态(1)。
- 释放全局锁:
UNLOCK TABLES;
该命令用于释放全局锁,恢复数据库的读写功能(1)。
7.3 死锁处理与调试技巧
以下是处理和调试 MySQL 死锁的常用技巧:
查看死锁信息:
使用 SHOW ENGINE INNODB STATUS 命令查看最近一次死锁的信息:
SHOW ENGINE INNODB STATUS\G;
在输出结果中,LATEST DETECTED DEADLOCK 部分包含了死锁的详细信息,如死锁发生的时间、涉及的事务、持有的锁和等待的锁等(12)。
开启详细死锁日志:
通过设置 innodb_print_all_deadlocks 参数,可以将所有死锁信息记录到错误日志中:
SET GLOBAL innodb\_print\_all\_deadlocks = ON;
该设置会在当前会话中生效,但数据库重启后会恢复默认值。要永久开启,需要在配置文件中设置 innodb_print_all_deadlocks=1(14)。
分析死锁日志:
死锁日志中包含了死锁发生的详细信息,包括:
-
死锁发生的时间
-
涉及的事务 ID
-
每个事务执行的 SQL 语句
-
持有的锁和等待的锁
-
死锁的类型和原因
通过分析这些信息,可以找出死锁的原因和规律,进而采取相应的优化措施(14)。
死锁重试机制:
在应用层实现死锁重试机制,捕获死锁异常后自动重试:
// Java示例代码
try {
  // 执行数据库操作
} catch (SQLException e) {
  if (e.getErrorCode() == 1213) { // MySQL死锁错误码
  // 等待一段时间后重试
  Thread.sleep(100);
  // 重新执行数据库操作
  }
}
禁用死锁检测:
在高并发场景下,如果应用能够确保死锁极少发生,可以考虑禁用死锁检测:
SET GLOBAL innodb\_deadlock\_detect = OFF;
禁用死锁检测后,事务将依赖 innodb_lock_wait_timeout 参数来处理死锁(57)。
7.4 锁机制相关配置参数
以下是与 MySQL 锁机制相关的重要配置参数:
死锁检测相关参数:
-
innodb_deadlock_detect:
-
作用:控制是否启用死锁检测
-
取值:ON(启用,默认值)或 OFF(禁用)
-
说明:在高并发场景下,如果应用能够确保死锁极少发生,可以考虑禁用死锁检测(57)。
-
-
innodb_print_all_deadlocks:
-
作用:控制是否将所有死锁信息记录到错误日志中
-
取值:1(启用)或 0(禁用,默认值)
-
说明:启用后,所有死锁信息都会被记录到错误日志中,而不仅仅是最近一次死锁的信息(14)。
-
锁等待超时相关参数:
-
innodb_lock_wait_timeout:
-
作用:控制事务等待行锁的超时时间
-
取值:整数,单位为秒,默认值为 50
-
说明:如果一个事务在等待行锁时超过了这个时间,就会被回滚,并返回错误(57)。
-
-
lock_wait_timeout:
-
作用:控制 MDL 锁的等待超时时间
-
取值:整数,单位为秒,默认值在 MySQL 5.x 中为 50,在 MySQL 8.0 中为 31536000(约 1 年)
-
说明:在 MySQL 8.0 中,该参数的默认值可能过长,建议根据实际情况调整(40)。
-
事务隔离级别相关参数:
-
transaction_isolation:
-
作用:控制事务隔离级别
-
取值:READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ(默认值)、SERIALIZABLE
-
说明:不同的隔离级别会影响锁的使用和持有时间,应根据业务需求选择合适的隔离级别(13)。
-
锁升级相关参数:
-
innodb_max_undo_log_size:
-
作用:控制 undo 日志的最大大小
-
取值:整数,单位为字节,默认值为 1073741824(1GB)
-
说明:该参数间接影响锁升级的行为,当 undo 日志超过这个大小时,可能会触发锁升级(32)。
-
在线 DDL 相关参数:
-
innodb_online_alter_log_max_size:
-
作用:控制在线 DDL 操作使用的日志文件最大大小
-
取值:整数,单位为字节,默认值为 134217728(128MB)
-
说明:该参数影响在线 DDL 操作的性能和锁机制(42)。
-
总结
本文全面解析了 MySQL 的锁机制,涵盖了 5.6、5.7 和 8.0 三个版本的锁类型、实现原理、应用场景、版本差异以及优化策略。通过本文的学习,读者可以深入理解 MySQL 锁机制的核心概念和工作原理,掌握死锁的检测和处理方法,以及实施锁机制优化的最佳实践。
锁机制是数据库系统中协调多个会话对共享资源并发访问的基本机制,它在保证数据一致性的同时,尽可能提高数据库的并发性能。MySQL 提供了多种锁类型,包括全局锁、表级锁、行级锁等,每种锁类型都有其特定的应用场景和优化策略。
随着 MySQL 版本的演进,锁机制也在不断改进和完善。MySQL 8.0 在锁机制方面进行了多项重大改进,如增强的 MDL 锁、新的备份锁机制、更完善的锁监控工具等,这些改进显著提高了数据库的性能和可靠性。
在优化锁机制时,应遵循一致性与并发性能平衡、锁粒度与性能权衡、锁持有时间与性能关系等基本原则,根据不同的业务场景选择合适的锁策略。同时,通过有效的锁监控和分析,可以及时发现和解决锁相关的性能问题,确保数据库系统始终处于最佳运行状态。
锁机制是数据库系统的核心组成部分,深入理解和掌握锁机制对于设计高效、可靠的数据库应用至关重要。通过不断学习和实践,读者可以在实际工作中灵活运用锁机制,解决各种复杂的数据库性能问题,提高系统的稳定性和可扩展性。
参考资料
[1] MySQL 锁机制深度解析:原理、场景、排查与优化-优快云博客 https://blog.youkuaiyun.com/u013127325/article/details/152721590
[2] mysql哪些场景下会出现锁升级_mob64ca12e04e7a的技术博客_51CTO博客 https://blog.51cto.com/u_16213364/13742552
[3] mysql 的select语句会加锁么_mob64ca12ee66e3的技术博客_51CTO博客 https://blog.51cto.com/u_16213423/13705315
[4] InnoDB中的锁_innodb引擎中的锁-优快云博客 https://blog.youkuaiyun.com/u014033218/article/details/76234473
[5] Mysql 5.7 InnoDB 锁机制_nk锁是加在索引上吗-优快云博客 https://blog.youkuaiyun.com/TBWood/article/details/79004523
[6] MySQL锁机制祥解-优快云博客 https://blog.youkuaiyun.com/qq_34478339/article/details/151968314
[7] MYSQL各种锁_区间锁-优快云博客 https://blog.youkuaiyun.com/TYUTyansheng/article/details/108174360
[8] What are the differences in locking the statement lock table… read between MySQL5.6 and MySQL5.7 https://www.shulou.com/a567
[9] 22.6.4 Partitioning and Locking https://dev.mysql.com/doc/refman/5.7/en/partitioning-limitations-locking.html
[10] 5.6.9.1 The Locking Service https://dev.mysql.com/doc/refman/8.1/en/locking-service.html
[11] MySQL Data Locks: Mapping 8.0 to 5.7 https://hackmysql.com/mysql-data-locks-mapping-80-to-57/
[12] mysql5.7&8.0锁表确认及解除锁表完全指南 https://blog.youkuaiyun.com/u012210662/article/details/149255804
[13] MySQL数据库死锁全解析:发生场景、定位方法、解决方案及最佳实践_mysql中出现死锁的情况-优快云博客 https://blog.youkuaiyun.com/lihaiming_2008/article/details/148633369
[14] 彻底搞懂MySQL死锁_mysql deadlock-优快云博客 https://blog.youkuaiyun.com/AlbenXie/article/details/118613349
[15] Mysql 加锁机制与死锁分析_mysql加锁分析-优快云博客 https://blog.youkuaiyun.com/nihao2q/article/details/146494469
[16] MySQL锁机制全面解析:从原理到实践的死锁防治指南-优快云博客 https://blog.youkuaiyun.com/vvilkim/article/details/147821875
[17] 6.4 Partitioning and Locking https://dev.mysql.com/doc/mysql-partitioning-excerpt/5.7/en/partitioning-limitations-locking.html
[18] 10.12.1 The metadata_locks Table https://dev.mysql.com/doc/mysql-perfschema-excerpt/5.7/en/performance-schema-metadata-locks-table.html
[19] Mysql 8.0 Metadata Lock Insights https://www.restack.io/p/mysql-8-0-knowledge-metadata-lock-answer-cat-ai
[20] MySQL: multiple user level locks per connection https://planet.mysql.com/entry/?id=35480
[21] MySQL存储引擎锁机制深度解析与高并发优化实践-优快云博客 https://blog.youkuaiyun.com/weixin_42434700/article/details/145922199
[22] mysql5.7从锁表吗,MySQL5.7 锁详解-优快云博客 https://blog.youkuaiyun.com/weixin_31301881/article/details/116110804
[23] mysql MATCH 粒度_lingjuli的技术博客_51CTO博客 https://blog.51cto.com/u_39029/12426309
[24] MySQL数据库 中的锁_数据库加锁防止操作同一个数据-优快云博客 https://blog.youkuaiyun.com/Amsssssssssss/article/details/144991979
[25] MySQL进阶突击系列(06)MySQL有几种锁?| 别背答案,现场演示一下-阿里云开发者社区 https://developer.aliyun.com/article/1649940
[26] MySQL数据库锁+优化 - 51Testing软件测试网-软件测试人的精神家园(pdf) http://download.51testing.com/ddimg/uploadsoft/20231031/MySQL%E6%95%B0%E6%8D%AE%E5%BA%93%E9%94%81+%E4%BC%98%E5%8C%96.pdf
[27] MySQL05 - 事务之锁机制_事务锁机制-优快云博客 https://blog.youkuaiyun.com/qq_43350524/article/details/145403783
[28] Mysql 锁机制_mysql乐观锁实现方式有几种-优快云博客 https://blog.youkuaiyun.com/m0_51111980/article/details/127286349
[29] mysql——锁的不同角度分类说明 https://blog.youkuaiyun.com/wpc2018/article/details/123555800
[30] mysql【四、锁】 https://blog.youkuaiyun.com/qq_30769437/article/details/140319400
[31] 数据库面试必备:MySQL中的锁类型详解_mysql有哪几种锁-优快云博客 https://blog.youkuaiyun.com/qq_58299462/article/details/147061809
[32] MySQL怎么获取锁_lemon的技术博客_51CTO博客 https://blog.51cto.com/u_14691/12752125
[33] 14.7.1 InnoDB Locking https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html
[34] 24.4.14 The INFORMATION_SCHEMA INNODB_LOCKS Table https://dev.mysql.com/doc/refman/5.7/en/information-schema-innodb-locks-table.html
[35] Percona XtraBackup and MySQL 5.7 Queries in Waiting for Table Flush State https://www.percona.com/blog/percona-xtrabackup-and-mysql-5-7-queries-in-waiting-for-table-flush-state/
[39] MySQL实现并发控制_mysql并发-优快云博客 https://blog.youkuaiyun.com/qq_48717633/article/details/142259563
[40] mysql8 设置lock_wait_timeout_mob64ca12dc54c5的技术博客_51CTO博客 https://blog.51cto.com/u_16213348/13631823
[41] mysql8 有行锁一直不释放_mob64ca13f70606的技术博客_51CTO博客 https://blog.51cto.com/u_16213578/11916497
[42] MySQL中,使用XtraBackup进行备份锁_xtrabackup备份会锁表吗-优快云博客 https://blog.youkuaiyun.com/hezuijiudexiaobai/article/details/143502861
[44] mysql5.7 In-Place升级_MySQL5.6到5.7版本升级采用IN-PLACE的升级方式需要具体关注的地方…-优快云博客 https://blog.youkuaiyun.com/weixin_39630498/article/details/113686539
[45] MySQL实战45讲 从原理到实战,丁奇带你搞懂 MySQL(pdf) https://wizard4wu.github.io/document/%E6%9E%81%E5%AE%A2%E6%97%B6%E9%97%B4%E6%96%87%E7%AB%A0/MySQL%E5%AE%9E%E6%88%9845%E8%AE%B2/19_%E4%B8%BA%E4%BB%80%E4%B9%88%E6%88%91%E5%8F%AA%E6%9F%A5%E4%B8%80%E8%A1%8C%E7%9A%84%E8%AF%AD%E5%8F%A5%EF%BC%8C%E4%B9%9F%E6%89%A7%E8%A1%8C%E8%BF%99%E4%B9%88%E6%85%A2%EF%BC%9F.pdf
[46] mysql 共享锁和独占锁的区别_mob649e816a77bf的技术博客_51CTO博客 https://blog.51cto.com/u_16175524/13782436
[47] [MySQL5.7] Innodb的索引锁优化-阿里云开发者社区 https://developer.aliyun.com/article/41087
[48] mysql5.6-8.0升级_mysql5.6升级到8.0-优快云博客 https://blog.youkuaiyun.com/qq_60891094/article/details/136462999
[49] MySQL锁升级与死锁预防指南:优化并发控制的专家建议 - 优快云文库 https://wenku.youkuaiyun.com/column/tcroqbvd94
[50] MySQL 5.6 metadata lock 源码解读-优快云博客 https://blog.youkuaiyun.com/cizhu86618/article/details/100291638
[51] 17.7.5.2 Deadlock Detection https://dev.mysql.com/doc/refman/8.0/en/innodb-deadlock-detection.html
[52] 一文弄懂如何发现 MySQL 死锁?_51CTO博客_mysql 死锁检测 https://blog.51cto.com/u_15077536/13326095
[53] 17.7.5.3 How to Minimize and Handle Deadlocks https://dev.mysql.com/doc/refman/8.0/en/innodb-deadlocks-handling.html
[54] 7.9.3 The LOCK_ORDER Tool https://oracle.hydrogen.sagittarius.connect.product.adaptavist.com/cd/E17952_01/mysql-8.0-en/lock-order-tool.html
[55] How Does MySQL Implement Concurrency Control? (Part II) https://www.alibabacloud.com/blog/601785
[56] 查询mysql LATEST DETECTED DEADLOCK_mob649e81607bf3的技术博客_51CTO博客 https://blog.51cto.com/u_16175484/13445022
[57] Moving from MySQL 5.7 to MySQL 8.0 – What You Should Know https://severalnines.com/blog/moving-mysql-57-mysql-80-what-you-should-know/
[58] 15.7.5.2 Deadlock Detection https://dev.mysql.com/doc/refman/8.1/en/innodb-deadlock-detection.html
[59] 【Mysql】Mysql8存储引擎优化与锁和事务管理优化_mysql8的引擎优化-优快云博客 https://blog.youkuaiyun.com/benshu_001/article/details/138134761
[60] 17.8.8 Configuring Spin Lock Polling https://dev.mysql.com/doc/refman/8.0/en/innodb-performance-spin_lock_polling.html
[61] 10.11.1 Internal Locking Methods https://dev.mysql.com/doc/refman//8.0/en/internal-locking.html
[62] WL#10314: InnoDB: Lock-sys optimization: sharded lock_sys mutex https://dev.mysql.com/worklog/task/?id=10314
[63] 15.7.5.3 How to Minimize and Handle Deadlocks https://dev.mysql.com/doc/refman/8.2/en/innodb-deadlocks-handling.html
[64] 17.12.2 Online DDL Performance and Concurrency https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-performance.html
[65] 14.7.5.2 Deadlock Detection https://dev.mysql.com/doc/refman/5.7/en/innodb-deadlock-detection.html
[66] MySQL · 引擎新特性 · 可开关的InnoDB死锁检测-阿里云开发者社区 https://m.aliyun.com/yunqi/articles/60941/
[67] MySQL 5.7 Reference Manual https://dev.mysql.com/doc/search/?q=innodb_lock_wait_timeout&d=12&p=1
[68] 14.7.5.3 How to Minimize and Handle Deadlocks https://dev.mysql.com/doc/refman/5.7/en/innodb-deadlocks-handling.html
[69] 7.9.3 The LOCK_ORDER Tool http://docs.oracle.com/cd/E17952_01/mysql-8.0-en/lock-order-tool.html
[70] Demystifying MySQL Deadlocks: Debunking Misconceptions and Optimization Strategies http://www.linkedin.com/pulse/3-steps-minimize-deadlocks-mysql-aftab-khan
[71] Mysql什么情况下行级锁会升级为表锁_mysql行锁升级为表锁-优快云博客 https://blog.youkuaiyun.com/qq_33764173/article/details/149228586
[72] MySQL 8.0 Reference Manual https://dev.mysql.com/doc/search/?d=201&p=1&q=lock_wait_timeout
[73] 17.8.8 Configuring Spin Lock Polling http://docs.oracle.com/cd/E17952_01/mysql-8.0-en/innodb-performance-spin_lock_polling.html
[74] WL#3262: Add mutex lock order checking to the server https://dev.mysql.com/worklog/task/?id=3262
[75] 8.11.2 Table Locking Issues https://dev.mysql.com/doc/refman/8.1/en/table-locking.html
[76] WL#11417: DevAPI : Support locking modes: NOWAIT and SKIP LOCKED https://dev.mysql.com/worklog/task/?id=11417
[77] mysql会自动检测死锁吗 - 优快云文库 https://wenku.youkuaiyun.com/answer/6ju33kikh6
[78] 8.11.1 Internal Locking Methods https://dev.mysql.com/doc/refman/8.2/en/internal-locking.html
[79] 15.8.8 Configuring Spin Lock Polling https://dev.mysql.com/doc/refman/8.1/en/innodb-performance-spin_lock_polling.html
[80] 15.7.5.3 How to Minimize and Handle Deadlocks https://dev.mysql.com/doc/refman/8.1/en/innodb-deadlocks-handling.html
[81] 10.11.2 Table Locking Issues https://dev.mysql.com/doc/refman/8.3/en/table-locking.html
[82] 15.12.2 Online DDL Performance and Concurrency https://dev.mysql.com/doc/refman/8.1/en/innodb-online-ddl-performance.html
(注:文档部分内容可能由 AI 生成)
4585

被折叠的 条评论
为什么被折叠?



