mysql数据库的事务隔离级别与并发控制

6次阅读

mysql默认隔离级别是REPEATABLE READ,通过Next-Key Locks与MVCC实现,快照读无幻读,当前读可能幻读;RC级每次快照读取最新版本,易发幻读;RU和SERIALIZABLE极少使用。

mysql数据库的事务隔离级别与并发控制

MySQL 默认隔离级别是 REPEATABLE READ,但它的实现方式和标准 SQL 不同

MySQL 的 REPEATABLE READ 并不靠锁住所有读取范围来防止幻读,而是用 Next-Key Locks(间隙锁 + 行锁)配合 MVCC 实现。这意味着:同一事务中多次 select 看到的数据快照一致,但新插入的行是否可见,取决于是否落在被锁定的间隙里。

常见误解是认为 REPEATABLE READ 能完全避免幻读 —— 实际上,在「当前读」(如 SELECT ... for UPDATEUPDATEdelete)下,间隙锁会阻止其他事务在范围内插入,从而抑制幻读;但在纯「快照读」下,新插入的行对本事务不可见,不是因为被锁住,而是因为 MVCC 只看到事务开始时已存在的版本。

  • READ COMMITTED 下每次快照读都重新获取最新已提交版本,所以不会复现「不可重复读」,但幻读更易发生(间隙锁在 RC 下只在唯一索引等少数场景启用)
  • READ UNCOMMITTED 几乎不用:可能读到未提交数据,SELECT 不加任何锁,性能高但业务逻辑极难保证
  • SERIALIZABLE 会让普通 SELECT 隐式加上 LOCK IN SHARE MODE,所有读写都串行化,开销大,通常只在极严苛审计场景才考虑

如何查看和修改当前会话或全局隔离级别

隔离级别是会话级变量,可动态调整,但需注意生效时机:修改后只影响后续语句,不影响已开启的事务。

SELECT @@transaction_isolation; -- 返回类似:REPEATABLE-READ(MySQL 8.0+)或 TRANSACTION_ISOLATION(5.7) 

SET session TRANSACTION ISOLATION LEVEL READ COMMITTED; SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 需 SUPER 权限,且新连接才生效

注意:GLOBAL 设置不会改变当前已连接会话的级别;如果应用使用连接池,改全局后旧连接仍维持原级别,必须重启连接或显式 SET SESSION

  • MySQL 5.7 使用 @@tx_isolation,8.0+ 统一为 @@transaction_isolation
  • 配置文件中设置:在 my.cnftransaction-isolation = READ-COMMITTED(注意用短横线)
  • JDBC 连接串可指定:添加 &sessionVariables=transaction_isolation='READ-COMMITTED'

SELECT ... FOR UPDATESELECT ... LOCK IN SHARE MODE 的锁行为差异

这两者都触发「当前读」,绕过 MVCC 快照,直接访问最新行并加锁,但锁类型和兼容性不同:

  • FOR UPDATE 在符合条件的记录上加 X 锁(排他锁),同时在扫描范围加 Gap Lock(除非唯一索引等效精确匹配)
  • LOCK IN SHARE MODES 锁(共享锁),允许其他事务加 S 锁,但阻塞 X 锁;同样有间隙锁行为
  • 若查询条件无索引,MySQL 会退化为全表扫描 + 全表记录加锁,极易引发锁等待甚至死锁

典型陷阱:执行 SELECT * FROM t WHERE name = 'foo' FOR UPDATE,但 name 列没有索引 → 锁住整张表所有行,而不是仅匹配行。

幻读到底在什么情况下会发生?

幻读指「同一事务中,前后两次相同范围的 SELECT 返回行数不同」。它是否发生,取决于读类型和隔离级别:

  • REPEATABLE READ 下,快照读(普通 SELECT)不会出现幻读 —— 因为始终读取事务启动时的快照
  • 但在当前读下(如 SELECT ... FOR UPDATE),如果另一个事务在间隙中插入了新行,本事务再次执行相同当前读时会看到新行 → 这就是幻读
  • READ COMMITTED 下,快照读每次取最新已提交版本,所以不仅可能幻读,还可能出现不可重复读

真正难处理的是「基于幻读做业务判断」的场景,比如先查有没有订单,再插入 —— 即使用了 SELECT ... FOR UPDATE,若没锁住对应间隙,仍可能被并发插入打破逻辑。这时候得靠唯一约束或应用层分布式锁兜底。

text=ZqhQzanResources