Oracle 丢失更新问题的解决方案

丢失更新是数据中一个比较常见的经典问题,在做项目时我们有时可能会没有注意到这个问题,但这个问题相当重要,有时会带来比较严重的结果。下面我们就来讨论下这个丢失更新。

  一、什么是丢失更新:

  用一个操作过程来说明:

  (1)会话Session1 中的一个事务获取(查询)一行数据,并显示给一个用户User1。

  (2)会话Session2 中的另一个事务也获取这一行,但是将数据显示给另一个用户User2。

  (3)User1 使用应用修改了这一行,让应用更新数据库并提交。会话Session1 的事务执行完毕。

  (4)User2 也修改这一行,让应用更新数据库并提交。会话Session2 的事务执行完毕。

  这个过程就叫做“丢失更新”,因为第(3)步做的操作会全部丢失(被第4步操作覆盖),最终数据库只会保存第(4)步的更新结果。这个情况在有些系统中可能不会有影响,但在有些系统中可能就影响很大了,举个简单的例子:

  财务系统加工资,若公司本次调薪决定给员工张三加1k人民币,财务部两名操作人员A和B,过程情况若是这样的:

  1)A操作员在应用系统的页面上查询出张三的薪水信息,然后选择薪水记录进行修改,打开修改页面但A突然有事离开了,页面放在那没有做任何的提交。

  2)这时候B操作员同样在应用中查询出张三的薪水信息,然后选择薪水记录进行修改,录入增加薪水额1000,然后提交了。

  3)这时候A操作员回来了,在自己之前打开的薪水修改页面上也录入了增加薪水额1000,然后提交了。

  其实上面例子操作员A和B只要一前一后做提交,悲剧就出来了。后台修改薪水的sql:update 工资表 set salary = salary + 增加薪水额 where staff_id = ‘员工ID’。这个过程走下来后结果是:张三开心了这次涨了2k,操作员A和B都郁闷了。

  二、解决思路:

  基本两种思路,一种是悲观锁,另外一种是乐观锁; 简单的说就是一种假定这样的问题是高概率的,最好一开始就锁住,免得更新老是失败;另外一种假定这样的问题是小概率的,最后一步做更新的时候再锁住,免得锁住时间太长影响其他人做有关操作。

  三、解决方案1(悲观锁)

  a)传统的悲观锁法(不推荐):

  以上面的例子来说明,在弹出修改工资的页面初始化时(这种情况下一般会去从数据库查询出来),在这个初始化查询中使用select ...for update nowait, 通过添加for update nowait语句,将这条记录锁住,避免其他用户更新,从而保证后续的更新是在正确的状态下更新的。然后在保持这个链接的状态下,在做更新提交。当然这个有个前提就是要保持链接,就是要对链接要占用较长时间,这个在现在web系统高并发高频率下显然是不现实的。

  b)现在的悲观锁法(推荐优先使用):

  在修改工资这个页面做提交时先查询下,当然这个查询必须也要加锁(select ...for update nowait),有人会说,在这里做个查询确认记录是否有改变不就行了吗,是的,是要做个确认,只是你不加for update就不能保证你在查询到更新提交这段时间里这条记录没有被其他会话更新过,所以这种方式也需要在查询时锁定记录,保证在这条记录没有变化的基础上再做更新,若有变化则提示告知用户。

  四、解决方案2(乐观锁)

  a)旧值条件(前镜像)法:

  就是在sql更新时使用旧的状态值做条件,SQL大致如下 Update table set col1 = newcol1value, col2 = newcol2value…. where col1 = oldcol1value and col2 = oldcol2value….,在上面的例子中我们就可以把当前工资作为条件进行更新,如果这条记录已经被其他会话更新过,则本次更新了0行,这里我们应用系统一般会做个提示告知用户重新查询更新。这个取哪些旧值作为条件更新视具体系统实际情况而定。(这种方式有可能发生阻塞,如果应用其他地方使用悲观锁法长时间锁定了这条记录,则本次会话就需要等待,所以使用这种方式时最好统一使用乐观锁法。)

  b)使用版本列法(推荐优先使用):

  其实这种方式是一个特殊化的前镜像法,就是不需要使用多个旧值做条件,只需要在表上加一个版本列,这一列可以是NUMBER或 DATE/TIMESTAMP列,加这列的作用就是用来记录这条数据的版本(在表设计时一般我们都会给每个表增加一些NUMBER型和DATE型的冗余字段,以便扩展使用,这些冗余字段完全可以作为版本列用),在应用程序中我们每次操作对版本列做维护即可。在更新时我们把上次版本作为条件进行更新。

  c)使用校验和法(不推荐)

  d)使用ORA_ROWSCN法(不推荐)

  五、结论:

  综上所述,我们对丢失更新问题建议采取上面的悲观锁b方法或乐观锁b方法(蓝色字体已标注),其实这两种方式的本质都一样,都是在更新提交时做一次查询确认在更新提交,我个人觉得都是乐观的做法,区别在于悲观锁b方法是通过select..for update方式,这个可能会导致其他会话的阻塞,而乐观锁b方法需要多一个版本列的维护。

  个人建议:在用户并发数比较少且冲突比较严重的应用系统中选择悲观锁b方法,其他情况首先乐观锁版本列法。

