zoukankan      html  css  js  c++  java
  • 商城商品超卖处理

    首先环境介绍下:商城商品可能存在几个端(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,我们在方法上加此注解,在方法内部判断,如果不同,我们就抛出一个自定义异常,这样就会自动进行回滚了。”

    “测试一下”

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

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

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

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

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

    “嗯嗯”

    “嗯嗯”

  • 相关阅读:
    DRUPAL-PSA-CORE-2014-005 && CVE-2014-3704 Drupal 7.31 SQL Injection Vulnerability /includes/database/database.inc Analysis
    WDCP(WDlinux Control Panel) mysql/add_user.php、mysql/add_db.php Authentication Loss
    Penetration Testing、Security Testing、Automation Testing
    Tomcat Server Configuration Automation Reinforcement
    Xcon2014 && Geekpwn2014
    phpMyadmin /scripts/setup.php Remote Code Injection && Execution CVE-2009-1151
    Linux System Log Collection、Log Integration、Log Analysis System Building Learning
    The Linux Process Principle,NameSpace, PID、TID、PGID、PPID、SID、TID、TTY
    Windows Management Instrumentation WMI Security Technology Learning
    IIS FTP Server Anonymous Writeable Reinforcement, WEBDAV Anonymous Writeable Reinforcement(undone)
  • 原文地址:https://www.cnblogs.com/maomao999/p/9223721.html
Copyright © 2011-2022 走看看