日期:2014-05-16  浏览次数:20587 次

事务并发问题解析
数据库事务并发问题:

1,第一类丢失更新

2,读脏

3,不可重复读(第二类丢失更新,幻影现象)

所有这些问题都是因为多个事务的交叉读写造成的,虽然数据库事务的本质特征就有一个隔离性,这是站在事务自身的角度,他不用知道其他人在干嘛,就自以为隔离了,可是真正的数据库系统是不会直接绝对隔离的,而是给了几种隔离级别给用户选择,不同的隔离级别有相应的问题。本文章就是解读这些问题的。

数据就是拿来读和写的,所以从读和写的维度来仔细分析它。


写问题:两类丢失更新

第一类丢失更新,我叫他回滚丢失,意思就是一个事务在回滚的时候把别个事务的更新抹去了,我去取钱,xumuzu去转账,我在1000上取了100,xmuzu在1000上加了100,我成功提交并闪人,但是xmuzu却后悔了并回滚到1000,我下次来发现,靠,卡上还有1000。

第二类丢失更新,我叫他覆盖丢失,意思是一个事务覆盖了别个事务的更新,导致别个事务丢失数据,同样我去取钱,xmuzu去转账,我从1000里取了100提交闪人,但是xmuzu在1000上转了100进去,我后来发现余额有1100,靠。

这两类丢失数据的关系是什么呢?后面慢慢道来

读问题:读脏,不可重读(幻影)

读脏,什么叫读脏?也就是读到垃圾数据,你应该相信数据库管理系统,数据库中的数据应该是永久性和正确的,那为什么你有读到了垃圾呢,这种情况只能发生在你读到了没有真正永久写到数据库中的数据,也就是说你读到了其他事务还没提交的数据,然后别人在某个时候回滚了,所以你拿到的数据就是垃圾了。

不可重读,重点在重(chong)字?怎么理解,就是说同一个事务你两次或多次去数据库里看了下,发现同样的查询条件,居然结果不一样,多了,变了,或者少了。

事务并发带来了这么多问题,怎么解决?也许你已经想到了,加锁,好自然的思维,一个事务在做某事的时候独占不就解决了?syncronize关键字,大家都用过吧?对于数据库,大家也许已经学过了,数据库概论里的:读锁和写锁,也叫排他锁和共享锁,通过组合这两种锁和锁时序就可以避免上面的问题了,不同的组合官话叫做封锁协议,不过可惜,现在的有些数据库系统已经不用锁了,为了追求更伸缩一点,oracle和postgreSQL都用了多版本控制取代了锁。

我们以排他锁和共享锁来解决上面提到的各种问题:

首先要明确加锁原则:排他锁加上之后其他任何锁都不能加了,共享锁呢?所以叫共享就是说这个锁加上了,其他事务还可以再加共享锁。

写事务也就是增删改事务的操作都必须排他,独占访问数据对象,好了,那怎么解决第一类丢失更新?只要写操作加上排他锁就搞定了,过程是:加锁 --->在写期间出现异常回滚 ---> 锁释放,第二个事务再写,数据库就一致了,没有丢失数据。但是因为整个过程只是在写的时候加了锁,所以单纯的读就有问题了,你写的时候,我读了你还没有提交的数据,我两次读可能出现不相同的数据,还有幻影,好我们来一个个的解决。

首先解决读脏,我读到了你正在准备写的数据,凭什么我要读你正在写的数据?我读的时候不准你更新不就解决了?是的,我在读的时候加共享锁,其他人了不起只能加共享锁了,他是写不了的,这样就不会读脏了。有人问了,我加共享锁的时候别人在写怎么办?那就去看加锁原则吧,你会被阻塞的,注意这个过程,加锁读取,一旦读完就要释放锁,因为已经解决问题了。

再来解决不可重读,解决读脏的时候你读完走人,然后其他人就可以写了,等你再拿起相同的查询条件回来的时候,你很可能发现和你刚才得到数据不一样,对,不可重读的问题来了,当然还有幻影。又怎么解决?不可重读因为多次回来看的过程中有小偷进来了,导致变化,我在整个过程中加上共享锁,可以吗?对,这就是答案,你要读一个数据对象就在整个事务过程中加锁,然后就可以一致的读了,注意你锁住的只是对当前这个数据对象的删和改操作,你并没有锁住对整张表的增加操作和对其他数据对象的删操作,好了,这就是幻影,即使在整个过程中加了共享锁,但是你可能会遇到你差多的数据记录多了或者少了。幻影现象一般是不很严重的,要解决幻影现象就必须求助最后一个方案,彻底排队。

还差点什么?第二类丢失更新,是吧

这个问题之所以留到最后,因为我们将深入另外一个更加有挑战的话题了,第二类丢失更新是不可重读一个特列,你第一次读,然后去修改,发现其实已经被修改了,你直接在修改,就覆盖了别人的数据。

前面讨论的并发问题都是在一个单一角色里讨论的,我丢失更新了,我读到垃圾了,我不可重读了,我发现幻影了,对吧,我们是在站在我的这个角度来分析和解决问题的,再复习一遍,我被其他事务因为回滚丢失更新了,我要求必须更新过程中排他,我读到垃圾了,我要求读的时候一定要加共享锁,我不可重读了我要求在我的来回过程我要一直锁住,同志们,注意我们的解决方案都只是徘徊在这一个事务里的事情,如果是多个事务呢?不在考虑我,而是你和他呢?这就是我们要讨论第二类丢失更新的原因,第二类丢失更新有人说是不可重读的特例,其实这个特例意思应该就是事务跨越了,典型案例,你获得一个user对象,然后发到界面去编辑,编辑过程所持续的时间鬼知道是多久?就在你编辑过程中又只有鬼知道这个user被别人编辑了不知道多少次,被删了也有可能,当你拿着你编辑完的user去更新数据库时,意味着什么?你当然希望你是唯一修改user的人,可惜不一定。要解决这个问题就得希望于乐观离线锁和悲观离线锁。