众所周知,金融业是一个直接和钱打交道的行业。通俗的讲,金融公司提供的是中介服务,并从中收取中介费,盈利模式是“本金”——“收益”。而实体经济的盈利模式是“本金”—“产品”—“收益”。所以,在金融业中对金额是非常敏感的,下面以“取款”、“转账”和“查询余额”等银行业务为例来说明,如何通过数据库事务保证该类业务正确,完整和安全的进行。

一. 数据库事务简介

数据库事务(Transaction)是指:作为单个逻辑工作单元执行的一些列操作。事务内的操作要么全部执行,要么全部不执行,回到初始状态。事务必须具备下面四点属性(ACID)

1. 原子性(Atomic):事务内的操作必须是全部执行,或者全部不执行,回滚到初始状态。

2. 一致性(Consistent):事务在完成后,数据库内的数据是一致的,没有脏数据产生。

3. 隔离性(Isolation):事务在并发执行时,事务之间的修改必须是隔离的,好比事务是串行执行的一样。

4. 持久性(Duration):事务完成后,对数据的修改是持久的。

这是个人理解的数据库事务,详细的定义和介绍可以参照维基百科。

二. 数据库并发问题

同一时间可能存在多个用户访问数据库,并做相应的操作。必然就会存在数据库中同一份数据被多个事务同时访问,如果处理不好,总体来说会引入五类问题:3类读问题(脏读、不可重复读和幻读)和2类写问题(第一类丢失更新和第二类丢失更新)。下面以“取款”、“转账”和“查询余额”三类业务来解释出现这五类问题的场景:

1. 脏读(Dirty Read)

举一个生活中的例子,结巴A来到超市买东西,老板B问他:“抽中华烟吗?”。顾客A说“我…抽…抽…抽”,老板B忙给他拿了包烟过来,这时结巴A终于憋出了后半句“抽不起啊”。在这个生活场景中,老板B对结巴A进行了脏读。

脏读的现象是:事务B读取了事务A没有提交的更改数据,并做了相应的操作。此时,事务A回滚,那么事务B基于之前数据的操作都是错误的。来看看转账和取款的例子:

时间 取款事务A 转账事务B
T1 开始事务  
T2   开始事务
T3   余额1000元,转出100元,余额改为900元
T4 余额900元(脏读)  
T5   撤销事务
T6 取款100元,余额改为800元  
T7 提交事务  

从上述例子中可以看出,脏读导致账户损失了100元。

2. 不可重复读(Unrepeatable Read)

还是刚才生活中的例子,A问B:“还有中华烟吗?”,B说:“还有一包,要吗?”,A思索了一下,此时B又去招待其他顾客了,而且将烟卖给了另一个顾客。等A想好,并等B忙完后说“给我来一包吧”,B说:“买完了”,然后两人开始吵架了。在这个场景中,在A身上就发生了不可重复读问题。

不可重复读现象是:事务A在事务B提交某条更新数据的前后,对该条数据访问时,会产生不一致的情况。来看看,查询和转账业务

时间 取款事务A 转账事务B
T1   开始事务
T2 开始事务  
T3   余额1000元
T4 查询余额1000元  
T5   转出100元,余额改为900元
T6   提交事务
T7 查询余额900元  
T8 提交事务  

从上面例子中可以看出,在同一事务A中,T4和T7查询的结果不一致。

3. 幻读(Phantom Read)

还是刚才生活中的例子,A问B:“店里有多少中华烟?”,B说:“还有2包,都要么?”,这时A在思索一下。恰巧,这时另一个顾客C退了一包回来。等A想好后说:“我都要了”,其实此时店里一共还有三包烟了。在这个场景中,在A身上发生了幻读的情况。

幻读的现象是:事务A在事务B插入某条数据的前后,对数据记录进行统计时,读取到事务B新插入的一条记录。幻读容易与不可重复读混淆,幻读指的是读取了新插入的数据,不可重复读指的是读取了新更新(修改,删除)的数据。在数据库中,防止这两类问题所采取的策略是不同的,不可重复读采取行级锁防止更新,而幻读需要采取表级锁防止插入。来看看“统计账户总额”和“开户”业务

时间 统计账户总额事务A 开户事务B
T1 开始事务  
T2   开始事务
T3 查询总额为1000元  
T4   新增一个账户,并存款100元
T5   提交事务
T6 查询总额为1100元  
T7 提交事务  

从上面例子中可以看出,在同一事务A中,T3和T6统计的结果不一致。

4. 第一类丢失更新(Lost Update)

还是刚才生活中的例子,不过角色要丰富一下,顾客变成2个:A1和A2,老板变成2个:B1和B2。场景是这样的,A1问B1:“还有多少中华烟”,B1清点了一下说:“还有2包”。A2问了B2同样的问题,B2也回答说是“还有2包”,然后A2买了1包。但是B2并没有告知B1。后来,A1也要了一包烟,不一样的事,不久A1又把烟给退了,可是B1还以为店里有2包烟。在这个场景中,B1身上发生了第一类更新问题。

第一类更新问题指的是:事务A撤销时,导致事务B已提交的修改被覆盖。来看看取款和转账业务:

时间 取款事务A 转账事务B
T1 事务开始  
T2   事务开始
T3 查询余额1000元  
T4   查询余额1000元,并转入100元余额1100元
T5   提交事务
T6 取出100元,余额900元  
T7 撤销事务  
T8 余额恢复为1000元  

从上面例子中可以看出,事务A撤销后,导致事务B已提交的修改被覆盖

5. 第二类丢失更新(Second Lost Update)

和第一类丢失更新不同的是,第二类丢失更新指的是:事务A提交的结果,将事务B提交的结果覆盖了。仍然以上面的取款和转账业务为例:

时间 取款事务A 转账事务B
T1 事务开始  
T2   事务开始
T3 查询余额1000元  
T4   查询余额1000元,并转入100元余额1100元
T5   提交事务
T6 取出100元,余额900元  
T7 提交事务  
T8 余额为900元  

从上面例子中可以看出,事务A提交后,导致事务B已提交的修改被覆盖。

posted on 2013-03-30 22:40  Maxwell Zhou  阅读(544)  评论(0编辑  收藏  举报