mysql中InnoDB存储引擎的事务隔离级别

10次阅读

mysql InnoDB默认隔离级别是REPEATABLE READ,其事务级快照保证同一事务内多次select结果一致;READ COMMITTED采用语句级快照,可能导致不可重复读;SERIALIZABLE自动为SELECT加共享锁,显著降低并发性。

mysql中InnoDB存储引擎的事务隔离级别

MySQL InnoDB 默认隔离级别是 REPEATABLE READ

启动一个新连接后,执行 SELECT @@transaction_isolation;SELECT @@tx_isolation;(旧版本),返回值通常是 'REPEATABLE-READ'。这是 InnoDB 的默认设置,不是 MySQL Server 层的全局默认(Server 层默认是 REPEATABLE READ,但实际生效由引擎决定)。

READ COMMITTED 能避免不可重复读,但需注意 MVCC 行为变化

InnoDB 在 READ COMMITTED 下,每个 SELECT 语句都会创建新的快照(即“语句级快照”),而 REPEATABLE READ 是“事务级快照”——第一次 SELECT 就确定了整个事务可见的数据版本。

  • 这意味着在 READ COMMITTED 中,同一事务内两次 SELECT 可能返回不同结果(如果其他事务已提交修改)
  • UPDATE / delete 语句在 READ COMMITTED 下只看到已提交版本,不会像 REPEATABLE READ 那样基于事务初态做一致性读
  • 某些场景下,READ COMMITTED 的锁范围更小(例如非唯一条件更新时,可能不加间隙锁),但具体取决于查询是否命中索引

SERIALIZABLE 会强制将所有普通 SELECT 转为 SELECT ... LOCK IN SHARE MODE

这不是加个表锁那么简单。InnoDB 在 SERIALIZABLE 下,对没有显式加锁的 SELECT 语句自动加上共享锁(S 锁),导致并发读写阻塞明显升高。

  • 例如:事务 A 执行 SELECT * FROM t WHERE id = 10;,事务 B 同时执行 UPDATE t SET x=1 WHERE id = 10;,B 会被阻塞直到 A 提交或回滚
  • 即使查询走的是二级索引,也可能触发临键锁(next-key lock),进一步限制并发
  • 除非业务强依赖绝对顺序一致性,否则一般不建议全局启用 SERIALIZABLE

修改隔离级别要注意作用域和生效时机

隔离级别可以在会话级、全局级甚至单条语句级设置,但行为有差异:

  • 会话级:SET session TRANSACTION ISOLATION LEVEL READ COMMITTED; —— 只影响当前连接后续事务
  • 全局级:SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; —— 新建连接继承该值,已有连接不受影响
  • 事务级:START TRANSACTION WITH CONSISTENT SNAPSHOT; 不指定隔离级别,仍按当前会话设置;若要覆盖,必须在 START TRANSACTION 前用 SET TRANSACTION ISOLATION LEVEL ...
  • MySQL 8.0+ 支持在 SELECT 语句末尾加 for UPDATELOCK IN SHARE MODE,但这属于加锁语义,不改变事务隔离级别本身
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; START TRANSACTION; SELECT * FROM orders WHERE status = 'pending'; -- 使用 READ COMMITTED 快照 UPDATE orders SET status = 'processing' WHERE id = 123; COMMIT;

真正容易被忽略的是:隔离级别只约束“读可见性”和“锁行为”,它不能替代应用层的逻辑校验。比如两个事务同时读到同一行并各自更新,在 READ COMMITTED 下可能产生覆盖写,这不是隔离级别能解决的——得靠乐观锁(version 字段)或应用层重试机制。

text=ZqhQzanResources