不可重复读是指在一个事务内,对同一行数据进行多次读取时,得到的结果不一致的现象。根本原因是:其他事务在该事务执行期间修改并提交了这行数据,导致后续读取“看到”了已提交的变更。
不可重复读发生的典型场景
以银行账户余额查询为例:
事务A开启,第一次查询用户U的余额为1000元 事务B将用户U余额更新为1200元,并成功提交 事务A再次查询同一用户余额,结果变为1200元 两次读取结果不同,且中间无回滚——这就是不可重复读和脏读的关键区别
不可重复读读到的是已提交的数据,而脏读读到的是未提交、可能被回滚的数据:
脏读:事务B读到了事务A还没提交的修改,A若回滚,B就拿到了无效值 不可重复读:事务B读到了事务A已提交的修改,数据真实存在,但破坏了本事务内读取的一致性MySQL中如何避免不可重复读
MySQL默认隔离级别 REPEATABLE READ(可重复读) 就是专门解决这个问题的:
它基于MVCC(多版本并发控制),为事务启动时生成一个一致性视图(Read View) 整个事务期间都按这个快照读取数据,即使其他事务已提交修改,本事务仍看到原始值 因此,在RR级别下,上面例子中事务A两次查询都会返回1000元,不会出现不可重复读为什么READ COMMITTED级别无法避免
在READ COMMITTED(读已提交)隔离级别下:
每次SELECT都会新建Read View,读取最新已提交版本 所以事务A第一次查是1000,事务B提交后,事务A第二次查就会变成1200 这种“每次读都刷新快照”的行为,正是不可重复读产生的直接原因