一般电子商务网站会有团购,秒杀等活动,而这样的活动特点是请求量激增,数以万计的用户会抢购一个商品,这样会面临活动商品库存有限,高并发下如何控制库存不出现超卖的问题。

注意:

我们的数据存放在MySQL中

使用的语言是Java

为何会发生超卖

一般库存扣除的逻辑代码如下:

//remainder为剩余库存数量

int remainder=statement.query("select remainder from stock where stock_id='$STOCK_ID$'");

//amount为本次订单抢购数量

if(remainder>=amount!=0){

statement.execute("update stock set remainder=remainder-amount where stock_id='$STOCK_ID$'")

}

防止超卖我们首先想到的是通过事务去解决这个问题:

startTransaction();//开启事务

try{

//remainder为剩余库存数量

int remainder=statement.query("select remainder from stock where stock_id='$STOCK_ID$'");

//amount为本次订单抢购数量

if(remainder>=amount!=0){

statement.execute("update stock set remainder=remainder-amount where stock_id='$STOCK_ID$'")

}

}catch(Exception e){

rollback();//回滚

}

commit();//提交事务

上面的事务下执行逻辑其实隐藏一个很大的漏洞-有可能库存会变为负数(即超卖),具体分析如下:

由于是高并发,假设有三个用户a,b,c同时抢购该物品,并进入到了这个事务中,这三个用户查到的库存数是一样的(MySQL rr级别下总是读取事务开始时的行数据)

然后进入到update,假设这三个用户同时进入update操作,这个时候由于 行级锁的排他性限制,MySQL会将update操作串行化

上面update执行完后,有可能会发生库存变为负数的情况(超卖)

怎么解决超卖

最简单的方式("锁方式")

针对上述超卖的情况我们可以通过更改下执行sql代码来实现:

startTransaction();//开启事务

try{

statement.execute("update stock set remainder=remainder-amount where stock_id='$STOCK_ID$' and $remainder>=$amount")

}

}catch(Exception e){

rollback();//回滚

}

commit();//提交事务

上面的修改可以杜绝库存超卖的现象。注意以上在MySQL一致性非锁定读(rr隔离级别下)。

针对以上,我们可以换个思路,在库存数据结构中加入version字段来控制记录修改版本,也可以解决上述问题,如下:

startTransaction();//开启事务

try{

//remainder为剩余库存数量

int remainder=statement.query("select remainder,version from stock where stock_id='$STOCK_ID$'");

//amount为本次订单抢购数量

if(remainder>=amount!=0){

statement.execute("update stock set remainder=remainder-amount,version=version+1 where stock_id='$STOCK_ID$' and version=$version")

}

}catch(Exception e){

rollback();//回滚

}

commit();//提交事务

但是我们真的能这么做吗?我们的业务可是高并发,面对的是1万+TPS,那如果按上面继续执行会遇到什么问题?

很显然不能,在高并发下,会有很多这样的修改(update),每个请求都需要等待"锁",某些请求可能永远都获取不到锁,这种请求就会卡在那里,直到超时。同时,由于这种写请求很多,会造成大量的请求超时,连锁反应就是应用系统连接数被耗光,直至系统异常crash。即使重启系统,由于请求量大,系统也会立马挂掉。

高并发下如何解决超卖

引入缓存

主要思路是:

首先在团购秒杀开始前将需要的物品库存信息放入缓存中

使用锁来处理其并发请求

将缓存中的数据同步到数据库。

我们此处使用redis作为缓存。

应用操作redis减库存的大体思路为:

首先通过redis api监听相关物品的库存信息,在事务开启前保证该物品库存信息无人修改

获取现有库存信息,判断库存不为0并且当前库存量大于等于订单所需数量

满足上述2的话则进行扣除操作

如果在1的过程中有别人更新了该物品库存信息版本,则重试

知道库存为0或者剩余库存不满足当前订单扣除数量退出

具体代码如下:

public void secondBuyProduct(Jedis jedis, String stockId, int orders) {

//CAS重试

while (true) {

try {

//监视key,如果在后续事务执行之前key的值被其他命令所改动,那么事务将被打断

jedis.watch(stockId);

int prdNum = Integer.parseInt(jedis.get(stockId));

//判断库存是否满足订单数量要求

if (prdNum > 0 && prdNum - orders >= 0)) {

Transaction transaction = jedis.multi();

//减库存并写入

transaction.set(stockId, String.valueOf(prdNum - orders));

List res = transaction.exec();

//事务提交后如果为null,说明key值在本次事务提交前已经被改变,本次事务不执行。

if (res != null && !res.isEmpty()) {

System.out.println("抢购成功!");

break;

}

} else {

System.err.println("被抢光了!");

break;

}

} catch (Exception e) {

System.err.println("抢购出错:" + e.toString());

e.printStackTrace();

} finally {

jedis.unwatch();

}

}

}

