事务提交后行锁立刻释放,但表锁可能延迟
MySQL 的行级锁(如
SELECT ... FOR UPDATE或
UPDATE加的锁)在
COMMIT或
ROLLBACK执行完毕后**立即释放**。这是 InnoDB 的默认行为,也是 ACID 中隔离性的基础保障。
但要注意:如果事务中包含隐式或显式的
LOCK TABLES(MyISAM 风格),这类表级锁不会随事务提交释放,必须显式执行
UNLOCK TABLES;InnoDB 一般不走这条路,除非手动切换存储引擎或误用。 使用
information_schema.INNODB_TRX和
INNODB_LOCKS(MySQL 5.6/5.7)或
performance_schema.data_locks(8.0+)可查当前持有锁 长事务会持续持锁,哪怕只读未修改——只要用了
SELECT ... FOR UPDATE或
SELECT ... LOCK IN SHARE MODE
AUTOCOMMIT=0下,一个语句不自动提交,锁会一直挂着,直到你敲
COMMIT
MySQL 语句执行顺序不是“从上到下”,而是按优化器重排 + 锁获取时机决定
你写的 SQL 顺序 ≠ 实际执行顺序。MySQL 优化器会基于索引、统计信息、成本估算重排执行计划,而加锁动作发生在执行器真正访问某行时,不是解析时。
例如:
UPDATE t1 SET a = 1 WHERE id IN (SELECT id FROM t2 WHERE status = 'pending');表面看是先查
t2再更新
t1,但实际可能是先锁定
t1匹配行(甚至全表扫描加锁),再回表查
t2过滤——取决于是否能走索引、是否启用 semi-join 优化等。
EXPLAIN FORMAT=TREE(8.0+)或
EXPLAIN ANALYZE能看到真实执行路径和锁范围
WHERE条件没走索引?很可能升级为临键锁(Next-Key Lock),锁住间隙,影响并发插入 子查询里带
FOR UPDATE?外层 UPDATE 可能被阻塞,因为子查询先持锁,且锁不会提前释放
唯一索引冲突导致的锁等待,常被误认为“提交后还卡着”
这不是锁没释放,而是下一个事务在尝试获取锁时被阻塞了。比如事务 A 插入
(id=100)并提交,事务 B 紧接着插入相同
id,B 会卡在
INSERT阶段,直到 A 提交完成并释放意向锁 —— 但 B 看到的现象是“A 提交后我还在等”,其实是 B 自己在等锁获取机会。 查
performance_schema.events_statements_current可确认 B 是否卡在
insert状态 查
sys.innodb_lock_waits(封装视图)能直接看到谁在等谁、等什么锁 用
SHOW ENGINE INNODB STATUS\G看
TRANSACTIONS部分,注意
lock struct(s)和
waiting for this lock to be granted
DDL 操作(如 ALTER TABLE)会触发元数据锁(MDL),与事务提交无关
很多人发现“刚 COMMIT 完,另一个 ALTER 就卡住了”,以为是事务锁没放干净。其实 DDL 加的是
MDL,它独立于事务生命周期,只要还有活跃事务访问该表(哪怕只是
SELECT),
ALTER就必须等所有事务结束才能获取排他 MDL。
SELECT不提交也占着 MDL 读锁,所以长查询会让 DDL 无限等待 MySQL 5.7+ 可设
lock_wait_timeout控制 DDL 等待上限,避免雪崩 线上改表务必避开业务高峰,优先用
pt-online-schema-change或 8.0 的
ALGORITHM=INSTANT(仅限部分操作) 锁释放本身很快,慢的往往是别人在等你释放之后的那一瞬间——而那一瞬间,可能已经被另一个没看清执行计划的 SQL 卡住了。
