首页 > 代码库 > 数据库原理之事务(二)

数据库原理之事务(二)

  本文主要分析数据库并发控制的一系列问题。事务是并发控制的基本单位,保证事务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元。

数据库原理之事务(二)