知乎feed流架构演进
如果无法正常显示,请先停止浏览器的去广告插件。
1. 知乎Feed流架构演进
2.
3.
4.
5. 姚钢强
2013 年年加⼊入知乎,知乎 Feed 流技术负责⼈人,负责期间 Server 端 P95 响应时间从
1.6S 降低到 700Ms,稳定性由 99.9% 提升到 99.995%
6. 提纲
A. Feed 流的需求和特点
B. ⽼老老 Feed 流的构架遇到的问题
C. 新构架 Redis module 技术⽅方案
D. Redis module ⽅方案遇到的问题
E. 新的问题与挑战
7. 知乎 Feed 流的需求和特点
A. DAU 2600万(2017.9)
B. 个性化推荐,每次请求返回内容不不同(与搜索不不同, cache 难做)
C. ⽤用户动态准实时分发
8. ⽆无 个 性 化 ( 热 ⻔门 榜 单 )
9. ⽤用 户 个 性 化 p u s h
10. push 存在的问题
A. 资源消耗严重,计算量量⼤大,存储量量⼤大
B. 智能推荐,排序难以实时调整
C. 过滤⽐比较难做(关注 or 被删除)
D. 动态准实时分发难以达到 (⾼高粉丝⽤用户)
11. 实时 pull
12. pull 计算流程(慢)
client
member
推荐的相关源
拼装 meta
从指定的源拉取条⽬目
N
选前 10 条
过滤条⽬目
条⽬目
够⽤用
Y
算法排序
13. 提前计算,做缓存
client
member
相关的源
N
缓存
从指定的源拉取条⽬目
Y
拼装 meta
前 10 条
后 10 条
缓存
N
选前 20 条
过滤条⽬目
条⽬目
够⽤用
Y
算法排序
14. 提前计算的问题
• 存在冗余计算,占⽤用资源多
• 冷启动 P95 响应时间⻓长
• ⽤用户⾏行行为分发延迟,体验差
• 离线计算策略略复杂,难以维护
• 推荐算法难以实时调整
15. 如 何 优 化 , 难 点 ⼉儿 在 哪 ⼉儿 ?
•
依赖服务响应慢
• • gevent 并发
•
•
redis cache + local cache
超时做降级
Python 计算太慢
•
•
Cython 模块替换
由于条⽬目不不够,反复访问底层源的存储 feesource
16. 可 能 的 解 决 ⽅方 案
•
拉取出更更多的条⽬目,防⽌止被过滤掉?
•
•
拉取更更多的条⽬目也会浪费时间,过滤压⼒力力⼤大
根据算法拉取出更更精准的条⽬目?
•
算法期望召回池越⼤大越好
17. 计算下推,接近存储
18. 新 feed 计算逻辑
19. Redis Module
Redis modules make possible to extend Redis functionality using external
modules, implementing new Redis commands at a speed and with features similar
to what can be done inside the core itself.
20. Redis Module
• 加载定制命令(MODULE LOAD module load lib_path/xx.so)
• 执⾏行行定制命令
• 卸载定制命令(MODULE UNLOAD mymodule)
21. 加载定制命令
Client
module load lib_path/xx.so
START
handle = dlopen("lib_path/XX.so")
dlsym(handle,"RedisModule_OnLoad")
RedisModule_Init 初始化模块
RedisModule_CreateCommand 注册定制命令
...
RedisModule_CreateCommand 注册定制命令
END
redisServer
attributes
+ modules: dict
+ commands: dict
operations
22. 执 ⾏行行 定 制 命 令
Client
AxxCmd argv1 argv2 …
START
查找命令
RedisModuleCommandDispatcher
执⾏行行命令
模块 callback
moduleHandlePropagationAfterCommandCallback
回收内存
moduleFreeContext
END
redisServer
attributes
+ modules: dict
+ commands: dict
operations
23. 卸载定制命令
Client
module unload ModuleName
START
清空注册符号
dictDelete(server.commands,cmdname)
dlclose
redisServer
attributes
+ modules: dict
+ commands: dict
operations
卸载模块
moduleFreeModuleStructure
END
24. 定制的命令
•
open_core, close_core
•
•
Redis module 更更新时打开 core dump,如果 crash ⽅方便便分析
ts_query
•
Redis 内部的过滤,归并流程
25. t s _ q u e r y 接 ⼝口 设 计
•
Request
• • black_items: 需要过滤的⿊黑名单条⽬目
• merge_strategy: 合并策略略
•
•
[source_type, source_id]: 需要拉取 source
required_number: 需要返回的条⽬目数量量
Response
•
[item_type, item_id, item_action, item_score]: 返回需要数⽬目的 feed 条⽬目
26. 带来的收益
• 响应时间 P95 降低 300ms
• 去掉了了离线计算,节省 60% 的计算资源
• 内容候选池更更⼤大,为算法提供了了更更⼤大的空间
• ⽤用户动态实时分发,算法实时调整
27. 遇到的问题
• Module 更更新不不⽣生效,继续调⽤用⽼老老 Module
• Redis 单线程 CPU 瓶颈,⾼高可⽤用
• Redis 的内存浪费
28. M o d u l e 更更 新 不不 ⽣生 效
•
加载新的 so 后,发现调⽤用的还是⽼老老逻辑
•
•
GDB 发现存在符号被标记 DF_1_NODELETE
dlcose 仅仅声明 so 不不在被系统使⽤用,so 内存占⽤用依旧存在
• gcc 编译使⽤用 STB_GNU_UNIQUE ,防⽌止符号被标记为 DF_1_NODELETE
• 按照依赖路路径加载 so,不不直接加载定制的 so
29. R e d i s 单 线 程 C P U 瓶 颈 , ⾼高 可 ⽤用
• 采⽤用 Redis Sentinel 部署集群,保证⾼高可⽤用
• ⼀一致性哈希 Redis shard,每个 shard 采⽤用 master slave 的⽅方式部署
30. Redis 内存浪费
• 采⽤用 protobuf 和 Redis ziplist 数据压缩 ,减少 shard
• 有多个 slave ,还是浪费内存,没有根本解决问题
• Redis 仅仅作为任务队列列,任务分派给其他进程处理理?
• 同⼀一台机器器伴随 Redis 部署计算节点?
31. Feed 架构的历程
• Feed 都⼀一样
• Feed 个性化,推模型
• Feed 个性化,拉模型 + 离线计算
• Feed 个性化,拉模型,采⽤用 Redis Module,计算接近存储
32. 参 考 资 料料
•
Serving Facebook Multifeed: Efficiency, performance gains through redesign
• dlclose - close a symbol table handle
• dlclose doesn't really unload shared object, no matter how many times it is called
• Redis Modules: an introduction to the API
• Redis Loadable Modules System
33.