在 MySQL 中,幻读的定义是什么? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
MySQL 5.5 Community Server
MySQL 5.6 Community Server
Percona Configuration Wizard
XtraBackup 搭建主从复制
Great Sites on MySQL
Percona
MySQL Performance Blog
Severalnines
推荐管理工具
Sequel Pro
phpMyAdmin
推荐书目
MySQL Cookbook
MySQL 相关项目
MariaDB
Drizzle
参考文档
http://mysql-python.sourceforge.net/MySQLdb.html
fantastM
V2EX    MySQL

在 MySQL 中,幻读的定义是什么?

  •  
  •   fantastM 2020-12-09 17:17:44 +08:00 3714 次点击
    这是一个创建于 1846 天前的主题,其中的信息可能已经有所发展或是发生改变。

    一直没太完全明白幻读的定义,自己在 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 锁锁定)。

      image

    • 在 REPEATABLE READ 中,事务可以锁定这段范围内的记录和区间,因此其它事务不能 UPDATE 、DELETE 该范围内的记录,也不能在这段范围的区间内 INSERT 新的数据。

      image

    在 REPEATABLE READ 级别中的 SELECT 语句默认是 一致性非锁定读取,只会读取 MVCC 快照中的数据,不会读取到其它事务中的数据(不论是 尚未 提交或者是 已经 提交的)。但是,其它事务对数据库中数据的操作又是真实存在的,因此 REPEATABLE READ 级别的隔离性和数据库中数据的一致性是存在冲突的。这个时候 REPEATABLE READ 级别中的 SELECT 语句读取的快照数据不是最新的(解决这个问题的话,就是使用 锁定读取 或者升级隔离级别至 SERIALIZABLE ),可以将这个情况理解为此时 SELECT 语句读取了一个幻影数据,这就是所谓的幻读吗?

    image

    9 条回复    2020-12-10 18:02:11 +08:00
    xuanbg
        1
    xuanbg  
       2020-12-09 17:45:34 +08:00
    在事务隔离的情况下,读到的数据不是期望的数据。
    wps353
        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 是插不进去的。
    wps353
        3
    wps353  
       2020-12-09 17:59:44 +08:00
    @wps353 应该是 insert into user values(2)
    Takamine
        4
    Takamine  
       2020-12-09 18:28:28 +08:00 via Android
    我觉得是因为当前事务只是锁了当前所有符合的数据行当做快照,进行基于这个快照的处理,但是挡不住新增。
    csl1995
        5
    csl1995  
       2020-12-09 18:59:04 +08:00 via iPhone
    幻读:同一个事务中,两次相同的查询取到的数据记录数不同。
    不可重复读:同一事务中,两次相同的查询取到的数据记录不同。

    幻读可以看作是一种特殊的不可重复读,幻读是数据数目增减,不可重复读是数据结果变化。

    mysql 通过 MVCC(读快照)可以完全避免幻读的问题(5.7 版本可以,其他版本没验证过。规避幻读最主要的方式)
    也可以通过加 next-key(空位置不允许插入)的方式避免幻读(比较少用)
    littlewing
        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.
    lewis89
        7
    lewis89  
       2020-12-09 19:58:33 +08:00
    @wps353 #2 不是间隙锁吗
    wps353
        8
    wps353  
       2020-12-10 09:35:02 +08:00
    @lewis89 next-key 是 gap lock 和 record lock 的结合体。
    dV9zZM1wROuBT16X
        9
    dV9zZM1wROuBT16X  
       2020-12-10 18:02:11 +08:00
    看 mysql 的定义,前后两次读返回的数据不一致就叫做幻读
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5640 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 43ms UTC 03:32 PVG 11:32 LAX 19:32 JFK 22:32
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86