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

10次阅读

mysql行锁在事务提交或回滚时立即释放,而非语句执行完或连接关闭时;其本质是事务粒度的锁,自动提交模式下每条DML为独立事务,关闭自动提交后未COMMIT会导致锁长期持有。

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

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

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

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

  • 显式开启事务(BEGINSTART 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"(极短暂)或已不在结果集中。

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

text=ZqhQzanResources