小红书如何应对万亿级社交网络关系挑战?图存储系统 REDtao 来了!


小红书如何应对万亿级社交网络关系挑战?图存储系统 REDtao 来了!

文章插图
1.背景小红书是以年轻人为主的生活记录、分享平台,用户可以通过短视频、图文等形式记录生活点滴,分享生活方式 。在小红书的社交领域里,我们有用户、笔记、商品等实体,这些实体之间有各种各样的关系 。例如,用户与笔记之间可能存在“拥有”(发布)、“点赞”、“收藏”等三种关系,同时还存在对应的反向关系“被点赞”,“被收藏”等 。
小红书如何应对万亿级社交网络关系挑战?图存储系统 REDtao 来了!

文章插图
图片
小红书的社交图谱数据已经达到了万亿条边的规模,且增长速度非常快 。当用户登陆小红书时,每个用户都会看到关注的好友、粉丝、点赞、收藏以及其他为其量身定做的内容 。
小红书如何应对万亿级社交网络关系挑战?图存储系统 REDtao 来了!

文章插图
图片
这些信息高度个性化,需要实时从这些海量社交关系数据中读取用户相关信息 。这是一个读为主的过程,读取压力非常大 。
过去,我们将这些社交图谱数据都存储在运维成熟的 MySQL 数据库中 。然而,即使我们只有百万请求每秒的规模,MySQL 的 CPU 使用率仍然到达了 55%。随着用户和 DAU 爆发式的增长,需要不断扩容 MySQL 数据库,这带来了巨大的成本和稳定性压力 。为了解决这些问题且考虑到没有合适的开源方案,2021 年初我们开启了从 0 到 1 自研 REDtao 的历程 。
2.REDtao的图模型和API我们充分调研了业内其他厂商的实现,发现有着强社交属性的公司基本上都有一个自研的图存储系统:
小红书如何应对万亿级社交网络关系挑战?图存储系统 REDtao 来了!

文章插图
Facebook 实现了一个叫做 “TAO” 专门的分布式社交图谱数据库,并将其作为最核心的存储系统;Pinterest 和 Facebook 类似,也实现了类似的图存储系统;字节跳动自研了 ByteGraph 并将其用于存储核心的社交图谱数据;Linkedln 在 KV 之上构建了社交图谱服务 。
考虑到当时我们的社交图谱数据已经存放在 MySQL 数据库上且规模巨大,而社交图谱数据服务是非常重要的服务,对稳定性的要求非常高 。回溯 Facebook 当年遇到的问题和我们类似,数据存储在 Memcache 和 MySQL 中 。因此,参考 Facebook 的 Tao 图存储系统更贴合我们的实际情况和已有的技术架构,风险更小 。
社交图谱的访问主要是边的关系查询 。我们的图模型将关系表示为一个 <key, value> 对,其中 key 是 ( FromId,  AssocType,  ToId ) 的三元组,value 是属性的  JSON 格式 。比如“用户 A ”关注“用户 B ”,映射到 REDtao 的数据存储结构为:
<FromId:用户A的ID, AssocType:关注,ToId:用户B的ID>->Value (属性的json字段)我们对业务方的需求进行分析,封装了 25 个图语义的 API 给业务方使用,满足了其增删改查的需求,并收敛业务方的使用方式 。相比于 Facebook 的 Tao,我们还补充了社交图谱所需要的图语义,为反作弊场景提供了额外的过滤参数 。同时,在缓存层面,我们支持对不同的字段在缓存中配置局部二级索引 。下面给一些典型的使用场景:
场景一:
获取关注了 A 的所有正常用户(并且剔除作弊用户)
getAssocs(“被关注类型”, 用户A的ID, 分页偏移量, 最大返回值, 只返回正常用户,是否按照时间从新到旧)场景二:
获取 A 的粉丝个数(并且剔除作弊用户)
getAssocCount(“被关注类型”, 用户A的ID, 只返回正常用户)3.REDtao架构设计REDtao 的架构设计考虑了下面这几个关键的要素:
小红书如何应对万亿级社交网络关系挑战?图存储系统 REDtao 来了!

文章插图
图片
3.1 整体架构整体架构分为三层:接入层、缓存层和持久层 。业务方通过 REDtao SDK 接入服务 。如下图:
小红书如何应对万亿级社交网络关系挑战?图存储系统 REDtao 来了!

文章插图
图片
在这个架构中,和 Facebook Tao 不一样的是,我们的缓存层是一个独立的分布式集群,和下面的持久层是解耦的 。缓存层和下面的持久层可以独立的扩容缩容,缓存分片和 MySQL 分片不需要一一对应,这样带来了更好的灵活性,MySQL 集群也变成了一个可以插拔替换的持久存储 。
读流程:客户端将读请求发送给 router,router 接收到 RPC 请求后,根据边类型选择对应的 REDtao 集群,根据三元组 ( FromId,  AssocType,  ToId ) 通过一致性 Hash 计算出分片所在的 Follower 节点,将请求转发到该节点上 。Follower 节点接收到该请求,首先查询本地的图缓存,如果命中则直接返回结果 。如果没有命中,则将请求转发给 Leader 节点 。同样的,Leader 节点如果命中则返回,如果不命中则查询底层 MySQL 数据库 。


推荐阅读