什么是秒杀?

秒杀功能是指在一段时间内,限定某种商品数量,让用户进行抢购的活动。由于抢购的商品数量有限,因此需要对系统进行优化,保证秒杀活动可以正常进行,同时保证系统的高可用性和安全性。在实现秒杀功能时,需要考虑以下几个方面的问题:

高并发:由于抢购活动会引发大量用户的访问请求,因此需要对系统进行性能优化,如使用缓存技术、负载均衡等手段,以保证系统可以承受高并发的访问请求。

重复购买:为了防止用户多次购买同一个商品,可以在后台设置每个用户只能购买一次,或者使用用户身份验证等方式。

库存控制:在秒杀活动中,商品数量是有限的,因此需要对库存进行实时监控,以确保用户购买的商品数量不会超过库存数量。

安全性:由于秒杀活动可能引发用户投机取巧、刷单等行为,因此需要对系统进行安全措施,如防止恶意攻击、防止DDoS攻击等。
订单处理:在用户成功购买商品后,需要及时处理用户的订单,包括生成订单、计算商品价格、扣减库存、发货等操作。

秒杀时的业务流程:

1、首先判断用户是否是第一次购买,如果不是,直接返回。

2、扣减库存,判断返回值是否小于0,如果小于0,表示库存不足,如果大于或等于0,将本次秒杀记录保存起来。

3、使用消息队列将秒杀成功记录信息保存到success表中并将秒杀订单转换为普通订单,需要使用dubbo调用order模块的生成订单方法。

4、此时秒杀成功的消息并未处理,秒杀成功信息用于统计秒杀数据,是秒杀结束后才需要统计的,所以在秒杀并发高时,消息队列的发送可以延缓,在服务器不忙时,再运行(削峰填谷)此时再连接数据库,对这个表进行新增,同时对库存进行更新。

在项目中的大致实现步骤 :

系统初始化:在秒杀活动开始前,需要初始化相关数据,如商品信息、库存数量、秒杀开始和结束时间等。在即将发生高并发业务之前, 我们将一些高并发业务中需要的数据保存到redis中, 高并发时,就可以直接从redis中获取, 无需查数据库。

利用Quartz定时的将每个批次的秒杀商品预热到redis中,我们预热的内容是将参与秒杀的商品的具体规格查询出来,根据规格id将该商品的库存保存在redis中,还要注意为了预防雪崩,在向redis保存数据时,都应该添加随机数。在秒杀开始前,生成布隆过滤器,访问时先判断布隆过滤器(Redis中有介绍),如果判断商品存在再继续访问。在秒杀开始之前,生成每个商品对应的随机码,保存在redis中,随机码可以绑定给spu(没有具体规格的商品)保存在前端页面,用户提交时,验证随机码的正确性,只有正确的随机码才能购买商品(随机码 主要是为了防黄牛)

设置定时任务,将库存和随机码保存到redis中。RedisTemplate对象在保存数据到Redis时,会将数据进行序列化(redis默认序列化是JDK序列化)后保存,这样做的好处是对java对象获取对象格式的数据读写效率高,占用空间小。缺点是不能在redis内部对这个数据进行修改。

这个数据如果用redisTemplate保存就会在高并发情况下容易出现线程安全问题,导致商品库存的“超卖”,解决办法就是需要一个能够在redis内部直接修改数据的方式,避免线程安全问题,防止“超卖“ ,使用Spring Data Redis框架提供的StringRedisTemplate类型对象,它可以在Redis内部修改值,StringRedisTemplate只能向redis中保存字符串,如果是数值类型的内容,支持数据的增减,并且因为redis内部操作数据的线程是单线程特征,也能从Reids层面防止超卖。如果Redis有台服务器,要想保证数据同步,就需要redission分布式锁。

然后调用LocalDateTime.now( ).plusMinutes(5) 查询5分钟后进行秒杀的商品列表,同时讲sku的库存数预热到redis中。要操作redis,首先需要先确定redis的key,一般都是定义好的常量,检查redis中是否有这个key,如果存在,证明之前缓存过了,直接跳过,如果不存在,要将库存数保存到redis中,保存时间:秒杀时间+提前的5分钟+防雪崩的随机数30秒。

根据商品id查询商品详情,使用布隆过滤器进行判断,防止缓存穿透。将查询出来的商品详情信息赋值给秒杀对象,再判断当前时间是否是允许秒杀购买当前商品的时间段,最后将随机码追加到url属性中。