八张图教你彻底理解数据库并发控制之隔离级别(下)

八张图教你彻底理解数据库并发控制之隔离级别(下)


三、不能重复读

以上我们介绍的两种隔离级别均是对单条记录进行管理,它所加的锁仅限于行锁,由于锁的颗粒度较小,所以具有良好的并发性能。到目前为止,并发操作会导致的针对单条记录修改的问题已经解决,接下来我们来看看并发操作对多条记录会产生什么样的影响,以及解决的手段。

还是来看一个例子,甲童鞋想买一张从北京飞往昆明的机票,然后去去哪儿网站进行查询,结果出现了很多项机票信息,甲童鞋一项一项的找,试图找到最便宜的机票。这时候乙童鞋也想买一张从北京飞往昆明的机票,也在去哪儿网站进行查找,他查找的速度比较快,一下子就找到了那张最便宜的机票,然后下订单提交(实际上这是最后一张最便宜的机票)。这时候,甲童鞋也找到了这张最便宜的机票,然后下订单提交,却发现这张机票已经被其他人买走了。(甲童鞋命真苦啊,55555555555555)

在CS隔离级别的会话中,我们来模拟下以上情形。

步骤一:将两个会话的隔离级别设置为CS。

步骤二:会话一选择读取name为test111的记录,这时出现4条相关记录,但不输入commit命令,事务尚未提交。

步骤三:会话二选择其中一条name为test111的记录,并将其修改,同时提交。

步骤四:会话一再次选择读取name为test111的记录,发现相对于上一次的读取操作,记录数少了一条,两次操作均在同一事务范围内。




问题出现了,在查询条件一样的情况下,步骤三查看的结果较步骤一少了一行。这是CS隔离级别中不能解决的问题,当我们把隔离级别由CS调整到RS后,也就解决了这类问题。

我们仍然按照先前的步骤,先通过set current isolation RS将隔离级别设置为RS,然后一步一步的操作,来看看它是如何解决这个问题。

步骤一:会话一读取name为test111的记录,其中包括id为8的记录,但并未提交。

步骤二:会话二修改id为8的记录,结果发现被卡住,除非会话一进行提交或者回滚,会话二的事物才能继续进行。



会话一将记录集从硬盘读取到至内存,同时对记录集的所有记录进行加S锁。会话二想修改id为8的记录,首先在内存中查找,结果在内存中找到该记录,但是同时发现该记录已经被加S锁,所以只能等待该记录的锁被释放。

当隔离级别由CS升级至RS时,加锁的对象也由单行变成了多行,这也意味着并发性能的降低。但在并发控制中,效率和安全从来都是一对矛盾。一方面效率的提高意味着安全性的降低,另一方面,安全性的提高是以牺牲效率为代价的。在对应用进行设计时,需要结合实际情况对这个矛盾的双方进行综合的考虑,最终的方案一定是个折中的方案。

RS对读取的行加锁,很好的解决的不可重复读的问题,但是我们稍微往前猜想一下,就会发现,没有对符合条件的读取行加锁会导致不可重复读取问题,那么,没有对整个表中的所有行加锁又将会导致什么问题呢?如果仅仅对符合条件的记录集加锁,那么随后插入一条符合条件的记录也是RS不能控制的,这个就是所谓的“幻象读取”,也是数据库系统隔离级别理论需要解决的最后一个问题。

我们还是以甲童鞋和乙童鞋为例,来描述整个过程。当然看到甲童鞋这么多次不爽的情况下,这回我们让他爽一次。甲童鞋想要买张由北京飞往昆明的机票,然后去去哪儿网站(虽然去哪儿网站机票种网罗了个机票网站的信息,种类颇多而且还能够找着最为便宜的机票,但我这里绝对不是做广告哦:))查看机票情况,当输入查询条件时候,网站罗列出一大堆符合条件的机票,甲童鞋仔细的找啊找,想找到一张比较便宜的机票。这时候某家新航空公司新推出一趟航班,定了个从北京飞昆明的超低价格的机票来吸引顾客,然后往机票系统中加入有关该低价机票的信息。这个时候,甲童鞋找得头昏眼花,决定重新提交一遍,按照顺序来对机票进行排序,结果再次查看是发现了某航空公司推出的超低价机票。恭喜甲童鞋,他终于可以买到张从北京飞往昆明的超低价机票了,而且还是超低超便宜的。


四、读“幻象”

如果工业上的应用对此要求不是很严格,需要严格控制在查询事务中插入数据,那么RS这个隔离级别也就足够用了,事实上很多应用也只要到达RS这个级别也就可以了。但是为了整个系统的完整性,我们还是要来看看最后一个隔离级别。

我们在接下来的实验中,揭示这个现象,首先需要将两个会话的级别设置成RS,然后再进行操作。

步骤一:会话一查看name属性为test111的记录集,结果记录集显示4条语句,语句并未提交,该事务尚未完成。

步骤二:会话二插入一条name属性为test111的记录,并提交。

步骤三:会话一再次查看name属性为test111的记录集,结果发现记录集出现了5条记录,较上次查看多出了一条记录,两次查询处于同一事务当中。




会话一将记录集从硬盘上读取至内存,由于此时会话的隔离级别设置为RS,所以这次操作将会给读取的数据集进行上S锁。会话二插入一条name属性为test111的数据,并提交到硬盘,同时也会同步内存,(由于插入的数据并不在会话一中的数据集中,所以可以插入,插入之后会加上S锁),这时候在硬盘中以及内存中name属性为test111的记录集发生改变,多了一条记录。当会话一再次读取数据时,改变了的记录集被读取,出现了步骤三的锁显示的问题。

当然,既然有问题,那必然有解决问题的的手段,这是矛盾的两个方面,千百年来此消彼长的对立着,发展着。只是有的时候我们只看到了矛盾的一面,而忽略了另一面,又或者是根本没有发现其另外一面,但是这个并不表明另一面不存在。就像是我们看不到原子,就不能说原子不存在一样。不过幸运的是,利用数据库系统提供的RR隔离级别可以解决幻像读取问题。

首先,我们通过set current isolation RR将两个会话设置RR隔离级别,然后按下列步骤进行操作。

步骤一:会话一读取name属性为test111的记录集,不提交,该事物尚未完成。

步骤二:会话二插入一条name属性为test111的记录集,这时发现该操作被卡住,无法继续,除非会话一提交或者回滚。




好了,目前为止利用DB2数据库做实验,同时结合理论、想象以及推论将数据库的隔离级别介绍完毕。我们发现,所谓的数据隔离级别,实际上是对S锁和X锁的综合灵活运用而已,当我们对其分析透彻之后,它本身也就再无奥妙可言了。

一般而言,出于性能的考虑,隔离级别采用到CS便足以应付需要了,除非特别严厉的应用系统。对于应用系统而言,往往是通过JDBC或者是ODBC进行数据库的连接,所以对隔离级别的设置通常也是在这些API中。要对一个应用系统选择最为合适的隔离级别,是一个艰辛的过程,需要大量的其他方面的知识,但最重要的,是要有良好的分析能力和想象能力。

当然,不管您懂还是不懂,反正我是讲完了:)



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值