共计 4676 个字符,预计需要花费 12 分钟才能阅读完成。
2020 年,因为 Figma 一直退出新性能,筹备第二条产品线和用户一直增长导致数据库流量每年以 3x 速度增长,咱们的基础设施遇到了增长瓶颈。很分明的是,本来的基础设施无奈扩大以满足新需要,咱们用了单个大型 Amazon RDS 数据库来存储元数据,比方权限、文件信息和评论等,尽管能够丝滑解决大多外围合作性能,但只有一个数据库的话限度很大。尤其在高峰期,流量达到 65% 以上时,单个数据库查问量过大导致 CPU 利用率回升。随着应用靠近极限,数据库提早变得越来越不可预测,重大影响用户体验。
如果数据库齐全饱和,Figma 就会进行工作。
咱们离宕机还挺边远,但作为基础设施团队,咱们得被动辨认并解决可扩展性问题。须要一种解决方案以缩小潜在的不稳固因素,并为将来的规模铺平道路。在施行该解决方案时,性能和可靠性是首要思考因素;咱们的团队旨在构建一个可继续倒退的平台,使工程师可能疾速迭代 Figma 而不影响用户体验。如果说 Figma 的基础设施是路线,那咱们不能敞开高速公路来进行工作。
咱们从一些修复开始,先缩短一下路线的生命,并为一个更残缺解决方案打下基础:
- 将数据库降级到最大实例(从 r5.12xlarge 到 r5.24xlarge),以最大化 CPU 利用率。
- 创立多个读取正本以扩大读取流量。
- 为新用例建设新数据库以限度原始数据库增长。
- 增加 PgBouncer 作为连接池器来限度新增连贯(曾经达到上千)所产生影响。
<center style=”font-size:14px;color:#C0C0C0″> 咱们退出了 PgBouncer 作为连接池 </center>
尽管以上措施改善了些许,但也有局限性。剖析了数据库流量后,咱们发现写入操作,比方收集、更新或删除数据对耗费了大量数据库利用率。此外,并非所有数据读取都能够挪动到正本中,因为应用程序对复制提早滞后的敏感度不同。因而,从读和写两个方面来看,咱们仍须要给原始数据库减压。是时候解脱渐进式变动并寻找长期解决方案了。
摸索之路
首先,咱们摸索了程度扩大数据库的可能。Figma 应用的数据库管理系统是 Postgres,很多风行的托管解决方案并不兼容。如果咱们决定应用可程度扩大的数据库,那要么找到一个兼容 Postgres 的托管解决方案,要么自托管。
迁徙到 NoSQL 数据库或 Vitess (MySQL) 要简单的双重读写迁徙,特地是对于 NoSQL 来说还要进行工程浩大的应用程序端更改。如果用反对 Postgres 的 NewSQL 数据库,咱们将会是云上分布式 Postgres 中的最大单集群,咱们不想冒险成为第一个遇到缩放问题的客户。对于托管计划,咱们能管制的比拟少,因而在没有通过针对咱们规模级别的压力测试就依赖它们会带来更多危险。如果不必托管计划,那就得自托管。但因为迄今为止咱们始终依赖托管计划,在团队可能反对自托管所需大量培训和投入,这意味着老本,也会扩散咱们次要关注的可扩展性 – 这才是个生死攸关的问题。
在决定不采取程度分区的两种后退门路之后,咱们决定垂直分区。这同时具备短期和长期效益:垂直分区当初能够缓解原始数据库的压力,并为当前程度划分子集提供了一条路。
咱们的分区办法
在开始前,咱们首先须要确定要将哪些表分区到本人的数据库中。有两个重要因素:
- 影响:挪动表应该可能解决大部分工作负载
- 隔离性:这些表不应与其余表严密相连
为了掂量影响,咱们参考了查问的均匀沉闷会话(AAS),它形容了在某一时刻给定查问的沉闷线程数量的平均值。咱们通过以 10 毫秒距离查问 pg_stat_activity 来计算 AAS,以辨认与查问相关联的 CPU 期待,并按表名聚合信息。
每个表「隔离」的水平对于是否容易进行分区至关重要。当咱们将表移到另一个数据库同时,咱们也失去了重要性能,例如原子事务、FK 验证和连贯表。因而,挪动表可能会须要开发人员从新编写 Figma 中很多代码,老本较高。咱们最好通过辨认易于分区的查问模式和表来制订策略。
然而,从后端角度看这很艰难。Ruby 作为咱们应用程序后端,服务了大部分 Web 申请,它们生成了大部分数据库查问语句,开发人员应用 Active Record 编写这些查问语句。因为 Ruby 和 Active Record 的动态性,仅通过动态代码剖析很难确定哪些物理表受到 Active Record 查问的影响。首先,咱们创立了运行时验证器,这些验证器连贯到 Active Record。这些验证器将生产查问和事务信息(例如调用者地位和波及的表)发送到 Snowflake(咱们的云上数仓)中进行解决。咱们应用此信息查找常常援用雷同组表格的查问和事务。哪儿工作负载老本高,那这些表就作为垂直分区的次要候选项。
治理迁徙
一旦确定了要分区的表,就要制订一个打算来在迁徙。尽管离线操作很简略,但对于咱们来说不是一个选项 – Figma 须要始终保持在线和高效以反对用户的实时合作。咱们要协调跨数千个应用程序后端实例的数据挪动,以便它们能够在正确的时刻将查问路由到新数据库。这样就能够在没有保护窗口或停机工夫(这会对用户造成烦扰,并且还须要工程师进行非工作工夫内的工作)状况下分区数据库。咱们的解决方案要满足以下指标:
- 将潜在可用性影响限度在 1 分钟内
- 自动化该过程,使其易于反复执行
- 近期的分区操作能撤回
没能找到合乎咱们要求的预构建解决方案,而且咱们也心愿灵便地适应将来状况。只有一个抉择:本人构建。
定制的解决方案
高层次上,咱们进行了以下操作(第 3-6 步在几秒钟内实现,以最小化停机工夫):
- 筹备客户端应用程序以从多个数据库分区查问
- 将表从原始数据库复制到新数据库,直到复制提早靠近 0
- 暂停原始数据库的流动
- 期待数据库同步
- 将查问流量从新定向到新的数据库
- 复原流动
正确筹备客户端应用程序是一个重要问题,Figma 应用程序后端的复杂性让咱们很焦虑。如果在分区之后错过了某些边缘状况会怎么样呢?为升高危险,咱们靠 PgBouncer 层来获取运行时可见性,并确保应用程序正确配置。与产品团队单干,以确保应用程序兼容分区后的数据库后,咱们创立了独自的 PgBouncer 服务虚构分流流量。平安组确保只有 PgBouncer 能够间接拜访数据库,这意味着客户端应用程序始终通过 PgBouncer 连贯。首先分区 PgBouncer 层将给客户留出余地。只管咱们能检测到路由不匹配,但因为两个 PgBouncer 具备雷同的指标数据库,客户端仍将胜利查问数据。
<center style=”font-size:14px;color:#C0C0C0″> 初始状态 </center>
<center style=”font-size:14px;color:#C0C0C0″> 将 PgBouncer 分区后的数据库状态 </center>
一旦验证了应用程序已筹备好为每个 PgBouncer 建设独自的连贯(并正确发送流量),咱们持续进行下一步。
<center style=”font-size:14px;color:#C0C0C0″> 数据库分区后的状态 </center>
合乎逻辑的抉择
在 Postgres 中,有两种复制数据的形式:流复制或逻辑复制。咱们抉择了逻辑复制,因为它容许咱们:
- 传输一部分表格 ,这样开始时,咱们能够在指标数据库应用更小的存储占用(缩小存储硬件占用能够减少可靠性)。
- 复制到运行着不同 Postgres 版本的数据库中 ,这意味着咱们能够进行最小停机工夫主版本升级。AWS 针对主版本升级反对蓝绿部署,但该性能尚未在 RDS Postgres 上提供。
- 设置反向复制 ,使咱们可能回滚操作。
应用逻辑复制的次要问题是咱们有几个 TB 的生产数据,因而初始数据正本可能要数天甚至数周能力实现。咱们心愿防止这种状况,不仅为了将窗口期限度在最小范畴内,也为了缩小重新启动的老本。咱们思考过在正确的工夫进行快照复原并开始复制,但一个快照复原就淘汰了能有较小存储占用空间。于是,咱们开始考察为什么逻辑正本性能如此低下。原来拷贝降速是因为 Postgres 在指标数据库中保护索引的形式导致的。逻辑复制批量复制行,但它更新索引的效率低下:一次更新一行。删除指标数据库中的索引并在初始数据正本之后重建索引后,制作正本工夫缩短到了几小时。
通过逻辑复制,咱们可能从新分区数据库构建反向复制流,并返回原始状态。在原始数据库进行接管流量后,立刻启动此复制流。对新数据库进行批改将被回传到旧数据库,在回滚事件中旧数据库会有这些更新。
关键步骤
解决了复制问题后,咱们到了协调查问重定向的关键步骤。每天每时都有数千个客户端服务查询数据库。逾越这么多客户端节点进行协调很容易出问题。通过分两个阶段(先对 PgBouncers 分区,而后再是数据)执行切片操作,要害的数据分区操作只须要在为分区表提供服务的大量 PgBouncer 节点之间进行协调。
以下是正在进行中的操作概述:咱们短暂地跨节点暂停所有相干数据库流量,不便同步新数据库以实现逻辑复制(PgBouncer 能够不便地反对暂停新连贯和重定向)。当 PgBouncer 暂停新连贯时,在原始数据库上撤销客户端对已分区表的查问权限。通过短暂的宽限期后,咱们会勾销任何残余未实现的查问。因为咱们的应用程序大多只会收回短时查问,因而通常会勾销不到 10 个查问申请。此时,在流量暂停状态下,咱们须要验证两个数据库是否雷同。
在重定向客户端之前确保两个数据库雷同是避免数据失落的根本要求。咱们应用日志序列号(LSN)来确定两个数据库是否同步。一旦确认没有新写入,就从原始数据库中取样一个 LSN,而后期待正本回放到此 LSN。此时,原始数据库和正本中的数据是雷同的。
<center style=”font-size:14px;color:#C0C0C0″> 同步机制 </center>
在确认正本曾经同步之后,咱们进行复制并将正本晋升为新数据库,并如前所述设置反向复制。而后复原 PgBouncer 中的流量,不过当初查问被转到了新数据库。
<center style=”font-size:14px;color:#C0C0C0″> 过程总结 </center>
为将来布局
咱们曾经在生产环境中胜利执行了屡次分区操作,每一次都达到了最后的指标:在不影响可靠性的状况下解决可扩展性问题。咱们第一次操作波及挪动两个高流量表,而 2022 年 10 月的最初一波操作波及 50 个表。在每个操作期间,咱们察看到大概 30 秒的局部可用性影响(申请失落率约为 2%)。当初,每个数据库分区都具还有大量空间。咱们最大的分区 CPU 利用率约为 10%,并且咱们曾经缩小了一些低流量区所调配的资源。
当初还有很多工作要做。数据库很多时,客户端应用程序必须保护对每一个数据库的理解,并且随着增加更多数据库和客户端路由复杂度呈乘法级别增长。因而,咱们引入了新的查问路由服务来集中和简化路由逻辑以便于扩大到更多分区。一些表有高写入流量或数十亿行和数千兆字节磁盘占用空间,这些表将别离遇到磁盘利用率,CPU 和 I/O 瓶颈问题。咱们分明,如果仅依赖垂直分区,最终还是会碰到扩大限度。回到最大化杠杆的指标上来说,咱们为垂直分区打造的工具将让咱们更好地解决高写入流量表的程度切片。它为咱们提供了足够的「跑道」来保护以后我的项目并放弃 Figma「高速公路」的畅通,同时也能够看到将来倒退方向。
原文:The growing pains of database architecture,作者 Tim Liang, Software Engineer at Figma
💡 你能够拜访官网:https://www.bytebase.com/,收费注册云账号,立刻体验 Bytebase。