InnoDB行锁仅在查询命中索引时生效,否则退化为表锁;MyISAM无行锁能力,所有写操作均锁全表。验证需分别用事务(InnoDB)或LOCK TABLES(MyISAM)测试阻塞行为。

InnoDB行锁只在命中索引时生效
InnoDB的行级锁不是无条件启用的,它依赖于查询是否能通过索引快速定位到具体行。如果WHERE条件没走索引(比如username LIKE "%test%"、字段无索引、或类型隐式转换导致索引失效),InnoDB会退化为锁整张表——这时行为就和MyISAM接近了。
常见错误现象:明明写了UPDATE t SET status=1 WHERE id=123,但其他事务更新id=456也被阻塞,说明id列没有索引,或者被定义为VARCHAR却用数字比较触发了全表扫描。
- 检查执行计划:
EXPLAIN SELECT * FROM t WHERE id = 123,确认type是const或ref,且key列显示用了哪个索引 - 主键和唯一索引最稳妥;普通索引需确保
WHERE条件是等值查询且无函数包裹 -
SELECT ... FOR UPDATE同样受此限制,不是加了语句就自动行锁
MyISAM写操作默认锁全表,且不可规避
MyISAM没有事务,也没有行锁能力。任何UPDATE、DELETE或INSERT语句执行时,都会对整张表加写锁,期间其他写操作必须排队,读操作(SELECT)虽然允许,但也会被阻塞直到写锁释放(除非启用了concurrent_insert且满足尾部插入条件)。
容易踩的坑:误以为“并发插入”等于并发写——实际上它仅指在表末尾追加新记录时,允许INSERT与SELECT同时进行,一旦涉及UPDATE或DELETE,表就彻底被锁死。
- 无法通过加索引改善锁粒度,因为MyISAM根本不按行管理锁
-
LOCK TABLES t WRITE只是显式暴露了它本来就在做的事,不是额外手段 - 即使只更新一行,
SHOW PROCESSLIST里也会看到其他写请求状态为Locked
锁冲突表现完全不同,验证方式也得分开
验证InnoDB是否真正在行锁,必须用两个客户端会话,且都开启事务:START TRANSACTION → UPDATE某行 → 不COMMIT → 另一会话尝试UPDATE同一行(应阻塞)和不同行(应成功)。而MyISAM没法靠事务维持锁,得用LOCK TABLES t WRITE手动锁住,再开另一会话去UPDATE——否则写完立刻释放,根本观察不到阻塞。
性能影响差异明显:InnoDB行锁下,10个用户同时更新不同订单,基本互不干扰;MyISAM下,第2个用户就得等第1个写完,QPS随并发数上升反而下降。
- InnoDB阻塞时,等待方报错通常是
Lock wait timeout exceeded - MyISAM阻塞时,等待方状态卡在
Locked,超时由lock_wait_timeout控制,默认一年 - 别用
SELECT ... FOR UPDATE测试MyISAM——它不支持该语法,直接报错
引擎选错会导致锁问题被误判为SQL或应用层问题
很多线上“慢更新”“请求堆积”问题,根源其实是表用了MyISAM引擎,但排查时只盯着慢查询日志和连接数,忽略了SHOW CREATE TABLE t里写着ENGINE=MyISAM。InnoDB虽支持表锁(如LOCK TABLES ... WRITE),但日常DML自动走行锁;MyISAM则从设计上就没给行锁留接口。
真正关键的区别不在“能不能锁行”,而在“锁的范围是否由查询条件决定”。InnoDB把锁粒度交给了索引和执行计划,MyISAM则完全无视查询内容,一律锁表。


















