事物的四个隔离状态和幻读

读未提交 READ-UNCOMMITTED | 0:存在脏读,不可重复读,幻读的问题
读已提交 READ-COMMITTED | 1:解决脏读的问题,存在不可重复读,幻读的问题
可重复读 REPEATABLE-READ | 2:解决脏读,解决不可重复读的问题(使用 MMVC机制实现可重复读),但是存在幻读的问题,默认隔离级别。
序列化 SERIALIZABLE | 3:解决脏读,解决不可重复读,幻读,可保证事务安全,但完全串行执行,性能最低
重点说一下幻读
幻读错误的理解:说幻读是 事务A 执行两次 select 操作得到不同的数据集,即 select 1 得到 10 条记录,select 2 得到 11 条记录。这其实并不是幻读,这是不可重复读的一种,只会在 RU 、RC 级别下出现,而 RR 隔离级别下仅通过MVCC机制就可以避免这种现象。
这里给出我对幻读的比较白话的理解:幻读,并不是说两次读取获取的结果集不同,幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读。

幻读举例:

table users: id primary key
step1 T1: begin;SELECT * FROM users WHERE id = 1; --T1查询无记录
step2 T2: begin;INSERT INTO users VALUES (1, ‘big cat’); --T2插入id=1的记录但是不提交
step3 T1: INSERT INTO users VALUES (1, ‘big cat’); --T1插入id=1的记录发现主键冲突
step4 T1: SELECT * FROM users WHERE id = 1; --T1再次查询依然无记录

结论:T1查不到id=1的记录,但是插入一行id=1的记录却报主键冲突,这就是幻读。
其实 RR 也是可以避免幻读的,通过对 select 操作手动加 行X锁(SELECT … FOR UPDATE 这也正是 SERIALIZABLE 隔离级别下会隐式为你做的事情),同时还需要知道,即便当前记录不存在,比如 id = 1 是不存在的,当前事务也会获得一把记录锁(因为InnoDB的行锁锁定的是索引,故记录实体存在与否没关系,存在就加 行X锁,不存在就加 next-key lock间隙X锁),其他事务则无法插入此索引的记录,故杜绝了幻读。

猜你喜欢

转载自blog.csdn.net/shunnianlv/article/details/108978386