SELECT ... FOR UPDATE 用于先查后改场景(如扣库存),加排他锁;SELECT ... LOCK IN SHARE MODE 用于协同只读场景(如报表),加共享锁;二者均需事务、索引支持,否则退化为表锁或间隙锁。

MySQL 中 SELECT ... FOR UPDATE 和 SELECT ... LOCK IN SHARE MODE 的适用场景
这两个语句是 InnoDB 实现行级读写锁的核心手段,但它们不是“加锁就能用”,而是必须在事务中生效,且只对索引列有效。如果 WHERE 条件未命中索引,InnoDB 会退化为表锁(或间隙锁组合),导致并发骤降。
-
SELECT ... FOR UPDATE:适用于需要“先查后改”的典型场景,比如扣减库存、转账前校验余额——它会加排他锁(X 锁),阻塞其他事务的读写 -
SELECT ... LOCK IN SHARE MODE:适合多事务需协同读取同一数据但不修改的场景,比如生成报表时防止基础数据被删——它加共享锁(S 锁),允许其他事务加 S 锁,但阻塞 X 锁 - 两者都要求事务隔离级别为
READ COMMITTED或REPEATABLE READ;在READ UNCOMMITTED下会被忽略
为什么 FOR UPDATE 有时锁住整张表而不是某几行
根本原因在于锁的粒度由执行计划决定,而非 SQL 表面写法。即使写了 WHERE id = 123,若 id 列没有索引,优化器无法定位具体行,只能走全表扫描,进而对所有聚簇索引记录加 X 锁——表现就是“锁表”。
- 用
EXPLAIN检查执行计划:type字段必须是const、ref或range,不能是ALL或index - 注意隐式类型转换:比如
WHERE user_id = '123'(字段是INT),会导致索引失效,锁范围扩大 - 唯一索引和普通索引行为不同:非唯一索引可能触发间隙锁(Gap Lock),锁住不存在的值区间,造成意外阻塞
高并发下锁等待超时与死锁的实际应对
Lock wait timeout exceeded 和 Deadlock found when trying to get lock 是线上最常遇到的两类锁问题,但它们成因和处理方式完全不同。
启科网络商城系统由启科网络技术开发团队完全自主开发,使用国内最流行高效的PHP程序语言,并用小巧的MySql作为数据库服务器,并且使用Smarty引擎来分离网站程序与前端设计代码,让建立的网站可以自由制作个性化的页面。 系统使用标签作为数据调用格式,网站前台开发人员只要简单学习系统标签功能和使用方法,将标签设置在制作的HTML模板中进行对网站数据、内容、信息等的调用,即可建设出美观、个性的网站。
- 锁等待超时(
Lock wait timeout exceeded):通常是事务持有锁时间过长(比如事务里混入 HTTP 调用、日志写入或 sleep),应缩短事务生命周期,把非数据库操作移出事务块 - 死锁(
Deadlock found when trying to get lock):本质是循环等待,MySQL 会自动回滚代价小的事务。关键不是避免死锁(不可能完全避免),而是让应用能捕获errno 1213并重试——尤其对幂等性操作(如更新状态)必须支持重试逻辑 - 通过
SHOW ENGINE INNODB STATUS\G查看最近死锁详情,重点关注TRANSACTION块中的lock_mode X locks rec but not gap等描述,定位哪几行/哪个索引被争抢
替代方案:用 INSERT ... ON DUPLICATE KEY UPDATE 或 REPLACE INTO 规避显式锁
当业务逻辑本质是“存在则更新,不存在则插入”,硬上 SELECT ... FOR UPDATE + INSERT/UPDATE 不仅代码冗长,还放大锁竞争。这类场景优先考虑原子性更强的替代语法。
-
INSERT ... ON DUPLICATE KEY UPDATE:依赖唯一索引(主键或UNIQUE约束),整个操作由 InnoDB 内部加锁完成,无需手动控制事务顺序,性能更稳 -
REPLACE INTO:底层是DELETE + INSERT,会触发两次索引查找和锁操作,且可能引发自增 ID 跳变,在高并发下不如前者可控 - 注意:这两者仍会加锁,但锁的持有时间极短(仅在索引查找和行修改瞬间),且无事务上下文依赖,天然规避了长事务锁表风险
INSERT INTO order_lock (order_id, status, updated_at) VALUES (1001, 'processing', NOW()) ON DUPLICATE KEY UPDATE status = 'processing', updated_at = NOW();锁的复杂性不在语法本身,而在于它和索引、事务、隔离级别、执行路径深度耦合。一个没走索引的
FOR UPDATE,比十个没加锁的 UPDATE 更容易拖垮系统。










