MySQL 锁机制是数据库并发控制的核心,用于保证数据的一致性和完整性。不同的存储引擎支持不同类型的锁,且锁的粒度、隔离级别和使用场景都会影响数据库的性能。以下是 MySQL 锁的详细介绍:
一、锁的分类
1. 按锁的粒度分类
-
表级锁
- 锁定整张表,开销小,加锁快;但并发度低。
- 适用于批量操作(如
ALTER TABLE
)或写入密集型场景。 - 示例:MyISAM、Memory 存储引擎默认使用表级锁。
-
行级锁
- 锁定单个行记录,开销大,加锁慢;但并发度高。
- 适用于高并发事务(如
UPDATE
、DELETE
操作)。 - 示例:InnoDB 存储引擎支持行级锁。
-
页级锁
- 锁定一页(通常 16KB),并发度介于表锁和行锁之间。
- 示例:BDB 存储引擎使用页级锁。
2. 按锁的模式分类
-
共享锁(Shared Lock,S 锁)
- 允许事务读取一行数据,多个事务可同时获取同一行的 S 锁。
- 语法 :
SELECT ... LOCK IN SHARE MODE
。
-
排他锁(Exclusive Lock,X 锁)
- 允许事务更新或删除一行数据,同一行上的 X 锁会阻塞其他事务的 S 锁和 X 锁。
- 语法 :
SELECT ... FOR UPDATE
。
3. 按锁的使用方式分类
-
自动锁
- MySQL 自动为
INSERT
、UPDATE
、DELETE
语句添加 X 锁。
- MySQL 自动为
-
显式锁
- 手动通过 SQL 语句加锁,如
SELECT ... LOCK IN SHARE MODE
或SELECT ... FOR UPDATE
。
- 手动通过 SQL 语句加锁,如
二、InnoDB 存储引擎的锁特性
InnoDB 是 MySQL 默认的事务性存储引擎,其锁机制具有以下特点:
1. 行级锁实现
- 记录锁(Record Lock):锁定单个行记录。
- 间隙锁(Gap Lock):锁定索引记录之间的 "间隙",防止幻读(Phantom Read)。
- 临键锁(Next-Key Lock) :记录锁 + 间隙锁,锁定记录及其前一个间隙。
-
示例 :
-- 假设表 t 有索引 id,值为 1, 3, 5 SELECT * FROM t WHERE id > 2 FOR UPDATE; -- 临键锁会锁定 (1, 3] 和 (3, 5] 以及 (5, +∞) 的间隙
-
2. 事务隔离级别与锁
- READ UNCOMMITTED:最低级别,允许脏读,几乎不使用锁。
- READ COMMITTED:仅锁定当前读取的记录,可能导致不可重复读。
- REPEATABLE READ(InnoDB 默认):使用临键锁,避免幻读,但可能导致死锁。
- SERIALIZABLE:最高级别,强制事务串行执行,使用表级锁。
3. 意向锁(Intention Locks)
- 表级锁,用于表明某个事务正在锁定表中的行。
- 类型 :
- 意向共享锁(IS):表示事务准备在表的行上加 S 锁。
- 意向排他锁(IX):表示事务准备在表的行上加 X 锁。
三、死锁(Deadlock)
1. 定义
- 两个或多个事务互相等待对方释放锁,导致所有事务无法继续执行。
2. 示例场景
-- 事务 1
BEGIN;
UPDATE t SET val = 1 WHERE id = 1; -- 锁定 id=1
UPDATE t SET val = 2 WHERE id = 2; -- 等待事务 2 释放 id=2 的锁
-- 事务 2
BEGIN;
UPDATE t SET val = 3 WHERE id = 2; -- 锁定 id=2
UPDATE t SET val = 4 WHERE id = 1; -- 等待事务 1 释放 id=1 的锁
3. 处理机制
- 超时机制 :当事务等待锁超过
innodb_lock_wait_timeout
(默认 50 秒)时,自动回滚。 - 死锁检测:InnoDB 自动检测死锁,回滚代价最小的事务。
四、锁的优化建议
-
选择合适的存储引擎
- 事务性操作优先选择 InnoDB(支持行级锁和事务)。
- 非事务场景可考虑 MyISAM(表级锁,写入性能高)。
-
优化查询
- 使用索引:无索引时,InnoDB 会对全表加锁。
- 缩小锁范围:避免
SELECT ... FOR UPDATE
锁定不必要的行。
-
调整事务隔离级别
- 大多数场景使用
REPEATABLE READ
(默认),读密集型场景可考虑READ COMMITTED
。
- 大多数场景使用
-
避免长事务
- 长事务持有锁时间长,增加死锁风险。
-
索引优化
- 确保
WHERE
子句中的条件字段有索引,减少锁的范围。
- 确保
-
处理死锁
- 捕获死锁异常并重试,或通过
SHOW ENGINE INNODB STATUS
分析死锁原因。
- 捕获死锁异常并重试,或通过
五、常用锁相关命令
-- 查看当前锁等待情况
SHOW ENGINE INNODB STATUS;
-- 查看当前事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;
-- 查看锁等待情况
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
-- 手动加共享锁
SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE;
-- 手动加排他锁
SELECT * FROM table_name WHERE ... FOR UPDATE;
六、总结
锁类型 | 粒度 | 并发度 | 典型场景 |
---|---|---|---|
表级锁 | 整张表 | 低 | 批量操作(ALTER TABLE) |
行级锁 | 单行 | 高 | 高并发事务(UPDATE/DELETE) |
共享锁(S) | 行 / 表 | 中 | 读取数据不允许被修改 |
排他锁(X) | 行 / 表 | 低 | 更新或删除数据 |
合理使用 MySQL 锁机制是保证数据库性能和数据一致性的关键。在实际开发中,需根据业务场景选择合适的锁粒度和隔离级别,并通过索引优化减少锁冲突。