mysql中存储引擎的行级锁与表级锁的区别

来源:这里教程网 时间:2026-02-28 20:42:24 作者:

行级锁只锁索引行,没走索引就退化成表锁

InnoDB 的行级锁不是“按数据行物理位置”锁的,而是基于索引记录(record lock)实现的。这意味着:只有 SQL 明确通过索引(主键、唯一索引、普通索引)定位到某几行时,才会真正加行锁;如果

WHERE
条件没命中任何索引(例如对无索引字段查询、或用
LIKE '%abc'
导致索引失效),InnoDB 会放弃行锁,转而对整张表加意向锁 + 全表扫描锁——效果等同于表级锁。

常见错误现象:
UPDATE users SET status=1 WHERE phone='138...' 
执行很慢且阻塞其他事务,查发现
phone
字段没建索引
实操建议:执行前用
EXPLAIN
确认是否走了索引;高频更新字段务必建索引,哪怕只是单列
注意:即使是二级索引,InnoDB 也会对索引条目加锁,并可能触发聚簇索引上的隐式锁(如 gap lock),不等于“只锁索引不锁数据”

表锁是粗粒度强制互斥,MyISAM 和 DDL 场景绕不开

表级锁分显式和隐式两种。显式如

LOCK TABLES t1 WRITE
,隐式如
ALTER TABLE
DROP TABLE
或 MyISAM 引擎下的所有写操作。它不管你在改哪一行,只要锁定了表,其他线程对这张表的读/写(取决于锁类型)就全被挡在外面。

常见错误现象:从库执行
OPTIMIZE TABLE
时,主从同步卡住,
SHOW PROCESSLIST
看到大量
Waiting for table metadata lock
实操建议:避免在业务高峰期对大表做 DDL;MyISAM 表严禁用于高并发写场景(比如订单、计数器) 关键细节:MDL(元数据锁)是自动加的,不可手动释放;
SELECT
也会持有 MDL 读锁,若长事务未提交,后续 DDL 会被死等

并发表现截然不同:行锁冲突低但怕死锁,表锁简单但一写全堵

行级锁并发度高,是因为两个事务更新不同用户的订单,互不影响;但一旦出现循环等待(比如事务 A 锁了行 100 再去锁行 200,事务 B 反过来先锁 200 再锁 100),就会触发 InnoDB 死锁检测并回滚其中一个。表锁没有死锁风险(只有一把锁),但代价是——哪怕你只更新 1 行,整张表的其他所有读写都得排队。

常见错误现象:应用日志频繁出现
Deadlock found when trying to get lock
,但业务逻辑看似无交集
实操建议:事务内更新多行时,固定顺序(如按主键 ASC);减少事务长度;监控
Innodb_row_lock_waits
等状态变量
性能影响:行锁内存开销明显更大(每行锁需维护锁结构),当单事务更新上万行时,可能触发锁升级失败或 OOM

选引擎就是选锁策略:InnoDB 行锁是默认,MyISAM 表锁是枷锁

MySQL 5.5+ 默认引擎是 InnoDB,它支持行锁、事务、外键;MyISAM 只支持表锁、不支持事务,适合纯读场景(比如配置表、日志归档表)。如果你看到某个表用了 MyISAM,那它天然不具备高并发写能力,强行加索引也救不了行锁缺失这个根本问题。

实操建议:新建表一律用
ENGINE=InnoDB
;存量 MyISAM 表迁移前,确认无长事务、无外键依赖,用
ALTER TABLE t ENGINE=InnoDB
注意兼容性:某些老备份脚本依赖 MyISAM 的
myisamchk
,转 InnoDB 后需同步调整
容易被忽略的一点:即使用了 InnoDB,如果隔离级别设为
READ UNCOMMITTED
READ COMMITTED
,间隙锁(gap lock)会关闭,可能导致幻读——这不是锁失效,而是设计取舍
行锁不是银弹,它依赖索引、受限于事务长度、对死锁敏感;表锁也不是古董,DDL 和元数据变更必须靠它兜底。真正要盯住的,从来不是“该用哪种锁”,而是“你的 SQL 走没走索引”“事务里干了什么”“有没有人正在 ALTER 这张表”。

相关推荐