zoukankan      html  css  js  c++  java
  • 数据库原理之事务(二)

      本文主要分析数据库并发控制的一系列问题。事务是并发控制的基本单位,保证事务ACID的特性是事务处理的重要任务,而并发操作有可能会破坏其ACID特性。

      一个数据库可能拥有多个访问客户端,这些客户端都可以并发方式访问数据库。数据库中的相同数据可能同时被多个事务访问,如果没有采取必要的隔离措施,就会导致各种并发问题,破坏数据的完整性。这些问题可以归结为5类,包括三类数据读问题(脏读、幻象读和不可重复读)以及两类数据更新问题(第一类丢失更新和第二类丢失更新)。下面,我们分别通过实例讲解引发问题的场景。

    一、数据读取问题

    1、脏读(Dirty Read)

      首先用一个例子来说,有这样一个笑话:一个结巴在饮料店柜台前转悠,老板很热情地迎上来:“喝一瓶?”,结巴连忙说:“我…喝…喝…”,老板麻利地打开易拉罐递给结巴,结巴终于憋出了他的那句话:“我…喝…喝…喝不起啊!”。在这个笑话中,饮料店老板就对结巴进行了脏读。 

      读“脏”数据是指事务A修改某一数据,并将其写回磁盘,事务B读取同一数据后,A由于某种原因被除撤消,而此时A把已修改过的数据又恢复原值,B读到的数据与数据库的数据不一致,则B读到的数据就为“脏”数据,即无效的数据。

      以存取款为例,来看看脏读的过程:

    时间
    转账事务A
    取款事务B
    T1
     
    开始事务
    T2
    开始事务
     
    T3
         
    查询账户余额为1000元    
    T4
            
    取出500元把余额改为500元
    T5
    查询账户余额为500元(脏读)
     
    T6
     
    撤销事务余额恢复为1000元
    T7
    汇入100元把余额改为600元
     
    T8
    提交事务
     

      在这个场景中,B希望取款500元而后又撤销了动作,而A往相同的账户中转账100元,就因为A事务读取了B事务尚未提交的数据,因而造成账户白白丢失了500元。

    2、不可重复读(Unrepeatable Read)

      不可重复读是指A事务读取了B事务已经提交的更改数据。假设A在取款事务的过程中,B往该账户转账100元,A两次读取账户的余额发生不一致:

    时间
    取款事务A
    转账事务B
    T1
     
    开始事务
    T2
    开始事务
                              
    T3
                                  
    查询账户余额为1000元     
    T4
    查询账户余额为1000元
                              
    T5
                      
    取出100元把余额改为900元
    T6
     
    提交事务                  
    T7
    查询账户余额为900元(和T4读取的不一致)
     

      在这个场景中,同一事务的T4时间点和T7时间点读取账户存款余额不一样。

    3、幻象读(Phantom Read)

      A事务读取B事务提交的新增数据,这时A事务将出现幻象读的问题。幻象读一般发生在计算统计数据的事务中,举一个例子,假设银行系统在同一个事务中,两次统计存款账户的总金额,在两次统计过程中,刚好新增了一个存款账户,并存入100元,这时,两次统计的总金额将不一致:  

    时间
    统计金额事务A
    转账事务B
    T1
     
    开始事务
    T2
    开始事务
                 
    T3
    统计总存款数为10000元
                 
    T4
     
    新增一个存款账户,存款为100元
    T5
     
    提交事务     
    T6
    再次统计总存款数为10100元(幻象读)
     

      如果新增数据刚好满足事务的查询条件,这个新数据就进入了事务的视野,因而产生了两个统计不一致的情况。 
      幻象读和不可重复读是两个容易混淆的概念,前者是指读到了其它已经提交事务的新增数据,而后者是指读到了已经提交事务的更改数据(更改或删除),为了避免这两种情况,采取的对策是不同的,防止读取到更改数据,只需要对操作的数据添加行级锁,阻止操作中的数据发生变化,而防止读取到新增数据,则往往需要添加表级锁——将整个表锁定,防止新增数据。

    二、数据写入问题

    1、第一类丢失更新

      A事务撤销时,把已经提交的B事务的更新数据覆盖了。这种错误可能造成很严重的问题,通过下面的账户取款转账就可以看出来: 

    时间
    取款事务A
    转账事务B
    T1
    开始事务
     
    T2
     
    开始事务
    T3
    查询账户余额为1000元    
     
    T4
     
    查询账户余额为1000元
    T5
     
    汇入100元把余额改为1100元
    T6
     
    提交事务
    T7
    取出100元把余额改为900元
     
    T8
    撤销事务
     
    T9
    余额恢复为1000元(丢失更新)
     

      在这个场景中,A事务在撤销时,“不小心”将B事务已经转入账户的金额给抹去了。 

    2、第二类丢失更新 
      A事务覆盖B事务已经提交的数据,造成B事务所做操作丢失:  

    时间
    转账事务A
    取款事务B
    T1
     
    开始事务
    T2
    开始事务
                             
    T3
                   
    查询账户余额为1000元    
    T4
    查询账户余额为1000元
                             
    T5
     
    取出100元把余额改为900元
    T6
     
    提交事务           
    T7
    汇入100元
     
    T8
    提交事务
     
    T9
    把余额改为1100元(丢失更新)
     

      在这个例子里,由于支票转账事务覆盖了取款事务对存款余额所做的更新,导致银行最后损失了100元,相反如果转账事务先提交,那么用户账户将损失100元。

  • 相关阅读:
    Java 打印HelloKitty
    Android四大组件:BroadcastReceiver 介绍
    详解 Handler 消息处理机制(附自整理超全 Q&A)
    垃圾回收机制 —— 整理介绍
    四种引用类型 —— 软引用与弱引用的应用
    线程池 —— 使用介绍
    倒计时器 CountDownTimer
    屏幕旋转时 Activity 的生命周期 —— 测试与结论
    arcengine Objects in this class cannot be updated outside an edit session(不能在编辑会话之外更新此类对象)解决办法
    基于rtmp+nginx 、vlc实现FFmpeg推流与wpf端拉流
  • 原文地址:https://www.cnblogs.com/kuliuheng/p/4079017.html
Copyright © 2011-2022 走看看