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

来源:这里教程网 时间:2026-02-28 20:52:56 作者:

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

在 MySQL 默认的

REPEATABLE READ
隔离级别下,「不可重复读」本不该发生——但很多人在测试时却看到了:同一事务中两次
SELECT
同一条记录,第二次读到了其他事务已
COMMIT
的修改值。这通常不是隔离级别失效,而是因为用了
SELECT ... FOR UPDATE
SELECT ... 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 真的能彻底避免不可重复读吗

能,但有前提:必须显式开启事务(

BEGIN
START TRANSACTION
),且不能中途
COMMIT
ROLLBACK
后再查——否则新
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(不锁间隙),所以幻读风险上升——但「不可重复读」和「幻读」是两个问题,别混为一谈。

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

相关推荐