• 理解MySql事务隔离机制、锁以及各种锁协议


        一直以来对数据库的事务隔离机制的理解总是停留在表面,其内容也是看一遍忘一边。这两天决定从原理上理解它,整理成自己的知识。查阅资料的过程中发现好多零碎的概念假设串起来足够写一本书,所以在这里给自己梳理一个脉络,详细的内容參考引文或在网上搜一下。因为平时接触最多的是MySQL。所以文章中某些部分是MySQL特有的特性,请读者注意。


    数据库并发操作会引发的问题:

    多个事务同一时候訪问数据库时候,会发生下列5类问题,包含3类数据读问题(脏读,不可反复读。幻读)。2类数据更新问题(第一类丢失更新,第二类丢失更新):
    1. 脏读(dirty read):A事务读取B事务尚未提交的更改数据。并在这个数据基础上操作。

      假设B事务回滚,那么A事务读到的数据根本不是合法的。称为脏读。

      在oracle中,因为有version控制,不会出现脏读。

    2. 不可反复读(unrepeatable read):A事务读取了B事务已经提交的更改(或删除)数据。比方A事务第一次读取数据,然后B事务更改该数据并提交,A事务再次读取数据,两次读取的数据不一样。
    3. 幻读(phantom read):A事务读取了B事务已经提交的新增数据。注意和不可反复读的差别。这里是新增,不可反复读是更改(或删除)。这两种情况对策是不一样的,对于不可反复读,仅仅须要採取行级锁防止该记录数据被更改或删除。然而对于幻读必须加表级锁。防止在这个表中新增一条数据。
    4. 第一类丢失更新:A事务撤销时。把已提交的B事务的数据覆盖掉。
    5. 第二类丢失更新:A事务提交时,把已提交的B事务的数据覆盖掉。

        数据库在并发操作下会出现上述这些问题,要解决它就要想办法在运行可能引发问题的操作之前将该操作堵塞住,让它等到合适的时机再运行。

    那么怎样挑选合适的时机堵塞操作的运行。又怎样保证在调度过程运行完毕后其运行结果与串行运行操作的结果同样呢?

    三级封锁协议

        数据库想要在“合适”的时机堵塞住数据库操作,那么首先要定义好怎么样的时机算是“合适”,由于各个系统支持的业务千差万别,对数据的实时性和有效性的要求也不同。

    于是数据库理论中就提出了封锁级别的概念,对不同的同步要求採用不同的封锁级别。

    三级封锁协议内容例如以下:

    • 一级封锁协议:事务T在改动数据R之前必须先对其加X锁,直到事务结束才释放。事务结束包含正常结束(COMMIT)和非正常结束(ROLLBACK)。

       一级封锁协议能够防止丢失改动,并保证事务T是可恢复的。使用一级封锁协议能够解决丢失改动问题。在一级封锁协议中。假设不过读数据不正确其进行改动。是不须要加锁的,它不能保证可反复读和不读“脏”数据。

       

    • 二级封锁协议:一级封锁协议加上事务T在读取数据R之前必须先对其加S锁。读完后方可释放S锁。

       二级封锁协议除防止了丢失改动,还能够进一步防止读“脏”数据。但在二级封锁协议中,因为读完数据后就可以释放S锁,所以它不能保证可反复读。

       

    • 三级封锁协议 :一级封锁协议加上事务T在读取数据R之前必须先对其加S锁,直到事务结束才释放。

       三级封锁协议除防止了丢失改动和不读“脏”数据外,还进一步防止了不可反复读。

    事务隔离级别:

        三级封锁协议反映在实际的数据库系统上。就是四级事务隔离机制。

    总的来说,四种事务隔离机制就是在逐渐的限制事务的自由度,以满足对不同并发控制程度的要求。下面就是数据库的四种隔离级别:

    Read Uncommitted、Read Committed、Repeatable Read、Serializable

        其对各个并发问题的制约强度见下表:

    √: 可能出现    ×: 不会出现

      脏读 不可反复读 幻读
    Read uncommitted
    Read committed ×
    Repeatable read × ×
    Serializable × × ×

        四种级别对并发问题的解决由弱到强。对应的系统性能由强到弱,MySQL的默认级别是Repeatable Read。

    Read Uncommitted

        在Read Uncommitted策略下,数据库遵循一级封锁协议,仅仅对改动数据的并发操作做限制。一个事务不能改动其它事务正在改动的数据,但能够读取到其它事务中尚未提交的改动,这些改动假设未被提交。将会成为脏数据。

    Read committed

        在Read committed策略下,数据库遵循二级封锁协议。仅仅同意读取已经被提交的数据,反过来讲,假设一个事务改动了某行数据且尚未提交。而第二个事务要读取这行数据的话,那么是不同意的。

    在MySql的InnoDB下,尽管这样的操作不被同意。但MySQL不会堵塞住数据的查询操作,而是会查询出数据被改动之前的备份。返回给client。MySQL的这样的机制称为MVCC(多版本号并发控制),就是说数据库在事务并发的过程中对数据维护多个版本号,使得不同的事务对不同的数据版本号进行读写(MVCC的实现參见引用中的文章)。这样的机制反映在应用中就是,在不论什么时候对数据库查询总是能够得到数据库中近期提交的数据。为被提交的脏数据被隔离起来,无法被查询到,即防止脏读发生。

    Repeat Read

        Repeat Read又比Read Committed更加严格一点。但仍然是在二级封锁协议的范畴,仅仅是读取过程受到很多其它MVCC的影响。在Read Committed下,同意一个事务中多次同样查询得到不同的结果,就是所谓的不可反复读问题。这在一些应用中是同意的。所以oracle、SQL server上默认这一隔离级别。但MySQL没有。它默认Repeat Read级别。在这一级别下,有赖于MVCC,同一个事务中的查询仅仅能查到版本号号不高于当前事务版本号的数据,即事务仅仅能看到该事务開始前或者被该事物影响的数据。反过来说。这一级别下,不同意事务读取在该事务開始后新提交的数据。即防止了不可反复读的发生。

        依靠上面的机制,已经做到了在事务内数据内容的不变,可是不能保证多次查询得到的数据数量一致。由于在一个事务运行的过程中别的事务全然能够运行数据插入。当插入了刚好符合查询条件的数据时,就会引发数据查询结果集添加。引发幻读。

    另一种情况就是。假设一个事务想插入一条数据,而另一个事务已经插入了含有同样主键的数据,那么当前事务也会被堵塞,并终于运行失败,尽管当前事务根本无法查询到这一条数据,这也是一种幻读。

    InnoDB提供的间隙锁机制能够在一定程度上防止幻读的发生。详细介绍见最后一篇引文。

    Serializable

        最后,最强事务隔离机制Serializable,它遵循三级封锁协议,使得全部的事务必须串行化运行,仅仅要有事务在对表进行查询,那么在此事务提交前,不论什么其它事务的改动都会被堵塞。这攻克了一切并发问题。但会造成大量的等待、堵塞甚至死锁。使系统性能减少。


        要注意,在不论什么一种隔离机制下,都是不同意一个事务删除或改动还有一个事务影响过而未提交的数据的。由于事务增、删、改数据以后,会在该行加上排它锁,排它锁会堵塞其它事务再次对该行数据操作。也正是由于排它锁的存在,这四种隔离机制都不会出现不论什么一种更新丢失的现象,由于一条信息根本不同意第二个事务进行改动。

    两段锁协议

        数据库在调度并发事务时遵循“两段锁”协议,“两段锁”协议是指全部事务必须分两个阶段对数据项进行加锁和解锁

    1. 扩展阶段:在对不论什么数据项的读、写之前,要申请并获得该数据项的封锁。
    2. 收缩阶段:每一个事务中,全部的封锁请求必须先于解锁请求。

        在数学上能够证明。遵循两段锁的调度能够保证调度结果与串行化调度同样。

    这种机制保证了数据库并发调度与串行调度的等价。


    *注:

    參考资料:

    http://blog.csdn.net/fg2006/article/details/6937413

    http://blog.csdn.net/chen77716/article/details/6742128

    http://www.2cto.com/database/201304/201415.html

    http://snailxr.iteye.com/blog/1143615

    http://blog.sina.com.cn/s/blog_711b11fd0101bhks.html

    http://blog.sina.com.cn/s/blog_499740cb0100ugs7.html

    转载请注明出处:

    http://blog.csdn.net/gklifg/article/details/38752691

  • 相关阅读:
    Web 日志分析过程
    nginx系列之九:lua服务
    Linux网络编程之IO模型
    从URL输入到页面展现到底发生什么
    CentOS 日常运维十大技能
    以MySQL为例,详解数据库索引原理(1)
    Elasticsearch的特点以及应用场景
    Ubuntu1804编译安装LNMP
    golang 高级
    Centos7 安装 Redis
  • 原文地址:https://www.cnblogs.com/yjbjingcha/p/6869087.html
Copyright © 2020-2023  润新知