mysql锁机制是什么_mysql行锁与表锁区别

10次阅读

行锁和表锁由存储引擎、sql类型及索引命中情况自动决定:InnoDB默认行锁但无索引时退化为表锁,MyISAM全程表锁;可通过EXPLaiN、SHOW ENGINE INNODB STATUS等验证实际锁行为。

mysql锁机制是什么_mysql行锁与表锁区别

行锁和表锁不是“选哪个”,而是由引擎+语句+索引自动决定的

mysql 本身不直接提供“手动切换行锁/表锁”的开关。锁类型由存储引擎、SQL 类型、是否命中索引共同触发:InnoDB 默认走行锁,但 UPDATEdelete 没走索引时会退化为表锁;MyISAM 则全程只用表锁,连 select 都会自动加读锁。

  • 查当前是否用了行锁?看执行计划:EXPLAIN SELECT ... for UPDATEkey 字段非 NULLrows 较小,大概率是行锁;若 typeALLkeyNULL,极可能升级为表锁
  • InnoDB 下,即使写了 WHERE id = ?,如果 id 列没建索引,照样全表扫描 → 表锁
  • MyISAM 不支持事务,也不支持 SELECT ... FOR UPDATE,强行写会报错:Error 1036 (HY000): table 'xxx' is read only

什么时候会意外拿到表锁?重点盯这三类操作

很多线上慢查询或阻塞,其实源于本想锁几行,结果锁了整张表——尤其在迁移 MyISAM 表到 InnoDB 后忽略索引设计时。

  • UPDATE/DELETE 语句中 WHERE 条件未命中任何索引(包括隐式类型转换,如 WHERE phone = 13800138000phoneVARCHAR
  • 对大字段(如 TEXTBLOB)做 ORDER BYGROUP BY,触发 filesort + 临时表,InnoDB 可能放弃行锁优化
  • 显式使用 LOCK TABLES t1 WRITE —— 这是强制表锁,哪怕你用的是 InnoDB,也会绕过 MVCC 直接上表级排他锁

如何验证当前锁行为?别只看文档,用真实命令看

光靠理论容易误判。InnoDB 提供了几个关键视图,比“应该加什么锁”更可信的是“实际加了什么锁”。

  • 查当前锁等待:运行 SHOW ENGINE INNODB STATUSG,重点关注 TRANSACTIONSLOCK WAIT 部分,能看到谁在等哪一行、被谁堵住
  • 查最近死锁日志:同样在 SHOW ENGINE INNODB STATUSG 输出末尾的 LATEST DETECTED DEADLOCK 区域,包含完整 SQL 和锁模式(X 表示排他锁,S 表示共享锁)
  • 监控表锁争用:执行 SHOW GLOBAL STATUS LIKE 'table_locks%';,若 table_locks_waited 持续增长,说明存在 MyISAM 表或 InnoDB 被迫升表锁的高频场景

行锁不是万能解药:它带来并发,也埋下死锁风险

行锁提升并发能力,但代价是锁管理更复杂。InnoDB 的行锁基于索引记录,而两个事务若以不同顺序访问相同几行,就极易触发死锁。

  • 典型死锁链:T1 先更新 id=1 再更新 id=2T2 反过来先更新 id=2 再更新 id=1 → 双方互相等待
  • 避免方式:所有业务逻辑中,对多行更新务必按**主键/索引顺序**统一访问(比如总是 ORDER BY id ASC),让加锁顺序一致
  • 别依赖 SELECT ... FOR UPDATE 做“乐观重试”:它本质是悲观锁,一旦加锁失败(超时或死锁回滚),必须重试整个事务,而不是仅重试某条语句

行锁和表锁的边界从来不是非黑即白——真正影响系统的,往往是那条没走索引的 UPDATE、那个忘记加 COMMIT 的事务、或是开发时以为“只是读一下”的 SELECT 却在 RR 隔离级别下触发了间隙锁。锁机制本身很稳,出问题的永远是人对它的假设。

text=ZqhQzanResources