在实际的多用户并发访问的生产环境里边,我们经常要尽可能的保持数据的一致性。而其中最典型的例子就是我们从表里边读取数据,检查验证后对数据进行修改,然后写回到数据库中。在读取和写入的过程中,如果在多用户并发的环境里边,其他用户已经把你要修改的数据进行了修改是非常有可能发生的情况,这样就造成了数据的不一致性。
最近在做快钱支付的时候就碰到了这个问题,原来的代码如下:
1. 表Order的结构:
    OrderId   int 自增长
    Status   nvarchar(10)  //未处理时的状态为"wait"
2. 相关SQL语句:
Select Status from order where OrderID= @OrderID
Update Order set Status = 'Y' where OrderID=@OrderID
3.程式伪代码:
var status  = GetOrderStatus(orderid); //获取用户充值状态
if(status= "wait")//如果状态为未处理
   UpdateOrderStatus(orderid);//则更新状态为已处理
   //后台给用户充值的代码


按道理这样的代码是没有问题的,因为对同一个用户而已,并不存在并发的问题,也就不存在一次付款两次充值的问题。
然而快钱的处理方式是用户通过付款后,快钱要重新转到我们的网站来,我们在收到快钱支付成功的请求后,给用户充值,并将再次定向的页面返回给快钱,快钱再定向到支付成功的页面。
流程如下:用户--->GoToPay.aspx-->快钱-->AfterPay.aspx-->快钱-->AfterPayMessage.aspx。
由于快钱使用的是轮循的机制,会每隔一秒钟就访问AfterPay.aspx,因此会多次访问AfterPay.aspx,这时问题出来了:
var status  = GetOrderStatus(orderid); //获取用户充值状态
if(status= "wait")//如果状态为未处理UpdateOrderStatus(orderid);//则更新状态为已处理  //后台给用户充值的代码,会充值两次
在程式还未对Status更新的时候,第二次请求已经到达,这时使用GetOrderStatus,得到的还是"wait",因此会充值两次。


解决方案:
方式一:
使用常规的乐观锁方案
表Order里边加上一列TimeStamp 列,该列是varbinary(8)类型。但是在更新的时候这个值会自动增长。 
Select Status,TimeStamp from order where OrderID= @OrderID-- 更新状态,但是要比较时间戳是否发生了变化.如果没有发生变化,影响行数为1,更新成功.如果发生变化,影响行数为0。
update Order
set Status="Y",
where OrderID=@OrderID and TimeStamp=@timestamp
set @rowcount=@@rowcount程式的修改
var status  = GetOrderStatus(orderid,out timestamp); //获取用户充值状态
if(status= "wait")//如果状态为未处理
   int rowcount = UpdateOrderStatus(orderid,timestamp);
if(rowcount= 1) //状态未更新
  充值
else //快钱第二次过来的时候,返回行数为0
 return   "已经充过值"
endif


方式二:
还是乐观锁方案:
由于表Order的Status本身就可以起到跟timestamp列一样的效果,修改如下:update Order
set Status="Y",
where OrderID=@OrderID and Status="wait"
set @rowcount=@@rowcount程式的修改var status  = GetOrderStatus(orderid); //获取用户充值状态
if(status= "wait")//如果状态为未处理
   int rowcount = UpdateOrderStatus(orderid);
if(rowcount= 1) //状态未更新
  充值
else //快钱第二次过来的时候,返回行数为0
 return   "已经充过值"
endif
此方案更为简单。


方案三:
使用悲观锁的方式,这次修改的SQL语句不是Update 而是Select
如下:
Select Status   from order    with (UPDLOCK) where OrderID= @OrderID
程式完全不用修改:
//获取用户充值状态,快钱第二次过来的时候,如果第一次还未更新,则该订单行还处于锁定状态,因此会等待第一次更新完以将锁释放
var status  = GetOrderStatus(orderid); 
if(status= "wait")//如果状态为未处理
   UpdateOrderStatus(orderid);//则更新状态为已处理
   //后台给用户充值的代码
这种方式最简单,程式完全不用修改,只需要在存储过程中加上with (UPDLOCK)即可。
缺点是对大量的并发性能会很差,而且会引起死锁。当然对于充值这种交易而言,还是可以比较适合的。


这是我写出来的第一篇技术类的文章,可能很多地方讲得不够透彻,希望大家指正。
下一篇文章我想就航空公司售票导致的并发问题提供一个更简单的解决方案。

