解决并发情况下库存减为负数问题--update2016.04.24
场景:
一个商品有库存,下单时先检查库存,如果>0,把库存-1然后下单,如果<=0,则不能下单,事务包含两条sql语句:
select quantity from products WHERE id=;
update products set quantity = ($quantity-) WHERE id=;
在并发情况下,可能会把库存减为负数(两个进程同时select出来的都>0,然后都会执行update),怎么办呢?
方法1:
InnoDB支持通过特定的语句进行显示加锁:
select...lock in share mode
select...for udpate
select quantity from products WHERE id= for update;
update products set quantity = ($quantity-) WHERE id=;
但是执行for update会产生一些其他的影响
1.select语句变慢
2.一些优化无法正常使用,例如索引覆盖扫描
3.很容易造成服务器的锁争用问题
方法二:
把udpate语句写在前边,先把数量-1,之后select出库存如果>-1就commit,否则rollback。
update products set quantity = quantity- WHERE id=;
select quantity from products WHERE id= for update;
上边的事务中先执行了update,所以id=3的行被加了行锁,只有commit/rollback是才会被释放(事务中锁是逐步获得的,但是都是commit时所释放的)。很好的解决了并发问题。
方法三:
update语句在更新的同时加上一个条件
$quantity = select quantity from products WHERE id=3;
update products set quantity = ($quantity-1) WHERE id=3 and queantity = $quantity;
这样虽然select语句没有加锁,但是因为mysql的事务隔离级别是可重复读,所以其他事务的修改不会影响到select的结果,当执行到update时,如果有其他事务锁住了这条记录,update会等待,等到其他事务释放锁,update会执行,但此时如果quantity的数量已经被修改,update的执行会返回影响行数为0。
原因:
场景比如先select出来的$quantity=3,然后执行update的条件为id = 3 and quantity=3,执行更新返回影响函数为0,但再次执行select时发现id=3的记录quantity确实是3啊,怎么有这条记录却更新不了呢?
这”归功于“mysql的事物隔离级别和MVCC,当第一次select时$quantity=3,然后其他事务先于这个update执行了,导致update的条件并没有找到合适的记录,因为在可重复读级别中,update的读是“当前读”,读取的是最新的数据。而再次select时查到的$quantity还是等于3,因为对于select的读是“快照读”,读取的是历史数据,这也是可重复读级别的特性。
注意方法二中:当一个事务提交后如果库存正好是0,此时另一个事务读取到的还是1,这就是可重复读,但是由于先执行的update,update执行完之后的库存就是-1,此时再执行select的时候的结果就是-1,所以可重复读是指当前事务没有写语句的时候,如果写语句发生则是建立在其他事务提交后的结果基础上的。
详细的Innodb中的事务隔离级别可以看美团点评技术团队的一篇文章,有对于MVCC和间隙锁的解释:http://tech.meituan.com/innodb-lock.html
解决并发情况下库存减为负数问题--update2016.04.24的更多相关文章
- 利用redis实现分布式事务锁,解决高并发环境下库存扣减
利用redis实现分布式事务锁,解决高并发环境下库存扣减 问题描述: 某电商平台,首发一款新品手机,每人限购2台,预计会有10W的并发,在该情况下,如果扣减库存,保证不会超卖 解决方案一 利用数据 ...
- PHP通过加锁实现并发情况下抢码实现
需求:抢码功能 要求: 1.特定时间段才开放抢码: 2.每个时间段放开的码是有限的: 3.每个码不允许重复: 实现: 1.在不考虑并发的情况下实现: function get_code($len){ ...
- 关于WCF服务在高并发情况下报目标积极拒绝的异常处理
最近弄了个wcf的监控服务,偶尔监控到目标服务会报一个目标积极拒绝的错误.一开始以为服务停止了,上服务器检查目标服务好好的活着.于是开始查原因. 一般来说目标积极拒绝(TCP 10061)的异常主要是 ...
- WCF服务在高并发情况下报目标积极拒绝的异常处理 z
http://www.cnblogs.com/kklldog/p/5037006.html wcf的监控服务,偶尔监控到目标服务会报一个目标积极拒绝的错误.一开始以为服务停止了,上服务器检查目标服务好 ...
- 并发情况下synchronized死锁
存在缺陷的代码: public class DataPropertyIdAndNameRepositoryImpl{ /** 发布标志 */ private volatile boolean publ ...
- Jackson高并发情况下,产生阻塞
情况:在高并发情况下,查看线程栈信息,有大量的线程BLOCKED. 从线程栈得知,线程栈中出现了阻塞,锁在了com.fasterxml.jackson.databind.ser.SerializerC ...
- Linux的虚拟内存管理-如何分配和释放内存,以提高服务器在高并发情况下的性能,从而降低了系统的负载
Linux的虚拟内存管理有几个关键概念: Linux 虚拟地址空间如何分布?malloc和free是如何分配和释放内存?如何查看堆内内存的碎片情况?既然堆内内存brk和sbrk不能直接释放,为什么不全 ...
- 高并发情况下分布式全局ID
1.高并发情况下,生成分布式全局id策略2.利用全球唯一UUID生成订单号优缺点3.基于数据库自增或者序列生成订单号4.数据库集群如何考虑数据库自增唯一性5.基于Redis生成生成全局id策略6.Tw ...
- PHP通过加锁实现并发情况下抢码功能
本文基于php语言使用加锁实现并发情况下抢码功能,特定时间段开放抢码并不允许开放的码重复: 需求:抢码功能 要求: 1.特定时间段才开放抢码: 2.每个时间段放开的码是有限的: 3.每个码不允许重复: ...
随机推荐
- python中的字符数字之间的转换函数
int(x [,base ]) 将x转换为一个整数 long(x [,base ]) 将x转换为一个长整数 float(x ) ...
- PHP读取XML
books.xml文件: 代码 <books> <book> <author>Jack Herrington</author> <title> ...
- bzoj4401: 块的计数
首先,块的大小确定的话,可以发现方案最多只有1种 然后就可以O(nsqrt(n))搞,不过会TLE 接着我们又发现,一个节点可以作一个块的根,当且仅当该节点的size能被块的大小整除 然后就可以O(n ...
- 面向对象_python
面向对象_python 类(Class): 用来描述具有相同的属性和方法的对象的集合.它定义了该集合中每个对象所共有的属性和方法.对象是类的实例. 类变量:类变量在整个实例化的对象中是公用的.类变量定 ...
- bzoj 1101 zap
gcd(x,y)=d-->gcd(x/d,y/d)=1. 即求Σ(i<=n/d)Σ(j<=m/d) e(gcd(i,j)) 因为e=miu×1,可以卷积. 因为多组询问,需要sqrt ...
- js-JavaScript高级程序设计学习笔记13
第十五章 canvas绘图 1.WebGL是针对Canvas的3D上下文. 2.类型化数组 WebGL涉及的复杂计算需要提前知道数值的精度,而标准的JS数值无法满足需求.因此WebGL引入了一个概念, ...
- 【VS2013】设定Nuget代理
@tags "visual studio 2013" nuget vs2013中用nuget想必是一件很爽的事情,就像java里面用maven来安装各种包一样.有时候网络不好,nu ...
- Linux fork()、exec() Hook Risk、Design-Principle In Multi-Threadeed Program
目录 . Linux exec指令执行监控Hook方案 . 在"Multi-Threadeed Program"环境中调用fork存在的风险 . Fork When Multi-T ...
- 软件产品案例分析(K米 APP)
关于 K米 -- 的案例分析 产品 K米的APP (全国KTV点歌,手机直播,互动,交友,预订)的Android客户端 第一部分 调研,评测 评测: 上手体验 第一次用这一类的软件,之前去KTV的时候 ...
- PhyLab2.0设计分析阶段任务大纲(α)
任务概述 由于接手软剑攻城队的PhyLab项目,省去了用户需求分析.团队编码规范.用户界面原型设计和后端逻辑设计的大部分环节,因此前期的主要任务落在了用户使用反馈.功能优化增改方向.用户体验优化以及源 ...