mysql不可重复读如何解决_mysql事务隔离级别解析

1次阅读

mysql默认repeatable read下不可重复读本不该发生,实因未显式开启事务或自动提交导致每次查询为独立快照;真正不可重复读发生在read committed级别下同一事务两次查询间被其他事务修改并提交。

mysql不可重复读如何解决_mysql事务隔离级别解析

不可重复读到底是什么现象

在 MySQL 默认的 REPEATABLE READ 隔离级别下,「不可重复读」本不该发生——但很多人在测试时却看到了:同一事务中两次 select 同一条记录,第二次读到了其他事务已 COMMIT 的修改值。这通常不是隔离级别失效,而是因为用了 SELECT ... for UPDATESELECT ... LOCK IN SHARE MODE 以外的普通查询,且没开启事务(或事务已自动提交),导致每次 SELECT 都成了独立快照。

真正触发不可重复读的典型场景是:事务 A 在 READ COMMITTED 级别下执行两次相同 SELECT,中间事务 B 修改并提交了某行数据——A 第二次查就会看到新值。

怎么确认当前会话的隔离级别

直接查变量比猜更可靠:

SELECT @@tx_isolation; -- 或 SELECT @@transaction_isolation;

注意:@@tx_isolation 在 MySQL 8.0+ 已弃用,优先用 @@transaction_isolation;返回值类似 REPEATABLE-READ(全大写短横线)或 READ-COMMITTED

  • 全局默认值存在 my.cnf[mysqld] 段里,配置项是 transaction-isolation = REPEATABLE-READ
  • 会话级可临时改:SET session TRANSACTION ISOLATION LEVEL READ COMMITTED;
  • 改完立刻生效,但只影响当前连接,不影响其他已连会话

REPEATABLE READ 真的能彻底避免不可重复读吗

能,但有前提:必须显式开启事务(BEGINSTART TRANSACTION),且不能中途 COMMITROLLBACK 后再查——否则新 SELECT 就属于下一个事务,自然读新快照。

常见误操作:

  • 用 ORM(如 django/SQLAlchemy)时未控制事务边界,autocommit=True 导致每条语句自成事务
  • 在 MySQL 客户端里执行 SELECT 前忘了 BEGIN,以为“没关连接就还是同一个事务”
  • 应用层连接池复用连接,上一个请求的事务没清理干净,残留了未提交状态

验证方式:在事务内执行 SELECT 后,手动在另一终端更新该行并 COMMIT,再回原事务查——只要没退出事务,结果一定不变。

什么时候该用 READ COMMITTED 而不是 REPEATABLE READ

核心权衡点是「一致性 vs 并发性」:前者允许不可重复读但减少间隙锁、提升并发更新能力;后者靠 MVCC 快照保证一致性,但可能因间隙锁引发死锁或锁等待。

适用 READ COMMITTED 的真实场景:

  • 日志类、统计类查询,不要求两次读绝对一致,只关心最新已提交状态
  • 高并发订单更新,大量 UPDATE ... WHERE status = 'pending' 类语句,在 REPEATABLE READ 下容易因间隙锁冲突
  • 使用物理备份(如 mysqldump --single-transaction)时,若业务库本身设为 READ COMMITTED,备份一致性仍由事务快照保障,不影响

注意:READ COMMITTED 下,InnoDB 的 next-key lock 退化为 record lock(不锁间隙),所以幻读风险上升——但「不可重复读」和「幻读」是两个问题,别混为一谈。

真正容易被忽略的是:隔离级别只是基础机制,事务是否真正「跨语句生效」,取决于你有没有让多条语句落在同一个事务上下文里。很多线上问题不是隔离级别选错了,而是事务根本没建起来。

text=ZqhQzanResources