商城商品超卖处理

首先环境介绍下:商城商品可能存在几个端(PC、APP),其次每个端对应的服务端又可能做了负载均衡(即也有多个服务端)。

要实现的目标和功能:保证商品不会出现超卖的情况。超卖商品后,无法对商品进行发货,是一种不负责任的行为。

方案实现讨论流程

“要实现不超卖,首先商品库存的扣减不能使用框架进行更新,因为框架是设置值,如果在这段时间,又有人购买了,则商品库存必然会出现问题。要采用手写SQL方式。并且sql中还要判断是否大于等于指定的购买量。”

UPDATE `SKU_Info`  SET skuNum=skuNum-1000 WHERE id=‘00293cb7-d8cf-4470-a66d-bb45ca2b130000293cb7-d8cf-4470-a66d-bb45ca2b1300‘ AND skuNum>=1000;

“要实现不超卖,我们可以对方法加上同步锁,这样可以解决”。

“方法加上同步锁后,用户下单将会出现排队的情况,性能有问题。”

“那我们可以实现对同一商品进行加锁,这样可以解决购买不同商品不会相互阻塞。如果有包含关系,也应该加锁。比如A用户购买商品1和商品2,B用户购买商品1,因为他们都有商品1,则应该加锁。”

“你这个方案应该可以解决问题,采用分布式锁的方式可以解决,我们可以使用redis来做。”

“是的,确实可以解决问题,并且多个服务端也不存在问题了,就这么干。”

“我们可以对订单中的所有商品的sku值进行排序,拼接成一个skuId值,然后MD5的值作为key,其它订单进来方法时,按同样的操作进行检测是否正在下单,如果是,则等待。”

“你这种方案忽略了商品不同的情况,就比如上面的例子中,A购买商品1和商品2,B购买商品1,那么他们的key是不同的,因而达不到效果。”

“我们可以对每个商品sku的id定义个锁,这样每次购买时,我们针对每个商品进行检测,这样就可以了,绝对能够保证同步。”

“这种方法可行,不过还是存在一个问题,服务端与redis的连接次数会比较多,如果一个用户下单商品种类较多,那么仍然会比较慢,但这确实不失为一个好的方案。”

“既然这个方案仍然有可能有问题,那么还有没有其它的方案。”

“数据库本身是有锁的,可以实现锁同步的问题,那么有没有办法使用到数据库的锁来解决这个问题?”

“对呀,我们可以写SQL语句去循环扣减库存,最后判断数据库影响行数与商品种类是否匹配?如果不匹配,则是扣减失败,进行还原,如果匹配,则扣减成功!”

“经过测试,我们用的MySQL不支持这种方案,里面需要用到if判断,而if判断必须要在存储过程中才能使用。”

“那我们可以使用存储过程来做。代码如下”

DELIMITER $$
USE anke_skucenter$$
CREATE PROCEDURE minusSkuNum()
BEGIN
SET AUTOCOMMIT=0;
START TRANSACTION;
UPDATE SKU_Info SET skuNum=skuNum-100 WHERE id=‘0031394c-8058-49f5-9ba9-f971480ac2f2‘ AND skuNum>=100;
 IF(SELECT ROW_COUNT()<=0)THEN
	ROLLBACK;
	END IF;
UPDATE `SKU_Info`  SET skuNum=skuNum-1000 WHERE id=‘00293cb7-d8cf-4470-a66d-bb45ca2b130000293cb7-d8cf-4470-a66d-bb45ca2b1300‘ AND skuNum>=1000;
IF(SELECT ROW_COUNT()<=0)THEN
	ROLLBACK;
END IF;
COMMIT;
SET AUTOCOMMIT=1;
END$$

“这个是初步的存储过程,仍然需要将update语句变更为循环,改变传入参数为商品id和数量,有谁会写?”

“额,目前大家都不会写,并且这个循环看上去也挺复杂的。”

“那么我们能不能在mybatis中获取多条更新语句的影响行数?”

“不能,没有任何框架支持,并且mysql本身就不支持,要不然也不会需要存储过程了。”

“既然多条SQL不行,能不能放到一条SQL中去做更新呢?”

“先baidu下”

“哈哈,找到了,我们查询的时候有时候回用到case when,那么我们更新的时候是否可以使用这个呢?尝试代码如下:”

update SKU_Info set skuNum=skuNum-(case when id=‘0031394c-8058-49f5-9ba9-f971480ac2f2‘  then 100
when id=‘00293cb7-d8cf-4470-a66d-bb45ca2b1300‘ then 1000 end)
where (id=‘0031394c-8058-49f5-9ba9-f971480ac2f2‘ AND skuNum>=100)
or (id=‘00293cb7-d8cf-4470-a66d-bb45ca2b1300‘ AND skuNum>=1000);

“经过测试,该段代码执行正常,并且能够正常返回需要的影响行数。”

“这个循环的SQL编写在mybatis中不难,那么判断最后扣减结果与商品种类不同时,如何进行补偿呢?”

“这个可以使用@Transactional,我们在方法上加此注解,在方法内部判断,如果不同,我们就抛出一个自定义异常,这样就会自动进行回滚了。”

“测试一下”

“经过几轮测试,确实可行,就这样做。”

“具体实施为:先生成订单,然后进行扣减,如果捕获到扣减失败的自定义异常,则对生成的订单执行删除标记。但存在一个问题,就是标记订单为删除状态失败的情况,这个订单仍然存在,也是超卖了。”

“可以调整下,改为先进行扣减,扣减成功再生成订单,这样可以避免此问题。”

“嗯,此方法可以解决超卖问题,可能会存在商品扣减成功,但订单未生成的情况。”

