美文网首页Spring-Boot程序员技术方案
只用数据库设计高效抢购业务

只用数据库设计高效抢购业务

作者: 一曲畔上 | 来源:发表于2019-03-11 12:02 被阅读23次

    不使用缓存(redis、memcache),如何设计高效抢购业务呢?
    常见的抢购业务主要有:
    商品抢购
    券抢购
    红包抢购
    今天咱们就谈谈如何对这些抢购业务做统一设计,只使用Mysql做高并发活动。
    我们先看下面一张表,分别标出了这三类业务涉及到的核心数据量:

    核心数据量

    数据量 商品库存 券库存 红包库存 数据金额
    1.总量 totalNum totalNum totalMoney 总金额
    2.销售量 sellNum sellNum sellMoney 领取金额
    3.锁定量 lockNum lockNum 0 锁定金额
    4.库存量 stockNum stockNum stockMoney 库存金额
    5.退货量 refundNum refundNum 0 0
    6,库存池 inventory inventory - -

    我们再看下这些业务涉及到的核心节点的数据量变化

    核心数据量变化(变化量为n)

    节点名称 总量 销售量 锁定量 库存量 退货量
    下单 totalNum不变 sellNum不变 lockNum+n stockNum不变 refundNum不变
    取消订单 totalNum不变 sellNum不变 lockNum-n stockNum不变 refundNum不变
    支付 totalNum不变 sellNum+n lockNum-n stockNum-n refundNum不变
    核销 totalNum不变 sellNum不变 lockNum不变 stockNum不变 refundNum不变
    退款 totalNum不变 sellNum-n(或不变) lockNum不变 stockNum+n(或不变) refundNum+n

    节点设计

    我们知道,每个操作节点最少可对应一个操作。而我们为了保证接口效率最高,我们需要尽量少的使用sql操作,如果每个接口只有一次数据操作那是最好的。
    但是,每个接口的逻辑可能都会涉及到若干数据业务操作,而我们为了能让这些抢购业务性能尽量好,那我们就要尽量把接口原子化设计。而在这些接口中,下单操作时重中之重,只要把这个下单接口设计好了,后续流程基本都没啥大问题。

    下单接口设计

    那我们来分析一下下单接口的设计业务:
    检查库存是否够用
    防止并发超卖
    锁库存
    等等...
    那这些要求改如何同时满足呢?那就要我们尽量合理使用核心数据量了!
    那我们在设计库存表的时候,尽量把总量、销售量、锁定量、库存量放到一起,可以如下设计:

    CREATE TABLE `goods_stock` (
      `ID` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
      `SPU_ID` bigint(20) NOT NULL DEFAULT '0' COMMENT '商品SPU_ID',
      `SKU_ID` bigint(20) NOT NULL DEFAULT '0' COMMENT '商品SKU ID',
      `STOCK_LEFT_NUM` int(11) NOT NULL DEFAULT '0' COMMENT '剩余库存数量',
      `STOCK_SALE_NUM` int(11) NOT NULL DEFAULT '0' COMMENT '售卖库存数量',
      `STOCK_LOCK_NUM` int(11) NOT NULL DEFAULT '0' COMMENT '被锁定的库存数量',
      `STOCK_TOTAL_NUM` int(11) NOT NULL DEFAULT '0' COMMENT 'SKU总库存量,0表示不限制库存',
      `STATUS` tinyint(4) NOT NULL DEFAULT '0' COMMENT '本条记录状态,0-有效,1-无效',
      `CREATE_TIME` datetime NOT NULL COMMENT '创建时间',
      `UPDATE_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
      `VERSION` int(11) NOT NULL DEFAULT '0' COMMENT '乐观锁版本号',
      PRIMARY KEY (`ID`),
      KEY `IDX_SPU_ID` (`SPU_ID`),
      KEY `IDX_SKU_ID` (`SKU_ID`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品SKU库存表';
    

    其他字段在实例中省略了。
    我们看看如何使用这一张表满足上述要求:
    1,检查库存是否够用:

    STOCK_LEFT_NUM >= n
    

    2,防止并发超卖:
    利用数据库行级锁防并发

    WHERE SKU_ID = ${sku_id}
    

    3,锁库存:

    STOCK_LOCK_NUM = STOCK_LOCK_NUM + n
    

    那么,具体的下单、取消订单、支付等业务节点就可以如下实现:
    1,下单

    UPDATE `goods_stock`
      SET `STOCK_LOCK_NUM` = `STOCK_LOCK_NUM` + ${lockNum}
      WHERE `STOCK_LEFT_NUM` <![CDATA[ >= ]]> ${lockNum}
        AND `SKU_ID` = ${skuId}
        AND ${lockNum} <![CDATA[ > ]]> 0
    

    当该操作执行成功,再处理其他逻辑;如果失败,就直接返回下单失败。
    2,取消订单

    UPDATE `goods_stock`
      SET `STOCK_LOCK_NUM` = `STOCK_LOCK_NUM` - ${lockNum}
      WHERE `SKU_ID` = ${skuId}
        AND ${lockNum} <![CDATA[ > ]]> 0
    

    3,支付订单

    UPDATE `goods_stock`
      SET `STOCK_LOCK_NUM` = `STOCK_LOCK_NUM` - ${lockNum},
          `STOCK_SALE_NUM` = `STOCK_SALE_NUM` + ${lockNum},
          `STOCK_LEFT_NUM` = `STOCK_LEFT_NUM` - ${lockNum}
      WHERE `STOCK_LOCK_NUM` <![CDATA[ >= ]]> ${lockNum}
        AND `SKU_ID` = ${skuId}
        AND ${lockNum} <![CDATA[ > ]]> 0
    

    目前MySql+SD硬盘的话,MySql事务并发量达到2000是没啥压力的,所以一般并发2000左右的都可以直接使用MySql数据库设计业务逻辑,无需使用Redis缓存。
    这里只是给出了商品库存核心逻辑,券和其类似,不过还多一点库存池(存放券码),其实这个可以放到后台去处理了;红包比这个更简单,只有下单(领红包)操作。
    -End-

    相关文章

      网友评论

        本文标题:只用数据库设计高效抢购业务

        本文链接:https://www.haomeiwen.com/subject/ucevpqtx.html