缺陷在发货前测试不到80%的用户流程时会悄悄溜走。但是,获得这种覆盖率并保持在那里对于任何规模的团队来说都是困难且昂贵的。
QA Wolf为您减轻测试负担:
→ 仅需4个月即可达到80%的测试覆盖率。
→ 通过24小时维护 (opens new window)和按需测试创建保持无缺陷。
→ 获得无限并行测试运行 (opens new window)。
→ 保证零Flakes。
QA Wolf已为Salesloft、AutoTrader、Mailchimp和Bubble等公司带来了惊人的结果 (opens new window)。
🌟 在G2上评分为4.5/5。
了解更多关于他们的90天试点计划 (opens new window)。 Figma,一个协作设计平台,在过去几年里一直在快速增长。自2018年以来,其用户群几乎增长了200%,吸引了约300万月活跃用户。
随着越来越多的用户加入,基础架构团队发现他们处于一个困境之中。他们需要一种快速扩展他们的数据库的方法,以满足不断增长的需求。
数据库堆栈就像Figma的骨干。它存储和管理所有重要的元数据,如权限、文件信息和评论。从2020年以来,它的增长高达100倍!
这是一个好问题,但也意味着团队必须寻找创造性的解决方案。
在本文中,我们将深入探讨Figma的数据库扩展之旅。我们将探讨他们面临的挑战、他们做出的决策以及他们提出的创新解决方案。最终,您将更好地了解如何为像Figma这样快速增长的公司扩展数据库。
在2020年,Figma仍然使用单一的解决方案。 亚马逊RDS数据库持久化大部分的元数据。虽然它处理得很好,但一台机器有其限制。
在高峰交通期间,CPU利用率超过65%,导致数据库延迟不可预测。
虽然完全饱和还远远未到来,Figma的基础设施团队希望积极识别和解决任何可扩展性问题。他们从一些战术上的修复开始,比如:
将数据库升级为可用的最大实例(从r5.12xlarge升级到r5.24xlarge)。
创建多个只读副本以扩展读取流量。
为新用例建立新的数据库,限制原始数据库的增长。
添加PgBouncer作为连接池器,限制不断增长的连接数量的影响。 这些修复措施为他们提供了额外的一年时间,但仍存在一些限制:
根据数据库流量,他们发现写操作占整体利用率的很大一部分。
由于某些用例对复制延迟的影响敏感,无法将所有读操作移至副本中。
很明显,他们需要一个长期解决方案。
当 Figma 的基础设施团队意识到他们需要扩展他们的数据库时,他们不能简单地关闭一切,从头开始。他们需要一个解决方案,以确保 Figma 在解决问题的同时能够正常运行。
这就是垂直分区的作用。
将垂直分区看作整理衣柜。与其一团糟,不如将事物分开。 将表按垂直分区移动到不同的数据库。对于Figma来说,垂直分区是一个救命稻草。它们可以将高流量、相关的表,比如“Figma文件”和“组织”,移动到它们各自的数据库中。这为它们提供了急需的喘息空间。
为了确定分区表,Figma考虑了两个因素:
影响:移动这些表应该移动工作负载的重要部分。
隔离:这些表不应与其他表强相关。
为了衡量影响,它们查看了查询的平均活动会话数(AAS)。这个统计数据描述了在某个特定时间点针对给定查询的活动线程的平均数量。
衡量隔离性有点棘手。它们使用运行时验证器,连接到ActiveRecord,它们的Ruby ORM。验证器将生产查询和事务信息发送到Snowflake进行分析,帮助它们识别。 识别适合根据查询模式和表关系进行分区的表。
一旦确定了表,Figma需要在数据库之间迁移这些表而无需停机。他们为迁移解决方案设定了以下目标:
将潜在可用性影响限制在1分钟以内。
自动化程序,使其易于重复。
具有撤销最近分区的能力。
由于他们找不到一个预先构建的解决方案能够满足这些要求,Figma构建了一个内部解决方案。在高层次上,它的工作方式如下:
准备客户端应用程序以从多个数据库分区进行查询。
从原始数据库复制表到一个新数据库,直到复制延迟接近0。
暂停原始数据库上的活动。
等待数据库同步。
将查询流量重定向到新数据库。
恢复活动。
进行分区化迁移。 为了使数据库更加流畅,他们创建了单独的PgBouncer服务来在虚拟上分割流量。安全组被实施以确保只有PgBouncers能直接访问数据库。
首先对PgBouncer层进行分区给了客户一些余地,使得查询错误路由在初始阶段所有PgBouncer实例都指向相同的目标数据库。在此期间,团队还可以检测到路由不匹配并进行必要的更正。
下图显示了这个迁移过程。 解锁SQL查询的威力,提高性能 (opens new window)
SQL执行时会发生什么? (opens new window)
API版本控制策略速成课 (opens new window)
拥抱混沌以改善系统韧性:混沌工程 (opens new window)
如果想获取所有完整文章并支持ByteByteGo,请考虑订阅:
数据复制是扩展实时数据流的好方法。 针对数据库的垂直分区,Figma 在 Postgres 中有两种选项来复制数据:流复制或逻辑复制。
他们选择了逻辑复制,主要有以下3个原因:
- 逻辑复制允许他们传输一部分表,从而可以在目标数据库中以更小的存储占用开始。
- 它使他们能够将数据复制到运行不同 Postgres 主要版本的数据库中。
- 最后,它允许他们设置反向复制,以便在需要时回滚操作。
然而,逻辑复制速度较慢。初始数据复制可能需要数天甚至数周才能完成。
Figma 迫切希望避免这种漫长的过程,不仅为了最小化复制失败的可能性,还为了减少如果发生错误时重新启动的成本。
但是是什么导致了这个过程如此缓慢呢?
问题在于 Postgres 如何在目标数据库中维护索引。 数据库复制过程以批量复制行的方式进行,同时逐行更新索引。通过在目标数据库中删除索引并在数据复制后重建索引,Figma将复制时间缩短为几小时。
随着Figma的用户群和功能集的增长,对他们数据库的需求也在增加。
尽管他们尽力进行垂直分区,但对于Figma最大的表格来说,这种方法存在一些局限性。一些表格包含数TB的数据和数十亿行,使它们过大,无法存放在单个数据库中。
一些问题特别突出:
Postgres 真空问题:真空是Postgres中的一个重要后台进程,用于回收被删除或过时行占用的存储空间。如果不定期进行真空处理,数据库最终会用尽事务ID并停止运行。然而,对大表进行真空处理可能会消耗资源并导致性能问题和停机。
每秒最大IO操作:Figma的最高写入操作 随着表格增长速度的加快,它们很快就会超过亚马逊的RDS的最大IOPS限制。
为了更好地理解,想象一下一个图书馆,它的藏书量在迅速增长。最初,图书馆可能通过增加更多的书架(垂直分区)来应对。但最终,建筑本身会耗尽空间。无论你如何高效地摆放书架,你都无法在一个建筑物中容纳无限数量的书籍。这时就需要考虑开设分馆。
这就是水平分片的方法。
对于Figma来说,水平分片是将大型表格分割到多个物理数据库中,使它们能够超越单台机器的限制。
下面的图表显示了这种方法: 然而,水平分片是一个复杂的过程,带来了一系列挑战:
- 一些SQL查询变得低效。
- 应用程序代码必须更新以有效地将查询路由到正确的分片。
- 必须在所有分片之间协调模式更改。
- Postgres不再能够强制执行外键和全局唯一索引。
- 事务跨多个分片,这意味着Postgres无法用于强制执行事务性。
Figma的工程团队评估了诸如CockroachDB、TiDB、Spanner和Vitess等替代SQL选项,以及NoSQL数据库。
然而,最终他们决定在现有的垂直分区的RDS Postgres基础架构上构建一个水平分片的解决方案。 他们有多个原因选择这种决定:
他们可以利用他们对RDS Postgres的现有专业知识,多年来一直可靠地运行。
他们可以根据Figma的特定需求定制解决方案,而不是调整他们的应用程序以适应通用的分片解决方案。
在出现任何问题的情况下,他们可以轻松回滚到未分片的Postgres数据库。
他们无需将建立在Postgres架构之上的复杂关系数据模型更改为像NoSQL这样的新方法。这使团队能够继续构建新功能。
Figma的水平分片方法是根据他们的特定需求以及现有架构量身定制的。他们做出了一些与其他常见解决方案有所不同的不寻常设计选择。
让我们来看看Figma分片方法的关键组成部分:
Figma引入了“colos”或colocations的概念,这是一组相关的表,共享相同 为了创建Colos,他们选择了一些分片键,如UserId,FileId或OrgID。在Figma的几乎每个表中,都可以使用这些键进行分片。
这为开发人员提供了一个友好的抽象,用于与水平分片的表进行交互。
Colo内的表支持跨表连接和完整事务,当限制为单个分片键时。大多数应用代码已经以类似的方式与数据库交互,这最大程度地减少了应用程序为使表准备好进行水平分片所需的工作。 Figma将应用程序层的“逻辑分片”与Postgres层的“物理分片”概念分开。
逻辑分片涉及为每个表创建多个视图,每个视图对应于给定分片中数据的子集。所有对表的读写操作都通过这些视图发送,使表看起来像是水平分片,尽管数据实际上位于单个数据库主机上。
这种分离使Figma能够将迁移的两个部分解耦,并独立实现它们。他们可以在执行更危险的分布式物理分片之前执行更安全、更低风险的逻辑分片部署。
撤销逻辑分片只需要简单的配置更改,而撤销物理分片操作则需要更复杂的协调来确保数据一致性。
为了支持水平分片,Figma工程团队构建了一个名为DBPro的新服务。 DBProxy包括一个轻量级查询引擎,能够解析和执行水平分片查询。它由三个主要组件组成:
查询解析器读取应用程序发送的SQL,并将其转换为抽象语法树(AST)。
逻辑规划器解析AST,提取查询类型(插入,更新等)和查询计划中的逻辑分片ID。
物理规划器将查询从逻辑分片ID映射到物理分片ID,与连接池层之间的中间层,例如PGBouncer。 数据库并重写查询以在适当的物理分片上执行。
在查询处理工作流程中,下面的图表显示了这三个组件的实际用途。
总是在水平分片的世界中进行查询时存在权衡。对于单个分片键的查询相对容易实现。查询引擎只需提取分片键并将查询路由到适当的物理数据库。
但是,如果查询不包含分片键,则查询引擎必须执行更复杂的“分散-聚集”操作。 这个操作类似于一个捉迷藏游戏,您将查询发送到每个分片(散射),然后从每个分片收集答案。
下面的图表显示了单个分片查询与散射-收集查询的工作原理。
如您所见,这会增加数据库的负载,有太多散射-收集查询可能会对水平可伸缩性造成伤害。
为了更好地管理事务,DBProxy处理负载分担、事务支持、数据库拓扑管理和改进的可观察性。
Figma增加了“阴影应用程序就绪”。 能够预测在不同潜在分片键下实时生产流量行为的框架。
这个框架帮助他们保持了 DBProxy 的简单性,同时减少了应用开发人员在重写不支持的查询时所需的工作量。
所有查询和相关计划都被记录到 Snowflake 数据库中,他们可以进行离线分析。根据收集的数据,他们能够选择一种支持最常见 90% 查询的查询语言,同时避免查询引擎中的最坏情况复杂度。
Figma 的基础设施团队在 2023 年 9 月首次发货了其第一个水平分片表,标志着他们在数据库扩展旅程中的重要里程碑。
这是一个成功的实施,对可用性几乎没有影响。此外,团队在分片操作后观察到延迟或可用性没有退化。
Figma 的最终目标是在他们的数据库中水平分片每个表,并实现近乎无限的可伸缩性。 他们已经确定了需要解决的几个挑战,例如:
- 支持水平分片模式更新
- 为水平分片的主键生成全局唯一标识符
- 为业务关键用例实现原子跨分片事务
- 启用分布式全局唯一索引
- 开发ORM模型以提高开发人员速度
- 自动重新分片操作,以便在点击按钮时启用分片拆分。
最后,在获得足够的发展空间之后,他们还计划重新评估他们目前使用的内部RDS水平分片方法,是否应在将来转向开源或托管替代方案。
参考文献:
- Figma数据库团队是如何应对规模挑战的 (opens new window)
- 数据库架构的成长烦恼 (opens new window) 获取你的产品,让超过 500,000 名技术专业人士看到。
我们的通讯直接将您的产品和服务展示给一众重要的受众——数十万工程领导和高级工程师——他们对重大技术决策和大额购买具有影响力。
广告位有限,请尽快预订
广告位通常提前约 4 周售罄。为确保您的广告触达这一具有影响力的受众群体,请立即通过电子邮件预订您的广告位:hi@bytebytego.com.