系统设计经典案例
目录
基本步骤:
- 拆分功能性需求和非功能性需求
- 抽象架构设计:web/app、前端、后端、数据库、缓存、部署、http/websocket、ci/cd
- 考虑可优化的点:负载均衡、数据库索引、分库分表、安全防范、分布式
- 完善系统抽象设计
基础知识
三高架构设计
- 高性能:读写分离、缓存、异步、负载均衡
- 高可用:BASE/最终一致性、集群、超时和重试机制、降级、熔断、限流
- 高扩展:拆分系统、MQ
系统设计原则
- 合适优于先进
- 演化优于一步到位
- 简单优于复杂
性能优化法则
优化的优先级如下:
- SQL、JVM、DB、Tomcat 参数调优
- 硬件性能优化
- 业务逻辑优化、缓存
- 读写分离、集群
- 分库分表
秒杀系统
- 热点数据:放在 Redis 缓存中,并且写入到 JVM 一份,进行多级缓存。一定要设置过期时间和淘汰策略。
- 静态资源:配置 CDN(内容分发网络)
- 一主二从三哨兵:通过 Sentinel 哨兵自动实现故障转移,保证集群可用性。
- 限流:SpringSecurity+Redis+Lua,根据用户 IP 进行拦截限流;MQ
- 降级:应对系统自身的故障。请求量达到阈值的时候,关闭或降低非核心功能,将资源留给核心业务(产生经济收益的)。
- 熔断:应对第三方系统的故障。
- 幂等性:Redisson 分布式锁
- 性能测试:Jmeter
Feed 流
- 智能推荐(基于标签)+时间线:实时性、高并发、高性能
- MySQL 持久化
- 读写分离:主服务器写,从服务器读。
- 加一层代理层,如 MySQL Router、MyCat
- 引入第三方组件:sharding-jdbc
- 一致性方案
- Sharding-JDBC 可以设置一些必须获取最新数据的字段,只读取主库。
- 前端设置一个跳转,为主从同步拖延时间
- 读写分离:主服务器写,从服务器读。
- Redis 集群做缓存
短链系统
- 状态码:302,表示临时重定向。(301 永久重定向)
- 哈希算法
- 加密型:MD5、SHA
- 非加密型:MurmurHash,效率更高
- 哈希冲突判断
- 给短链字段添加唯一索引
- 布隆过滤器
- 哈希冲突解决:拼接分布式 id(Redis incr)
第三方授权
- OAuth2.0 通过为第三方应用颁发一个有时效性的令牌Access Token,解决了授权问题,而不是认证问题。
- 核心流程:客户端向资源拥有者(微信)发送授权申请,用户同意给予客户端授权(授权模式一般采用授权码模式 Authorization Code,一般为 5-20 分钟),客户端使用授权先向认证服务器申请 Access Token 令牌,再向资源服务器申请获取资源。
- Access Token 失效的时机
- 用户修改密码
- 第三方应用将用户冻结或拉入黑名单
- 用户取消对第三方应用的授权
- 有效期到,一般为 2-3 小时
- 可以通过 refresh token 进行刷新(比Access Token时间长,可以续期或获取新的 Access Token)
- 对于微信登录:用户统一 OAuth2.0 登录请求后,第三方应用通过 code(授权临时票据)+appsecret 换取 access_token
- 服务器 B 如何获取服务器 A 的 Session 信息:分布式缓存,Redis 集群。
抢订单/抢红包
- 悲观锁
- JVM 本地锁:synchronized、ReentrantLock,性能差
- MySQL 行锁:
SELECT...FOR UPDATE
SELECT...LOCK IN SHARE MODE
,性能差,存在死锁问题 - 分布式锁:Redis,需要集群保证可靠性
- 唯一索引,保证一个订单只与一名用户建立联系
排行榜
小型业务使用 MySQL 的 ORDER BY,数据量大使用 Redis 的 Sorted Set
- 和 Set 相比,Zset 增加了一个权重参数 score
- 添加:
zadd zset_key 1 user1 2 user2 3 user3
- 排序:
zrevrange zset_key 0 -1
倒序,-1 表示最后一个元素- 后缀
withscores
可以同时展示分数
- 后缀
- 查询分数:
zscore zset_key "user1"
- 查询排名:
zrevrank zset_key "user1"
- 更新数据:
zincrby zset_key -2 "user1"
负数代表减
如何实现近7天数据排序?
zunionstore last_n_days n 20231004 20231005...
用不同的天数作为不同的 zset_key ,再合并时间范围内的 zset
大文件上传
- 分片上传:断点续传、多线程上传
- 前端:
Blob.slice()
- 后端:RandomAccessFile 类进行合并
- 前端:
- 秒传:根据文件内容来计算 MD5 的值
统计 UV
- HyperLogLog:节约服务器资源(10w 数据 Hyperloglog 消耗 10kb,Set 消耗 965kb)
- PFADD key values
- PFCOUNT key
- PFMERGE newkey sourcekey1 sourcekey2