数据库架构是否该随着公司估值一起变化?

news2024/12/23 8:54:19

原文|The growing pains of database architecture

作者|Tim Liang, Software Engineer at Figma


2020 年,因为 Figma 不断加入新功能,筹备第二条产品线和用户不断增长导致数据库流量每年以 3x 速度增长,我们的基础设施遇到了增长瓶颈。很清楚的是,原本的基础设施无法扩展以满足新需求,我们用了单个大型 Amazon RDS 数据库来存储元数据,比如权限、文件信息和评论等,虽然可以丝滑处理大多核心协作功能,但只有一个数据库的话限制很大。尤其在高峰期,流量达到 65% 以上时,单个数据库查询量过大导致 CPU 利用率上升。随着使用接近极限,数据库延迟变得越来越不可预测,严重影响用户体验。

如果数据库完全饱和,Figma 就会停止工作。

我们离宕机还挺遥远,但作为基础设施团队,我们得主动识别并解决可扩展性问题。需要一种解决方案以减少潜在的不稳定因素,并为未来的规模铺平道路。在实施该解决方案时,性能和可靠性是首要考虑因素;我们的团队旨在构建一个可持续发展的平台,使工程师能够快速迭代 Figma 而不影响用户体验。如果说 Figma 的基础设施是道路,那我们不能关闭高速公路来进行工作。

我们从一些修复开始,先延长一下道路的生命,并为一个更完整解决方案打下基础:

  • 将数据库升级到最大实例(从 r5.12xlarge 到 r5.24xlarge),以最大化 CPU 利用率。
  • 创建多个读取副本以扩展读取流量。
  • 为新用例建立新数据库以限制原始数据库增长。
  • 添加 PgBouncer 作为连接池器来限制新增连接(已经达到上千)所产生影响。

file

我们加入了 PgBouncer 作为连接池

虽然以上措施改善了些许,但也有局限性。分析了数据库流量后,我们发现写入操作,比如收集、更新或删除数据对消耗了大量数据库利用率。此外,并非所有数据读取都可以移动到副本中,因为应用程序对复制延迟滞后的敏感度不同。因此,从读和写两个方面来看,我们仍需要给原始数据库减压。是时候摆脱渐进式变化并寻找长期解决方案了。

探索之路

首先,我们探索了水平扩展数据库的可能。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 具有相同的目标数据库,客户端仍将成功查询数据。

file

初始状态

file

将 PgBouncer 分区后的数据库状态

一旦验证了应用程序已准备好为每个 PgBouncer 建立单独的连接(并正确发送流量),我们继续进行下一步。

file

数据库分区后的状态

合乎逻辑的选择

在 Postgres 中,有两种复制数据的方式:流复制或逻辑复制。我们选择了逻辑复制,因为它允许我们:

  • 传输一部分表格,这样开始时,我们可以在目标数据库使用更小的存储占用(减少存储硬件占用可以增加可靠性)。
  • 复制到运行着不同 Postgres 版本的数据库中,这意味着我们可以进行最小停机时间主版本升级。AWS 针对主版本升级支持蓝绿部署,但该功能尚未在 RDS Postgres 上提供。
  • 设置反向复制,使我们能够回滚操作。

使用逻辑复制的主要问题是我们有几个 TB 的生产数据,因此初始数据副本可能要数天甚至数周才能完成。我们希望避免这种情况,不仅为了将窗口期限制在最小范围内,也为了减少重新启动的成本。我们考虑过在正确的时间进行快照恢复并开始复制,但一个快照恢复就淘汰了能有较小存储占用空间。于是,我们开始调查为什么逻辑副本性能如此低下。原来拷贝降速是由于 Postgres 在目标数据库中维护索引的方式导致的。逻辑复制批量复制行,但它更新索引的效率低下:一次更新一行。删除目标数据库中的索引并在初始数据副本之后重建索引后,制作副本时间缩短到了几小时。

通过逻辑复制,我们能够从新分区数据库构建反向复制流,并返回原始状态。在原始数据库停止接收流量后,立即启动此复制流。对新数据库进行修改将被回传到旧数据库,在回滚事件中旧数据库会有这些更新。

关键步骤

解决了复制问题后,我们到了协调查询重定向的关键步骤。每天每时都有数千个客户端服务查询数据库。跨越这么多客户端节点进行协调很容易出问题。通过分两个阶段(先对 PgBouncers 分区,然后再是数据)执行切片操作,关键的数据分区操作只需要在为分区表提供服务的少量 PgBouncer 节点之间进行协调。

以下是正在进行中的操作概述:我们短暂地跨节点暂停所有相关数据库流量,方便同步新数据库以实现逻辑复制(PgBouncer 可以方便地支持暂停新连接和重定向)。当 PgBouncer 暂停新连接时,在原始数据库上撤销客户端对已分区表的查询权限。经过短暂的宽限期后,我们会取消任何剩余未完成的查询。由于我们的应用程序大多只会发出短时查询,因此通常会取消不到 10 个查询请求。此时,在流量暂停状态下,我们需要验证两个数据库是否相同。

