场景设计

一、库存扣减逻辑

1)依赖缓存不依赖数据库,因为缓存能抗更高的tps。纯redis实现可能带来的问题:
a、如果redis server实际扣减成功了,但是redis client接口返回失败。可能导致库存的浪费。怎么解决?可以加入库存数据库,每次更新redis成功后再更新数据库,如果redis更新失败则不更新数据库了。然后写一个对账程序,通过对比redis和数据库库存是否一致,很可能出现的case是redis扣减多,数据库扣减少,可以把数据库比redis多的库存加回到redis中。
b、如果redis更新成功,db更新失败了(出现的可能性较小)。可以用日志记录下,把数据库少扣减的部分进行手动扣减。
2)为了避免redis热key,会用到分桶,分桶带来的问题,某些分桶有数据,某些分桶无数据,可能在其他分桶有数据的情况下出现无库存的情况。解决:用户hash到指定的分桶,允许不同用户看到不同的库存余量,所路由到的分桶没有库存时直接展示无库存,这是最简单的解决方式。还可以用本地缓存,定时计算各分桶中有无库存,本地缓存中记录有库存的分桶,请求到来之后可以先读本地缓存,选一个有库存的分桶进行扣减

二、用户打赏后给主播榜单记值场景

1) 用redis的zset数据结构,key为赛段,member是主播id,score是分数。有订单时使用zincrBy增加主播的分数值。或者使用lua脚本,脚本中使用zscore查询主播当前值,然后手动增加score后,使用zadd更新榜单。用redis可能导致的问题:zset的命令返回失败或者超时,此时不能无脑重试,因为可能redis server执行命令成功了但是redis client接口返回失败。重试的话可能会加重了。

2)如果数据库资源允许且追求高鲁棒性的情况下,可以使用数据库,数据库包括两张表,流水表(记录订单流水,orderId作为唯一键)和用户分值表(记录用户最后分值,对于每个订单都update分值)。有订单过来时,将insert流水和update分值打包成事务,流水表可以做幂等校验。


二、下单和扣减库存的顺序问题
1)下单的时候扣减库存,缺点:恶意买家大量下单,将库存用完,但是不付款,真正想买的人买不到。
2)付款的时候扣减库存,下单时前台页面显示最新的库存,下单时不会立即减库存,而是等到买家支付成功时才会减库存。缺点:下单页面显示的库存数可能不是最新的库存数,可能下单的时候有库存,支付的时候可能提示库存不足。
3)预扣库存,下单页面显示最新的库存,买家下单后先预扣库存一段时间(比如30分钟),等到超过保留时间后自动取消订单或者手动取消订单,将释放库存。预扣库存总数量不能超过总库存数,若达到最大限制后,买家是不能再下单的,为了防止超卖风险
通常来说预扣库存是较通用的方式
三、实现订单30分钟未支付则自动取消
https://mp.weixin.qq.com/s/kHb49hcIuG1FzUuBxsbWMQ
1、数据库轮询
即定时(eg:每秒或者每分钟)扫数据库,通过订单时间判断是否有超时订单,然后进行update或delete操作,
优点:简单易行。
缺点:
a)存在延迟,比如每分钟扫一次,则最坏的延迟时间就是3分钟。
b)如果订单较多如几千万条,每隔一段时间扫描,数据库损耗较大。
2、JDK的延迟队列
利用JDK自带的DelayQueue来实现,这是一个无界阻塞队列,该队列只有在延迟期满的时候才能从中获取元素。take()方法可以获取并移除队列的超时元素,如果没有则wait当前线程,直到有元素满足超时条件,返回结果。
优点:效率高,任务触发时间延迟低
缺点:
a)内存限制,比如下单未付款的订单数太多,那么很容易就出现OOM异常
b)逻辑在服务器本地执行,服务器重启后,数据全部消失,怕宕机
3、时间轮算法
原理概述:存储定时任务的环状数组,类比时钟,时间轮的时钟按固定频率转动,待处理的任务按到期时间分布在时间轮上,时针转的过程中就执行对应的任务。有可能某个时间刻度上存着多个定时任务,会使用双向链表进行存储,当时钟指向此刻度,会把链表中每个定时任务依次执行
优点:任务触发时间延迟时间比数据库轮询低,代码复杂度比延迟队列低
缺点:
a)逻辑在服务器本地执行,服务器重启后,数据全部消失,怕宕机
b)内存限制,比如下单未付款的订单数太多,那么很容易就出现OOM异常
4、redis缓存
使用redis zset,member是订单号,score是订单超时时间戳。
订单生成时往redis中加入数据,取订单时无限循环,用zrange命令取第一个订单,发现订单超时时间戳大于当前时间戳后即可执行业务逻辑,并删除redis中对应key。为了防止多线程下多个线程消费同一个订单,可以先执行删除,即先执行zrem,如果zrem返回值大于0才消费订单数据
5、使用消息队列
使用RocketMq的延迟消息,比如订单30分钟未支付自动取消,可以把延迟消息的时间设为30分钟,则30分钟后消息被投递到consumer,consumer收到消息后查询订单是否被支付,如果未支付则取消订单
优点:简单高效,可以利用RocketMq的延迟消息功能实现订单取消
缺点:引用中间件,所以系统复杂度和成本变高

6、使用scheduledExecutor的延时消息,在指定时间后执行

优点:开发简单

缺点:任务存在服务器内存中,如果服务机器重启,会导致任务丢失

 

四、订单表每天新增500W数据,并且支持按uid和订单id查询,分库分表的方案应该如何设计

每天500w,一年是18亿数据,两年36亿,按50亿算,可以分1000张表,单表500w数据。分库可以看实际tps。
分表依据:可以按照订单id作为分表键,则根据订单id查询的需求可以解决。如果想按uid查,业务上可以建立orderid和uid的对应关系,比如根据uid可以生成对应的订单id,这样用uid查询也可以复用订单id的路由规则

五、设计个短链系统

1、生成短链
短链组成有两部分:域名和映射字符串,短链会根据映射字符串找到匹配的长链,所以需要找到映射字符串的唯一性和长度,可以用MD5等算法把长链转化
2、如何存储

 也可以用redis存储

 

posted @   MarkLeeBYR  阅读(54)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示