MySQL锁有哪些类型以及如何区分

作者:袖梨 2026-08-28

InnoDB默认用行锁,但仅当WHERE条件命中索引时生效;无索引则退化为表锁;意向锁(IS/IX)是自动加的表级预告锁,不阻塞其他意向锁,但与显式表锁互斥,并指示表内存在行锁。

行锁、表锁、意向锁,到底谁在锁什么

InnoDB 默认用行锁,但不是所有 UPDATE/SELECT FOR UPDATE 都真锁一行——它只在 WHERE 条件命中索引时生效。没索引?直接退化成表锁,整张表卡住,其他事务全等。

意向锁(IS/IX)是表级“预告”,不阻塞别的意向锁,但会和真正的表锁(LOCK TABLES ... WRITE)互斥。它本身不拦人,但它是行锁存在的信号灯:看到 IX,你就知道“这张表里肯定有某几行被 X 锁了”。

  1. 查当前有没有行锁冲突?跑 SELECT * FROM sys.innodb_lock_waitsG,看 blocking_query 字段
  2. 想确认是不是表锁?SHOW OPEN TABLES WHERE In_use > 0In_use 值非 0 就说明被显式锁住了
  3. 意向锁没法手动加,它是自动的:你一执行 UPDATE t SET x=1 WHERE id=5(id 是主键),InnoDB 就先悄悄加 IX,再给那行加 X

共享锁 S 和排他锁 X 的实际表现差异

S 锁(SELECT ... LOCK IN SHARE MODE)允许多个事务同时读同一行,但谁都改不了;X 锁(UPDATEDELETESELECT ... FOR UPDATE)是独占的,别人连读带写全被挡在外面。

关键区别不在语法,而在兼容性:两个事务对同一行加 S 锁可以并存;一个加了 S,另一个想加 X?立刻阻塞;两个都加 X?也立刻阻塞。这就是锁等待超时(Lock wait timeout exceeded)的常见起点。

  1. S 锁不会阻止其他事务加 S,但会阻止 XX 锁阻止一切(SX
  2. SELECT 不加 LOCK IN SHARE MODEFOR UPDATE 时,默认走 MVCC,不加任何行级锁
  3. 在 RR 隔离级别下,X 锁实际是 Next-Key Lock(记录锁 + 间隙锁),比如 WHERE id > 10 AND id ,不仅锁住匹配的行,还锁住 (10,20) 这个区间,防幻读

死锁报错 vs 锁等待超时,一眼识别法

报错信息就是最准的诊断入口:ERROR 1213 (40001): Deadlock found when trying to get lock 是死锁;ERROR 1205 (40001): Lock wait timeout exceeded 是锁等待超时——注意错误码不同,别搞混。

死锁发生快(毫秒级)、自动回滚一个事务;锁等待默认卡 50 秒才报错,且只卡住当前事务,不影响别人。如果线上频繁出现后者,大概率是大事务没提交、或某条 SQL 没走索引导致锁范围过大。

  1. 立刻查死锁现场:SHOW ENGINE INNODB STATUSG,重点看 LATEST DETECTED DEADLOCK 段落
  2. 查当前锁等待:SELECT * FROM performance_schema.data_locksG(MySQL 8.0+)或 information_schema.INNODB_TRX + INNODB_LOCK_WAITS
  3. 别依赖 SHOW PROCESSLIST,它只显示连接状态,看不到锁关系

间隙锁和临键锁最容易被忽略的触发条件

间隙锁(Gap Lock)只在 RR 隔离级别、且 WHERE 条件是范围查询(>BETWEEN)或非唯一索引等值查询时才激活。它不锁数据行,只锁“空隙”,目的是防止幻读——但副作用是,两个事务同时执行 SELECT ... FOR UPDATE WHERE name='alice'(name 无唯一索引),可能互相锁住同一个间隙,然后 INSERT 冲突,引发死锁。

临键锁(Next-Key Lock)是 RR 下的默认行为,等于记录锁 + 左边间隙锁,区间左开右闭。比如索引值为 [1,5,10],WHERE id > 5 AND id 会锁住 (5,10),而 WHERE id = 5(主键)只锁住记录本身,不锁间隙。

  1. 唯一索引 + 精准等值查询(如 WHERE id=5)→ 只有记录锁,无间隙锁
  2. 非唯一索引 or 范围查询 → 必然带间隙锁,哪怕没数据也会锁住空隙
  3. 想关间隙锁?设隔离级别为 READ-COMMITTED,但代价是可能读到幻读

真正难的不是记住锁类型,而是理解“同一句 SQL 在不同索引、不同隔离级别、不同 WHERE 条件下,锁的类型和范围可能完全不同”。一次没走索引的 UPDATE,就能让行锁变成表锁;一个没注意的范围查询,就可能把间隙锁变成死锁导火索。

相关文章

精彩推荐