分布式消息系统秒杀
一、秒杀系统的核心问题与基本要求
1. 核心问题
并发读:优化理念是减少用户到服务端来“读”数据,或者让他们读更少的数据,通过缓存热点数据,减少数据库的频繁读取。
并发写:在数据库层面独立出一个库,做特殊处理,如设计专门的表,精简表字段。
2. 基本要求
高性能:涉及大量并发读写,可以从缓存、消息队列、请求削峰等角度进行设计。
一致性:保证秒杀减库存中的数据一致性。
高可用:保证系统的高可用和正确性,设计PlanB进行兜底。
二、架构原则
数据要尽量少:用户请求与响应的数据尽可能得少,可以减少数据序列化与反序列化的性能损耗。
请求数要尽量少:减少或合并css/java script、图片以及Ajax请求等,TCP三次握手,DNS解析等都会有资源消耗。
路径要尽量短:用户发出请求到返回数据这个过程中,需求经过的中间节点数尽可能少,尽可能使用RPC调用,提升系统性能。
依赖要尽量少:依赖指的是要完成一次用户请求必须依赖的系统或者服务。
不要有单点:无论是系统资源还是数据资源在设计的时候一定要考虑冗余。
三、秒杀系统的实现方案
1. 分布式限流
采用Sentinel的方式进行分布式限流,诸如warm up(预热)、拒绝、匀速排队等手段。
2. 负载均衡
对系统进行微服务化,拆分成商品中心、用户中心、订单中心,以实现负载均衡。
3. 缓存的使用
对热点数据(遵循二八原则)并且对实时性要求不高的数据进行缓存处理,如商品的基本信息。
4. 消息队列的使用
针对可以异步处理的操作,如下单,采用消息队列的方式进行处理,实行流量削峰。
5. 分布式锁
进行抢购时采用分布式锁的方式保证不会出现“超卖现象”。
四、如何防止“超卖”现象
秒杀系统防止的“超卖”的关键在于减库存的方式,通常有三种:
下单减库存:一定不会出现超卖情况,但是有些人下单完不付款会影响其他人。
付款减库存:付款减库存,可能会因为并发高导致付款时已经卖光,付不了款。
预扣库存:最常用,如下单后扣库存,保留十分钟,在十分钟内未付款就不保留,如果付款时发现库存不足则不允许付款,预扣库存存在“黄牛抢单”恶意抢单的情况,可以通过信誉积分、设置最大购买数、已有未支付订单不允许再次下单等方式进行控制,预扣库存通常使用分布式锁来实现。
技术方案包括:
setnx的方式:实现分布式锁,在获取锁,进行查库存 -> 创建订单 -> 扣减库存(并不会对库存数量进行上锁),这种方案将实现请求的序列化,但是并发量有限。
分段锁:进行抢购,可以借鉴LongAdder的实现,通过订购时随机分配一个分段锁,如果该分段库存不足,要自动释放锁,切换到一下分段库存尝试获取锁,分布式锁不会保存库存数量,获取锁只是获取购买销售品的资格,库存数量由数据库控制。
五、解决订单过期库存回库问题
解决订单过期库存回库的潜在方案包括:
后台线程不断扫描数据库:性能极低,弃用。
将订单数据存入Redis中并设置失效时间:考虑到要保留订单信息,弃用。
使用delayedQueue延时队列:设置到期时间,其中的对象只能在到期时才能从队列中取走,进行过期操作,但是不支持分布式,弃用。
使用RabbitMQ延迟队列:并使用定时任务处理可能的消息丢失导致的库存无法释放,采用。
六、如何解决用户重复订购问题
用户重复订购可以理解为请求幂等性的实现:
使用数据库唯一键:在锁座表中,设定场次Id和座位Id作为唯一键,锁定座位时,如果座位已经售卖,会报出数据库异常,不允许某一个座位重复售卖。
基于Redis实现:使用set操作具有天然的幂等性,当业务处理完再删除对应的key。
先查一次数据:来判断是新增操作还是更新操作。
前置布隆过滤器:向数据库前置一个布隆过滤器来判断数据是新数据还是旧数据,再使用主键索引来实现。
状态机:使用状态机来保证幂等性,订单状态可以有初始化、订购中、订购失败、订购成功,从而限制重复订购。
前端订单幂等性校验:对于前端的订单采用token幂等性校验,防止重复点击或者网络原因导致重复提交,外部接口的请求采用外部订单号做幂等性校验。
借助消息队列的串行化:借助第三方的幂等性校验,也能保证我们业务上的幂等性。
相关问题与解答
问1:什么是分布式锁?它是如何在秒杀系统中应用的?
答1:分布式锁是一种在分布式系统中用于控制多个进程或节点对共享资源的访问的机制,在秒杀系统中,分布式锁常用于控制对热点商品库存的访问,以防止并发操作导致的数据不一致问题,在用户下单时,通过分布式锁确保同一时间只有一个用户的请求能够修改库存数据,其他用户的请求则需等待锁释放后再进行操作,这有效避免了“超卖”现象的发生。
问2:为什么需要使用消息队列来处理秒杀系统中的订单?
答2:在秒杀系统中,由于瞬时并发量极大,直接操作数据库可能会导致系统崩溃或响应缓慢,消息队列作为一种异步处理机制,可以有效地削峰填谷,缓解系统压力,当用户发起秒杀请求时,系统先将请求消息放入消息队列中,然后由后台服务逐步从队列中取出消息进行处理,这样既可以保证用户请求的快速响应,又可以避免数据库因瞬间高并发而崩溃,提高了系统的稳定性和可扩展性,消息队列还支持持久化和重试机制,确保消息不丢失且最终被处理。
各位小伙伴们,我刚刚为大家分享了有关“分布式消息系统秒杀”的知识,希望对你们有所帮助。如果您还有其他相关问题需要解决,欢迎随时提出哦!
原创文章,作者:K-seo,如若转载,请注明出处:https://www.kdun.cn/ask/666336.html