在重定向客户端之前确保两个数据库相同是防止数据丢失的基本要求。我们使用日志序列号(LSN)来确定两个数据库是否同步。一旦确认没有新写入,就从原始数据库中取样一个 LSN,然后等待副本回放到此 LSN。此时,原始数据库和副本中的数据是相同的。

file

同步机制

在确认副本已经同步之后,我们停止复制并将副本提升为新数据库,并如前所述设置反向复制。然后恢复 PgBouncer 中的流量,不过现在查询被转到了新数据库。

file

过程总结

为未来规划

我们已经在生产环境中成功执行了多次分区操作,每一次都达到了最初的目标:在不影响可靠性的情况下解决可扩展性问题。我们第一次操作涉及移动两个高流量表,而 2022 年 10 月的最后一波操作涉及 50 个表。在每个操作期间,我们观察到大约 30 秒的部分可用性影响(请求丢失率约为 2%)。现在,每个数据库分区都具还有大量空间。我们最大的分区 CPU 利用率约为 10%,并且我们已经减少了一些低流量区所分配的资源。

现在还有很多工作要做。数据库很多时,客户端应用程序必须维护对每一个数据库的了解,并且随着添加更多数据库和客户端路由复杂度呈乘法级别增长。因此,我们引入了新的查询路由服务来集中和简化路由逻辑以便于扩展到更多分区。一些表有高写入流量或数十亿行和数千兆字节磁盘占用空间,这些表将分别遇到磁盘利用率,CPU 和 I/O 瓶颈问题。我们清楚,如果仅依赖垂直分区,最终还是会碰到扩展限制。回到最大化杠杆的目标上来说,我们为垂直分区打造的工具将让我们更好地处理高写入流量表的水平切片。它为我们提供了足够的「跑道」来维护当前项目并保持 Figma「高速公路」的畅通,同时也可以看到未来发展方向。


💡 你可以访问官网,免费注册云账号,立即体验 Bytebase。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/648994.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