本文出自seven的测试人生公众号最新内容请见作者的GitHub页:http://qaseven.github.io/

时间: 2024-07-28 12:49:27

Oracle 丢失更新问题的解决方案的相关文章

图片-oracle数据库更新问题,求大神解决

问题描述 oracle数据库更新问题,求大神解决 如图,t1,t2表,我想通过t2来更新t1的name,试着写了一个 update t1 a set a.name=(select b.name from t2 b where a.id=b.id) 但这样的话,在t2中不存在的id=3的name就会成空,该怎么写呢? 解决方案 update t1 A a set name= ( select b.name from t2 B where ID = a.ID ) where exists(selec

关于hibernate 处理oracle Blob 更新的问题

问题描述 1.怎么把oracle.sql.BLOB;转换为java.sql.Blob;?或者怎么建立一个空的java.sql.Blobp.setPhoto(这里要放个空的blob);session.flush();session.refresh(p);p.setPhoto(这里放图片的blob);session.update(p);2.是这样更新吗? 解决方案 解决方案二:显然不对,不能让用常规的方法处理BLOB单独写一个方法,使用二进制流的方法读写!

当oracle丢失所有控制文件后可以重新创建控制文件来恢复数据库

当oracle丢失所有控制文件后可以重新创建控制文件来使数据库正常打开 重新创建控制文件的方法如下: 第一步是查询出该数据的所有日志文件,数据文件和控制文件 SQL> select member from v$logfile; MEMBER -------------------------------------------------------------------------------- D:\ORACLE\PRODUCT\10.1.0\ORADATA\OCP\REDO03.LOG

请求- Https跳到http时session信息丢失可能出现的情况和解决方案

问题描述 Https跳到http时session信息丢失可能出现的情况和解决方案 问题是从https跳转到http有时能取到我们需要的session数值,有时取不到.所以请求大神能够给出完整的从Https调到http时session信息丢失出现的问题及解决方案. 解决方案 http://name327.iteye.com/blog/1591796

Oracle医疗卫生私有云解决方案

Oracle医疗卫生私有云解决方案 Oracle企业架构部,云计算团队 --医疗行业信息化需求和建设考虑 --Oracle云计算助力医疗信息化建设 --案例介绍 Oracle医疗卫生私有云解决方案

ORACLE 数据库容灾复制解决方案share Plex

oracle|解决|数据|数据库 SharePlex 是业界最成熟的高性能/高可用性数据复制解决方案.具有网络占用少.配置灵活.准实时复制等特点,可以解决关键应用的多种可用性问题.推出五年来,该产品技术已经被世界各地大量的用户成功实施,成为企业应用底层最成熟.最领先的高可用性数据复制解决方案. l??????? 通过Redo Log俘获数据变化 用户对数据对象的更新,一旦写入Oracle日志,就可被SharePlex的俘获机制快速捕捉,即使该事务可能尚未执行.SharePlex实现了复制速度和数

Oracle PRKC-1002错误原因和解决方案

小编在 VMware Server 1.0.8 中安装 Oracle RAC 时, 碰到到了 PRKC-1002 错误:WARNING: Error while copying directory /u01/oracle/product/10.2.0/db_1 with exclude file list 'null' to nodes 'rac2′. [PRKC-1002 : All the submitted commands did not execute successfully] 苦搜

【oracle】更新大批量数据变更步骤

    生产环境中遇到更新或者删除大批量数据的时候,不能直接进行操作,要批量进行. 1 获取要进行更新的数据的主键,保存为文本文件或者csv文件.这一步一定要正确,否则下面的操作会造成更新错误的数据,造成数据不一致! 2 创建临时表并将获取的数据主键导入到创建! #!/bin/sh  # Created by yangql on 2011-11-23 # Parameters  ## . /home/oracle/.profile ORA_USER=yang ORA_PASSWD=yang TO

浏览器缓存导致FLASH资源更新问题的解决方案

在网上搜浏览器缓存问题时,遇上了很多问题.一是不知道应该用何种关键字搜索,二是一搜出来,就全是讲的是如何禁用浏览器缓存的方案.   作为大型点的FLASH WEBGAME来说,不缓存显然是不行的.总体上来说,我们要想达到的目标就是 一.浏览器需要缓存 二.当服务器资源更新时,浏览器缓存里相应的老版本资源失效.   下面两篇文章讲到了一个很好的解决方案,并且给出了源码..   更新文件避免浏览器缓存的解决方案(基于svn)   http://www.itamt.com/2010/06/browse