【数据库锁机制】—全局锁、表锁、行锁、悲观锁、乐观锁、共享锁、排他锁、意向锁、记录锁、间隙锁、临键锁
·
一、按锁的粒度划分
1. 全局锁
- 定义:锁定整个数据库实例,所有表都无法进行写入操作(读操作可正常执行)。
- 核心作用:保证全库数据的一致性,避免备份过程中数据被修改。
- 适用场景:全库逻辑备份(如使用mysqldump时),确保备份数据是某一时刻的完整快照。
命令:'FTWRL'
FLUSH TABLE WITH READ LOCK; -- 加全局锁 UNLOCK TABLES; -- 释放全局锁
- 注意:加锁期间,UPDATE/DELETE/INSERT等写操作会被阻塞,谨慎使用
2.表锁
- 定义:锁定整张表,同一时刻只有一个事务能对表执行写操作。
- 特点:
- 开销小(无需逐行判断锁状态),但并发能力极低(读写操作相互阻塞)。
- 类似 “一个房间只能进一个人,其他人必须排队”。
- 支持引擎:MyISAM(默认表锁)、InnoDB(也支持表锁,但通常用行锁)。
LOCK TABLES table_name READ; -- (自己可以读,别人也可读,禁止写) LOCK TABLES table_name WRITE; -- 加表级写锁(自己可以读写,别人读写都禁止) UNLOCK TABLES; -- 释放表锁
- 使用场景:全表数据迁移、极少更新的静态表操作
3.行锁
- 定义:仅锁定表中某一行或多行数据,不影响其他行的操作
- 特点
- 并发性能高(多事务科同时操作不同行),但开销较大(需维护每行锁状态)
- 可能差生死锁(如事务A锁行1等待行2,事务B锁行2等待行1)
如果你拿着我的钥匙,你拿着我的钥匙 ——> 死锁
- 支持引擎:InnoDB(默认行锁,基于索引实现)
- 示例
- 当两个事物分别更新同一张表的不同行时,互不阻塞
-- 事务1 BEGIN; UPDATE user SET name='Alice' WHERE id = 1; -- 锁定id=1的行 -- 事务2(同时执行) BEGIN; UPDATE users set name='Bob' WHERE id=2; -- 锁定id=2的行(正常执行不阻塞)
二、按冲突策略划分
1.悲观锁:“世界充满危险,先锁上再说”
- 核心思想:假设冲突一定会发生,操作前先加锁,确保数据独占
- 使用场景:写操作频繁,并发冲突高的场景(如秒杀、强库存、转账)
- MySQL实现:通过SELECT ... FOR UPDATE加排他锁,阻止其他事务修改数据
BEGIN; -- 查询并锁定库存记录,防止其他事务同时修改 SELECT stock FROM products WHERE id=10 for update; -- 扣减库存(此时其他事务无法直接修改该记录) UPDATE products SET stock=stock-1 WHERE id=10; COMMIT; -- 提交后释放锁
2.乐观锁:“世界很美好,冲突只是偶然”
- 核心思想:假设冲突很少发生,操作时不加锁,提交时验证数据是否被修改
- 使用场景:读操作频繁、写操作少的场景(如编辑文章,更新商品描述)
- MySQL实现:通过版本号(version)或时间戳(timestamp)验证数据一致性
示例(编辑文章):
-- 1.查询文章时获取当前版本号 SELECT content, version FROM articles WHERE id=5; -- 假设version=3 -- 2.编辑后提交,仅当版本号为变时更新 UPDATE articles SET content='新内容',version=version+1 WHERE id=5 AND version=3; -- 若version已被其他事务修改(如变为4),则更新失败 -- 3.判断影响行数,若为0则说明冲突,需重试
三、按读写权限划分
1.共享锁(S锁/读锁)
- 规则:“我读的时候你也可以读,但不能写”(读读共享,读写互斥)
- 作用:多个事务可同时读取同一行,但若有事务加了共享锁,其他事务不能加2排他锁(防止读取时数据被修改)
- MySQL命令:'LOCK IN SHARE MODE'
BEGIN; -- 加共享锁,允许其他事务读,但禁止写 SELECT * FROM orders WHERE id=10 LOCK IN SHARE MODE; COMMIT; -- 提交后释放锁
2.排他锁(X锁/写锁)
- 规则:“我写的时候,谁都别动”
- 作用:事务独占一行数据,其他事物既不能读(加共享锁)也不能写(加排他锁)
- MySQL命令:
BEGIN; -- 加排他锁,禁止其他事务读写 SELECT * FROM orders WHERE id=10 FOR UPDATE; UPDATE orders SET status='paid' WHERE id=10; -- 修改数据 COMMIT; -- 提交后释放锁
四、辅助性锁:意向锁
- 定义:表级锁,用于标识“某事务即将对表中的行加共享锁或排他锁”,是行锁的“预告”
- 作用:优化表锁与行锁的交换频率。例如:当事务想要加表级写锁时,无需检查每行是否有行锁,只需判断表上是否有意向锁即可
- 自动机制:InnoDB会在事务获取行锁时,自动为表加上对应的意向锁(无需手动操作)
- 分类:
- 意向共享锁(IS):事务即将对行加共享锁(S锁)
- 意向排他锁(IX):事务即将对行加排他锁(X锁)
- 兼容性:意向锁之间不互斥(IS与IX可共存),但意向锁与表级锁互斥(如IX与表级写锁互斥)

