mysql中的事务隔离级别与执行顺序的关系

9次阅读

事务隔离级别决定的是“能看到什么”,而非执行顺序;它通过MVCC快照(read view)控制select可见的数据版本,而执行顺序由锁机制、语句类型及加锁时机决定。

mysql中的事务隔离级别与执行顺序的关系

事务隔离级别决定的是「能看到什么」,不是「执行顺序」

mysql 的事务隔离级别(READ UNCOMMITTEDREAD COMMITTEDREPEATABLE READSERIALIZABLE)不控制 SQL 语句的执行先后,而是约束一个事务中 SELECT 能读到哪些版本的数据。真正影响执行顺序的是锁机制、MVCC 快照生成时机和语句是否触发加锁(比如 UPDATESELECT ... for UPDATE)。

常见误解是:设成 REPEATABLE READ 就能保证两个事务按提交顺序“串行执行”。实际不是——它们仍可并发执行,只是读视图被冻结在事务第一次 SELECT 或开启时,导致“读一致性”,而非“执行一致性”。

不同隔离级别下,同一时间点的 SELECT 结果为何不同

关键在事务开启时获取的 MVCC 快照(read view)范围不同:

  • READ UNCOMMITTED:不创建 read view,SELECT 直接读最新行版本(可能脏读)
  • READ COMMITTED:每次 SELECT 都新建 read view,只能看到已提交的、在本语句开始前已提交的事务修改
  • REPEATABLE READ:事务第一次 SELECT 时创建 read view,后续所有 SELECT 复用它(所以不会出现不可重复读)
  • SERIALIZABLE:隐式为所有 SELECT 加共享锁,强制阻塞写操作,实际变成串行执行

例如两个事务 T1 和 T2 同时运行:

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; START TRANSACTION; SELECT * FROM t WHERE id = 1; -- 此刻生成 read view -- 即使 T2 此后更新并提交 id=1 的行,T1 再 SELECT 仍看到旧值

执行顺序受锁和语句类型影响,比隔离级别更直接

即使在 REPEATABLE READ 下,UPDATESELECT ... FOR UPDATE 仍会尝试加行锁或间隙锁,导致阻塞。这时“谁先拿到锁”就决定了实际执行顺序,跟隔离级别无关。

  • INSERT 可能触发插入意向锁,与间隙锁冲突
  • UPDATE t SET x=1 WHERE y=5 在无索引列 y 上会升级为表锁
  • SELECT ... LOCK IN SHARE MODEFOR UPDATE 显式申请锁,立即参与锁竞争

典型现象:事务 A 执行 UPDATE t SET v=1 WHERE id=10 未提交,事务 B 在相同行上执行 UPDATESELECT ... FOR UPDATE 就会被挂起,直到 A 提交或回滚——这个等待链就是真实执行顺序的体现。

容易忽略的关键点:autocommit 和 START TRANSACTION 的时机

MySQL 默认 autocommit = 1,单条 DML(如 UPDATE)会自动成为独立事务。此时隔离级别只作用于该语句本身,没有“跨语句一致性”可言。只有显式 START TRANSACTIONBEGIN 后,才进入多语句事务上下文,隔离级别才有持续效果。

  • 忘记关 autocommit 就直接写多个 UPDATE,误以为它们在一个事务里
  • 在存储过程中调用 START TRANSACTION,但没处理异常回滚,导致连接长期持有锁
  • SET session TRANSACTION ISOLATION LEVEL ... 只对后续事务生效,不影响当前已开启的事务

验证当前事务状态最直接的方式是查:

SELECT @@autocommit, @@transaction_isolation, trx_state  FROM information_schema.INNODB_TRX  WHERE trx_mysql_thread_id = CONNECTION_ID();

text=ZqhQzanResources