一、项目背景
用户动态存储在mongodb中。
动态按用户可见性分为三种类型
- 公开动态:所有好友可见
- 私有动态:仅自己可见
- 半公开动态:指定好友可见
用户刷新朋友圈需查询出以上三种类型的动态,以及自己发布的动态,按自增ID(和发布时间正相关)倒序排列,并分页显示。
后端有一台mongodb服务器提供查询服务,访问量较大时,服务器负载很高,查询速度很慢,出现较多慢查询,甚至查询超时。
二、性能优化
2.1 优化目标
- 减轻mongodb服务器压力
- 提高查询速度
2.2 优化方案
- 维护用户可见的动态队列
- 化复杂查询为简单查询(利用主键索引)
2.3 技术细节
- 用户动态队列存储在redis有序集合中,集合中存储动态ID,队列最大长度做了限制
- 查询时先从用户动态队列取动态ID,如果数据量足够,直接做一个in查询,否则做一次复杂查询以补全数据
2.4 队列的初始化
从mongodb查出用户可见的动态ID,放入用户动态队列
2.5 队列的更新
用户做以下操作时,需更新动态队列
- 用户登录:初始化用户队列,每个用户只做一次初始化(异步)
- 发布动态 | 删除动态:更新自己的队列(同步);更新好友的队列(异步)
- 添加好友 | 删除好友:更新自己的队列(异步);更新好友的队列(异步)
- 打开朋友圈 | 关闭朋友圈:更新好友的队列(异步)
实现细节
- 用户做以上操作时记录用户操作事件(观察者模式)。事件存储在mysql中。
- 服务器启动定时任务处理用户事件。根据不同事件类型、业务需要、事件处理复杂度等因素灵活调整定时任务的运行频率以及每个定时任务处理的事件数量。
- 多进程并发场景:事件开始处理前将状态置为“正在处理”,防止有多个进程重复处理同一个用户事件。
- 事件处理失败后重试,并设置最大重试次数,对处理失败且超过最大重试次数的事件做预警。
三、优化效果
- redis缓存380000用户动态队列,占用内存1.2G
- mongodb服务器8核CPU,访问量高峰期的CPU负载从799%降至303%,服务器负载(load值)峰值维持在3~5之间。