最近,参与和负责公司的一次秒杀活动的设计开发,收获颇多,与大家分享。其实大家在生活中或见过或参见过秒杀活动,用户以极低的成本获得高价值的商品,所以也导致活动期间出现拥挤现象,进而导致一些高并发问题,所谓每一次的秒杀活动都是考研公司技术架构的时刻,可真是一点不假。下面就以公司的此次活动为例,介绍下整个秒杀活动从无到有的过程。

秒杀活动期间原价为三位数的商品,只需一元,且该热点商品有以下限制:

1、库存限制,秒杀商品有N件,不可以超卖

2、每个用户仅可以购买一次,即对用户限购

3、限定地区为A城市,即活动只允许A城市用户参加

4、活动有时间限制

5、未抢购到的用户,给予优惠券补偿,且只允许一张

6、用户可能在多个端操作(APP、小程序等)

整个活动的流程如下图:


       经过上述描述后,秒杀活动对象、要求限制已经很详细,接下来开始分析,如何设计我们的系统来满足活动要求。活动时间限制非常简单,不再赘述。其中地域要求可以通过地图服务商(高德、百度、搜狗等)提供的经纬度实现限制,当然也可以通过IP,但是准确性不高,常用到的包括API如下:

淘宝API:http://ip.taobao.com/service/getIpInfo.php?ip=127.0.0.1
新浪API:http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=json&ip=218.192.3.42
pconline API:http://whois.pconline.com.cn/
百度API:http://api.map.baidu.com/location/ip?ip=218.192.3.42

接下来是整个设计的重头戏:库存,活动库存要求N件,卖少了,不讨论,而多卖了,公司会损失。库存限制的重要性不言而喻,接下来重点分析。现在很多公司的支付都会使用支付宝、微信以及银联,支付过程分为发起和三方回调通知两大步骤。用户每买一件,就会做减库存操作,重复上述过程直至库存为零。

库存的基本操作逻辑已经明确,那我们在什么时候减,怎么减,库存是否有时效性、如果用户退款怎么办,退款后库存怎么操作?一系列问题等待被解决。那么我们将不同的减库存操作一一列出讨论。

1、用户下单减库存:首先判断用户是否具有购买资格(新用户、活动时间、活动地区、重复购买等检查),用户有了购买资格后检查是否有库存,无库存提示退出,有库存,用户便会占用这个库存资格,同时减库存。首先抢到的用户,占用一个库存资格,用户可以选择继续付款或放弃。正常情况下用户付款走完全流程,用户等待商家发货即可。可是也会有用户占有资格,却不付款或是付款后退款。假如有恶意刷库存的用户,他们通过一些手段抢占不付款或退款,那么会导致正常用户抢不到,影响用户体验,同时给公司带来不利影响,原本希望通过秒杀抢购回馈老用户或扩大影响力目的无法实现。

2、支付回调后减库存:资格检查同上不再赘述。用户发起支付前有库存,那么用户可以正常购买支付,只要等待回调修改该库存(减库存)即可。可是从发起请求到真正减库存,中间会有时间差。那么当请求量很大时,会出现用户A即将准备减库存前,B用户请求进来发现还有库存,等B真正到支付回调减库存前发现库存早已经被抢光。此时B虽然付款却已经没有库存,导致订单失败,或者超卖。用户体验差,还会给公司带来影响。

3、预扣库存:用户下单后,库存为其保留N分钟(N由具体业务定义)。等用户发起付款前检查库存库存有效性,有效则继续,若无效,则重复库存扣减逻辑。

以上3个方案已经将扣减库存各个环境都试了一遍,看似第三个不会出现前两个的问题,可是观察发现恶意用户仍然可以等待N分钟刷单。三种方案好想都不够完美。那么我们继续分析,是否有一个最优解。我们总结上述方案的可能发生的问题反向思考,秒杀活动的本质是低价获取高价值商品,那么正常用户一般只要抢到即会去付款,所以我们只要通过一定的手段控制恶意刷库存即可。那么我们选择怎么选择方案呐?首先方案一,我们只需要保证减库存线程安全,同时结合防作弊手段(给用户打标签、风控、黑名单、重复下单不付款次数限制等)即可。方案二在并发量很大的时候是无法控制库存,极大程度会出现超卖现象,放弃。方案三同方案一相似仍需要反作弊手段,减库存则先预扣,付款前检查,相比方案一复杂一些。因此,相较于方案三我们选择了性能更优的方案一。

有了技术方案,那开始我们的开发工作,我们需要保证下单减库存的数据一致性。可采用的方案包括:

(1) 同步关键字Synchronized,获取减库存方法或代码块使用同步关键字,保证访问是串行

(2) 通过数据库的事务,事务中库存为负数,回滚退出

