很多做过电商系统的人应该知道,我们在设计电商系统中关于商品库存扣减时,在大部分情况下(并发量不高时),商品库存都可以直接在关系型数据库中进行扣减,那么在限时抢购活动正式开始后,那些单价比平时更给力、更具吸引力的热卖商品大家肯定都会积极踊跃地参与抢购,这必然会产生大量针对数据库同一行记录的并发更新操作。因此数据库为了保证原子’性, InnoDB 引擎默认会对同一行数据记录加锁,把前端的并发请求变成串行操作,以确保数据更新时的正确性。
 
如果直接在数据库中扣减库存,应该如何避免商品超卖呢?
 
在生产环境中我们可以通过乐观锁机制来避免这个问题。所谓乐观锁,简单来说,就是在 item表中建立一个 version 字段。假设某一个热卖商品的实际库存为n,出于对性能的考虑,查询库存操作是不建议加 for update (悲观锁,代价太大)的,那么在并发场景下,必然会导致多个用户拿到的 stock 和 version都一样。因此当第1个用户成功扣减商品库存后, 需要将 item表中的 version加1, 当第 2个用户扣减库存时,由于 version 不匹配,那么无法扣减成功,并且会抛出:StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)的异常。
 
当然,对于乐观锁一般的做法,是较为友好的提醒用户:“数据已经被其他人更改,请重新操作!”。可能一般系统这种方式可行,但是对于高并发的电商系统来讲,这就非常不友好,甚至直接导致客户大面积流失,那么有没有相对较为好的解决方案呢?
 
实际上,我们可以这样干
 
为了提升库存扣减的成功率,可以适当进行重试,如果库存不足,则说明商品已经售罄,反之扣减库存后 version 继续加1 。关于在数据库中使用乐观锁扣减库存的伪代码,如下所示:
除了使用乐观锁,还可以在扣减商品库存时,利用“实际库存数 大于 扣减库存数” 作为条件来替代 version 匹配,防止商品超卖。相对于乐观锁,采用这种方式会更加
直接,由于充分利用了 InnoDB 引擎提供的行锁特性,因此大大提升和保障了库存扣减的成功率,如下所示:
 
/* 实际库存数 大于 扣减库存数*/
UPDATE item SET stock=stock - 扣减库存数 WHERE item id=l AND stock >= 扣减库存数
 
两种方案均能够有效避免商品超卖,当然还是推荐使用乐观锁的使用方案。
 
 
 
 