快钱支付与Sql Server的乐观锁和悲观锁相关推荐

  1. 在SQL Server里为什么我们需要更新锁

    今天我想讲解一个特别的问题,在我每次讲解SQL Server里的锁和阻塞(Locking & Blocking)都会碰到的问题:在SQL Server里,为什么我们需要更新锁?在我们讲解具体需 ...

  2. [精选]MySQL的各种锁(表锁,行锁,悲观锁,乐观锁,间隙锁,死锁)

    不少人在开发的时候,应该很少会注意到这些锁的问题,也很少会给程序加锁(除了库存这些对数量准确性要求极高的情况下),即使我们不会这些锁知识,我们的程序在一般情况下还是可以跑得好好的.因为数据库隐式帮我们 ...

  3. 乐观锁与悲观锁——解决并发问题

    引言   在多用户环境中,在同一时间可能会有多个用户更新相同的记录,这会产生冲突.这就是著名的并发性问题. 典型的冲突有: 丢失更新:一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失.例如: ...

  4. Java开发技巧——并发控制中的乐观锁与悲观锁

    1.为什么需要锁? 在多用户环境中,在同一时间可能会有多个用户新相同的记录,这会产生冲突.这就是的并发性问题. 2.典型的冲突类型: (1)丢失新:一个事务的新覆盖了其它事务的新结果,就是所谓的新丢失 ...

  5. 并发编程中常见的锁机制:乐观锁、悲观锁、CAS、自旋锁、互斥锁、读写锁

    文章目录 乐观锁 VS 悲观锁 悲观锁 乐观锁 CAS CAS机制 ABA问题 CAS的优缺点 互斥锁 VS 自旋锁 互斥锁 自旋锁 对比及应用场景 读写锁 实现方式 读写锁 VS 互斥锁 乐观锁 V ...

  6. python乐观锁和悲观锁

    使用场景: 你银行卡现在100块, A地花了10块,然后A地停电,会有延迟扣款 然后你的公司给你同时发了200工资, 如果公司先査你的钱100,同时A地查也是查到100 你公司先update了,你现在 ...

  7. 程序员过关斩将--数据库的乐观锁和悲观锁并非真实的锁

    菜菜哥,告诉你一个消息 你有男票啦? 非也非也,我昨天出去偷偷面试,结果又挂了 哦,看来公司是真的不想让你走呀 面试官让我说一下乐观锁和悲观锁,我没回答上来,回来之后我查了,数据库没有这两种锁呀 了解 ...

  8. 独占锁、共享锁、更新锁,乐观锁、悲观锁

    转载自   独占锁.共享锁.更新锁,乐观锁.悲观锁 1.锁的两种分类方式 (1)从数据库系统的角度来看,锁分为以下三种类型: 独占锁(Exclusive Lock)       独占锁锁定的资源只允许 ...

  9. mysql行锁还需要乐观锁吗_mysql行锁、表锁。乐观锁,悲观锁

    锁定用于确保事务完整性和数据库一致性. 锁定可以防止用户读取其他用户正在更改的数据,并防止多个用户同时更改相同的数据. 如果不使用锁定,数据库中的数据可能在逻辑上变得不正确,而针对这些数据进行查询可能 ...

最新文章

  1. Service Work
  2. 一、MySQL数据库基础
  3. JAVA进阶教学之(序列化和反序列化)
  4. 看门狗性能软件测试,《看门狗:军团》PC版性能测试 不建议光追,优化极差
  5. php工具箱mysql停止进程_PHP进程卡死和MySQL超时时间的设置方法
  6. (93)FPGA模块例化传递参数(defparam)
  7. Epic Games表示不服苹果垄断案裁决 继续提出上诉
  8. linux防火墙--iptables(三)
  9. LeetCode(389)——找不同(JavaScript)
  10. 荣耀30S首销全平台夺冠 赵明:满足消费者对5G的所有需求和想象
  11. BZOJ 1717: [Usaco2006 Dec]Milk Patterns 产奶的模式( 二分答案 + 后缀数组 )
  12. vp230引脚功能_SN65HVD230:具有待机模式的 3.3V CAN 收发器
  13. 计算机组成原理与汇编语言参考答案,计算机组成原理与汇编语言试题及答案
  14. 五胡十六国、东晋南北朝这280年历史,你知道多少?5000字带你看个清楚明白
  15. UIkit框架之轮播特效
  16. 区别主要在于服务器对带宽的分配:
  17. 平稳性检验和白噪声检验
  18. 作为程序员,在挑选 iPad 时我在想什么?
  19. Teradata 记事本
  20. 环信 php后台集成,集成环信IM功能

热门文章

  1. C语言实现boyer moore(博伊尔-摩尔搜索)算法(附完整源码)
  2. C++class类(I)
  3. C和C++线性表基本概念
  4. C++ Multisets
  5. c++ 智能指针_详解 C++ 11 中的智能指针
  6. 零售连锁管理软件_连锁超市用哪个收银软件好?
  7. 01_Nginx安装,nginx下部署项目,nginx.conf配置文件修改,相关文件配置
  8. 13寸笔记本电脑尺寸_2020笔记本电脑推荐(华为篇)
  9. 更改图片位置_如何轻松快速地将图片转换到JPG/JPEG/PNG/BMP/TIFF
  10. 同时读取两个USB摄像头采集