可重复读如何彻底解决幻读?

通过上一篇文章https://www.jianshu.com/p/55f79dc4b289,分析了RR级别下并没有彻底解决幻读的问题。这是由于事务执行过程中,允许别的事务插入新数据;而当前事务的更新操作(update,delete)都是当前读。如果要彻底幻读问题,就得在事务查询执行中,不允许别的事务插入影响该查询结果的数据。例如:事务A查询id>10的数据,那么事务B就不允许插入id>10的数据。这样的实现需要锁来保证。下面介绍一下:Gap锁。

Gap锁

a363afcd47909f1a33bd42cc4f2a78c.png

范围分析

假设有一张表t,主键:id,普通索引列:col1,索引idx_col1


image.png
  • 锁的是索引数据,不是行数据

    事务a:
    select * from t where col1=30 for update;
    +----+------+------+
    | id | name | col1 |
    +----+------+------+
    | 10 | a2 | 30 |
    | 51 | lll | 30 |
    +----+------+------+
    2 rows in set
    锁住了索引数据col1=30;
    事务b:
    update t set col1='51' where id=51; 阻塞(该行数据的索引列col数据是30,被事务a锁住了)
    update t set col1='30' where id=50;阻塞(索引列col数据为30已经被事务a锁住了)
    update t set col1='70' where id=50;不阻塞

  • 唯一索引

    唯一索引来锁定唯一行来锁定行的语句,不需要间隙锁定;此时的锁是行锁
    事务a:select * from t where id=30 for update;
    +----+------+------+
    | id | name | col1 |
    +----+------+------+
    | 30 | b2 | 50 |
    +----+------+------+
    1 row in set
    事务b:
    insert into t(id,name,col1) values(32,'1',31);
    Query OK, 1 row affected
    update t set col1='51' where id=30;阻塞了(事务a上了行锁)

    范围锁定行
    1.select * from t where id>30 for update;
    | id | name | col1 |
    +----+------+------+
    | 40 | n40 | 60 |
    | 50 | 30 | 70 |
    | 51 | lll | 30 |
    +----+------+------+
    范围:[30,51)、[51,正无穷大),
    2.select * from t where id>30 and id<40 for update;
    范围:[30,30)、(30,40)、(40,40]
    3.select * from t where id between 30 and 40 for update;
    范围:[20,30)、(30,40)、(40,50]
    4.select * from t where id<30 for update;
    范围:(-无穷大,30)、(30,30]

你可能感兴趣的:(可重复读如何彻底解决幻读?)