Figma实现Postgres数据库架构扩展的方案

2023年 7月 10日 29.3k 0

2020 年,由于新功能的组合、准备推出第二个产品以及更多的用户(数据库流量每年增长约 3 倍),Figma 的基础设施遇到了一些成长的烦恼。我们知道,早年支持 Figma 的基础设施无法扩展以满足我们的需求。我们仍然使用单个大型 Amazon RDS数据库来保存我们的大部分元数据(如权限、文件信息和评论),虽然它可以无缝处理我们的许多核心协作功能,但一台机器有其局限性。

最明显的是,由于一个数据库服务的查询量,我们观察到在高峰流量期间 CPU 利用率高达 65%。

随着使用量接近极限,数据库延迟变得越来越不可预测,从而影响核心用户体验。

如果我们的数据库完全饱和,Figma 将停止工作。

远非如此,作为一个基础架构团队,我们的目标是在可扩展性问题接近迫在眉睫的威胁之前主动识别并修复它们。

我们需要设计一个解决方案来减少潜在的不稳定性并为未来的规模铺平道路。此外,在我们实施该解决方案时,性能和可靠性将继续成为首要考虑因素;我们的团队旨在构建一个可持续发展的平台,让工程师能够在不影响用户体验的情况下快速迭代 Figma 的产品。如果 Figma 的基础设施是一系列道路,我们就不能在施工时暂时关闭高速公路。

我们从一些战术修复开始,以确保额外一年的跑道,同时我们为更全面的方法奠定了基础:

  • 将我们的数据库升级到可用的最大实例(从r5.12xlarge 到 r5.24xlarge)以最大化 CPU 利用率跑道
  • 创建多个只读副本以扩展读取流量
  • 为新用例建立新数据库以限制原始数据库的增长
  • 将PgBouncer添加为连接池,以限制连接数量不断增加(以数千计)的影响
  • Figma实现Postgres数据库架构扩展的方案

    我们添加了 PgBouncer 作为连接管理器

    尽管这些修复起到了推动作用,但它们也有局限性。通过分析我们的数据库流量,我们了解到写入(如收集、更新或删除数据)占数据库利用率的很大一部分。此外,由于应用程序对复制延迟敏感,并非所有读取或数据提取都可以移动到副本。因此,从读写的角度来看,我们仍然需要从我们的原始数据库中卸载更多的工作。是时候放弃增量更改并寻找更长期的解决方案了。

    选择垂直扩展

    我们首先探索了水平扩展数据库的选项。许多流行的托管解决方案本身并不兼容我们在 Figma 使用的数据库管理系统Postgres 。如果我们决定使用水平可扩展的数据库,我们要么必须找到与 Postgres 兼容的托管解决方案,要么自行托管。

    迁移到NoSQL 数据库或Vitess (MySQL) 将需要复杂的双读写迁移,尤其是 NoSQL 还需要进行重大的应用程序端更改。对于与 Postgres 兼容的NewSQL,对于云管理的分布式 Postgres,我们将拥有最大的单集群足迹之一。我们不想成为第一个遇到某些扩展问题的客户。我们对托管解决方案几乎没有控制权,因此在没有按我们的规模进行压力测试的情况下依赖它们会使我们面临更多风险。

    如果不选择托管解决方案,我们的另一个选择是自托管。但由于迄今为止我们一直依赖托管解决方案,因此我们的团队需要进行大量的前期工作来获得支持自托管所需的培训、知识和技能。这将意味着巨大的运营成本,这将使我们不再关注可扩展性——这是一个更存在的问题。

    在决定反对水平分片的之后,我们不得不转向。

    我们决定按表对数据库进行垂直分区,而不是水平分片。

    我们不会将每个表拆分到多个数据库中,而是将表组移动到它们自己的数据库中。

    这被证明具有短期和长期的好处:垂直分区现在减轻了我们原来的数据库的负担,同时为将来水平分片我们的表的子集提供了前进的道路。

    垂直分区方法

    然而,在我们开始这个过程之前,我们首先必须确定要分区到它们自己的数据库中的表。有两个重要因素:

  • 影响:移动表格应该会移动很大一部分工作负载
  • 隔离:表不应与其他表强连接
  • 为了衡量影响,我们查看了查询的平均活动会话 (AAS),它描述了在特定时间点专用于给定查询的平均活动线程数。我们通过以pg_stat_activity10 毫秒为间隔进行查询来计算此信息,以识别与查询相关的 CPU 等待,然后按表名聚合信息。

    每个表的“隔离”程度证明了它是否易于分区的核心。当我们将表移动到不同的数据库时,我们将失去重要的功能,例如表之间的原子事务、外键验证和连接。因此,就开发人员必须重写多少 Figma 应用程序而言,移动表格的成本可能很高。我们必须通过专注于识别易于分区的查询模式和表来制定战略。

    事实证明,这对我们的后端技术堆栈来说很困难。

    我们使用Ruby作为应用程序后端,它为我们的大部分 Web 请求提供服务。反过来,这些会生成我们的大部分数据库查询。我们的开发人员使用ActiveRecord来编写这些查询。由于 Ruby 和 ActiveRecord 的动态特性,仅通过静态代码分析很难确定哪些物理表受到 ActiveRecord 查询的影响。

    作为第一步,我们创建了连接到 ActiveRecord 的运行时验证器。这些验证器将生产查询和交易信息(例如调用者位置和涉及的表)发送到Snowflake,我们在云端的数据仓库。我们使用此信息来查找始终引用同一组表的查询和事务。如果这些工作负载的成本很高,那么这些表将被确定为垂直分区的主要候选者。

    管理迁移

    一旦我们确定了要分区的表,我们就必须想出一个在数据库之间迁移它们的计划。虽然这在离线执行时很简单,但离线并不是 Figma 的选择——Figma 需要始终保持高性能以支持用户的实时协作。我们需要协调数以千计的应用程序后端实例之间的数据移动,以便它们可以在正确的时刻将查询路由到新数据库。这将允许我们对数据库进行分区,而无需为每个操作使用维护窗口或停机时间,这将对我们的用户造成干扰(并且还需要工程师在下班时间工作!)。我们想要一个满足以下目标的解决方案:

  • 将潜在的可用性影响限制在
  • 相关文章

    Oracle如何使用授予和撤销权限的语法和示例
    Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
    下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
    社区版oceanbase安装
    Oracle 导出CSV工具-sqluldr2
    ETL数据集成丨快速将MySQL数据迁移至Doris数据库

    发布评论