(3) 使用redis,redis做分布式锁,同时库存操作也在缓存中操作

上述三种方案我们对比发现,1和2的在并发量很大时因为存在锁竞争,会导致吞吐量减小和响应时间变长,同时事务因为使用数据库锁,可能会影响其他业务对表的读写。综合分析,我们选择延迟小,吞吐量高的方案3。

此处,我们使用了高性能分布式缓存方案:redisson。相较于jedis,redisson的所提供的功能更加丰富,如可重入锁、连锁、红锁等,同时还有原子类AtomicLong、AtomicDouble,github地址:https://github.com/redisson/redisson,大家可以阅读使用。我们并没有直接使用lock。大家都知道原子类也是提供了线程安全的读写操作,我们完全可以使用该特性。以下为下单减存库的实现逻辑:

... 初始化库存
... 前置检查// 库存数
RAtomicLong cardStock = redissonClient.getAtomicLong("YOU_CUSTOMIZE_STOCK_KEY");
boolean isSuccess = true;
// 判断库存缓存是否存在且库存数>0
if (!cardStock.isExists() || cardStock.get() <= 0) {// 库存为空,提示抢购失败,进入其他逻辑isSuccess = false;
} else if (cardStock.decrementAndGet() < 0) {// 如果库存大于0,则进行-1操作,当有多个线程同时判断库存>0,那么在-1操作时则会排队等待,如果            // 首先抢到锁的线程-1后库存数为0,那之后的线程-1后均为负数,则直接进入其他处理逻辑isSuccess = false;
}// 判断是否抢购成功
if (isSuccess) {// 抢购成功,建立用户和库存关系,在支付前再做一次判断,支付回调后将库存关系解除RBucket<Integer> userStock = redissonClient.getBucket("YOU_CUSTOMIZE_USER_STOCK_RELATION_KEY:" + uid);// 设置用户库存关系失效时间userStock.set(1, 60, TimeUnit.DAYS);// 订单抢购成功发短信通知threadPoolTaskExecutor.submit(() -> mqService.sendRpcMessageService(uid, MessageActionEnum.PROMOTION, cardServiceConfig.getJulyCampaignSms2(), Maps.newHashMap()));
}... 后置处理

支付发起前的检查:

... 其他检查// 再次检查在下单时建立的用户库存关系是否存在,此操作也能防止用户多段操作带来的风险
RBucket<Integer> userStock = redissonClient.getBucket("YOU_CUSTOMIZE_USER_STOCK_RELATION_KEY:" + userDO.getUid());
if (!userStock.isExists() || userStock.get() == 0) {// 没有或已经购买过,则进行友好提示Yi23ParamAssert.isTrue(false, "活动太过火爆,活动商品已被抢光~");
}... 后置操作

支付回调后的解除操作:

// 支付回调检查用户库存关系缓存
RBucket<Integer> userStock = redissonClient.getBucket("YOU_CUSTOMIZE_USER_STOCK_RELATION_KEY:" + userDO.getUid());
// 有购买资格,消耗个人占有库存
if (userStock.isExists()) {// 将库存置为0userStock.set(0);
}

经过上述三个步骤,基本的库存操作逻辑开发完毕,当中还有其他很多小的细节再次没有赘述,需要根据各自的业务分析。

那么,我们采用的方案是不是已经很完美了?其实,仍然有许多的问题会随着并发量的进一步增大而显现,此时我们可能需要考虑更多的技术手段,,如:将秒杀热点数据隔离、熔断降级、设计兜底方案、流量削峰等等。篇幅有限,以后会将更过的技术方案写出,供大家参考。

开发结束了,那么我们接下来需要去验证我们的技术方案是否可靠,也即是我们需要模拟大并发场景下,库存数据操作是否一致,其他逻辑是否如期按照我们的设计进行。此处,给大家推荐两个性能压测工具:jmeter、阿里的PTS。jmeter可以在本机测试,但是因为机型性能会有一些影响。PTS性能更好,但是需要一定的费用。各位可以根据自身情况选择。

jmeter : https://jmeter.apache.org/

PTS    : https://www.aliyun.com/product/pts

经过这次的秒杀活动,收获颇丰。之前都只是停留在理论层面,真正到实践时发现有很多的细节需要考虑。并发这个话题的讨论从来都没停过,希望大家有机会多多总结实践。

文章中的有不少细节未体现,后期会不断完善,供大家启发参考。

