一直没太完全明白幻读的定义,自己在 MySQL 手册也没找到幻读( Phantom Reads )的官方定义,另外我在另一篇帖子里 t/692386 也看到评论里对「可重复读隔离级别下是否解决了幻读问题」的回答不尽相同。因此就发个帖,想问一个权威的答案。
先说一下我对隔离级别的一些理解,如果有误,恳请各位指出。
快照读:读取 MVCC 快照中的数据。
当前读:读取数据库中最新的数据。SELECT … FOR SHARE 语句和 SELECT … FOR UPDATE 语句属于当前读,并且还有显式加锁的语义。
在 READ UNCOMMITTED 级别中,事务可以读取到其它事务中 尚未 提交的数据,这种问题被称为脏读。
在 READ COMMITTED 级别中,事务不可以读取到其它事务中 尚未 提交的数据,但是可以读取到其它事务中 已经 提交的数据,这种问题被称为不可重复读。
在 REPEATABLE READ 级别中,事务读取的是 MVCC 快照中的数据,因此事务中的 SELECT 语句是可重复读的。
READ COMMITTED 级别会使用记录锁,不会使用间隙锁和 next-key 锁。(从 MySQL 手册 Transaction Isolation Levels 中的「 InnoDB locks only index records, not the gaps before them 」这句话得知。)
REPEATABLE READ 级别中会使用记录锁、间隙锁和 next-key 锁( next-key 锁就是间隙锁 + 记录锁)。
假设使用 SELECT … FOR UPDATE 读取一段范围内的记录。
在 READ COMMITTED 中,事务虽然可以锁定这段范围内的记录,但是无法锁定这段范围的区间,因此其它事物不能 UPDATE 、DELETE 该范围内的记录(因为这些范围内的记录已经被记录锁锁定了),但是可以在这段范围的区间内 INSERT 新的数据(因为这个范围没有被间隙锁或者 next-key 锁锁定)。
在 REPEATABLE READ 中,事务可以锁定这段范围内的记录和区间,因此其它事务不能 UPDATE 、DELETE 该范围内的记录,也不能在这段范围的区间内 INSERT 新的数据。
在 REPEATABLE READ 级别中的 SELECT 语句默认是 一致性非锁定读取,只会读取 MVCC 快照中的数据,不会读取到其它事务中的数据(不论是 尚未 提交或者是 已经 提交的)。但是,其它事务对数据库中数据的操作又是真实存在的,因此 REPEATABLE READ 级别的隔离性和数据库中数据的一致性是存在冲突的。这个时候 REPEATABLE READ 级别中的 SELECT 语句读取的快照数据不是最新的(解决这个问题的话,就是使用 锁定读取 或者升级隔离级别至 SERIALIZABLE ),可以将这个情况理解为此时 SELECT 语句读取了一个幻影数据,这就是所谓的幻读吗?
1
xuanbg 2020-12-09 17:45:34 +08:00
在事务隔离的情况下,读到的数据不是期望的数据。
|
2
wps353 2020-12-09 17:58:34 +08:00
我是这么认为的,不知道对不对。
MySQL 的幻读和 SQL 标准的幻读有所差异。 MySQL 中幻读是针对当前读. 举个例子: 在 RC 级别下,user 表 id >=1 and id <=10 的数据现在只有 id=1 一条数据。这时候事务 A 执行 SQL(delete from user where id >=1 and id <=10),未 commit ; 如果此时事务 B 执行 SQL ( insert into user values(5)) 。这时候我们再提交事务 A,你会发现 user 里面居然还有一条 id=2 的记录,这和我们事务 A 执行的结果预期不符。 那么这个 id=2 的记录就属于幻读。 所以 MySQL 在 RR 级别下用 next-key 来保证例子中的事务 B 是插不进去的。 |
4
Takamine 2020-12-09 18:28:28 +08:00 via Android
我觉得是因为当前事务只是锁了当前所有符合的数据行当做快照,进行基于这个快照的处理,但是挡不住新增。
|
5
csl1995 2020-12-09 18:59:04 +08:00 via iPhone
幻读:同一个事务中,两次相同的查询取到的数据记录数不同。
不可重复读:同一事务中,两次相同的查询取到的数据记录不同。 幻读可以看作是一种特殊的不可重复读,幻读是数据数目增减,不可重复读是数据结果变化。 mysql 通过 MVCC(读快照)可以完全避免幻读的问题(5.7 版本可以,其他版本没验证过。规避幻读最主要的方式) 也可以通过加 next-key(空位置不允许插入)的方式避免幻读(比较少用) |
6
littlewing 2020-12-09 19:39:03 +08:00 via iPhone
P3: r1[P]...w2[y in P]...((c1 or a1) [and (c2 or a2) any order)]
ref: Berenson H, Bernstein P, Gray J, et al. A critique of ANSI SQL isolation levels[C]//ACM SIGMOD Record. ACM, 1995, 24(2): 1-10. |
9
Flourite 2020-12-10 18:02:11 +08:00
看 mysql 的定义,前后两次读返回的数据不一致就叫做幻读
|