Twitter 架构决策
技术决策需要在不同限制条件下做出权衡,本文介绍了 Twitter 早期应对用户大规模增长所做出的技术架构决策。原文:Twitter’s Tough Architectural Decision[1]
Jeremy Bezanger@Unsplash
研究大规模互联网应用可以学到很多东西,像 Netflix 这样的公司,其复杂的架构不仅能够向数百万用户提供内容,而且还改善了用户体验,增加了用户粘性。
在一家小公司中,像推送通知这样的机制可以是主代码库的一部分,并且可以在同一台机器上与应用程序的其他部分一起运行。
但在 Netflix,负责推送通知的服务器部署在全球多个数据中心,在本文的最后有一个关于这一架构演进的详细讨论的链接。
虽然学习这些案例研究很有趣,但大多数人仍然没有在如此大的规模下工作的经验。在某些情况下,我们更容易了解公司如何从传统的主流应用服务方式转变为迎合用户体验的现代解决方案。
下面我们以 Twitter 为例加以说明。
10 年前,大约在 2012 - 2013 年,Twitter 呈指数级增长,服务 1.5 亿用户。
当时 Twitter 每秒钟处理 6000 条来自用户的推文。我想说的是,这仍然在可控范围之内,我们拥有高吞吐量的数据库可以处理这一写入速率。
但对于阅读推文的用户来说,就有问题了。当每个用户查询 Twitter 主页时,必须获取到所关注的每个人的推文组合,这就是每秒钟 30 万请求!!
如果直接从数据库读取,这个读取速率会让人发疯!
每个请求是什么样子?我举个例子。想象一个人关注了 5000 个用户,当他打开 Twitter 网站时,后端使用如下查询访问数据库:
即使将查询限制为几条推文,仍然会给数据库带来沉重的负担。
Twitter 如何解决容量问题🚀
Twitter 有两条时间线:
用户时间线: 用户自己所有推文的集合,从磁盘中获取。
主页时间线: 用户关注的人的推文的集合,组成了用户的主页。
在设计数据库时,最简单的数据库只是将所有写操作附加到文件的末尾,并在需要时读取。没有什么比简单的附加到文件中更快的了,但是,随着数据库文件的增长,从这种类型的数据库中查询某些内容将花费很长时间。为了减少查询时间,我们在其中添加索引。但是添加索引将意味着写入将花费更长的时间,因为必须在将其写入数据库之前编辑索引。但由于读取的数量将远远多于写入的数量,这是一种公平的权衡。
同样,Twitter 的阅读量也远远超过了写入量。所以他们构建了一个系统,可以更好的为用户的主页时间线服务。他们预先计算出所有用户的主页时间线,并将其存储在 Redis 集群中。就这么简单!
现在,无论用户何时发布消息,该消息都会被插入每个关注者的时间线队列中。所以如果你有 5000 个粉丝,你的推文就会有 5000 个写操作!!外加上 1 个数据库本身的写操作😅
这听起来很疯狂,但你仔细想想,这是有道理的。现在可以同时为数百万用户提供服务而不需要访问磁盘,这可以从根本上减少延迟。这个过程叫做扇出(fan-out)!
那么到目前为止,Twitter 的架构是怎样的呢?
用户访问 Twitter 的主页。
Twitter 在 Redis 集群中查找用户的主页时间线。
一旦找到,就向用户直接展示。
当用户发送一条推文时,该推文会被复制到用户的所有关注者的时间线上。
还有一些其他的细节:
Twitter 维护了一个图数据库[2],其保存了用户关注关系的所有数据。当发生扇出时,扇出服务查询这个图数据库以确定将推文推送到何处。
扇出将在数据中心的 3 台不同的机器上进行复制,即每个用户的时间线存储在 3 台不同的机器上。这是必需的,因为如果其中一台机器出现故障,其他机器可以作为备份提供服务。
推文本身并不存储在集群中,而只存储推文 ID。推文将在传递给用户的同时被检索出来。
如果用户超过 30 天没有登录 Twitter,该用户的时间线将不会保存在 Redis 集群中。对于这类用户,只有当他们返回并向主页时间线发出请求时,才会从磁盘重构他们的时间线。
每个用户的主页时间线上存储的推文数量是有限制的,每次只向用户显示 800 条推文,这是为了控制内存使用而做出的设计决策!
名人用户的特殊处理💃🏻🕺🏻
上述优化适用于普通用户,但名人有特殊的处理,下面我们以 Lady Gaga 为例,她当时有大约 3100 万粉丝。
当 Lady Gaga 发布推文时,会发生 3100 万次扇出操作!!而且要复制 3 次!!
结果就是,有些用户可能会在 Lady Gaga 的推文发布 5 分钟后才能看到,因为他们的时间线没有被及时更新。
这就产生了一个不太好的效果,一个可以看到 Lady Gaga 推文的用户会回复这条推文,这条回复将会在其他用户的时间线中出现,而这些用户还没有收到 lady Gaga 的原始推文,也许可以称之为无头推文(Headless tweets)🤔
为了避免这种情况,Twitter 想出了一种混合方法:
如果一个人有太多的关注者,那么就不对他们的推文做扇出操作。
这些用户的推文只有在有人请求主页时才会被合并到时间线中。
又一个简单的解决方案!
总结
研究 Twitter 的整个架构是非常困难的,尤其是现在已经成为了大公司以后。
但从他们过去解决大规模问题的方式中学习,也可以帮助我们在工作的时候做出决策。即使在成千上万的地方存储一条推文听起来很荒谬,如果能够有效解决问题并且没有任何副作用,这就一个很好的解决方案!
参考
References:
[1] Twitter’s Tough Architectural Decision: https://dennysam.medium.com/twitters-tough-architectural-decision-c61e4d0d41a5
[2] SQL, NoSQL, Graph: A Commentary on Databases: https://softwareengineeringwk.substack.com/p/nosql-sql-graph-database-types?r=7esi1&utm_campaign=post&utm_medium=web
[3] Timelines at Scale: https://www.infoq.com/presentations/Twitter-Timeline-Scalability/
[4] The Infrastructure Behind Twitter: Scale: https://blog.twitter.com/engineering/en_us/topics/infrastructure/2017/the-infrastructure-behind-twitter-scale
[5] Scaling Push Messaging for Millions of Devices @Netflix: https://www.youtube.com/watch?v=6w6E_B55p0E
你好,我是俞凡,在 Motorola 做过研发,现在在 Mavenir 做技术工作,对通信、网络、后端架构、云原生、DevOps、CICD、区块链、AI 等技术始终保持着浓厚的兴趣,平时喜欢阅读、思考,相信持续学习、终身成长,欢迎一起交流学习。
微信公众号:DeepNoMind
版权声明: 本文为 InfoQ 作者【俞凡】的原创文章。
原文链接:【http://xie.infoq.cn/article/6eef701ac3687374c6fef7c58】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论