行级锁仅在走索引时生效,否则退化为表锁;InnoDB基于索引记录加锁,未命中索引则全表扫描并加意向锁,等效表锁;MyISAM始终表锁,DDL操作也需表锁;锁策略选择取决于引擎与SQL是否走索引。

行级锁只锁索引行,没走索引就退化成表锁
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)会关闭,可能导致幻读——这不是锁失效,而是设计取舍










