根据Feed流中的数据类型,本文分别设计系统存储、同步与元数据方案。不同类型的Feed流产品架构存在差异,本文设计的产品是基于单向关系的时间排序Feed流,类似于微博。
存储
Feed流系统中的存储库主要存放具体的用户Feed消息,而Feed消息具备以下特点:
数据量大,很容易达到100 TB,甚至PB级别。
数据可靠性要求高,不能丢失数据。
因此根据以上特点,存储库有几个问题是设计的关键:
如何能支持100 TB,甚至PB级数据量?
数据量与成本成正比,如何能降低成本?
如何保证Feed不丢失?
确保消息ID在个人发件箱中严格递增,这样读取时只需要范围读取即可。由于个人发布的Feed并发度很低,用时间戳也能满足基本需求,但是当应用层队列堵塞,网络延迟变大或时间回退时,用时间戳还是无法保证严格递增。最好的方法是使用主键自增功能。
最佳的存储库应该是具有主键自增功能的分布式NoSQL数据库,但是在开源系统里没有完全符合特点的数据库,所以有两种存储方案:
对比项 | 表格存储 | 关系型数据库(分库分表) |
可靠性 | 极高可靠性,11个9的SLA保障 | 可靠性低于非关系型数据库,关系型数据库的可靠性通常最多6个9 |
运维复杂度 | 简单,无需分库分表 | 复杂,需要分库分表,带来了逻辑层和数据层的极大耦合性 |
主键自增功能 | 不需要加锁 | 需要加锁,且表锁会严重限制并发度,影响性能 |
阿里云的表格存储也属于有序性的分布式NoSQL数据库,具有以下优势:
单表支持10万亿行+、10 PB+的数据量,再快的数据增长速度都无需担心。
数据按主键列排序,可保证有序性和可预期性。
单key读写延迟在毫秒级别,可保证响应时间。
两种实例类型:高性能实例可提供极佳的读写性能;容量型实例可提供低存储成本。
通过对比分析,表格存储在功能、性能、扩展性以及成本方面都要更加适合存储Feed消息。
使用表格存储后,系统的架构如图所示:
同步
方案选择
Feed消息的同步主要有读扩散和写扩散两种方案:
读扩散(拉模式):用户发送消息时存入自己的发信箱,并不主动推送给其他用户。当其他用户获取Feed流时,系统需要去各个用户的发信箱中主动拉取。
写扩散(推模式):用户发送消息时存入自己的发信箱,同时会主动推动给其他用户,写入他们的收件箱。当其他用户获取Feed流时,系统只需按顺序读取收件箱中的消息即可。
读扩散和写扩散的对比请参见下表。
对比 | 读扩散(拉模式) | 写扩散(推模式) |
网络最大开销时刻 | 获取Feed流时 | 发布Feed时 |
读写放大 | 放大读 | 放大写 |
读写比例 | 99:1 | 1:99 |
用户读取延时 | 秒 | 毫秒 |
系统要求 | 读能力强 | 写能力强 |
架构复杂度 | 复杂 | 简单 |
拉模式和推模式在很多方面完全相反,并且对Feed流产品的用户而言,刷新Feed流(读取)时的延迟敏感度要远远大于发布(写入)。
因此可以看出,推模式优于拉模式,但是推模式也有一个缺点:数据会极大膨胀。针对这个缺点,可以考虑采用推拉结合模式。具体内容,请参见方案扩展。
架构设计
Feed流系统最大的特点是读写严重不平衡,一般读写比例都在10:1,甚至100:1之上。并且刷新Feed时的延时本质上由推送方案决定,其他的任何操作都只能是优化。设计推送系统具有以下关键点:
如何才能提供千万的TPS和QPS?
如何保证读写延迟在10ms,甚至2ms以下?读写直接会影响用户发布,刷新Feed流时的延迟,尤其是极其敏感的刷新时的延迟。
如何保证Feed的必达性?
如果要实现一个千万量级的Feed流产品,那么推送系统需要具备:
千万TPS/QPS的能力。
读写链路延迟低。
Feed消息的必达性高。
主键自增功能,保证用户收件箱中的Feed ID是严格递增的,保证可以通过Scan读取到最新未读消息。
最好能为用户存储Timeline中所有的Feed。
从上述特点来看,搭建推送系统最优的选择是使用高性能、高可靠,且有自增功能的NoSQL系统。在开源方案中,通常会在选择了关系型数据库作为存储系统的基础上,选择开源Redis,这样既能覆盖上述的几个特征,还能保证Feed流系统正常运行起来,但是也会带来一些其他问题:
表格存储 | Redis | |
存储成本 | 磁盘性数据库,费用比内存性的要低几个量级。 | 纯内存系统,内存价格极高,导致整体成本高。 |
运维复杂度 | 运维简单,单表可存储十万亿行以上的数据;天然分布式,单表可支持千万级TPS/QPS | 属于单机系统,为了支持千万TPS和保证消息必达性,需要使用cluster和replica模式,这样不仅带来了运维的复杂性,而且还需要增加机器,导致成本再次上升。 |
综上,Feed流系统的消息同步可以选择使用表格存储。在加入同步功能后,系统架构如下图所示:
元数据
处理系统基本的存储和推送功能外,还需要对元数据进行处理,元数据包括用户信息和用户关系等。下面以用户关系进行介绍。
用户账号之间的关注关系具有如下特点:
变长链表,长度可达亿级别。
数据量大,但关系简单。
性能敏感。直接影响关注、取关的响应速度。
有序性。不要求具有排序功能,只需要能按照主键排序即可。只要能按照主键排序,那么关注列表和粉丝列表的顺序就固定、可预期。
根据以上特点,最适合存账号关系的方案应该是分布式NoSQL服务。很多企业选择开源HBase来存储账号关系,开源HBase在满足了上述四个特征的同时可以把系统搭建起来,但是仍会有如下问题:
对比项 | 表格存储 | 开源HBase |
运维复杂度 | 全托管的分布式NoSQL存储服务,无需任何运维 | 需要自己运维、调查问题、修复问题,运维成本高 |
毛刺问题 | 由C++实现,彻底无GC问题,不会由于GC而导致较大的毛刺 | GC会导致比较大的毛刺,影响用户体验 |
以上可见,使用表格存储来存储账号关系是一个比较好的选择。
在加入关系表、用户表后,系统架构如下图所示: