如何解决保存冲突问题

首先说明一下什么是保存冲突问题,这里举一个例子。假设有一个订单001,操作员A首先打开了这个订单,在这之后操作员B也打开了这个订单。A对订单001做了一些修改,然后提交订单,B也对订单001做了一些修改,提交订单。这个时候,操作员B覆盖了操作员A对订单001的修改。要解决这个问题,有两大类方法,分别是悲观锁与乐观锁。

首先来说说悲观锁方法。悲观锁方法的解决方案就是,当A打开了订单后,对订单001加入了一个锁。在操作员A提交对001的操作前,其它的操作者只能对001查看,而不能对001进行更新,从界面设计角度来说,当其它人打开订单001时,界面上的提交按钮是不可用状态。关于悲观锁,网上有很多文章的描述都不准确,那些文章认为悲观锁是对操作的数据加入数据库级锁来锁定数据,实际上这样会给数据库带来很大的开销,对于Sqlserver2000数据库来说,如果使用了该方法,所有访问到订单001的查询与报表都会被阻塞,严重的影响用户体验。对于Oracle数据库这种基于版本来管理并发的数据库来说,也会由于加入了过多的锁而极大的影响读取性能。正确的方式是在订单表中增加一个锁定者ID,当操作员A打开订单001时,将订单001的锁定者ID设置为操作员A,然后当操作员B打开订单时,如果发现锁定者ID不为空且不等于操作员B,就将提交按钮设置为不可用。其实在设置锁定者ID的时候也是要加一定的数据库锁的,否则也会出现问题,这里就不展开说了。

另外一种方法是乐观锁。这种方法的解决方案是,在订单表中设置一个Version字段,当操作员A读取订单001时,记录Version的值,在保存时,如果数据库中的Version与之前读到的Version不相等,则报错,否则保存数据并重新生成版本号。这样的结果是,当操作员A保存数据时,如果数据在之前已经被操作员B保存,系统会报错。虽然操作员A不得不放弃对订单001的修改,重新读取数据,但这样好歹不会覆盖其它操作员对订单001的修改。乐观锁有多种实现方式,一种是对数据表的更新操作增加触发器,重新生成版本号,这样做的好处是数据无论是在系统中被其它人更新还是被直接从数据库中更新,都会被侦测到,缺点是如果系统支持多种数据库,需要针对不同的数据库分别编写触发器。另外一种方法是不设置Version字段,在读取数据时记录订单001的全部状态,在更新时比较数据库中的数据是否与读取时的状态一致,如果有任何一个字段的状态不一致就提示错误,在这个方案中,订单的全部状态起到了Version字段的作用。使用这种方法的好处是可以不依赖与具体的数据库编写而且可以侦测出数据直接从数据库中更新的情况,缺点是如果数据表的字段比较多,或有些字段的长度比较大,性能会比较差。最后一种方法是使用数据访问框架(如Nhibernate)来管理Version字段。这种方法的好处是不依赖与具体的数据库,而且性能比较高,缺点是不能侦测出数据不通过系统直接在数据库中被修改的情况,不过这种情况非常罕见,基本可以忽略。这里顺便提一下,Nhibernate在处理Version时有一个问题,就是当Nhibernate向数据库提交数据出现数据库错误时(如字段超长),内存中对象的Version字段还是会被修改,而数据库中的Version字段没有修改,这个时候再次保存数据时系统就会提示数据已经被其它用户修改!这个问题主要会对一些CS架构的系统产生影响,因为很多CS架构的系统,界面是直接绑定业务实体的,所以当数据保存失败,再次提交时,还是使用相同的实体提交,这个时候就会遇到那个问题。不过总的来说这个问题出现的几率都是比较小的,而且这个问题也可以通过改进自己的数据访问框架来解决。对于乐观锁,我个人还是比较推崇最后一种方法。

最后聊一聊这两种锁的使用场景。乐观锁适用于保存冲突出现几率比较小的场景,咱们做的绝大部分系统都可以归为这一类。而对于悲观锁来说,首先它要在读取数据的同时更新UserId字段,而且为了保证并发还要在数据库中加入一些行级锁定,所以它的性能会稍微差一些。而且如果锁定数据的用户非正常退出了,还需要有人手工的解除锁定,会影响用户体验,所以除非保存冲突的几率比较高,否则不要使用该方法。

时间: 2024-10-17 22:59:36

如何解决保存冲突问题的相关文章

Altiium Designer 09 解决局域网冲突的办法(转载)

Altiium Designer 09 解决局域网冲突的办法(转载) 一 通过防护墙禁止进程访问网络: 1.1打开DXP.EXE,然后在360的流量防护墙或WINDOWS防护墙禁止该进程访问网络.注意是局域网内用该软件的人都这样操作. 二通过修改许可文件的办法: 1 局域网内的所有用户都先删除原有的.alf文件.并将ad9.ini备份保存 2 .运行AD9KeyGen,点击“打开模板”,加载ad9.ini,修改参数1) LicenseType=2 (1为有时间限期网络用户2147444857天,

git 解决二进制文件冲突

1.冲突的产生 当我们向远程git服务器提交某一个文件的修改时,恰巧这个文件相同的修改地方其他人也有修改,并且已经提交到服务器,这时冲突就产生了. 通常,当我们合并两个相同的地方都有修改的分支时,都会产生冲突. 2.文本文件冲突解决 出现冲突时git不知道如何自动合并,需要我们解决冲突手动合并. 如果是文本文件,git会在有冲突的地方作上标记(如 HEAD >>> ==== <<< HASH_ID等),标记哪些是当前分支的修改,哪些是其他分支的修改.参考这些标记,解决冲

解决hash冲突之分离链接法

解决hash冲突之分离链接法 分离链接法:其做法就是将散列到同一个值的所有元素保存到一个表中. 这样讲可能比较抽象,下面看一个图就会很清楚,图如下 相应的实现可以用分离链接散列表来实现(其实就是一个linkedList数组) 至于基本的增加.删除和查询的思路都是先根据散列函数来确定遍历哪个链表.然后再到被确定的链表中执行一次查找,然后再进行相应的操作. 接下来就讲几个注意点吧 (一)什么时候需要rehash来扩大散列表的大小 讲这个的时候,先介绍一下什么是装填因子. 装填因子 = 关键字个数 /

大厂面试必问!HashMap 怎样解决hash冲突?

HashMap冲突解决方法比较考验一个开发者解决问题的能力. 下文给出HashMap冲突的解决方法以及原理分析,无论是在面试问答或者实际使用中,应该都会有所帮助. 在Java编程语言中,最基本的结构就是两种,一种是数组,一种是模拟指针(引用),所有的数据结构都可以用这两个基本结构构造,HashMap也一样. 当程序试图将多个 key-value 放入 HashMap 中时,以如下代码片段为例: HashMap<String,Object>m=newHashMap<String,Objec

C++拾遗--多线程:原子操作解决线程冲突

C++拾遗--多线程:原子操作解决线程冲突 前言 在多线程中操作全局变量一般都会引起线程冲突,为了解决线程冲突,引入原子操作. 正文 1.线程冲突 #include <stdio.h> #include <stdlib.h> #include <process.h> #include <Windows.h> int g_count = 0; void count(void *p) { Sleep(100); //do some work //每个线程把g_c

Operation System - Peterson&amp;#39;s Solution算法 解决多线程冲突

Person's solution 是用来一种基于软件的解决关键区域问题的算法(critical-section). 它并不是完美的,有可能不对地工作.并且是限制解决两个进程同步的问题. 可是它非常easy,非常原始,学习起来也是非常轻松的. 代码例如以下: do { flag[i] = true; turn = j; while (flag[j] && turn == j); critical section flag[i] = false; remainder section } wh

如何解决GitHub冲突&lt;一&gt;:GitHubDesktop同步你的分支

原文地址:https://help.github.com/desktop/guides/contributing/syncing-your-branch/ 当一个代码提交被推送到你的github项目时,你可以通过同步远程代码仓库的方式来使你本地的代码复件保持最新. 你必须让你的本地分支和远程仓库保持同步,只有这样,任何被增加到上游分支的新增代码提交才会被正确的更新. 1.更新你本地的上游分支 为了找到哪个分支是上游分支,请看对比图. ·上游分支会出现在最顶部 ·你的项目分支会出现在底部 (1)在

如何解决GitHub冲突&lt;二&gt;:使用命令行解决合并冲突

如何解决GitHub冲突<二>:使用命令行解决合并冲突 原文地址:https://help.github.com/desktop/guides/contributing/syncing-your-branch/ 你可以使用命令行和文本编辑器来解决"合并冲突". 合并冲突往往会发生在以下情况: (1)多个代码更改发生在同一行代码上 (2)一个提交删除了某一个文件而另一个提交尝试去编辑该文件 1.解决同行代码竞争引起的合并冲突 为了解决一个由更改同行代码引起的合并冲突,你必须决

jQuery基础之(五)jQuery自定义添加&quot;$&quot;与解决&quot;$&quot;的冲突

1.自定义添加$ 从上面四篇文章我们看到jQuery的强大,但无论如何,jQuery都不可能满足所有用户的需求,而且有一些需求十分小众,也不适合放到整个jQuery框架中,正是因为这一点,jQuery提供了用户自定义添加“$”的方法. 代码如下: $.fn.disable = function() { return this.each(function() { if (typeof this.disabled != "undefined") this.disable = true; }