彩世界平台-彩世界时时app-彩世界开奖app苹果下载

热门关键词: 彩世界平台,彩世界时时app,彩世界开奖app苹果下载

您的位置:彩世界平台 > 工作委员会 > MySQL ACID及四种隔离级别的解释

MySQL ACID及四种隔离级别的解释

发布时间:2019-11-15 06:53编辑:工作委员会浏览(98)

    以下内容出自《高性能MySQL》第三版,了解事务的ACID及四种隔离级有助于我们更好的理解事务运作。

    在实际的业务场景中,并发读写引出了事务控制的需求。主要关注事务的ACID和隔离性的4个级别。

    下面举一个银行应用是解释事务必要性的一个经典例子。假如一个银行的数据库有两张表:支票表(checking)和储蓄表(savings)。现在要从用户Jane的支票账户转移200美元到她的储蓄账户,那么至少需要三个步骤:

    ACID

    事务指"一个被视为单一的工作单元的操作序列"。一个良好的事务处理系统,必须具备四个标准特性,即ACID

    1. 原子性(Atomicity):一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作。
    2. 一致性(Consistency):数据库总是从一个一致性的状态转换到另一个一致性的状态。
    3. 隔离性(Isolation):通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的。针对不同的业务需求,隔离性分为4个级别:读未提交、读已提交、可重复读、串行化。见下
    4. 持久性(Durability):通常来说,一旦事务提交,则其所做的修改会永久保存到数据库(即使系统崩溃,修改的数据也不会丢失)。针对不同的业务需求,持久性也分为多个级别,此处略。

    1、检查支票账户的余额高于或者等于200美元。

    隔离性的4个级别

    在理解隔离性级别时,很容易混淆“幻读”与“不可重复读”的问题。这里先对4个隔离性级别给出概览;然后分析原理,从实现角度理解各种问题;最后作出总结。

    2、从支票账户余额中减去200美元。

    概览

    关注隔离性的4个级别,包括读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)、可序列化(Serializable);及其对应的问题,包括脏读(Dirty Read)、不可重复读(Nonrepeatble Read)、幻读彩世界开奖app苹果下载,(Phantom Read)。

    3、在储蓄帐户余额中增加200美元。

    读未提交

    读未提交是数据库应保证的最低的隔离性级别:事务中的修改,即使没有提交,对其他事务也都是可见的

    读未提交面临脏读的问题:事务可以读取未提交的数据,而该数据可能在未来因回滚而消失。从性能上来说,读未提交不会比其他的级别好太多,但却缺乏其他级别的很多好处。除非真的有非常必要的理由,在实际应用中很少使用。

    上述三个步骤的操作必须打包在一个事务中,任何一个步骤失败,则必须回滚所有的步骤。

    读已提交

    读已提交满足前面提到的隔离性的简单定义:一个事务所做的修改在最终提交以前,对其他事务是不可见的。换句话说,一旦提交,该事务所作的修改对其他正在进行中的事务就是可见的

    狭义上,读已提交解决了脏读的问题。这个级别有时候叫做不可重复读,面临不可重复读的问题:两次执行同样的查询,如果第二次读到了其他事务提交的结果,则会得到不一样的结果

    大多数数据库的默认隔离级别都是Read Committed,但MySQL不是。

     

    可重复读

    在读已提交的基础上,可重复读解决了部分不可重复的问题:同一个事务中多次读取同样记录结果是一致的记录指具体的数据行。

    未能解决的那部分称为幻读当某个事务在读取目标范围内的记录时,另一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生第一次读取范围时不存在的幻行(Phantom Row)。需要注意的是,只有插入会产生幻行

    MySQL的默认隔离级别是可重复读,有幻读问题。

    可以用START TRANSACTION语句开始一个事务,然后要么使用COMMIT提交将修改的数据持久保存,要么使用ROLLBACK撤销所有的修改。事务SQL的样本如下:

    可序列化

    可序列化是最高的隔离级别:强制事务序列化执行

    可序列化解决了幻读问题。简单来说,可序列化会在目标范围加独占锁,将并发读写相同范围数据的请求序列化。可序列化会导致大量的超时和锁争用问题,因此,实际应用中很少用到这个隔离级别,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑采用该级别。

    1. start transaction;

    2. select balance from checking where customer_id = 10233276;

    3. update checking set balance = balance - 200.00 where customer_id = 10233276;

    4. update savings set balance = balance + 200.00 where customer_id = 10233276;

    5. commit;

    原理

    讨论基于锁的并发控制中,4种隔离性级别的实现原理。重点关注读锁(read lock)、写锁(write lock,或称排它锁)、范围锁(range lock)、锁的持有时间等概念。

     

    读未提交

    读未提交的实现:读锁、写锁都在一个原子操作(如select、insert等)完成后立即释放。换句话说,事务作出更新后,不管是否提交,由于已经释放了目标记录的写锁,更新对其他事务就是可见的。

    读未提交存在脏读问题,假设操作序列:

    1. 事务1开始
    2. 事务1读取目标记录
    3. 事务2开始
    4. 事务2修改目标记录
    5. 事务1读取目标记录
    6. 事务2回滚
    7. 事务1提交

    操作5中,事务1读到了事务2修改但未提交的记录,然后事务2回滚导致修改丢失,也就称事务1读到了“脏数据”,即脏读。

    区分目标记录与目标范围(见后文可重复读的实现原理):

    • 目标记录指一个具体的数据行;读锁、写锁只针对目标记录。
    • 目标范围指一个where语句描述的范围;范围锁针对目标范围,见后。

    ACID表示原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)。一个很好的事务处理系统,必须具备这些标准特性:

    读已提交

    出现脏读的原因是写锁的持有时间过短。读已提交针对这一问题作出了优化:读锁仍然在一个原子操作完成后立即释放;写锁从写操作开始持有,事务提交后释放。事务作出更新前,会先申请目标记录的写锁,并持续持有至事务提交后,释放锁后,更新对其他事务才是可见的。

    对于读未提交中的操作序列,操作5发生时,由于事务2持有目标记录的写锁,事务1会阻塞,直到事务2提交释放该写锁,解决了脏读问题。

    读已提交还存在不可重复读问题。假设操作序列:

    1. 事务1开始
    2. 事务1读取目标记录
    3. 事务2开始
    4. 事务2修改目标记录
    5. 事务2提交
    6. 事务1修改目标记录
    7. 事务1提交

    操作5完成后,事务2的修改对事务1可见,从而操作6中,事务1会读到修改,与操作2的结果不同,因此修改结果无法保证(如根据操作2读取的结果做修改);但是事务1在此之前未对目标记录作出任何修改,因此事务1进行操作6时的状态理应与操作2后一致(回顾事务的一致性要求)。以上即为不可重复读。

    不可重复读与脏读之间存在交叉。脏读侧重读到不应存在的数据,不可重复读强调两次相同查询的结果不一样。实际上,可以将描述放宽到“目标记录的状态不符合预期状态”,如本应该不同,却读到了相同。本质上也是由于读已提交实现原理导致的问题。

     

    可重复读

    解决不可重复读可以使用两种方法:

    1. 悲观策略:串行化
    2. 乐观策略:多版本 + 冲突检测

    原子性(atomicity)

    悲观策略:串行化

    “串行化”不需要解释,放弃并发、串行执行当然不存在任何问题。

    “串行化”的可重复读实现是:读锁、写锁从读、写操作开始持有,事务提交后释放。与读已提交的实现相比,可重复读延长读锁的持有时间直到事务提交后,在此期间,目标记录无法被修改。

    对于读已提交中的操作序列,操作2发生时,事务1开始持有目标记录的读锁,导致事务2的操作4会陷入阻塞,直到事务1提交释放锁。

    “串行化”不同于“可序列化”。为了区分,前、后文中均将隔离性级别称为“可序列化”,将此处的悲观策略称为“串行化”。

      一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性

    乐观策略:多版本 + 冲突检测

    “多版本 + 冲突检测”是更常见的实现方案:多个事务采用多个版本,最后提交时检测是否与当前数据版本冲突,如果冲突则报错提醒,否则成功提交

    “多版本 + 冲突检测”的可重复读实现是:事务开始时持有当前数据的快照,读写均不冲突,提交时检测修改的快照与当前数据是否冲突。使用乐观的冲突检测策略代替悲观的锁策略,在中低程度的并发情况下性能更好。

    对于读已提交中的操作序列,事务1、2各自持有不同版本的快照,在操作4修改自己版本的目标记录后,操作5提交事务2,检测不冲突(假设没有其他事务),合并到当前数据,当前数据完成修改;然后操作6继续修改自己版本的目标记录,操作7提交事务1,发现与当前数据冲突,给出报错。

    一致性(consistency)

    幻读问题

    幻读是一种特殊的不可重复读。

    为什么会出现幻读问题呢?

    Java的内置锁以对象为单位,RDBMS的锁呢?前面的注释中略有介绍。为了提高并发性能,简单的以数据表、数据库为单位实现锁的性能过低;标准SQL中,读、写锁以记录(数据行)为单位,范围锁以范围(逻辑上的范围,用where描述)为单位。如果没有范围锁,那么显然读、写锁只能“锁”在已存在的记录上。假设操作序列,这次具体一些:

    1. 事务1开始
    2. 事务1统计表内数据的总行数
    3. 事务2开始
    4. 事务2插入一条新纪录
    5. 事务2提交
    6. 事务1利用“旧的总行数+新的数据表内容”计算区分度
    7. 事务1提交

    该操作序列是读已提交中操作序列的一个具体实例。因此,可以解决部分不可重复读问题,不能解决的那部分就是幻读了。

    以基于锁的“串行化”方案为例(“多版本+并发冲突”同理),假设不使用范围锁,则幻读表现如下:由于事务2插入的记录不获取锁,操作2获取的读锁无法发挥作用,操作5提交事务2后,新记录就对事务1可见了;操作6读取时,事务1认为一致性依然满足,便使用了旧的总行数,并重新读表计算distinct count,却读到了一条意料之外的新纪录,破坏了一致性——好像出现了幻觉一样,这条新纪录就被称为“幻行”,该现象即“幻读”。

         数据库总是从一个一致性的状态转换到另一个一致性的状态。(在前面的例子中,一致性确保了,即使在执行第三、四条语句之间时系统崩溃,支票账户中也不会损失200美元,因为事务最终没有提交,所以事务中所做的修改也不会保存到数据库中。)

    可序列化

    对于基于锁的“串行化”方案,可序列化实现:从各操作开始前持有读锁、写锁、范围锁,直到事务提交后释放。对于“多版本 + 冲突检测”方案,可序列化基于更严格的写冲突检测来实现,详见“快照隔离”技术,此处不展开。

    范围锁如何解决幻读问题呢?

    范围锁是一个逻辑概念上的锁,事务从读、写操作(带显式或隐式where)开始前持有范围锁,直到事务提交后释放。忽略读、写锁,对可重复读中操作序列的影响如下:操作2中事务1获取了目标范围上的范围锁,操作4发现目标范围被锁,陷入阻塞,直到操作7事务提交。

    隔离性(isolation)

    隔离性级别的总结

    各隔离级别解决了不同的问题。"Y"说明存在问题,"-"说明不存在:

    隔离级别/问题 脏读 不可重复读 幻读
    读未提交 Y Y Y
    读已提交 - Y Y
    可重复读 - - Y
    可序列化 - - -

    在基于锁的并发控制中,依靠不同的锁持有时间实现各隔离级别。锁均从操作前开始持有,"S"表示操作结束后释放,"C"表示事务提交后释放:

    隔离级别/问题 脏读 不可重复读 幻读
    读未提交 S S S
    读已提交 C S S
    可重复读 C C S
    可序列化 C C C

    MySQL的默认隔离级别是可重复读,解决了脏读、部分不可重复读问题,有幻读问题。


    参考:

    • MySQL ACID及四种隔离级别的解释
    • 事务隔离
    • 数据库的事务隔离与锁机制有什么差别和联系,最近在看,感觉两个很混乱~求解释。?

    本文链接:事务的ACID和四个隔离级别
    作者:猴子007
    出处:https://monkeysayhi.github.io
    本文基于知识共享署名-相同方式共享 4.0国际许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名及链接。

    本文由彩世界平台发布于工作委员会,转载请注明出处:MySQL ACID及四种隔离级别的解释

    关键词:

上一篇:MySQL ACID及四种隔离级别的解释

下一篇:没有了