# MySQL 中有哪些锁类型?
8 min read
Table of Contents
MySQL 中有哪些锁类型?
MySQL 锁类型详解
【核心定义】
MySQL 中的锁是数据库管理系统用于协调多个会话并发访问同一数据资源,保证数据一致性和完整性的核心机制。
【关键要点】
-
按锁的粒度划分
- 表级锁:锁定整张表,开销小,加锁快,但并发度最低。MyISAM 引擎主要使用表锁。
- 行级锁:锁定数据行,开销大,加锁慢,但并发度高。InnoDB 引擎支持。
- 页级锁:锁定数据页(一组行),开销和并发度介于表锁和行锁之间。Berkeley DB 引擎使用。
-
按锁的兼容性划分
- 共享锁(S 锁):允许其他会话读取被锁资源,但禁止写入。
SELECT ... LOCK IN SHARE MODE语句会加 S 锁。 - 排他锁(X 锁):禁止其他会话对锁定的资源进行任何读写操作。
SELECT ... FOR UPDATE、UPDATE、DELETE、INSERT语句通常会加 X 锁。
- 共享锁(S 锁):允许其他会话读取被锁资源,但禁止写入。
-
按锁的实现方式划分(InnoDB 特有)
- 记录锁(Record Lock):锁定索引中的一条具体记录。
- 间隙锁(Gap Lock):锁定一个索引范围,但不包括记录本身。用于防止幻读。
- 临键锁(Next-Key Lock):记录锁和间隙锁的组合,锁定一个左开右闭的区间。是 InnoDB 默认的行锁算法。
- 插入意向锁(Insert Intention Lock):一种特殊的间隙锁,表示有事务想在某个间隙中插入新记录,多个事务只要插入位置不冲突,可以同时持有。
-
按锁的意图划分
- 意向共享锁(IS 锁):事务打算给数据行加共享锁前,必须先取得该表的 IS 锁。
- 意向排他锁(IX 锁):事务打算给数据行加排他锁前,必须先取得该表的 IX 锁。
- 作用:意向锁是表级锁,用于快速判断表中是否有行被锁定,避免为了检查每行是否有锁而遍历所有行,从而提升效率。
【深度推导/细节】
1. InnoDB 行锁的加锁规则与幻读解决
核心矛盾:在可重复读(RR)隔离级别下,如何防止幻读?
- Step 1:对于等值查询(如
WHERE id = 5),如果使用唯一索引,InnoDB 只会对id=5这条记录加记录锁。 - Step 2:对于等值查询,如果使用非唯一索引或没有命中记录,InnoDB 会对扫描到的间隙加间隙锁。例如,表中有 id=3 和 id=7 的记录,执行
SELECT * FROM t WHERE id = 5 FOR UPDATE,由于 id=5 不存在,会锁定 (3, 7) 这个间隙,防止其他事务插入 id=5 的记录。 - Step 3:对于范围查询(如
WHERE id > 5 AND id < 10),InnoDB 会使用临键锁,锁定满足条件的所有记录及其周围的间隙。这是防止幻读的关键。
2. 锁的冲突矩阵与兼容性
| 请求锁类型 | 当前持有 S | 当前持有 X | 当前持有 IS | 当前持有 IX |
|---|---|---|---|---|
| 请求 S | 兼容 | 冲突 | 兼容 | 冲突 |
| 请求 X | 冲突 | 冲突 | 冲突 | 冲突 |
| 请求 IS | 兼容 | 冲突 | 兼容 | 兼容 |
| 请求 IX | 冲突 | 冲突 | 兼容 | 兼容 |
| 表锁与行锁的意向锁通过此矩阵高效协同。 |
【关联/对比】
-
MyISAM vs InnoDB 锁机制
- MyISAM:仅支持表级锁。读操作加共享表锁,写操作加排他表锁。并发写性能差,适合读多写少场景。
- InnoDB:支持行级锁和表级意向锁。通过 MVCC 支持非锁定读,大大提升了并发性能,是现代 OLTP 应用的首选。
-
乐观锁 vs 悲观锁(应用层概念)
- 悲观锁:上述数据库锁机制即是悲观锁,认为并发冲突总会发生,故先加锁再操作。
SELECT ... FOR UPDATE是典型实现。 - 乐观锁:认为冲突不常发生,在更新时通过版本号或时间戳判断数据是否被修改。通常通过应用逻辑实现(如
UPDATE ... SET ... WHERE version = old_version)。
- 悲观锁:上述数据库锁机制即是悲观锁,认为并发冲突总会发生,故先加锁再操作。
【面试官追问】
-
什么时候行锁会升级为表锁?
- 当 SQL 语句无法有效使用索引时,InnoDB 会进行全表扫描,此时行锁会升级为表锁,严重影响并发。
- 这也是为什么强调查询必须走索引的重要原因之一。
-
如何查看 MySQL 中的锁信息?
- 使用
SHOW ENGINE INNODB STATUS\G命令,查看LATEST DETECTED DEADLOCK和TRANSACTIONS部分。 - 查询
information_schema库中的INNODB_LOCKS、INNODB_LOCK_WAITS、INNODB_TRX表(MySQL 5.7及之前)。 - MySQL 8.0+:使用
performance_schema库中的data_locks和data_lock_waits表,提供了更清晰、更强大的锁监控能力。
- 使用
-
什么是死锁?MySQL 如何检测和处理死锁?
- 死锁:两个或以上事务互相等待对方释放锁,形成循环等待。
- 处理机制:InnoDB 有死锁检测机制(默认开启)。当检测到死锁时,会选择一个回滚代价最小的事务(通常涉及行数最少的事务)进行回滚,并释放其锁,让其他事务得以继续。
-
自增锁(AUTO-INC Lock)是什么?
- 这是一种特殊的表级锁,用于在事务中为
AUTO_INCREMENT列生成连续的自增值。 - 模式:
innodb_autoinc_lock_mode = 0(传统模式):所有 INSERT 语句都会获得特殊的表级 AUTO-INC 锁,语句结束后释放,保证主从复制安全性,但并发性差。innodb_autoinc_lock_mode = 1(连续模式,默认):对于能预先确定插入行数的语句(如单条 INSERT),使用轻量级的互斥量,避免表锁;对于不确定的语句(如INSERT ... SELECT),仍使用 AUTO-INC 锁。innodb_autoinc_lock_mode = 2(交错模式):所有 INSERT 语句都使用轻量级互斥量,自增值不保证连续,只保证递增,性能最高,但可能对基于语句的复制(SBR)不安全。
- 这是一种特殊的表级锁,用于在事务中为
【版本差异与最佳实践】
- MySQL 5.7 及之前:
information_schema中的锁信息表是监控锁的主要手段。 - MySQL 8.0+:
performance_schema全面替代,提供了更精细、更强大的可观测性。 - 最佳实践:
- 尽量使用 InnoDB 引擎,并利用其行锁特性。
- 确保事务中的 SQL 语句都使用索引,避免锁升级。
- 保持事务简短,尽快提交或回滚,减少锁的持有时间。
- 在业务设计上,可以通过“尝试-回退”机制或队列来减少高并发下的锁竞争。