精华内容
下载资源
问答
  • MySQL 间隙锁解决幻读问题

    千次阅读 2020-08-26 14:09:53
    间隙锁(Gap Lock)是Innodb在可重复读提交下为了解决幻读问题时引入的锁机制,(下面的所有案例没有特意强调都使用可重复读隔离级别)幻读的问题存在是因为新增或者更新操作,这时如果进行范围查询的时候(加锁查询...

    间隙锁(Gap Lock)是Innodb在可重复读提交下为了解决幻读问题时引入的锁机制,(下面的所有案例没有特意强调都使用可重复读隔离级别)幻读的问题存在是因为新增或者更新操作,这时如果进行范围查询的时候(加锁查询),会出现不一致的问题,这时使用不同的行锁已经没有办法满足要求,需要对一定范围内的数据进行加锁,间隙锁就是解决这类问题的。在可重复读隔离级别下,数据库是通过行锁和间隙锁共同组成的(next-key lock),来实现的

    加锁规则有以下特性,我们会在后面的案例中逐一解释:

    • 1.加锁的基本单位是(next-key lock),他是前开后闭原则
    • 2.插叙过程中访问的对象会增加锁
    • 3.索引上的等值查询--给唯一索引加锁的时候,next-key lock升级为行锁
    • 4.索引上的等值查询--向右遍历时最后一个值不满足查询需求时,next-key lock 退化为间隙锁
    • 5.唯一索引上的范围查询会访问到不满足条件的第一个值为止

    案例数据

    id(主键)c(普通索引)d(无索引)
    555
    101010
    151515
    202020
    252525

    以上数据为了解决幻读问题,更新的时候不只是对上述的五条数据增加行锁,还对于中间的取值范围增加了6间隙锁,(-∞,5](5,10](10,15](15,20](20,25](25,+supernum] (其中supernum是数据库维护的最大的值。为了保证间隙锁都是左开右闭原则。)

    案例一:间隙锁简单案例

    步骤事务A事务B
    1begin;
    select * from t where id = 11 for update;
    -
    2-

    insert into user value(12,12,12)

    (block)

    3commit;-

    当有如下事务A和事务B时,事务A会对数据库表增加(10,15]这个区间锁,这时insert id = 12 的数据的时候就会因为区间锁(10,15]而被锁住无法执行。

    案例二: 间隙锁死锁问题

    步骤事务A事务B
    1begin;
    select * from t where id = 9 for update;
    -
    2-begin;
    select * from t where id = 6 for update;
    3-

    insert into user value(7,7,7)

    (block)

    4

    insert into user value(7,7,7)

    (block)

    -

    不同于写锁相互之间是互斥的原则,间隙锁之间不是互斥的,如果一个事务A获取到了(5,10]之间的间隙锁,另一个事务B也可以获取到(5,10]之间的间隙锁。这时就可能会发生死锁问题,如下案例。
    事务A获取到(5,10]之间的间隙锁不允许其他的DDL操作,在事务提交,间隙锁释放之前,事务B也获取到了间隙锁(5,10],这时两个事务就处于死锁状态

    案例三: 等值查询—唯一索引

    步骤事务A事务B事务C
    1begin;
    update u set d= d+ 1 where id = 7;
    --
    2-

    insert into u (8,8,8);

    (block)

    -
    4--update set d = d+ 1 where id = 10

    1.加锁的范围是(5,10]的范围锁
    2.由于数据是等值查询,并且表中最后数据id = 10 不满足id= 7的查询要求,故id=10 的行级锁退化为间隙锁,(5,10)
    3.所以事务B中id=8会被锁住,而id=10的时候不会被锁住

    案例四: 等值查询—普通索引

    步骤事务A事务B事务C
    1begin;
    select id form t where c = 5 lock in share mode;
    --
    2-update t set d = d + 1 where id = 5-
    4--

    insert into values (7,7,7)

    (block)

    1.加锁的范围是(0,5],(5,10]的范围锁
    2.由于c是普通索引,根据原则4,搜索到5后继续向后遍历直到搜索到10才放弃,故加锁范围为(5,10]
    3.由于查询是等值查询,并且最后一个值不满足查询要求,故间隙锁退化为(5,10)
    4.因为加锁是对普通索引c加锁,而且因为索引覆盖,没有对主键进行加锁,所以事务B执行正常
    5.因为加锁范围(5,10)故事务C执行阻塞
    6.需要注意的是,lock in share mode 因为覆盖索引故没有锁主键索引,如果使用for update 程序会觉得之后会执行更新操作故会将主键索引一同锁住

    案例五: 范围查询—唯一索引

    步骤事务A事务B事务C
    1begin;
    select * form t where id >= 10 and id <11 for update
    --
    2-

    insert into values(8,8,8)
    insert into values(13,13,13)

    (block)

    -
    4--

    update t set d = d+ 1 where id = 15

    (block)

    1. next-key lock 增加范围锁(5,10]
    2. 根据原则5,唯一索引的范围查询会到第一个不符合的值位置,故增加(10,15]
      3.因为等值查询有id =10 根据原则3间隙锁升级为行锁,故剩余锁[10,15]
      4.因为查询并不是等值查询,故[10,15]不会退化成[10,15)
      5.故事务B(13,13,13)阻塞,事务C阻塞

    案例六: 范围查询—普通索引

    步骤事务A事务B事务C
    1begin;
    select * form t where c >= 10 and c <11 for update
    --
    2-

    insert into values(8,8,8)

    (block)

    -
    4--

    update t set d = d+ 1 where c = 15

    (block)

    1. next-key lock 增加范围锁(5,10],(10,15]
      2.因为c是非唯一索引,故(5,10]不会退化为10
      3.因为查询并不是等值查询,故[10,15]不会退化成[10,15)
      4.所以事务B和事务C全部堵塞

    案例八: 普通索引-等值问题

    上面的数据增加一行(30,10,30),这样在数据库中存在的c=10的就有两条记录

    步骤事务A事务B事务C
    1begin;
    delete from t where c = 10
    --
    2-

    insert into values(12,12,12)

    (block)

    -
    4--

    update t set d = d+ 1 where c = 15

    (OK)

    1. next-key lock 增加范围锁(5,10],(10,15]
      2.因为是等值查询故退化为(5,10],(10,15),故事务B阻塞,事务C执行成功
      加锁的范围如下图

    案例九: 普通索引-等值Limit问题

    步骤事务A事务B事务C
    1begin;
    delete from t where c = 10 limit 2
    --
    2-

    insert into values(12,12,12)

    (OK)
     

    -
    4--

    update t set d = d+ 1 where c = 15

    (OK)

    1.根据上面案例8改造,将delete增加limit操作2的操作
    2.因为知道了数据加锁值加2条,故在加锁(5,10]之后发现已经有两条数据,故后面不在向后匹配加锁。所以事务B执行成功,加锁范围如下

     

    展开全文
  • 灵魂拷问,MySQL到底能否解决幻读问题

    万次阅读 多人点赞 2020-08-02 21:21:55
    MySQL能否解决“幻读”?是DBA同行们面试时很喜欢考察的问题。或许你了解脏读、不可...却一直不理解“幻读”,以及MySQL到底是如何解决幻读的。如果有缘看到本文,这篇文章很可能帮助你彻底搞懂幻读原理及解决方式。

    先说结论,MySQL 存储引擎 InnoDB 在可重复读(RR)隔离级别下是解决了幻读问题的。

    方法:是通过next-key lock在当前读事务开启时,1.给涉及到的行加写锁(行锁)防止写操作;2.给涉及到的行两端加间隙锁(Gap Lock)防止新增行写入;从而解决了幻读问题。

    下面,让我带大家从原理出发,一起来搞懂MySQL并发问题 -- “幻读”。如果有好的看法,咱们评论见吧。

    小伙伴想精准查找自己想看的MySQL文章?喏 → MySQL专栏目录 | 点击这里

    目录

     

    什么是幻读

    一、幻读定义

    二、幻读示例

    三、幻读出现的场景

    四、解决幻读问题的必要性

    如何解决幻读

    一、原理解读

    二、next-key lock

    一张照片的故事


    什么是幻读

    要知道什么是幻读,首先要知道以下四点:

    一、幻读定义

    幻读是指在同一个事务中,存在前后两次查询同一个范围的数据,但是第二次查询却看到了第一次查询没看到的行,一般情况下特指事务执行中新增的其他行。

    二、幻读示例

    测试表数据:

    mysql> select * from LOL;
    +----+--------------+--------------+-------+
    | id | hero_title   | hero_name    | price |
    +----+--------------+--------------+-------+
    |  1 | 刀锋之影     | 泰隆         |  6300 |
    |  2 | 迅捷斥候     | 提莫         |  6300 |
    |  3 | 光辉女郎     | 拉克丝       |  1350 |
    |  4 | 发条魔灵     | 奥莉安娜     |  6300 |
    |  5 | 至高之拳     | 李青         |  6300 |
    |  6 | 无极剑圣     | 易           |   450 |
    |  7 | 疾风剑豪     | 亚索         |  6300 |
    +----+--------------+--------------+-------+
    7 rows in set (0.00 sec)
    


    下面是一个出现幻读情况示例,我们一起来看一下;

    时刻TSession ASession BSession C
    T1

    begin;

    -- Query1

    select * from LOL where price=450 for update; 

    Result:(6,'无极剑圣',450)

      
    T2 update LOL set price=450 where hero_title = '疾风剑豪' 
    T3

    -- Query2

    select * from LOL where price=450 for update; 

    Result:(6,'无极剑圣',450),(7,'疾风剑豪',450)

      
    T4  insert into LOL values(10,'雪人骑士','努努','450')
    T5

    -- Query3

    select * from LOL where price=450 for update; 

    Result:(6,'无极剑圣',450),(7,'疾风剑豪',450),(10,'雪人骑士',450)

      
    T6commit;  

     

    可以看到,session A 里执行了三次查询,分别是 Q1、Q2 和 Q3。它们的 SQL 语句相同,都是 select * from LOL where price=450 for update。这个语句的意思你应该很清楚了,查所有 price=450 的行,而且使用的是当前读,并且加上写锁。现在,我们来看一下这三条 SQL 语句,分别会返回什么结果。

    1. Q1 只返回 "无极剑圣" 这一行;
    2. 在 T2 时刻,session B 把 "疾风剑豪" 这一行的 price 值改成了 450,因此 T3 时刻 Q2 查出来的是 "无极剑圣""疾风剑豪" 这两行;
    3. 在 T4 时刻,session C 又插入一行 (10,'雪人骑士','努努','450'),因此 T5 时刻 Q3 查出来 price = 450 的是"无极剑圣" 、"疾风剑豪" 和 "雪人骑士" 这三行。

    其中,Q3 读到 (10,'雪人骑士',450) 这一行的现象,被称为“幻读”。也就是说,幻读指的是一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行。

    三、幻读出现的场景

    1. 幻读出现在可重复读(RR)隔离级别下,普通的SELECT查询就是快照读,是不会看到别的事务插入的数据的。因此,幻读在“当前读”下才会出现。(当前读会生成行锁,但行锁只能锁定存在的行,针对新插入的操作没有限定)
    2. 上面 session B 的修改结果,被 session A 之后的 select 语句用“当前读”看到,不能称为幻读。幻读仅专指“新插入的行”。

    因为这三个查询都是加了 for update,都是当前读。而当前读的规则,就是要能读到所有已经提交的记录的最新值。并且,session B 和 sessionC 的两条语句,执行后就会提交,所以 Q2 和 Q3 就是应该看到这两个事务的操作效果,而且也看到了,这跟事务的可见性规则并不矛盾。


    四、解决幻读问题的必要性

    在高并发数据库系统中,需要保证事务与事务之间的隔离性,还有事务本身的一致性。

     


    如何解决幻读

    如果你看到了这篇文章,那么我会默认你了解了脏读 、不可重复读与可重复读。如果还不清楚可以先参阅《上个厕所的功夫,搞懂MySQL事务隔离级别》

    场景如上,场景隔离级别为RR,当前读。

    一、原理解读

    那么幻读能仅通过行锁解决么?答案是否定的,如上面示例,首先说明一下,select xx for update(当前读)是将所有条件涉及到的(符合where条件)行加上行锁。但是,就算我在select xx for update 事务开启时将所有的行都加上行锁。那么也锁不住Session C新增的行,因为在我给数据加锁的时刻,压根就还没有新增的那行,自然也不会给新增行加上锁。

    所以要解决幻读,就必须得解决新增行的问题。

    现在你应该明白了,产生幻读的原因是:行锁只能锁住行,但是新插入记录这个动作,要更新的是记录之间的“间隙”。因此,为了解决幻读问题,InnoDB 只好引入新的锁,也就是间隙锁 (Gap Lock)。顾名思义,间隙锁,锁的就是两个值之间的空隙。比如文章开头的表 LOL,初始化插入了 7 个记录,这就产生了 8 个间隙。

    二、next-key lock

    这样,当你执行 select * from LOL where hero_title = '疾风剑豪' for update 的时候,就不止是给数据库中已有的 7 个记录加上了行锁,还同时加了 8 个间隙锁。这样就确保了无法再插入新的记录,也就是Session C在T4新增(10,'雪人骑士','努努','450') 行时,由于ID大于7,被间隙锁(7,+∞)锁住。

    在一行行扫描的过程中,不仅将给行加上了行锁,还给行两边的空隙,也加上了间隙锁。MySQL将行锁 + 间隙锁组合统称为 next-key lock,通过 next-key lock 解决了幻读问题。

    注意

    next-key lock的确是解决了幻读问题,但是next-key lock在并发情况下也经常会造成死锁。死锁检测和处理也会花费时间,一定程度上影响到并发量。


    参考资料

    《高性能MySQL》

    《丁奇MySQL实战45讲》

     

    一张照片的故事

    或许京剧自己都没想到
    清末的洋人,民国的战火都没能毁了它
    最后居然是衰落在中国人自己的手里

    展开全文
  • 字节后台一面凉经放这,有需要自己去看哈 字节跳动飞书后台工程师一面,应该是个凉经 Q:你讲讲innodb的锁机制吧 A:blablablabla介绍了行锁表锁意向锁,读...Q:在rr级别下能解决幻读问题那为什么不叫幻读级别呢,你做

    字节后台一面凉经放这,有需要自己去看哈 字节跳动飞书后台工程师一面,应该是个凉经

    Q:你讲讲innodb的锁机制吧
    A:blablablabla介绍了行锁表锁意向锁,读锁写锁
    Q:还有呢
    A:介绍了四种锁算法,record lock, gap lock, next-key lock, previous-key lock,最后我提了一嘴,innodb在rr隔离级别下是通过next-key算法来避免幻读问题的(先别骂,第一次面试紧张死了,确实有问题)
    Q:在rr级别下能解决幻读问题那为什么不叫幻读级别呢,你做过实验吗?
    A:我跟着书里做过实验,开了两个事务blablablabla

    反正最后凉了就是了,感谢信也无,冷处理(安详.jpg
    然后重新翻书看,然后和牛友们探讨,然后和学长询问,仍旧觉得没问题,只是觉得应该加上mvcc来描述会更好

    《MySQL技术内幕 InnoDB存储引擎》
    在这里插入图片描述
    MySQL8.0官方文档在这里插入图片描述

    并且自己也重新做了实验
    有以下数据
    在这里插入图片描述
    现在我们开启两个事务A,B,事务A执行select,事务B执行insert,分快照读和当前读两种情况。

    快照读

    顾名思义,读的是快照,具体实现是mvcc,再详细是undo log,不做细究。
    在这里插入图片描述

    当前读

    当前读是通过上锁实现的,也就是innodb通过next-key算法(行锁+间隙锁)为我们将这块区域锁上了,所以事务B无法对其修改。

    在这里插入图片描述

    然后和朋友研究,最终发现,就描述上来说,next-key算法确实能避免幻读现象,这句话本身没问题,但是面试官的问题应该是rr隔离级别下怎么能避免幻读问题?

    确实如书中所说,next-key能解决幻读问题,但这是手动加的,而在可序列化的隔离级别下则是默认实现的。
    其次你说那mvcc的快照读呢,不也能避免吗?

    起先,我确实一直是这么理解的,直到朋友给我看了这篇博客: 史上最详尽,一文讲透 MVCC 实现原理

    然后我进行了如下实验,也就是那篇博客的6.3的例子
    还是原汁原味的表
    在这里插入图片描述
    事务A除了select还执行update,事务B负责插入数据,于是。
    在这里插入图片描述

    幻读现象发生了!

    由这个例子说明,一旦事务A的修改操作覆盖到了其他事务插入的“幻行”,那么在下次select的时候,也会把这行数据一起查出来

    什么是快照读?

    原因在这里大概说一下,我们知道可重复读级别下mvcc读到的是当前事务开始时的数据,实质上是这样的:
    首先mvcc会给行记录额外增加三个字段
    • DB_TRX_ID:记录插入或更新后的最后一个事务的事务ID(事务id可以当作自增,也就是说事务id与事务开启先后顺序相关)
    • DB_ROLL_PTR:指向该行对应的undo log的指针
    • DB_ROW_ID:单调递增的行ID
    在快照读中,每当一个事务更新一条数据时,都会在写入undo log后将这行记录的隐藏字段 DB_TRX_ID 更新为当前事务的ID,当另一个事务来select数据时,读到该行数据的 DB_TRX_ID 不为空并且与自身不同,就会进行判断,如果他指向的事务ID比当前ID小,说明对应的事物是在当前事务开始前提交的,那就可以读;如果他指向的事务ID比当前ID大,则说明这行数据是在当前事务执行过程中被其他事务所修改提交的,于是就会根据隐藏的 DB_ROLL_PTR 字段指向的undo log进行逻辑上的回溯操作拿到当前事务开启时的原数据。
    拿上面的例子来说就是:
    事务A快照读后,事务B启动并提交了insert操作,这个时候这条记录的事务id指向事务B,事务A再执行快照读时不会把它纳入结果,因为事务B的id大于事务A的id,根据undo log发现这条记录他本就不存在。但是如果此时事务A执行的操作覆盖到了这条insert的记录,那这条记录的事务id就会指向事务A,此时事务A再次执行快照读的时候就会发生幻读现象了。

    这个问题MySQL官方文档中也提出了,引用了其他博主的翻译,没原链接
    在这里插入图片描述

    最后,假如面试官再次问我 —— RR级别下能解决幻读问题那为什么不叫幻读级别?
    A:平常我们学习的rr确实存在幻读问题,但是在innodb下不同,它可以解决,但是解决的并不完美。
    首先我们可以通过手动加锁阻塞另一个线程的insert,也就是通过innodb的next-key算法,其次我们也可以通过mvcc实现快照读,但是mvcc存在缺陷,就是一旦某个事物在事务中的修改操作覆盖到了其他事务插入的“幻行”,那么这些“幻行”在下次查询时就会再次出现,从而出现幻象问题。

    展开全文
  • MySQL是怎么解决幻读问题的?

    万次阅读 2020-09-18 10:05:18
    首先幻读是什么? 根据MySQL文档上面的定义 The so-called phantom problem occurs within a transaction when the same query produces different sets of rows at different times. For example, if a SELECT is...

    问题分析

    首先幻读是什么?

    根据MySQL文档上面的定义

    The so-called phantom problem occurs within a transaction when the same query produces different sets of rows at different times. For example, if a SELECT is executed twice, but returns a row the second time that was not returned the first time, the row is a “phantom” row.

    幻读指的是在一个事务内,同一SELECT语句在不同时间执行,得到不同的结果集时,就会发生所谓的幻读问题。

    可以看看下面的例子:

    这是网上找的一张图(事务的务字写错了,不过不影响我们理解)

    假设这个例子中的MySQL的隔离级别是提交读,也就是一个事务内可以读到其他事务提交后的结果。

    那么事务1第一次查询dept表中所有部门时,结果是没有"研发部",但是由于隔离级别是提交读,在事务2插入“研发部”这一行数据后,并且提交后,事务1是可以读取到的,所以第二次查询时,结果集中会有“研发部”。这就是幻读。

    SELECT语句分类

    首先我们的SELECT查询分为快照读和实时读,快照读通过MVCC(并发多版本控制)来解决幻读问题,实时读通过行锁来解决幻读问题。

    快照读

    1.1 快照读是什么?

    因为MySQL默认的隔离级别是可重复读,这种隔离级别下,我们普通的SELECT语句都是快照读,也就是在一个事务内,多次执行SELECT语句,查询到的数据都是事务开始时那个状态的数据(这样就不会受其他事务修改数据的影响),这样就解决了幻读的问题。

    1.2 那么innodb是怎么解决快照读的幻读问题的?

    快照读就是每一行数据中额外保存两个隐藏的列,插入这个数据行时的版本号,删除这个数据行时的版本号(可能为空),滚动指针(指向undo log中用于事务回滚的日志记录)。

    事务在对数据修改后,进行保存时,如果数据行的当前版本号与事务开始取得数据的版本号一致就保存成功,否则保存失败。

    当我们不显式使用BEGIN来开启事务时,我们执行的每一条语句就是一个事务,每次开始事务时,会对系统版本号+1作为当前事务的ID。

    1.2.1插入操作

    插入一行数据时,将事务的ID作为数据行的创建版本号。

    1.2.2删除操作

    执行删除操作时,会将原数据行的删除版本号设置为当前事务的ID,然后根据原数据行生成一条INSERT语句,写入undo log,用于事务执行失败时回滚。delete操作实际上不会直接删除,而是将delete对象打上delete flag,标记为删除,最终的删除操作是purge线程完成的。但是会将数据行的删除版本号设置为当前的事务的ID,这样后面的事务B即便查到这行数据由于事务B的ID>删除版本号,也会忽略这条数据。

    1.2.3更新操作

    更新时可以简单的认为是先将旧数据删除,然后插入一条新数据。

    所以执行更新操作时,其实是会将原数据行的删除版本号设置为当前事务的ID,生成一条INSERT语句,写入undo log,用于事务执行失败时回滚。插入一条新的数据,将事务的ID作为数据行的的创建版本号。

    1.2.4查询操作

    数据行要被查询出来必须满足两个条件,

    数据行删除版本号为空或者>当前事务版本号的数据(否则数据已经被标记删除了)

    创建版本号<=当前事务版本号的数据(否则数据是后面的事务创建出来的)

    简单来说,就是查询时,

    如果该行数据没有被加行锁中的X锁(也就是没有其他事务对这行数据进行修改),那么直接读取数据(前提是数据的版本号<=当前事务版本号的数据,不然不会放到查询结果集里面)。

    该行数据被加了行锁X锁(也就是现在有其他事务对这行数据进行修改),那么读数据的事务不会进行等待,而是回去undo log端里面读之前版本的数据(这里存储的数据本身是用于回滚的),在可重复读的隔离级别下,从undo log中读取的数据总是事务开始时的快照数据(也就是版本号小于当前事务ID的数据),在提交读的隔离级别下,从undo log中读取的总是最新的快照数据。

    1.3 补充资料:undo log段是什么?

    undo_log是一种逻辑日志,是旧数据的备份。有两个作用,用于事务回滚和为MVCC提供老版本的数据。

    可以认为当delete一条记录时,undo log中会记录一条对应的insert记录,反之亦然,当update一条记录时,它记录一条对应相反的update记录。

    1.3.1.用于事务回滚

    当事务执行失败,回退时,会读取这行数据的滚动指针(指向undo log中用于事务回滚的日志记录),就可以在undo log中找到相应的逻辑记录,读取到相应的回滚语句,执行进行回滚。

    1.3.2.为MVCC提供老版本的数据

    当读取的某一行被其他事务锁定时(也就是有其他事务正在改这行数据),它可以从undo log中分析出该行记录以前的数据是什么,从而提供该行版本信息,让用户进行快照读。在可重复读的隔离级别下,从undo log中读取的数据总是事务开始时的快照数据(也就是版本号小于当前事务ID的数据),在提交读的隔离级别下,从undo log中读取的总是最新的快照数据(也就是比正在修改这行数据的事务ID修改前的数据。)。

    实时读

    2.1实时读是什么?

    如果说快照读总是读取事务开始时那个状态的数据,实时读就是查询时总是执行这个查询时数据库中的数据。

    一般使用以下这两种查询语句进行查询时就是实时读。

    SELECT *** FOR UPDATE 在查询时会先申请X锁

    SELECT *** IN SHARE MODE 在查询时会先申请S锁

    首先看一个实时读产生幻读的案例:

    这是《MySQL技术内幕++InnoDB存储引擎++第2版》里面的一张图,就是先将隔离级别设置为提交读,这样第一次执行SELECT...FOR UPDATE查询出来的数据是a:4,事务B插入了一条新的数据,再次执行SELECT...FOR UPDATE语句时,查询出来就是a:4,a:5两条数据,这就是幻读的问题。

    2.1那么innodb是怎么解决实时读的幻读问题的?

    如果我们不在一开始将将隔离级别设置为提交读,其实是不会产生幻读问题的,因为MySQL的默认隔离级别是可重复读,在这种情况下,我们执行第一次SELECT...FOR UPDATE查询语句是,其实是会先申请行锁,因为一开始数据库就只有a:4一行数据,那么加锁区间其实是

    (负无穷,4]

    (4,正无穷)

    我们查询条件是a>2,上面两个加锁区间都会可能有数据满足条件,所以会申请行锁中的next-key lock,是会对上面这两个区间都加锁,这样其他事务不能往这两个区间插入数据,事务B会执行插入时会一直等待获取锁,直到事务A提交,释放行锁,事务B才有可能申请到锁,然后进行插入。这样就解决了幻读问题。

    如果大家对行锁了解得比较少,下一期会对innodb中的锁进行介绍。

    最后

    大家有什么想法,可以一起讨论!

    参考链接:

    https://dev.mysql.com/doc/refman/8.0/en/innodb-next-key-locking.html

    https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html

    展开全文
  • 幻读是指一个事务在前后两次查询同意范围的时候,后一...Next-Key Lock能够解决幻读问题的关键在于MySQL的数据存储结构是B+树,并且Next-Key Lock只有对于建立索引的列才能够起作用,在没有建立索引的列为了解决幻读使
  • 很多文章提到mysql的默认隔离级别是rr,且rr隔离级别是能够解决幻读的。如果能解决,串行化的存在意义何在呢,其实在rr隔离级别下,显然是不可以解决所有幻读的问题的的。如图:
  • 什么是幻读?以及如何解决幻读问题

    万次阅读 多人点赞 2020-02-10 15:57:24
    关注公众号ITwords,了解更多的java,大数据的相关知识,大家一起学习,一起进步。...为了便于说明问题,这一篇文章,我们就先使用一个小一点儿的表。 CREATE TABLE `t` ( `id` int(11) NOT NULL, ...
  • MySQL的InnoDB默认隔离级别为 Repeatable read(可重复读)为啥能解决幻读问题
  • MySQL是如何解决幻读问题

    千次阅读 2020-07-07 17:20:08
    MySQL默认的隔离级别和其他数据库不一样,它默认的是可重复读(Repeatable-Read),其他大部分数据库是读已提交(Read-Commited...幻读 未提交读 可能 可能 可能 已提交读 不可能 可能 可能 可重复读 不可能 不可能
  • InnoDB在RR隔离级别下解决幻读问题

    千次阅读 2020-03-07 17:26:37
    在RR的隔离级别下,innodb使用伪MVVC和next-key锁解决幻读,伪MVVC解决的是普通读(快照读)的幻读,next-key锁解决的是当前读情况下的幻读。 表象:快照读(非阻塞读)—伪MVCC 内在:next-key锁(行锁+gap锁) ...
  • MVCC的幻读问题读操作不会出现幻读更新操作会出现幻读问题这种现象的原因快照读当前读如何解决当前读导致的幻读问题使用可串行化的隔离级别使用next-key锁,即更新时基于非唯一索引更新数据 事务的ACID 原子性:...
  • MySQL的可重读读并没有解决可重读的问题,Innodb通过一种特殊的锁解决了这个问题
  • 首先,讲mysql的隔离级别之前需要复习一下事务的四个特性 (注:mysql存储引擎InnoDB)(待完善) ...幻读 隔离性有不同的级别 事务隔离级别 读未提交(read uncommitted) 读提交(read committed) 可重复读(rep
  • MySQL锁分析前言 前言 上一篇,我们分析了MySQL中事务以及MVCC相关知识,也知道了并发场景下事务会存在很多问题,需要通过锁来实现事务的原子性,那么今天就为大家介绍一下MySQL中锁相关的知识 ...
  • 很明显可重复读的隔离级别没有办法彻底的解决幻读问题,如果我们的项目中需要解决幻读的话也有两个办法: 使用串行化读的隔离级别 MVCC+next-key locks:next-key locks由record locks(索引加锁) 和 gap locks...
  • 案例准备 主键id,索引c。 下面的语句怎么加锁,何时释放? 该语句会命中d=5一行,...幻读 若只在id=5一行加锁,而其他行不加锁: 假设只在id=5一行加行锁 session A执行三次查询-Q1、Q2和Q3,SQL语句相同:查所有d
  • MySQL的默认隔离级别是RR,但是却没有幻读问题,那他是怎么解决的呢?答案就是Gap锁。 隔离级别 脏读 不可重复读 幻读 Read Uncommitted 可能 可能 可能 Read Committed 不...
  • Innodb是采用next-key + MVCC去解决幻读问题的! 在RR的隔离级别下,Innodb使用MVVC和next-key locks解决幻读 MVVC解决的是快照读的幻读 next-key locks解决的是当前读情况下的幻读 快照读 快照读是不会加锁的读...
  • 二、为什么要解决幻读 在高并发数据库系统中,需要保证事务与事务之间的隔离性,还有事务本身的一致性。 三、MySQL 是如何解决幻读的 如果你看到了这篇文章,那么我会默认你了解了 脏读 、不可重复读与可重复读。...
  • InnoDB默认的隔离级别是RR(可重复读),可以解决脏读和不可重复读,只解决了快照读情况下的幻读问题,当前读情况下解决幻读问题得靠next-key锁。 mysql如何实现避免幻读: 在快照读读情况下,mysql通过mvcc来避免幻...
  • 如何解决幻读

    千次阅读 2020-02-26 11:36:41
    因此,为了解决幻读问题,InnoDB 只好引入新的锁,也就是间隙锁 (Gap Lock)。 顾名思义,间隙锁,锁的就是两个值之间的空隙。比如文章开头的表 t,初始化插入了 6个记录,这就产生了 7 个间隙。 这样,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 44,808
精华内容 17,923
关键字:

如何解决幻读问题