mysql超卖问题处理_高并发下超卖问题及如何解决相关推荐

  1. mysql并发获取唯一数值_高并发分布式环境中获取全局唯一ID[分布式数据库全局唯一主键生成]...

    需求说明 在过去单机系统中,生成唯一ID比较简单,可以使用MySQL的自增主键或者Oracle中的sequence, 在现在的大型高并发分布式系统中,以上策略就会有问题了,因为不同的数据库会部署到不同 ...

  2. mysql余额高并发_高并发下作余额扣减的一些经验

    前一段时间参加了优化一个老的计费系统,学习了一些高并发下做余额扣减的常用手段,也做了一些尝试,因此在这里总结记录一下. 问题描述 对于一个计费系统来说,并发问题事实上分为两类,一类是应用并发高,也就是 ...

  3. .net core高并发_高并发下的Node.js与负载均衡

    阅读本文约需要6分钟 大家好,我是你们的导师,我每天都会在这里给大家分享一些干货内容(当然了,周末也要允许老师休息一下哈).上次老师跟大家分享了下浅谈前端自动化构建的相关知识,今天跟大家分享浅谈前端自 ...

  4. automation 服务器不能创建对象_高并发服务器逻辑处理瓶颈,如何解决?

    高并发服务器逻辑处理瓶颈,如何解决?首先我们先了解什么是并发! 并发,在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有 ...

  5. mysql并发插入死锁_高并发下insert死锁 · Issue #ITUNR · baomidou/mybatis-plus - Gitee.com...

    当前使用版本(必须填写清楚,否则不予处理) springboot版本:2.0.3.RELEASE mybatis版本:3.0.5 jdk:1.8 该问题是怎么引起的?(最新版上已修复的会直接close ...

  6. redis和mysql数据不一致_高并发下为什么 redis 和数据库不一致?怎么解决?

    现在的web架构一般都用redis作为缓存层来减轻数据库的压力,数据在此架构下的读取问题,一般都是先判断redis缓存是否有数据,如果有,直接返回,否则读取数据库的数据,写入redis,返回数据,这是 ...

  7. sob攻略超详细攻略_北海涠洲岛旅游超详细住宿攻略!!!

    涠洲岛虽然不大,但是上面的民宿和酒店却星罗棋布,如何挑选最适合自己行程的住宿是旅客们计划行程中最耗时耗力的一部分,这篇攻略详细的分析了涠洲岛各个位置民宿和酒店的优势与劣势,是准备出行涠洲岛的朋友们必看 ...

  8. 华擎主板bios设置图解_【华擎Z170评测】BIOS设置及超频方法简介_华擎 Z170 超频方程式_主板评测-中关村在线...

    由于本次使用了是Intel酷睿i5-6600K处理器,默认频率为3.5GHz-3.9GHz,因此我们首先将处理器超频至4.5GHz,为了确定平台的稳定性,此时需要将处理器的电压调整到1.35V.具体操 ...

  9. 低版本Druid连接池+MySQL驱动8.0,在高并发下出现线程阻塞、性能受限问题

    现象 应用升级MySQL驱动8.0后,在并发量较高时,查看监控打点,Druid连接池拿到连接并执行SQL的时间大部分都超过200ms 对系统进行压测,发现出现大量线程阻塞的情况,线程dump信息如下: ...

  10. 超轻粘土机器人_当电子元件和超轻粘土融合在一起是什么样?DFRobot电子粘土 既能玩粘土又能学知识...

    当电子元件和超轻粘土融合在一起是什么样?DFRobot电子粘土 既能玩粘土又能学知识 2020-05-01 17:12:43 8点赞 17收藏 1评论 上次逛街的时候偶遇一间店铺,叫DFRobot 里 ...

最新文章

  1. 计算机软考证书英文名称完全翻译指南
  2. 第5章 Python 数字图像处理(DIP) - 图像复原与重建11 - 空间滤波 - 自适应滤波器 - 自适应局部降噪、自适应中值滤波器
  3. 法律规则鬼畜图解||全面易懂的旅游投诉赔偿标准
  4. python 标签字体大小_这文档动画,怎么用 Python 实现的?
  5. Spark开发指南(0.8.1中文版)
  6. 优先经验回放(Prioritized Experience Replay)
  7. 通过hosts文件配置域名ip
  8. linux外设驱动实现专栏:各虚拟外设驱动代码实现集合
  9. jq html怎么优化seo,长沙网站seo:浅谈SEO优化技巧之HTML结构调整!
  10. 三口烧瓶规格有哪些_三口烧瓶有哪些规格,用途是什么 | | 化工资讯网
  11. 知道创宇区块链安全实验室|Deus Finance预言机攻击事件分析
  12. mysql 基础语法3
  13. 淘宝详情页排版布局怎么做?大神导航,一个神奇的网站,从此开启大神之路!
  14. 从互联网+角度看云计算的现状与未来(2)
  15. 字节跳动校招内推开始了
  16. 听Polychain Capital创始人Olaf Carlson- Wee讲述他为何愿意为Celo背书
  17. linux查看并安装字体
  18. User root is not allowed to impersonate anonymous
  19. 你们要的线上GC问题案例来啦
  20. 华三交换机使用U盘导入/出配置文件

热门文章

  1. 如何系统学习经济学 -- 来自知乎建议
  2. JSP智能小区物业管理系统
  3. dismiss和remove_你真的了解iOS中控制器的present和dismiss吗?
  4. 人力资源书籍排行榜,这些好书不容错过!
  5. PCL库中I/O操作
  6. 机器学习:PageRank
  7. 阿里云centos7系统下载
  8. SaaS到底是什么?如何做?
  9. 如何关闭“数据执行保护”?
  10. 你有没有一个御用冷笑话 说来听听~