MySQL 锁和事务隔离怎么理解:当前读、快照读、间隙锁和死锁排查

2次阅读
没有评论

MySQL 锁机制容易被记成名词:行锁、表锁、间隙锁、next-key lock、MVCC、可重复读。真正排查问题时,更有用的是先分清两件事:这条 SQL 是当前读还是快照读,它有没有走到合适索引。

快照读和当前读

普通 select 通常是快照读。InnoDB 会通过 MVCC 读取一个一致性版本,不直接加行锁。

select * from orders where id = 1;

带锁读、更新和删除是当前读,读的是最新版本,并且需要加锁:

select * from orders where id = 1 for update;
update orders set status = 2 where id = 1;
delete from orders where id = 1;

所以不要只看“是不是 select”。select for update 和普通 select 的并发行为完全不同。

行锁依赖索引

InnoDB 的行锁锁在索引记录上。where 条件能命中索引,锁范围通常比较小;没有合适索引,可能扫描大量记录,锁范围也会变大。

例如:

update orders set status = 2 where order_no = 'A100';

如果 order_no 有唯一索引,锁定目标记录即可。如果没有索引,数据库要扫描更多记录,可能造成大量行被锁,甚至表现得像表锁。

排查锁等待时,第一步要看执行计划:

explain update orders set status = 2 where order_no = 'A100';

没有索引的更新语句,是高并发业务里的重点风险。

间隙锁解决什么问题

MySQL InnoDB 默认可重复读隔离级别下,为了避免幻读,范围查询加锁时可能产生间隙锁。

例如:

select * from orders
where amount between 100 and 200
for update;

数据库不仅可能锁住已有记录,还会锁住范围之间的空隙,阻止其他事务往这个范围插入新记录。

这对一致性有帮助,但也会降低并发。业务里如果大量使用范围条件 for update,要特别关注索引选择和事务时长。

死锁并不等于数据库坏了

死锁是两个事务互相等待对方持有的锁。InnoDB 会检测死锁,并回滚其中一个事务。应用看到死锁异常时,通常应该做有限重试,而不是认为数据库不可用。

常见死锁原因:

  • 多个事务更新相同表,但更新顺序不同。
  • 范围更新没有合适索引。
  • 批量更新时排序不稳定。
  • 事务里混入慢操作,持锁时间太长。

排查死锁看:

show engine innodb status;

重点关注 LATEST DETECTED DEADLOCK 里的两条事务、SQL、锁类型和索引名。

事务越短越好

锁问题很多时候不是锁类型选错,而是事务太大。一个事务里如果包含 RPC、HTTP、文件处理、复杂循环和人工等待,数据库连接和锁都会被长时间占用。

更稳的做法是:

  • 事务里只放必须原子提交的数据库操作。
  • 外部 IO 放到事务外,或用事务消息/任务表异步处理。
  • 批量更新先拆批,并固定排序。
  • 热点行更新考虑乐观锁、队列化或拆分计数。

最后抓住一句话

MySQL 锁排查的主线是:当前读会加锁,锁范围依赖索引,范围锁可能带来间隙锁,长事务会放大一切问题。先看 SQL 类型和执行计划,再看事务边界,通常比背锁名更有效。

正文完
 0
bdspAdmin
版权声明:本站原创文章,由 bdspAdmin 于2026-07-01发表,共计1296字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)