一、按锁的粒度划分

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级别下彻底解决幻读。

内容准确性说明

  1. InnoDB 的行锁基于索引实现,若查询未命中索引,会退化为表锁(需特别注意)。
  2. 乐观锁并非数据库原生锁,而是通过业务逻辑(版本号)实现的锁机制。
  3. 临键锁仅在REPEATABLE READ隔离级别下生效,READ COMMITTED级别会关闭间隙锁(可能出现幻读)。
  4. MyISAM 不支持行锁和事务,仅支持表锁,适用于读多写少的场景。

如果您觉得这篇文章对您有帮助,请点赞关注,我会持续分享更多实用的技术文章。如有任何问题,欢迎在评论区留言讨论。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