锁由事务生命周期控制,语句执行完不立即释放:RC/RC下语句级释放行锁(间隙锁除外),RR下持续至事务结束;显式加锁和自动提交单DML亦遵循此规则。
MySQL 中事务和锁是强绑定关系:**锁由事务持有,也由事务生命周期控制释放时机**。不是语句执行完就放锁,也不是事务一开启就全加锁——关键看隔离级别和具体语句。
READ COMMITTED 和 READ UNCOMMITTED 下,InnoDB 通常在语句执行完就释放行锁(非唯一条件扫描可能保留间隙锁);REPEATABLE READ(InnoDB 默认)下,行锁会持续到事务结束(COMMIT 或 ROLLBACK),包括 next-key 锁(行锁 + 间隙锁),这是可重复读不出现幻读的底层保障;SELECT ... LOCK IN SHARE MODE 和 SELECT ... FOR UPDATE 显式加锁,锁一定撑到事务结束;autocommit=1)下,单条 DML 本质是独立事务,锁在语句执行完即释放——这点极易被忽略,导致误以为“没加事务就安全”,其实照样阻塞别人。常见错觉:“我写了 START TRANSACTION; UPDATE ...; ,肯定锁住了”。但实际是否上锁、锁什么、锁多宽,取决于 WHERE 条件是否命中索引、是否唯一、是否走范围扫描。
status=1)→ 不仅锁匹配的行,还会锁索引间隙(gap lock),防止幻读;id=100)→ 只加 record lock(精确行锁),不锁间隙;WHERE id=999 但该 ID 不存在),用于防止其他事务插入该位置,这点常被当成“无操作就不锁”而踩坑。SELECT ... FOR UPDATE 是典型的悲观锁语句,它不只是“查”,而是立刻申请排他锁(X lock)。卡住的本质是锁等待队列阻塞,不是 SQL 慢。
innodb_lock_wait_timeout=50 秒)或被唤醒;Deadlock found when trying to get lock);SELECT ... LOCK IN SHARE MODE 替代(如果后续只是读+校验)、必要时加重试逻辑;FOR UPDATE 在 READ COMMITTED 下只锁命中的行,但在 REPEATABLE READ 下还锁间隙,影响面更大。很多人以为“先 SELECT 再 UPDATE 就安全”,其实锁策略完全由最终执行的语句决定,SELECT 本身在默认隔离级别下不加锁(快照读),除非显式加锁。
SELECT(无 FOR UPDATE / LOCK IN SHARE MODE)→ InnoDB 使用 MVCC,读的是事
务开始时的快照,不加任何锁(非阻塞读);REPEATABLE READ 下,先 SELECT ... FOR UPDATE 查一行,再 UPDATE 同一行 → 第二个 UPDATE 不会重复加锁,而是复用已持有的 X 锁;version 字段或 SELECT ... FOR UPDATE 一起查);FOR UPDATE,只要事务里有 DML,InnoDB 就已在后台悄悄加锁;而看似无害的普通 SELECT,一旦脱离 MVCC 快照语义(比如用了 SELECT ... FOR UPDATE),就会立刻暴露并发冲突。