UPDATE单行卡住请求是因为多事务竞争同一行导致X锁排队,常见于秒杀等热点场景;提前用SELECT...FOR UPDATE加锁反而延长持锁时间,应避免事务中混入非DB操作,优先用乐观锁或Redis分布式锁解耦。
UPDATE 单行会卡住一堆请求?不是语句写错了,而是多个事务同时更新同一行(比如订单状态、库存计数器、用户积分),InnoDB 会对该行加 X 锁,后续请求只能排队等锁释放。常见于秒杀、抢券、支付回调等场景——热点行一旦出现,innodb_row_lock_waits 和 innodb_row_lock_time_avg 会明显升高。
SELECT ... FOR UPDATE 提前加锁反而更危险很多人想“提前占住”,在业务逻辑开头就执行 SELECT id FROM t WHERE id = 123 FOR UPDATE,但实际会让锁持有时间变长:只要事务没提交,锁就一直挂着。如果中间有 RPC 调用、日志写入或异常分支延迟提交,等待队列会指数级膨胀。
FOR UPDATE 后立即做 UPDATE,不要中间穿插其他逻辑WHERE 条件必须命中索引哪怕只更

WHERE id = ? 中的 id 没走主键或唯一索引,InnoDB 可能升级为间隙锁甚至表锁,导致无关行也被阻塞。用 EXPLAIN 确认执行计划是否走了索引;尤其注意隐式类型转换(比如字段是 VARCHAR,但传了数字参数)会让索引失效。
SHOW ENGINE INNODB STATUS\G 中的 TRANSACTIONS 部分,看锁等待具体卡在哪一行status、version)单独建二级索引,避免全表扫描触发锁升级WHERE 中使用函数(如 WHERE DATE(create_time) = '2025-01-01')UPDATE ... SET version = version + 1 WHERE id = ? AND version = ?
适用于冲突不频繁但锁等待代价高的场景。失败时捕获影响行数为 0,由应用重试(带退避)或降级处理。注意:version 字段必须有初始值,且所有更新路径都必须带上该条件,否则乐观锁形同虚设。
SELECT ... FOR UPDATE NOWAIT 强制快速失败SELECT ... FOR UPDATE 混用在同一事务里,会失去意义WAIT n(如 FOR UPDATE WAIT 1)限制等待时长,防止无限挂起真正难处理的不是单次锁等待,而是锁等待引发的连接池耗尽、超时链式传播、以及监控盲区——很多团队只看慢 SQL,却没盯住 innodb_row_lock_time_max 的毛刺突增。