将数组中指定位置的元素替换为指定值np.put()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 将数组中指定位置的元素替换为指定值 np.put() 选择题 下列说法错误的是? import numpy as np a np.array([1, 2, 3]) print("【显示】a ", a) print("【执行】np.put(arr…

Spring Boot进阶(47):Spring Boot之集成Cache缓存 | 超级详细,建议收藏

1. 前言 今天,我们来聊聊缓存这个话题。身为开发者肯定都知道,程序的瓶颈绝大体现在于数据库方面,而内存读取远远快于硬盘,当并发上升到一定高度,一次又一次的重复请求数据导致大量时间耗费在数据库查询上,…

win10搭建hmailserver邮件服务器(hmailserver+phpstudy+roundcube)

环境安装:Mysqlhmailserverphpstudyroundcube 一、Mysql安装 官网下载链接:https://www.mysql.com/ zip安装包下载链接:https://dev.mysql.com/downloads/mysql/ 1、zip安装包安装 (1)下载合适版本的mysql zip包&…

LeetCode------ 相交链表

前言 &#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏: &#x1f354;&#x1f35f;&#x1f32f;C语言进阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f349;栏目介绍:<<刷题集>>用于记录,力扣,牛客等刷题网站上的刷…

EasyExcel 批量导出

文章目录 前言一、EasyExcel 导出封装二、食用步骤1.自定义excel样式2.导出数据 三、复杂excel导出3.1. 自定义复杂表头2. 多sheet 前言 上篇写了数据导入&#xff0c;本文补充一下EasyExcel 批量导出 包括常规excel和复杂excel 一、EasyExcel 导出封装 import com.alibaba.…

手把手教你如何解开安装UKUI的黑屏故障

手把手教你如何解开安装UKUI的黑屏故障 引子 作为一个不折腾不舒服斯基的Linuxer&#xff0c;我又开始安装配置开放欧拉操作系统了。这是国产自主安全可控的Linux发行版。欧拉发行版Linux的发起者就是干正经事的华为&#xff0c;比其它拉大旗扯虎皮的国产Linux低调务实多了。…

远程控制之原理和实战

按理来说&#xff0c;本人不该发表此类专业文章&#xff0c;鄙人零星碎片化的开发经历&#xff0c;让本人斗胆向诸位网友&#xff0c;在远控方面做一点演示说明&#xff0c;谈论一点自己的认识。 程序工程代码地址&#xff1a;点击此处下载。 程序分为两个部分&#xff0c;控…

(三)Kafka 生产者

文章目录 1. Kafka 发送消息的主要步骤2.创建 Kafka 生产者3.发送消息到 Kafka&#xff08;1&#xff09;发送并忘记&#xff08;2&#xff09;同步发送&#xff08;3&#xff09;异步发送 4.生产者配置&#xff08;1&#xff09;client.id&#xff08;2&#xff09;ack&#x…

查看P端日志操作步骤

1.登录PUTTY,这里以联调环境103.160.139.82为例。 2.登录&#xff0c;查看用户名&#xff1a;hxb或zzkpt,密码&#xff1a;用户名01动态口令。 例如hxb, sunmenglei01888888 3.进入P端日志存放目录&#xff0c; cd /home/zzkpt/logs/bcip 4.比如我要查看2023年5月5日&#xf…

索引常见问题

被问到SQL和索引优化问题&#xff0c;如何切入&#xff1f; 可以用 explain 进行分析 思考流程&#xff1a;找到哪些运行时间长浪费性能的sql&#xff0c;然后再用explain分析 慢查询日志 MySQL可以设置慢查询日志&#xff0c;当SQL执行的时间超过我们设定的时间&#xff0…

在UE中使用SVT(VirtualTexture)功能

前几年VT技术非常的火&#xff0c;这项技术主要运用在地形上&#xff0c;可以达到更高级别的精细度和更多次数的纹理混合&#xff0c;但实际非地形也可以用&#xff0c;特别是对于贴图尺寸比较大且多维度子材质比较多的模型&#xff0c;做了材质合并以及VT优化后&#xff0c;可…

二、线性神经网络

文章目录 前言一、线性回归1. 线性回归的基本元素1.1 线性模型1.2 损失函数1.3 解析解1.4 梯度下降1.5 用模型进行预测 2. 正态分布与平方损失3. 从线性回归到深度网络 二、线性回归的代码实现1. 生成数据集2. 读取数据集2.1 手动实现读取数据集2.2 简洁实现读取数据集 3. 初始…

便携补光LED化妆镜方案

近段时间&#xff0c;现代科技的衍生产品&#xff0c;智能化妆镜很受爱美女士的喜爱。为此&#xff0c;宇凡微推出无极调光的LED化妆镜方案。主控芯片采用宇凡微YF单片机&#xff0c;根据LED化妆镜方案的不同功能&#xff0c;支持定制开发。 一、LED化妆镜方案介绍 在日常过程中…

Html span标签的详细介绍

HTML &#xff1c;span&#xff1e;标签_span标签_allway2的博客-CSDN博客 一、span标签的定义及用法 在html中&#xff0c;span标签是使用来组合文档中的行内元素&#xff0c;以便使用样式来对它们进行格式化。 span标签本身并没有什么格式表现&#xff08;比如&#xff1a;换…

利用Matlab和cadence实现离散傅里叶分析(DFT)

例1&#xff1a; 采样定律&#xff0c;取100个点&#xff0c;信号频率是100HZ&#xff0c;采样频率是1000HZ&#xff0c;相当于采样十个周期&#xff0c;每个周期采样十个点。 cos&#xff08;2πT&#xff09;函数是以Ts1/fs为时间间隔对样本进行采样&#xff0c;取N个采样样…

Mini热风枪 制作过程

首先引个流吧 立创开源广场&#xff1a;https://oshwhub.com/abby_qi/mini-re-feng-qiang 哔哩哔哩&#xff1a; 实物图 然后说一下硬件的选型和图 风扇&#xff1a;3010无刷风扇 额定电压3.7V&#xff08;其实这个风扇还有其他额定电压的&#xff0c;比如9V12V&#xff0c;…

PyTorch 深度学习 || 专题九:PyTorch 全连接自编码网络的无监督学习

PyTorch 全连接自编码网络的无监督学习 文章目录 PyTorch 全连接自编码网络的无监督学习1. 数据去噪1.1 计算库和数据准备工作1.2 构建自编码网络1.3 调用主函数1.4 可视化 2. 数据的重建与降维2.1 计算模块和数据的准备2.2 自编码网络数据准备2.3 自编码网络的构建2.4 自编码网…

1.5 掌握Scala内建控制结构(一)

一、条件表达式 &#xff08;一&#xff09;语法格式 if (条件) 值1 else 值2 &#xff08;二&#xff09;执行情况 条件为真&#xff0c;结果是值1&#xff1b;条件为假&#xff0c;结果是值2。如果if和else的返回结果同为某种类型&#xff0c;那么条件表达式结果也是那种…

微信小程序开发20__第三方UI组件 ColorUI 的应用

ColorUI 有鲜艳的高饱和色彩&#xff0c; 是专注视觉的微信小程序组件库。 gitee 网址 &#xff1a;ColorUI: 鲜亮的高饱和色彩&#xff0c;专注视觉的小程序组件库 一 使用方法 在微信小程序中使用 ColorUI 需要两个步骤&#xff1a; 第一步&#xff1a; 下载源码解压…

【Linux】详解环境变量与命名行参数

目录 环境变量了解PATH什么是环境变量&#xff1f;使用环境变量系统自带环境变量示例 命名行参数argc与argvenvenviron 环境变量 了解PATH 提出问题&#xff1a; 我写的可执行程序&#xff0c;与系统的可执行程序都是可执行程序&#xff0c;那么为什么执行系统的可执行程序…