五、行锁细分(InnoDB解决幻读的核心)
1.记录锁(Record Lock)
- 定义:仅锁定单行索引记录,精准锁定特定行
- 触发条件:通过唯一索引(主键、唯一键)进行等值查询时
- 示例:
BEGIN; -- 锁定id=10的行(唯一索引等值查询) SELECT * FROM users WHERE id=10 FOR UPDATE; -- 其他事务无法修改id=10的行,但可修改id=11,12等行 COMMIT;
2.间隙锁(Gap Lock)
- 定义:锁定索引之间的“间隙”(开区间),不锁定记录本身,防止其他事务在间隙中插入新数据
- 触发条件:在非唯一索引或范围查询时触发(如WHERE id > 5 AND id < 10)。
- 示例:
表users中已有id=5、10的记录,执行以下语句
BEGIN; -- 锁定(5,10)的区间,防止插入id=6、7等数据 SELECT * FROM users WHERE id BETWEEN 5 AND 10 FOR UPDATE; -- 其他事务插入id=7会被阻塞 COMMIT;
3.临键锁
- 定义:记录锁 + 间隙锁的组合,锁定左开右闭的区间(如 (5,10]),是 InnoDB 默认的行锁模式。
- 核心作用:在REPEATABLE READ隔离级别下,防止幻读(即事务两次查询结果不一致,出现新增的 “幻影行”)。
- 示例:表users中已有 id=5、10 的记录,执行以下语句:
注:如果写成WHERE id <=10 AND id >5,锁定范围仍是(5,10](和WHERE id <=10完全一致),但数据库原本会自动判断的左边界,左边界由已有记录id=5自动填充
BEGIN; -- 锁定(5,10]的区间(包含id=10的记录锁+(5,10)的间隙锁)) SELECT * FROM users WHERE id<=10 FOR UPDATE; -- 其他事务无法修改id=10的行,也无法插入id=6、7等数据 COMMIT;
六、MVCC 与锁的关系(为何需要锁?)
- MVCC(多版本并发控制):通过保存数据的历史版本,让事务读取数据时无需加锁(快照读),保证 “读不阻塞写,写不阻塞读”,但只能解决 “读时看到幻影” 的问题(防读)。
- 锁(如临键锁):通过锁定间隙和记录,阻止其他事务插入或修改数据,解决 “写时产生幻影” 的问题(防写)。
- 结论:MVCC 与锁配合,才能在REPEATABLE READ级别下彻底解决幻读。
内容准确性说明
- InnoDB 的行锁基于索引实现,若查询未命中索引,会退化为表锁(需特别注意)。
- 乐观锁并非数据库原生锁,而是通过业务逻辑(版本号)实现的锁机制。
- 临键锁仅在REPEATABLE READ隔离级别下生效,READ COMMITTED级别会关闭间隙锁(可能出现幻读)。
- MyISAM 不支持行锁和事务,仅支持表锁,适用于读多写少的场景。
如果您觉得这篇文章对您有帮助,请点赞关注,我会持续分享更多实用的技术文章。如有任何问题,欢迎在评论区留言讨论。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)