Notion如何解决其可扩展性危机
2021年,Notion的流行度飙升,但其服务变得极其缓慢。即使压缩后,该公司的数据库体积也达到了TB级别,即将崩溃。面对失去用户的风险,Notion需要快速解决这个问题。
核心而言,Notion中的所有内容都是一个块,可以是一段文本、一张图片,甚至是一个完整的页面本身。每个块都以RLE格式存储在PostgreSQL中,并具有其唯一的ID。块可以嵌套在其他块中以创建复杂的树状结构,从而实现令人难以置信的多功能性。但是,这种结构也意味着即使是一个简单的文档也会导致数百或数千个数据库条目。
随着数百万用户的涌入,Notion的数据库不堪重负,导致延迟增加和响应时间延长。该公司的单体数据库无法再处理如此大的负载,成本也呈指数级增长。
解决方案:水平扩展
Notion决定对所有通过外键链接到块表的表进行分片。这包括工作区、讨论和评论,将相关数据保留在同一个分片中。分区键选择为工作区ID,因为用户通常一次只请求单个工作区的数据。
新的分片设置需要处理现有数据,并扩展以满足至少两年的预期增长。实例类型至少需要60k总IOPS,为了维护RDS复制,他们将每个表的限制设置为500 GB,每个物理实例的限制设置为10 TB。
经过仔细考虑,Notion选择创建32个物理数据库实例,每个实例包含15个独立的逻辑分片。这导致在32个物理数据库中总共有480个分片。
路由机制
路由机制是在应用程序级别确定的,用于确定数据存储位置。应用程序使用负载分配,为新的分片配置更小的实例,并希望降低CPU、IOPS和成本。
迁移计划
Notion的迁移计划包括向PG bouncer添加96个新条目,暂时将它们映射到32个现有分片。这使他们能够随着数据写入新数据库而逐渐将数据迁移到新的分片。
然而,测试发现了一个严重的问题:由于每个旧分片映射到三个新分片,他们要么需要减少每个PG bouncer实例的连接数,要么将其增加3倍。解决方案是将PG bouncer集群本身进行分片,创建四个新的集群,每个集群管理24个数据库。这使他们能够将每个PG bouncer每个分片的连接数增加到8个,将每个PostgreSQL实例的总连接数限制为200个。
暗读和测试
在将这些更改部署到生产环境之前,Notion实现了暗读用于测试。他们在请求发出时添加了从旧数据库和新数据库中提取数据的功能,比较结果的一致性,并锁定任何差异以避免影响用户体验。
故障转移过程
故障转移过程包括流量暂停、停止新连接同时允许正在进行的查询完成、复制检查、确保新数据库完全同步以及配置更新。应用程序访问旧数据库的权限被撤销,PG bouncer被更新为指向新数据库,并通过从新数据库向旧数据库流式传输更改来反转复制方向,以防万一。
结果
重新分片项目对Notion来说是一次重大的成功。主要成果包括:
- 增加了容量
- 提高了性能
- CPU和IOPS利用率大幅下降,在高峰流量期间,新的利用率徘徊在20%左右,而之前为90%。
这个新的架构使Notion能够应对持续的用户增长和数据需求。