“这种问题会存在,但比超卖要好很多。”

“嗯嗯”

“嗯嗯”

原文地址:https://www.cnblogs.com/maomao999/p/9223721.html

时间: 2024-08-30 10:52:09

商城商品超卖处理的相关文章

订单并发商品超卖问题解决

问题:商品超卖(库存数出现负数). 模拟并发: goods商品表: /** * 下单 * @return string * @throws \yii\db\Exception */ public function actionIndex() { $redis = Yii::$app->redis; // 使用redis做一些统计 $redis->incr('total'); // 自增(记录一共成功进来了多少个请求) //$redis->del('total'); //$redis-&g

以商品超卖为例讲解Redis分布式锁

本案例主要讲解Redis实现分布式锁的两种实现方式:Jedis实现.Redisson实现.网上关于这方面讲解太多了,Van自认为文笔没他们好,还是用示例代码说明. 一.jedis 实现 该方案只考虑Redis单机部署的场景 1.1 加锁 1.1.1 原理 jedis.set(String key, String value, String nxxx, String expx, int time) key: 使用key来当锁,因为key是唯一的; value: 我传的是唯一值(UUID),很多童鞋

避免商品超卖的4种方案

原始方案(失败):在每次下订单前我们判断促销商品的数量够不够,不够不允许下订单,更改库存量时加上一个条件,只更改商品库存大于0的商品的库存,当时我们使用ab进行压力测试,当并发超过500,访问量超过2000时,还是会出现超卖现象. public function buyOne() { $shop = Shop::find(1); if ($shop->number > 0) { DB::update("update shop set number = number - 1 where

超卖频发or商品滞销?压倒卖家的最后一根稻草竟是库存!

超卖频发or商品滞销?压倒卖家的最后一根稻草竟是库存! 云南逸神生态茶业有限公司便是一家从最初的单体销售到目前的生产.加工.销售一体化经营,并拥有60多家遍布云南省内.省外城市的直营专卖店.想要发展壮大,除了产品品质之外,更重要的是方法,拥有60多家门店的"逸神"已经出现了库存不准导致的商品超卖,商品滞销带来的库存积压,如无法解决这些问题,发展壮大只能是个遥远的梦.逸神管理者具有长远的眼光,他认为想要把逸神发展成为更大的连锁企业,不仅依靠逸神的销售团队,同时还要借助先进的管理工具实现对

大型车祸现场,电商秒杀超卖,这个锅到底有谁来背?

背景 小明在一家在线购物商城工作,最近来了一个新需求,需要他负责开发一个商品秒杀模块,而且需求很紧急,老板要求必须尽快上线. 方案 小明一开始是这么做的,直接用数据库锁进行控制,获取秒杀商品数量并加锁,如果数量大于零则成功,否则秒杀失败. @Override @Transactional public Result startSeckilDBPCC_ONE(long seckillId, long userId) { //获取秒杀商品数量并加锁 String nativeSql = "SELEC

mysql处理高并发,防止库存超卖

先来就库存超卖的问题作描述:一般电子商务网站都会遇到如团购.秒杀.特价之类的活动,而这样的活动有一个共同的特点就是访问量激增.上千甚至上万人抢购一个商品.然而,作为活动商品,库存肯定是很有限的,如何控制库存不让出现超买,以防止造成不必要的损失是众多电子商务网站程序员头疼的问题,这同时也是最基本的问题. 从技术方面剖析,很多人肯定会想到事务,但是事务是控制库存超卖的必要条件,但不是充分必要条件. 举例: 总库存:4个商品 请求人:a.1个商品 b.2个商品 c.3个商品 程序如下: beginTr

PHP并发、超卖处理

做电商网站,经常会有各种秒杀和热门商品,所以高并发的处理一直是电商最重要的事情.这里记录下当初自己是如何处理的 写在前面: 1.本文设计到的并发处理均是针对纵向,不针对横向扩展,即只设计从PHP层面到数据库层面的处理,不涉及多台服务器,集群.大带宽等的横向设计. 2.本文中涉及到的高并发并不是淘宝京东等几百万几千万等的高并发,仅仅只是普通最多上万的并发处理 3.本文不对悲观锁乐观锁做设计 问题: 普通电商中的秒杀中的并发问题,超卖问题 实例:商品数量为100,秒杀人数为10000,整点开始秒杀

秒杀的性能和超卖

一.秒杀带来了什么? 秒杀或抢购活动一般会经过[预约][抢订单][支付]这3个大环节,而其中[抢订单]这个环节是最考验业务提供方的抗压能力的. 抢订单环节一般会带来2个问题: 1.高并发 比较火热的秒杀在线人数都是10w起的,如此之高的在线人数对于网站架构从前到后都是一种考验. 2.超卖 任何商品都会有数量上限,如何避免成功下订单买到商品的人数不超过商品数量的上限,这是每个抢购活动都要面临的难题. 二.如何解决? 首先,产品解决方案我们就不予讨论了.我们只讨论技术解决方案 1.前端 面对高并发的

[转] 基于MySQL的秒杀核心设计(减库存部分)-防超卖与高并发

商品详情页面的静态化,varnish加速,秒杀商品库独立部署服务器这种就略过不讲了.只讨论库存部分的优化 mysql配置层面的优化可以参考我的这篇文章 <关于mysql innodb引擎性能优化的一点心得> 重点设计在数据库层面. 2张表: 第一张:判重表(buy_record),该用户有没秒杀过该商品 字段: id, uid, goods_id, addtime 第二张表:商品表 goods 字段: goods_id   goods_num 方案1: start transaction; s