• 4种事务的隔离级别,InnoDB怎样巧妙实现?


    版权声明:本文为博主原创文章,未经博主同意不得转载。

    https://blog.csdn.net/z50L2O08e2u4afToR9A/article/details/82186189

    事务ACID特性,当中I代表隔离性(Isolation)。

     

    什么是事务的隔离性?

    隔离性是指,多个用户的并发事务訪问同一个数据库时,一个用户的事务不应该被其它用户的事务干扰。多个并发事务之间要相互隔离。

     

    一个事务怎么会干扰其它事务呢?

    咱们举样例来说明。假设有InnoDB表:

    t(id PK, name);

     

    表中有三条记录:

    1, shenjian

    2, zhangsan

    3, lisi

     

    case 1

    事务A,先运行,处于未提交的状态:

    insert into t values(4, wangwu);

     

    事务B,后运行,也未提交:

    select * from t;

     

    假设事务B能够读取到(4, wangwu)这条记录,事务A就对事务B产生了影响,这个影响叫做“读脏”,读到了未提交事务操作的记录。

     

    case 2

    事务A,先运行:

    select * from t where id=1;

     

    结果集为:

    1, shenjian

     

    事务B,后运行。而且提交:

    update t set name=xxoo where id=1;

    commit;

     

    事务A。再次运行同样的查询:

    select * from t where id=1;

     

    结果集为:

    1, xxoo

     

    这次是已提交事务B对事务A产生的影响。这个影响叫做“不可反复读”,一个事务内同样的查询。得到了不同的结果。

     

    case 3

    事务A,先运行:

    select * from t where id>3;

     

    结果集为:

    NULL

     

    事务B,后运行。而且提交:

    insert into t values(4, wangwu);

    commit;

     

    事务A。首次查询了id>3的结果为NULL,于是想插入一条为4的记录:

    insert into t values(4, xxoo);

     

    结果集为:

    Error : duplicate key!

     

    事务A的内心OS是:你TM在逗我。查了id>3为空集,insert id=4告诉我PK冲突?

     

    这次是已提交事务B对事务A产生的影响。这个影响叫做“幻读”。

     

    能够看到,并发的事务可能导致其它事务:

    • 读脏

    • 不可反复读

    • 幻读

     

    InnoDB实现了哪几种事务的隔离级别?

    依照SQL92标准。InnoDB实现了四种不同事务的隔离级别:

    • 读未提交(Read Uncommitted)

    • 读提交(Read Committed, RC)

    • 可反复读(Repeated Read, RR)

    • 串行化(Serializable)

     

    不同事务的隔离级别,实际上是一致性与并发性的一个权衡与折衷

     

    InnoDB的四种事务的隔离级别。各自是怎么实现的?

    InnoDB使用不同的锁策略(Locking Strategy)来实现不同的隔离级别。

     

    一。读未提交(Read Uncommitted)

    这样的事务隔离级别下。select语句不加锁。

    画外音:官方的说法是

    SELECT statements are performed in a nonlocking fashion.

     

    此时。可能读取到不一致的数据,即“读脏”。

    这是并发最高,一致性最差的隔离级别。

     

    二。串行化(Serializable)

    这样的事务的隔离级别下,全部select语句都会被隐式的转化为select ... in share mode.

     

    这可能导致。假设有未提交的事务正在改动某些行,全部读取这些行的select都会被堵塞住。

    画外音:官方的说法是

    To force a plain SELECT to block if other transactions have modified the selected rows.

     

    这是一致性最好的,但并发性最差的隔离级别。

     

    在互联网大数据量,高并发量的场景下,差点儿不会使用上述两种隔离级别

     

    三,可反复读(Repeated Read, RR)

    这是InnoDB默认的隔离级别,在RR下:


    (1)普通的select使用快照读(snapshot read)。这是一种不加锁的一致性读(Consistent Nonlocking Read)。底层使用MVCC来实现,具体的原理在《InnoDB并发如此高,原因居然在这?》中有具体的描写叙述;

     

    (2)加锁的select(select ... in share mode / select ... for update), update, delete等语句。它们的锁,依赖于它们是否在唯一索引(unique index)上使用了唯一的查询条件(unique search condition),或者范围查询条件(range-type search condition):

    • 在唯一索引上使用唯一的查询条件。会使用记录锁(record lock),而不会封锁记录之间的间隔,即不会使用间隙锁(gap lock)与临键锁(next-key lock)

    • 范围查询条件。会使用间隙锁临键锁。锁住索引记录之间的范围,避免范围间插入记录。以避免产生幻影行记录,以及避免不可反复的读

    画外音:这一段有点绕,多读几遍。


    关于记录锁,间隙锁,临键锁的很多其它说明。详见《InnoDB。select为啥会堵塞insert?》。

     

    四,读提交(Read Committed, RC)

    这是互联网最经常使用的隔离级别,在RC下:


    (1)普通读是快照读;


    (2)加锁的select, update, delete等语句。除了在外键约束检查(foreign-key constraint checking)以及反复键检查(duplicate-key checking)时会封锁区间,其它时刻都仅仅使用记录锁;


    此时,其它事务的插入依旧能够运行,就可能导致。读取到幻影记录。


    总结

    • 并发事务之间相互干扰。可能导致事务出现读脏不可反复度幻读等问题

    • InnoDB实现了SQL92标准中的四种隔离级别

    (1)读未提交:select不加锁。可能出现读脏;

    (2)读提交(RC):普通select快照读。锁select /update /delete 会使用记录锁,可能出现不可反复读;

    (3)可反复读(RR):普通select快照读,锁select /update /delete 依据查询条件情况,会选择记录锁,或者间隙锁/临键锁。以防止读取到幻影记录;

    (4)串行化:select隐式转化为select ... in share mode。会被update与delete相互排斥。

    • InnoDB默认的隔离级别是RR,用得最多的隔离级别是RC


    也许有朋友问,为啥没提到insert?能够查阅《InnoDB并发插入,居然使用意向锁?》。

     


    640?wx_fmt=jpeg

    架构师之路-分享可落地的架构文章


    本文新名词颇多。前置文章更精彩:

    InnoDB,MVCC。快照读

    InnoDB,记录锁,间隙锁,临键锁

    InnoDB,B+树的细节

    InnoDB与MyISAM索引的差异

  • 相关阅读:
    vue学习(1)
    10.贝叶斯理论
    9.高等数学-导数
    5.6.7.8.高等数学-两个重要的极限定理
    4. 高等数学——元素和极限
    1.2.3.《万门大学-人工智能、大数据、复杂系统》课程大纲
    1.0初识机器学习
    django2+uwsgi+nginx上线部署到服务器Ubuntu16.04(最新最详细版)
    ERRORS: ?: (staticfiles.E002) The STATICFILES_DIRS setting should not contain the STATIC_ROOT setting.
    Ubuntu下MySQL报错:ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/9892671.html
Copyright © 2020-2023  润新知