InnoDB的UPDATE锁整表是因为WHERE条件未命中索引,导致全表扫描并逐行加记录锁与间隙锁,等效于表锁;需用EXPLAIN确认索引使用、避免隐式转换和函数操作。

InnoDB 默认用行级锁,MyISAM 只能用表级锁——这不是“能不能”的问题,而是底层设计决定的硬约束。
为什么 InnoDB 的 UPDATE 有时会锁整张表
行级锁只在条件命中索引时生效;一旦 WHERE 没走索引(比如对无索引字段查询、类型隐式转换、函数包裹字段),InnoDB 就无法定位具体行,退化为意向锁(LOCK TABLES ... WRITE 级别),实际效果等同于表锁。
- 检查执行计划:
EXPLAIN看type是否为ALL或index,key是否为NULL - 常见诱因:
WHERE status+0 = 1、WHERE DATE(created_at) = '2025-01-01'、WHERE name LIKE '%abc'(没走索引) - 辅助索引更新可能触发额外锁:若更新涉及二级索引字段,且该索引未覆盖查询所需列,InnoDB 需回表,期间可能扩大锁范围
MyISAM 的“自动加锁”根本不是事务锁
MyISAM 没有事务概念,所谓“锁表”是语句执行期间的临时行为:每条 UPDATE/DELETE 语句开始前自动加表写锁,语句结束立即释放。它不支持 START TRANSACTION,也不响应 ROLLBACK。
-
LOCK TABLES t1 WRITE是唯一能手动延长锁期的方式,但会阻塞所有其他连接对该表的读写 - 并发插入(
CONCURRENT INSERT)仅在表尾追加时有效,且要求中间无空洞(即没执行过DELETE) - 即使只查一行:
SELECT * FROM t WHERE id = 1,MyISAM 仍需加读锁(READ LOCAL),只是允许其他INSERT并发
验证锁行为必须用两个独立 MySQL 连接
单个会话无法观察阻塞——因为锁在语句结束就释放了。真正验证要开两个终端,分别连上 MySQL,且确保它们属于不同会话 ID(SELECT CONNECTION_ID() 可查)。
- InnoDB 示例:会话 A 执行
UPDATE users SET name='x' WHERE id=1后不提交;会话 B 执行UPDATE users SET name='y' WHERE id=2能立刻返回,但WHERE id=1会卡住 - MyISAM 示例:会话 A 执行
LOCK TABLES myisam_table WRITE;会话 B 执行任何UPDATE或SELECT都会等待,直到 A 执行UNLOCK TABLES - 注意:
SHOW PROCESSLIST中状态为Waiting for table level lock(MyISAM)或Updating+State: Locked(InnoDB 行锁争用)是关键线索
锁机制差异背后是数据组织方式的根本分歧:InnoDB 的聚簇索引让“定位一行”成为可能,而 MyISAM 的非聚簇结构天然缺乏行粒度寻址能力。想绕过锁瓶颈,不能只调参数,得先看懂自己写的 SQL 到底有没有走索引。


















