| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 大数据 -> 秒杀活动总结(秒杀之一) -> 正文阅读 |
|
[大数据]秒杀活动总结(秒杀之一) |
(1)对于秒杀活动的过程中,系统一般会先查一下库存是否足够,如果足够才允许下订单,写数据库。如果不足够的话,则直接返回该商品已经抢购完成。属于读多写少的情景。 出现的问题:如十万 的请求过来,同时通过数据库查缓存是否足够,此时数据库可能会挂掉。因为数据库的连接资源非常有限,比如:mysql,无法同时支持这么多的连接。此时建议使用redis (2)通常情况下,我们在redis中保存商品信息,里面包含:商品id、商品名称、规格属性、库存等信息,同时数据库中也要有相关信息,毕竟缓存并不完全可靠(问题:这种情况下会存在缓存击穿,在高并发下,同一时刻会有大量的请求,都在秒杀同一件商品,这些请求同时去查缓存中没有数据,然后又同时访问数据库。结果悲剧了,数据库可能扛不住压力,直接挂,这个这就需要加锁,最好使用分布式锁) (3)项目启动之前,先把缓存进行预热,即事先把所有的商品,同步到缓存中,这样商品基本都能直接从缓存中获取到,就不会出现缓存击穿的问题了,但如果缓存中设置的过期时间不对,缓存提前过期了,或者缓存被不小心删除了,如果不加速同样可能出现缓存击穿。 (4)防止缓存穿透的时候,可以加分布式锁来实现,但是请求的处理性能并不好,这个时候可以用布隆过滤器,系统根据商品id,先从布隆过滤器中查询该id是否存在,如果存在则允许从缓存中查询数据,如果不存在,则直接返回失败。(但是布隆过滤器中的数据无法做到和更缓存中的数据保持一致,所以这个方法也不是太可取) (5)对于库存问题,采用预扣库存,真正的秒杀商品的场景,不是说扣完库存,就完事了,如果用户在一段时间内,还没完成支付,扣减的库存是要加回去的。 (6)减库存 ?数据库扣减方式一:update?product?set?stock=stock-1?where?id=product?and?stock?>?0; 在sql最后加上:stock > 0,就能保证不会出现超卖的情况,但需要频繁访问数据库,在高并发的场景下,可能会造成系统雪崩。而且,容易出现多个请求,同时竞争行锁的情况,造成相互等待,从而出现死锁的问题。 ??redis扣减库存方式二: redis的incr方法是原子性的,可以用该方法扣减库存。伪代码如下: ?boolean?exist?=?redisClient.query(productId,userId); ??if(exist)?{ ????return?-1; ??} ??int?stock?=?redisClient.queryStock(productId); ??if(stock?<=0)?{ ????return?0; ??} ??redisClient.incrby(productId,?-1); ??redisClient.add(productId,userId); return?1; 代码流程如下: ?1.先判断该用户有没有秒杀过该商品,如果已经秒杀过,则直接返回-1。 ?2.查询库存,如果库存小于等于0,则直接返回0,表示库存不足。 ?3.如果库存充足,则扣减库存,然后将本次秒杀记录保存起来。然后返回1,表示成功。 方式三:加个synchronized解决 加synchronized确实能解决库存为负数问题,但是这样会导致接口性能急剧下降,每次查询都需要竞争同一把锁,显然不太合理 (7)减库存的时候可以用lua脚本扣减库存 我们都知道lua脚本,是能够保证原子性的,它跟redis一起配合使用,能够完美解决上面的问题。 (8)在秒杀场景下,每1万个请求,有1个成功。再1万个请求,有1个成功。如此下去,直到库存不足。这就变成均匀分布的秒杀了,跟我们想象中的不一样。可以使用自旋锁。在规定的时间,比如500毫秒内,自旋不断尝试加锁,如果成功则直接返回。如果失败,则休眠50毫秒,再发起新一轮的尝试。如果到了超时时间,还未加锁成功,则直接返回失败。使用redis分布式锁,还有锁竞争问题、续期问题、锁重入问题、多个redis实例加锁问题。可以使用redisson解决 (9)mq异步处理 (秒杀--》下单--〉支付) 而这三个核心流程中,真正并发量大的是秒杀功能,下单和支付功能实际并发量很小。所以,我们在设计秒杀系统时,有必要把下单和支付功能从秒杀的主流程中拆分出来,特别是下单功能要做成mq异步处理的。而支付功能,比如支付宝支付,是业务场景本身保证的异步。因此秒杀下单的流程就变为:秒杀--(发送mq消息)--------------》mq服务端-----(消费mq消息)------〉下单 (10)防止消息丢失的一个处理 秒杀成功了,往mq发送下单消息的时候,有可能会失败。原因有很多,比如:网络问题、broker挂了、mq服务端磁盘问题等。这些情况,都可能会造成消息丢失。 在生产者发送mq消息之前,先把该条消息写入消息发送表,初始状态是待处理,然后再发送mq消息。消费者消费消息时,处理完业务逻辑之后,再回调生产者的一个接口,修改消息状态为已处理。如果生产者把消息写入消息发送表之后,再发送mq消息到mq服务端的过程中失败了,造成了消息丢失。这个时候需要使用job,增加重试机制。用job每隔一段时间去查询消息发送表中状态为待处理的数据,然后重新发送mq消息。 (11)重复消费的问题(加一张消息处理表) 消费者读到消息之后,先判断一下消息处理表,是否存在该消息,如果存在,表示是重复消费,则直接返回。如果不存在,则进行下单操作,接着将该消息写入消息处理表中,再返回。 (12)垃圾消息的处理问题 如果出现了消息消费失败的情况。比如:由于某些原因,消息消费者下单一直失败,一直不能回调状态变更接口,这样job会不停的重试发消息。最后,会产生大量的垃圾消息。每次在job重试时,需要先判断一下消息发送表中该消息的发送次数是否达到最大限制,如果达到了,则直接返回。如果没有达到,则将次数加1,然后发送消息。 (13)延迟消费问题 通常情况下,如果用户秒杀成功了,下单之后,在15分钟之内还未完成支付的话,该订单会被自动取消,回退库存。我们都知道rocketmq,自带了延迟队列的功能。下单时消息生产者会先生成订单,此时状态为待支付,然后会向延迟队列中发一条消息。达到了延迟时间,消息消费者读取消息之后,会查询该订单的状态是否为待支付。如果是待支付状态,则会更新订单状态为取消状态。如果不是待支付状态,说明该订单已经支付过了,则直接返回。 (14)限流的处理方式 1.同一用户限流:同一用户id请求次数是否超过阈值(比如:每分钟请求只能请求5次) 2.验证码进行移动滑块进行限流。 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/24 11:02:11- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |