目录
利用redis的原子性控制请求
利用redis的原子性控制人数请求访问到service层
问题:
在秒杀服务中,如果有1万个请求进入到service层,而我们秒杀的商品库存只有10个,说明只有10个请求会成功,剩余的9990个请求进入到service层是失败的,但是即使失败,这9990个请求也会操作到我们的数据库DB
需求:
需要控制进入到service层的人数,比如我们只放10个请求到service层。
思路
把库存数量放在redis里面,利用redis的原子性操作,因为redis是单线程的,即使项目使用到了集群,多个线程同时去秒杀访问,但是在redis里面,因为是单线程的,所以也是一个一个去减的,减的方法是decr操作。
什么是原子性的操作?
原子性指的是因为redis是单线程的,即使多个集群服务,多线程去访问redis的时候,redis也是一个线程一个线程执行的。这就是redis的原子性。
代码思路:
1、项目都是有一个后台管理系统进行数据管理的,所以我们可以在进行秒杀操作前进行一个初始化操作,通过去管理系统获取秒杀商品的库存,放到redis里面作为预库存,作为判断条件。
2、然后在controller的秒杀方法里面,通过判断redis是否有有预库存,来判断是否进行后续访问数据库的减库存操作。从而实现控制人数访问到service层。
代码:
1、手动初始化redis里面秒杀商品的预库存—就是把库存查出来set到redis里面去
工具类
把秒杀服务的redis前缀生成工具类
依赖
SeckillGoodController
手动初始化库存到redis
初始化成功
http://localhost:8092/seckillGood/initSeckillData
SeckillOrderInfoController
秒杀方法加判断
测试:
测试前数据清空处理:
1、flushdb 删除当前数据库中的所有Key
2、
清除t_user 表数据、
清除D盘的 token.txt 内容,
清空t_order_info 和 t_seckill_order 表。
t_seckill_goods 的 stock_count 改成10
结果:
注意事项:
通过几次测试,正常压测数据就符合期望,如果在压测的时候进行debug,打个断点,那么会导致数据不正确,就是秒杀成功的数量小于10,就是10个秒杀商品没有全部被秒杀成功。
如果不打断点,就一切正常。
本地标识的分析和实现
问题:
有一万个请求,上面的超卖跟重复下单,通过redis,最终只让10个请求访问到service层,访问到数据库,那么数据库的压力就小了。但是,虽然redis基于内存访问很快,但是也是有网络开销的。
需求:
一万个秒杀请求访问到redis也是要有网络开销的,但是秒杀商品只有10个,那么处理到访问redis也只有10次的网络开销就可以了。
之前是这样的,1万个请求,都会去访问redis,即使没有库存了,后面的9990个请求也都会去访问redis进行判断
思路:
定义一个变量作为本地标识,如果redis还有库存,那么就为false,没有库存就为true,当10个库存秒杀完了,那么其他到redis的请求就抛个异常就可以了,就没有必要再去访问redis了。
选用ConcurrentHashMap形式把商品秒杀的id和标识存到redis中。
图片中的ConcurrentHashMap 少写了Con
代码:
测试:
测试条件:
因为debug可能会有些超时,导致数据不准确,用打印的方式来看测试结果,数据库的结果也都是正确的,成功秒杀10个商品,没有重复下单和超卖的问题。
测试成功
这里的失败测试,因为测试线程数是20,这里应该也是10次才对,只要9次,先不理。