alibaba工程师,如何解决乐观锁冲突问题?的更多相关文章

  1. 【进阶之路】Mybatis-Plus中乐观锁@version注解的问题与解决方案

    大家好,我是练习java两年半时间的南橘,从一名连java有几种数据结构都不懂超级小白,到现在懂了一点点的进阶小白,学到了不少的东西.知识越分享越值钱,我这段时间总结(包括从别的大佬那边学习,引用)了 ...

  2. 使用mysql乐观锁解决并发问题

    案例说明: 银行两操作员同时操作同一账户.比如A.B操作员同时读取一余额为1000元的账户,A操作员为该账户增加100元,B操作员同时为该账户扣除50元,A先提交,B后提交.最后实际账户余额为1000 ...

  3. 使用mysql乐观锁解决并发问题思路

    本文摘自网络,仅供个人学习之用 案例说明: 银行两操作员同时操作同一账户.比如A.B操作员同时读取一余额为1000元的账户,A操作员为该账户增加100元,B操作员同时为该账户扣除50元,A先提交,B后 ...

  4. ElasticStack系列之六 & 版本冲突处理之乐观锁

    悲观并发控制(PCC) 这一点在关系数据库中被广泛使用.假设这种情况很容易发生,我们就可以阻止对这一资源的访问.典型的例子就是当我们在读取一个数据前先锁定这一行,然后确保只有读取到数据的这个线程可以修 ...

  5. 使用MySQL乐观锁解决超卖问题

    在秒杀系统设计中,超卖是一个经典.常见的问题,任何商品都会有数量上限,如何避免成功下订单买到商品的人数不超过商品数量的上限,这是每个抢购活动都要面临的难点. 1 超卖问题描述 在多个用户同时发起对同一 ...

  6. mybatis乐观锁实现,解决并发问题

    银行两操作员同时操作同一账户就是典型的例子.比如A.B操作员同时读取一余额为1000元的账户,A操作员为该账户增加100元,B操作员同时为该账户扣除50元,A先提交,B后提交.最后实际账户余额为100 ...

  7. Hibernate解决高并发问题之:悲观锁 VS 乐观锁

    高并发问题是程序设计所必须要解决的问题,解决此类问题最主要的途径就是对对程序进行加锁控制.hibernate对加锁机制同样做出了实现,常用加锁方式为悲观锁和乐观锁.悲观锁指的是对数据被外界(包括本系统 ...

  8. 使用数据库乐观锁解决高并发秒杀问题,以及如何模拟高并发的场景,CyclicBarrier和CountDownLatch类的用法

    数据库:mysql 数据库的乐观锁:一般通过数据表加version来实现,相对于悲观锁的话,更能省数据库性能,废话不多说,直接看代码 第一步: 建立数据库表: CREATE TABLE `skill_ ...

  9. 基于Django的乐观锁与悲观锁解决订单并发问题的一点浅见

    订单并发这个问题我想大家都是有一定认识的,这里我说一下我的一些浅见,我会尽可能的让大家了解如何解决这类问题. 在解释如何解决订单并发问题之前,需要先了解一下什么是数据库的事务.(我用的是mysql数据 ...

随机推荐

  1. Docker的save和export命令的区别

    我最近在玩Docker,一种应用程序容器和Linux的虚拟技术.它太酷了,创建Docker镜像和容器只需要几分钟.所有的工作都是开箱即用的. 在结束我一天的工作之前,我希望能保存下我的工作.但我在Do ...

  2. .net core3.1 web api中使用newtonsoft替换掉默认的json序列化组件

    在微软的文档中,有着较为详细的替换教程 https://docs.microsoft.com/zh-cn/aspnet/core/web-api/advanced/formatting?view=as ...

  3. CAD制图系列之怎么画平行线

    CAD怎么画平行线 输入O,点空格,输入距离100,选择已知的线,往你要偏移的方向就好! 具体方法如下:1.打开CAD制图 2.输入快捷键O 3.按下空格键 4.输入你所要的距离,例如12,并按下空格 ...

  4. Dubbo(三):深入理解Dubbo源码之如何实现服务引用

    一.前言 前面讲了服务是如何导出到注册中心的.其实Dubbo做的一件事就是将服务的URL发布到注册中心上.那现在我们聊一聊消费者一方如何从注册中心订阅服务并进行远程调用的. 二.引用服务时序图 首先总 ...

  5. Codeforces_851

    A.分三种情况. #include<bits/stdc++.h> using namespace std; int n,k,t; int main() { ios::sync_with_s ...

  6. 用Java实现简单的网络聊天程序

    Socket套接字定义: 套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开.读写和关闭等操作.套接字允许应用程序将I/O插入到网络中,并与网络中的其他 ...

  7. DaSiamRPN学习

    9月14日,2018年视觉目标跟踪挑战赛(Visual-Object-Tracking Challenge 2018)的结果在ECCV Workshop上揭晓.VOT2018共设三项任务:Baseli ...

  8. 我的一个配置redux(实现一次存储与调用方法)之旅

    前言 : 今天呢,就配置一下redux,redux的重要性呢,就叭叭叭一大堆,什么也没有带着配置一次来的重要,因为许多涉及到的属性和方法,用法是活的,但格式是需要记忆的. 过程中不要嫌我唠叨,有的地方 ...

  9. Windows搭建IIS服务器使用NATAPP实现内网穿透

    目的:外网可以访问本地网页. 步骤: 一.实现内网访问 1.Win+Q搜索[控制面板],选择[程序],点击[启用或关闭Windows功能], 2.勾选[Internet Information Ser ...

  10. 基于 H5和 3D WebVR 的可视化虚拟现实培训系统

    前言 2019 年 VR, AR, XR, 5G, 工业互联网等名词频繁出现在我们的视野中,信息的分享与虚实的结合已经成为大势所趋,5G 是新一代信息通信技术升级的重要方向,工业互联网是制造业转型升级 ...