mysql事务提交后锁什么时候释放_mysql执行顺序解析

来源:这里教程网 时间:2026-02-28 20:45:08 作者:

事务提交后行锁什么时候释放

MySQL 的行级锁(如

SELECT ... FOR UPDATE
UPDATE
持有的锁)在事务提交(
COMMIT
)或回滚(
ROLLBACK
)时**立即释放**,不是在语句执行完就释放,也不是等到连接关闭。

关键点在于:InnoDB 的锁是**事务粒度**的,不是语句粒度。即使你只更新一行、很快执行完,只要事务没结束,锁就一直挂着。

显式开启事务(
BEGIN
START TRANSACTION
)后,所有 DML 操作的锁都会延续到
COMMIT
自动提交模式(
autocommit=1
)下,每条 DML 本身就是一个独立事务,锁在语句返回后立刻释放
若用
SET autocommit = 0
关闭自动提交,但忘了
COMMIT
,锁会一直持有,可能造成其他事务长时间阻塞甚至超时(
Lock wait timeout exceeded

为什么有时候 COMMIT 后还看到锁没释放?

这不是锁没释放,而是你观察的时机或方式有问题。常见误判场景:

SELECT * FROM performance_schema.data_locks
查锁时,结果反映的是「当前活跃事务持有的锁」,如果事务已提交,该视图里不会出现对应记录
SHOW ENGINE INNODB STATUS
看到的
TRANSACTIONS
部分若仍有事务 ID,说明那个事务还没结束(可能卡在应用层没发
COMMIT
客户端连接未断开、事务处于空闲状态(
TRX_STATE = "RUNNING"
但没执行语句),锁依然有效

MySQL 语句执行顺序和锁获取时机

InnoDB 中,锁是在**语句实际访问到某行数据时才加上的**,不是解析 SQL 就加锁,也不是按 SQL 文本顺序逐字执行。执行流程大致如下:

SQL 解析 → 生成执行计划 → 开始执行 执行器逐行读取存储引擎返回的数据;每读到一行满足条件的记录,InnoDB 就对其加锁(比如
UPDATE t SET x=1 WHERE id=5
,只会对 id=5 这行加 X 锁)
如果是范围条件(
WHERE id > 10
),可能加间隙锁(Gap Lock)或临键锁(Next-Key Lock),影响的是索引区间,不单是命中行
全表扫描 + 条件过滤的写法(如没走索引的
WHERE status='draft'
)会导致扫描全表并为每行加锁,极易引发锁冲突

典型反例:

UPDATE orders SET paid=1 WHERE user_id=123 AND status='unpaid'
—— 如果
user_id
没索引,InnoDB 可能扫全表,锁住成百上千行,哪怕最终只改 1 行。

如何验证锁是否已释放

最直接的方式是用另一个会话尝试获取相同资源的锁,看是否被阻塞:

会话 A:
BEGIN; UPDATE t SET v=1 WHERE id=1;
(不提交)
会话 B:
SELECT * FROM t WHERE id=1 FOR UPDATE;
→ 会卡住
会话 A:
COMMIT;
→ 会话 B 立即返回,说明锁已释放

注意:不要依赖

information_schema.INNODB_TRX
中的
TRX_STARTED
时间判断“是否刚提交”,它只反映事务开始时间;真正要看的是
TRX_STATE
是否为
"COMMITTED IN MEMORY"
(极短暂)或已不在结果集中。

锁释放这件事本身没有延迟、不依赖后台线程清理,但如果你在高并发下观察到“提交后别人还是等了一两秒”,大概率是对方事务在等别的锁,或者网络/应用层延迟掩盖了真实释放时刻。

相关推荐