zoukankan      html  css  js  c++  java
  • 你了解幻读吗?

    首先我们创建一个表,并插入测试数据:

    CREATE TABLE `t` (
      `id` int(11) NOT NULL,
      `c` int(11) DEFAULT NULL,
      `d` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `c` (`c`)
    ) ENGINE=InnoDB;
    
    insert into t values(0,0,0),(5,5,5),
    (10,10,10),(15,15,15),(20,20,20),(25,25,25);

    什么是幻读?

    1. 在可重复读隔离级别下,普通的快照读诗不会看到别的事务插入的数据的,因此,幻读只在“当前读”下才会出现
    2. 幻读专指新插入的行。修改的数据被其他事务的“当前读”看到,不能称为幻读。

    幻读有什么问题?

    语义:

      如图:T1时刻, sessionA的语言是将所有d=5的数据,加行锁。T2时刻,SessionB将id=0这条数据改成了d=5,此时有两条 d=5的数据,但是由于第二条数据并没有加锁,可以对其进行修改。这就破坏了sessionA里的Q1语句要锁住所有d=5的行的加锁声明。

    数据一致性:

    数据一致性不仅指数据库内部数据状态的一致性,还包含了数据和日志在逻辑上的一致性
    T6时刻后,数据库中的数据为:
    (5,5,100)
    (0,5,5)
    (1,5,5)
    而bin log里的内容如下:
    update t set d=5 where id=0; /*(0,0,5)*/
    update t set c=5 where id=0; /*(0,5,5)*/
    
    insert into t values(1,1,5); /*(1,1,5)*/
    update t set c=5 where id=1; /*(1,5,5)*/
    
    update t set d=100 where d=5;/* 所有 d=5 的行,d 改成 100*/ 
    可以看到id=0和id=1这两行,发生了数据不一致。
    可以看到id=0和id=1这两行,发生了数据不一致。

    InnoDB如何解决幻读问题?

    为了解决幻读的问题,InnoDB引入了新的锁——间隙锁(Gap Lock)
    即将两个值之间的空隙进行加锁,比如表t,初始化插入了6个记录,这就会产生7个空隙
    此时,我们执行
    select * from t where d = 5 for update;
    的时候,就不仅给这已有的6个记录加上了行锁,同时加了7个间隙锁,这样就保证了无法再插入新的记录。
    注意:
    对于非索引字段进行update或select..for update操作的时候,代价极高(上述SQL)。所有记录上锁,已经所有间隙加锁
    对于索引字段执行上述操作,只有字段本身一级附近的间隙会被加锁。
    间隙锁存在冲突,是“往这个间隙中插入一个记录”这个操作。
    思考一下这是为什么呢?
    InnoDB索引是一颗B+树,同一级上的节点是顺序排列的,所以只需要锁住字段本身和附近间隙即可,因为后插入的数据位置是固定的。非索引字段则不具备这样的结构,所以只能采用全表扫描的方式加锁。
    例如:

    这里由于表中没有c=7这条记录,并且 c是索引字段,所以SessionA会在(5,10)这个间隙上加锁。而SessionB也是在这个间隙进行加锁,这两个锁的目的都是为了保护这个间隙不被插入数据,所以他们之间是不冲突的。

    间隙锁和行锁合城 next-key lock。每个next-key lock都是前开后闭区间。

    间隙锁带来的问题:

    例如:

    这是一个间隙锁导致死锁的问题。
      1. SessionA执行更新语句后,由于id=9这一行数据不存在,所以会在(5,10)上加上间隙锁。
      2. SessionB同理,两个间隙锁不会冲突。
      3. SessionB试图插入一行数据(0,9,9),被SessionA的间隙锁挡住,进入等待。
      4. SessionA试图插入一行数据(0,9,9),被SessionB的间隙锁挡住了。
      5. 循环等待,形成死锁。
    间隙锁的引入,可能会导致同样的语句锁住更大的范围,这其实是影响了并发度的。
    那么有没有简单的方式,去解决幻读呢?
    可以把数据库的隔离级别设置为读提交,再将bin log的格式设置为row,这样可以解决数据和日志不一致的问题。如果我们的业务需求,用读提交就够了,不用可重复度。可以采用这种方法进行配置。
     

  • 相关阅读:
    有爱,有期待——开启囤货模式(一)
    我们都爱你,悠悠球少年
    三月随笔——漫长的等待
    奢侈的休闲度假时光——带着小样儿去三亚(三)
    这个夏天,飞到北纬18°去玩海——带着小样儿去三亚(二)
    一场说走就走的旅程——带着小样儿去三亚(一)
    获取URL的参数-详解如何获取(自己写的,实测有效)
    背景图自适应
    百家姓
    判断是手机端还是PC端的代码
  • 原文地址:https://www.cnblogs.com/nedulee/p/11835340.html
Copyright © 2011-2022 走看看