您的位置:首页 > 数据库 > Oracle

Oracle脏读处理方式

2017-02-27 09:28 162 查看
本博文引自:http://hhw3.blog.163.com/blog/static/2690966201273144634595/

需要先了解一下这一特性的产生的背景原因。看下以下这个例子。在一个银行系统中(一般涉及到钱的问题对并发事务要求是最严格的^_^),会计正在统计当月某地区的个人存款总额,她的这个操作,在后台肯定要产生一条SQL语句,对这一地区的所有用户的存款额作SUM(),我们假设这一操作产生的语句为A,时间点是T1。由于存款用户非常多,再加上会有一些对其他表的JOIN条件,语句A的执行时间可能比较长。这时,在A的执行过程中,A已经统计了账户X的钱,但还没有统计到账户Y的钱的时候,正好有一个客户通过ATM机从账户X中转250元钱到帐户Y,他的操作也产生了一条语句B,对存款表进行更新。开始时间点是T2,结束时间是T3。因为只更新两条记录,这个过程非常短。A结束时的时间点是T4。让我们看下过程图,看看会产生什么结果:



  从图中,我们可以看到,T2时刻A已经统计过帐户X中的钱,但在这时B从X中转了250元到帐户Y中,在B结束的时刻T3,A还没有统计到Y,但Y已经多出了250元了,所以到T4,统计结束时,A实际上多统计出250元。这就是并发事务中的“脏读(dirty read)”问题。

  在标准SQL中,为了防止并发事务中产生脏读,就需要通过加锁来控制。这样就会带来死锁、阻塞的问题,即时是粒度最小的行级锁,也无法避免这些问题。再看下上面这个例子。为了防止脏读,A在开始时就需要对表加锁,防止其他事务更新表。这样,B就会被阻塞,假如A事务要执行1个小时,B可能最长就要被阻塞1个小时(如果你是那个倒霉的客户,可能早就*骂出来了)。再看下图,



  从图中,可以看到,B开始时,存款表被加锁了,所以B被A阻塞,只有等A释放锁以后,B才能更新表。所以B被阻塞了很长时间。在大量并发事务系统中,可能会使整个系统慢得不可想象。

  一致性读

  为了解决这一矛盾。Oracle充分利用的回归段,通过会滚段进行一致性读取,即避免了脏读,又大大减少了系统的阻塞、死锁问题。下面就看下Oracle是如何实现一致性读的:

  当Oracle更新数据块(Data Block Oracle中最小的存储单位)时,会在两个地方记录下这一更新动作。一个是重做段(Redo Segment),是用于数据库恢复(Recover)用的。一个是回滚段(UNDO Segment),而回滚段是用于事务回滚(Rollback)的(我们只关心回滚段了)。并在数据块头部标示出来是否有修改数据。一个语句在读取数据快时,如果发现这个数据块是在它读取的过程中被修改的(即开始执行读操作时并没有被修改),就不直接从数据块上读取数据,而是从相应的回滚段条目中读取数据。这就保证了最终结果应该是读操作开始时的那一时刻的快照(snapshot),而不会受到读期间其他事务的影响。这就是Oracle的一致性读,也可以叫做多版本(Multi-Versioning)。

  以上面的例子为例,A在读取到Y帐户时,发现这条记录已经被修改了,于是就从回滚段读取保留的回滚数据,最终就能正确得到T1时刻的正确存款总额了。看下图:



  从图上看出,A即能得出正确的数据,又保证B不会被阻塞。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  oracle 脏读