redis分布式锁实现秒杀活动相关推荐

  1. redis分布式锁及秒杀系统实战

    本文分为两部分: 一.介绍redis分布式锁的原理和使用方法: 二.使用redis分布式锁实现一个简单的秒杀系统. 注意:本文使用java1.8,最后的例子为springboot项目. 目录 redi ...

  2. 基于redis分布式锁实现“秒杀”

    作者丨lsfire https://blog.csdn.net/u010359884/article/details/50310387 最近在项目中遇到了类似"秒杀"的业务场景,在 ...

  3. Java程序猿笔记——基于redis分布式锁实现“秒杀”

    最近在项目中遇到了类似"秒杀"的业务场景,在本篇博客中,我将用一个非常简单的demo,阐述实现所谓"秒杀"的基本思路. 业务场景 所谓秒杀,从业务角度看,是短时 ...

  4. Redis分布式锁实现秒杀

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u010359884/article/details/50310387 最近在项目中遇到了类似&quo ...

  5. @scheduled cron启动后和每小时执行_小耶哥: 一个Redis分布式锁又要和小鑫同学扯半个小时!...

    1 Redis分布式锁 |1-1 定时任务重复执行-问题引入 最近小耶哥在做一个功能, 什么功能呢? 就是超时未支付的订单我们要定时关闭, 释放库存, 并且短信通知用户该订单因超时被取消了.由于小耶哥 ...

  6. 秒杀商品超卖事故:Redis分布式锁请慎用!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:浪漫先生 来源:juejin.im/post/6854573 ...

  7. 京东秒杀系统模块的Redis分布式锁深度剖析,没给你讲明白你打我

    1|0背景 目前开发过程中,按照公司规范,需要依赖框架中的缓存组件.不得不说,做组件的大牛对CRUD操作的封装,连接池.缓存路由.缓存安全性的管控都处理的无可挑剔.但是有一个小问题,该组件没有对分布式 ...

  8. 【使用Redis分布式锁实现优惠券秒杀功能】-Redis学习笔记05

    前言 本章节主要实现限时.限量优惠券秒杀功能,并利用分布式锁解决<超卖问题>.<一人一单问题>. 一.优惠券下单基本功能实现 1.功能介绍及流程图 2.代码实现 @Resour ...

  9. redis分布式锁java代码_基于redis实现分布式锁

    " 在上一篇文章中介绍了动态配置定时任务,其中的原理跟spring 定时任务注解@Scheduled一样的,都是通过线程池和定义执行时间来控制.来思考一个问题,如果我们的定时任务在分布式微服 ...

最新文章

  1. c语言n位水仙花数简书,Kotlin中函数式编程API(8)求阶乘和计算水仙花数
  2. 今天感觉有点冷了其实。
  3. 创建预留mb21添加附加字段的增强
  4. shiro 字段不是username 和password_Shiro整合JWT
  5. 第33讲:可见即可爬,Appium 的使用
  6. 在DelayQueue中更改延迟,从而更改顺序
  7. 服务器遍历文件夹不按顺序,绕过遍历检查 (Windows 10) - Windows security | Microsoft Docs...
  8. oracle数据库集群日志,Oracle集群数据库中恢复归档日志
  9. NSA-LDL论文修改建议20211116(R-T Bai)
  10. Kettle入门--作业和转换的使用
  11. 最著名的10位程序员,你都知道吗?
  12. java找出最高工资和下标_(java)leetcode852 山脉数组的封顶索引(二分查找法找出数组中最大值的下标)(Peak Index in a Mountain Array)...
  13. cocos android 热更新,Cocos creator 大厅子游戏和热更新
  14. 计算机密码无法输完整,笔记本电脑键盘失灵无法输入密码怎么解决
  15. PDF页面太大要如何才能缩小?
  16. 单片机设计:基于stm32智能语音识别蓝牙音响(ld3320语音识别模块+mp3模块+喇叭+点阵屏+OLED+蓝牙+手机app)
  17. JQuery给指定的表格的输入框或其他组件赋值
  18. 使用Jsch执行Shell脚本
  19. 【论文阅读笔记】语义三维重建CVPR2011:Semantic Structure from Motion
  20. App项目设计开发完整流程

热门文章

  1. 巅峰Q神个人版 V7.8(最新)
  2. session_start(): open(/tmp/sess_1e6e96186c98e4dbb1947e63e20c340f, O_RDWR) failed: Permission denied
  3. 微信小程序入门7--传感器设备综合使用
  4. 微信QQ发布新版,接近一个G?内置4个虚拟引擎,这才叫做“大”更新
  5. 车辆购置税二维条码申报在大连市试点运行
  6. GIS+=地理信息+行业+大数据——基于云环境流处理平台下的实时交通创新型app
  7. 华为手机助手上架流程_华为手机资料导入功能使用教程 看完你就知道了
  8. uni-app开启消息通知
  9. 交替性注意力_如何提升专注力
  10. 必读 | 回顾2020年最掉头发的一个项目