秒杀系统个人总结
Posted 43726581gavin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了秒杀系统个人总结相关的知识,希望对你有一定的参考价值。
秒杀系统架构
秒杀系统是一个并发量要求高、负载均衡要求高的、业务场景简单但是逻辑稍微复杂的系统,所以经常会作为面试高级后端开发的面试题。主要考察的就是对问题的拆解、分析、解决,以及架构设计的能力。
基础架构
客户端
服务端是一个潜在的考察点,还是有很多问题需要解决的。有些网上给出的设计方案没有对这块做详细考虑。
客户端限流(在浏览器上行不通)
客户端做一定控制来限流(比如概率),防止刷单,减少成功次数,显示在排队,实际上没发网络请求
前端展示
秒杀按钮展示要有个定时器,涉及到前后端时钟同步的问题
校对时间差
获取服务端时间,客户端时间 - 服务端时间,比较得到差值,用这种方法来同步。然而注意到网络是有开销的,这个开销需要想办法消除。否则这种毫秒级甚至秒级的时间差,会影响到秒杀的公平性。
- 发送更轻量级的服务响应
- 优化代码,使客户端和服务端计算时间的流程很短
- 回调计算时间先执行
如果这个同步是很长时间之前同步的呢,可能时间过了很久后已经相差较多了。
- 定时同步,半小时一次
如果客户修改系统时间怎么办
- 记录客户端的周期时间序列(这个周期可以设置的短一些,比如10秒这样子),第一次为基准,先做客户端与服务端同步,得到差值 T
- 在第二个周期,计算两个时刻的时间差,再减去周期时间,就是相差的时间,如果在某一个很小的范围内,说明没有问题,如果不在范围内,说明可能修改了时间,那么修改 T 为 T + 差值
web 层
这一层要考虑限流问题,以及防止恶意刷量的问题。首先限流要尽量在上层去做,以最大程度减少后端系统的压力。其次,要避免用户找到url,不停的大量发送网络请求,或者在活动前就发送,这样也是有问题的。
限流
这属于分布式限流,一般采用 redis 来做限流,可以用令牌桶来做
防止提前刷 url
这个可以在服务端根据系统时间来决定要不要处理,也可以用一个随机的网址来保证无法模拟 url 请求(这个点还比较模糊)
而且这里涉及到服务端各服务器的时钟同步同一个 url
这里可以用 redis 记录或者本地记录来进行计数过滤,保证用户每秒发送请求响应次数不超过一个阈值
service 层
- 如果有库存,并且拿到了资源,再生单的逻辑顺序
- 解决并发问题的思路
- 锁
悲观锁
性能比较差
乐观错
性能好些
缓存
缓存来保存库存量,减少访问 mysql 带来的并发,用 redis 可以做到
但是如果在拿到资格后出现问题,怎么办?在缓存里已经被减掉了,这时需要归还资格,否则卖出的数就会少,这个错误可能会出现在生单,订单入库的阶段,直到入库,这个资格才能算作彻底被消费掉
- 锁
数据存储层
mysql 更合适,有唯一键的限制,hbase 存放海量数据
同步还是异步的问题
同步
好处,等待结果写入库里,完全闭环
异步
可能会写入失败,丢失订单信息,因为订单详情是要尽快展示给用户的,所以一旦失败,该取消这次秒杀的结果,还是继续认为成功,是比较棘手的问题。异步出错了,可能可以修复,但是也可能会一直出错,重试无效。这种应该归还,然后把结果通知给用户。如果异步默认生单成功,但是怎么也写不进去,那就会有问题了。(这块每太想清楚)
先说说异步做法
交由本地线程池处理
占用 service 层资源
发送 kafka
减少了 service 层资源占用,但是要保证 kafka 可靠,这里需要保证 有副本,ack -> ALL,replica 设置>1
其他问题
如何保证同一个用户只能下一个单
- 如果是用 redis 来做
- 那么可以在获取权限的前一层写入,做第一层判断
- 也就是如果获取到一次资格,立马锁定,如果后面的生单失败了,再解锁
- 如果用 mysql 来做
- 会导致在最下层才判断出来,而前面已经拿到了资格,导致很多人没抢到,这时应该返回什么?一般返回失败,直到全局的秒杀结束,再通知结束。
- 如果是用 redis 来做
因为资源被占用后,后续不一定生单成功,所以如果资源没了,不应该直接展示秒杀结束
- 要有一个全局的标识,确认秒杀结束
server 端时间同步
- 服务启动时以某一个为基准就好,可以是集群中的,也可以是集群外的
以上是关于秒杀系统个人总结的主要内容,如果未能解决你的问题,请参考以下文章