update没走索引会锁整张表,因innodb需全表扫描并为每行加x锁;无索引、函数/类型转换致索引失效;myisam只有表锁,不升级但阻塞严重;查innodb_trx和锁视图可定位锁问题。

为什么 UPDATE 没走索引会直接锁整张表?
不是 MySQL 故意“升级”成表锁,而是 InnoDB 在找不到有效索引时,根本没法精准定位行,只能全表扫描——扫描过程中,它会为**每一条遍历到的记录**都加上 X 锁。结果就是:逻辑上你只想改 1 行,物理上锁了上万行,其他事务一碰这张表就卡住。
-
user_id字段没建索引?UPDATE users SET status=1 WHERE user_id=123就会扫全表 - 即使有索引,但用了函数或隐式类型转换(比如
WHERE user_id = '123'而user_id是INT),索引也会失效 - MyISAM 确实不支持行锁,但它压根没“锁升级”这回事——它只有表锁,每次写都是天然阻塞全表
死锁日志里看到 WAITING FOR THIS LOCK TO BE GRANTED 怎么快速定位?
别从头读日志。先盯住这一行后面紧跟着的 SQL 和锁类型,再反查那条语句是否命中索引、是否用了范围条件(比如 >、BETWEEN)、有没有 FOR UPDATE 或 LOCK IN SHARE MODE。
- 如果显示
gap lock或next-key lock,基本可以确定是 RR 隔离级别下间隙锁惹的祸,尤其发生在插入或范围更新时 - 如果锁类型是
X但对象是supremum pseudo-record,说明在往索引末尾插数据,被别的事务的间隙锁挡住了 - 用
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX对照看事务执行时间,长事务大概率是“锁持有者”,不是“等待者”
MyISAM 真的能避免死锁?但它为什么更危险?
MyISAM 不会出现死锁,因为它的表锁是串行化的:第二个写请求必须等第一个彻底完成才能开始,不存在“互相卡住”的闭环。但这不等于安全——它只是把死锁转化成了大面积阻塞。
- 一个慢
UPDATE执行 5 秒,这 5 秒内所有对该表的读写(包括SELECT)全被堵死 -
table_locks_waited状态值持续上涨,说明表锁争用严重,这时换成 InnoDB + 合理索引,往往比坚持 MyISAM 更治本 - MyISAM 没事务、没崩溃恢复,一旦写中断,表可能直接损坏,这种“无锁”代价其实更高
怎么验证一行 UPDATE 到底锁了哪些记录?
不能只看执行计划,得进数据库现场抓。最直接的办法是开两个会话,在事务里执行目标语句后,立刻查锁视图。
- 会话 A:
START TRANSACTION; UPDATE orders SET amount=99 WHERE order_no='ORD-001';(不提交) - 会话 B:
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;和SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS; - 如果
INNODB_LOCKS里出现多条记录,说明语句锁了不止一行——大概率是没走索引,或用了范围条件
间隙锁和 next-key 锁不会显式出现在 INNODB_LOCKS 中,它们只在死锁日志或 SHOW ENGINE INNODB STATUS 的锁信息块里体现,这点容易漏掉。











