Datadog 的 Metrics Summary 页面有个问题。对一个客户,每次加载页面,数据库必须连接 82,000 个活跃指标表和 817,000 个指标配置表。p90 延迟达到 7 秒。每次用户点击过滤器,它触发另一个昂贵连接。

团队尝试通常修复,如查询优化、索引和调整。然而,问题不在查询。他们要求设计用于事务的数据库做搜索引擎的工作。修复那个页面引发一连串架构决策,不仅解决性能问题。它导致 Datadog 从根本上重新定义数据复制如何在其整个基础设施中工作。

问题:Postgres 做两份工作

Datadog 运营数千服务,许多由共享 Postgres 数据库支持。长时间,那个共享数据库是正确调用。Postgres 可靠、 хорошо 理解,在小到中等规模成本有效。然而,随着数据量增长,裂缝开始显示,Metrics Summary 页面只是最可见症状。

团队第一直觉是优化数据库。他们尝试调整连接顺序、添加多列索引,并使用基于表大小的查询启发式。没有一个能坚持,有几个问题:

  • 磁盘和索引膨胀减慢插入和更新
  • VACUUM 和 ANALYZE 操作添加维护开销
  • 内存压力推高 I/O 等待时间

使用 Datadog 自己的 APM 监控确认这些查询消耗不成比例系统资源份额,并随着数据增长变得更糟。当多个组织跨过每个组织 50,000 指标阈值时,警告信号无处不在,如慢页面加载、不可靠过滤器和堆积操作开销。

Postgres 被要求同时做两个根本不同工作。OLTP 工作负载是关系数据库设计的。然而,跨大规模非规范化数据集实时搜索是完全不同工作负载,一个像 Elasticsearch 这样的搜索引擎专门构建处理的。

因此,Datadog 停止让 Postgres 搜索。他们将数据从 Postgres 复制到专用搜索平台,沿途将关系结构扁平化为非规范化文档。这背后的机制是变更数据捕获(CDC)。

CDC 架构

Postgres 已经记录每个变化(每个插入、更新和删除)在其 Write-Ahead Log(WAL)中。这个日志主要为崩溃恢复存在,但也可以被外部工具读取。Datadog 使用 Debezium(开源 CDC 工具)读取该日志并将变化流式传输到 Kafka(持久消息代理)。从 Kafka,sink 连接器将数据推送到搜索平台。

这个方法优势是应用本身不需要改变如何写入数据。它仍然像以前一样写入 Postgres。然而,搜索查询现在命中搜索平台,它是专门为那个负载构建的。

同步 vs. 异步复制

在扩展那个模式前,团队面临关键设计选择:同步或异步复制。

同步复制

主数据库在确认写入给应用前等待每个副本确认接收它。这保证强一致性,意味着每个系统在所有时间有相同数据。但它慢。如果一个副本跨网络或暂时不健康,整个写入管道停滞等待确认。一个慢消费者成为一切瓶颈。

异步复制

异步复制翻转这个。主数据库立即确认写入,副本之后赶上。应用从不等待下游系统。这更快和更有弹性,但它引入窗口副本落后于源。这个差距是已知为复制延迟。数据将到达那里,但不是立即。

Datadog 选择异步。在他们规模,有数千服务跨多个数据中心分布,同步复制将耦合他们应用的性能到每个下游消费者的网络延迟和健康。那是不可行的。

帮助决策一个因素是成本具体。对短暂窗口在写入后,搜索平台可能显示稍微陈旧结果。如果用户添加新指标配置并立即搜索它,搜索平台可能还没有更新。对 Datadog 的用例(搜索、过滤、分析仪表板),几百毫秒延迟是完全可接受权衡相比 7 秒页面加载。

模式演进挑战

异步复制解决性能问题。然而,它引入新挑战。当你的数据形状变化会发生什么?

在正常应用,改变数据库 schema 是你和你的数据库之间。你运行迁移、添加列、改变类型,然后继续。有 CDC,每个 schema 变化传播到每个下游消费者,如果那些消费者没准备好变化,管道破坏。

考虑具体例子。团队使用 ALTER TABLE … ALTER COLUMN … SET NOT NULL 添加必需 region 字段到表。Debezium 开始生产包含这个字段的消息。但已经坐在 Kafka 中消息在旧 schema 下写入并没有它。期望每个消息有非 null region 字段的消费者开始失败,管道关闭。

Datadog 构建两部分防御。

第一道防线

自动化验证系统在 schema 迁移 SQL 应用到数据库前分析它。它捕获危险变化,如添加 NOT NULL 约束,并阻止它们部署没有协调。大多数迁移自动通过。那些没有标记需要团队直接与平台团队合作协调安全推出。

第二道防线

多租户 Kafka Schema Registry 配置为向后兼容。这意味着任何新 schema 必须仍然可由只理解旧 schema 的消费者读取。实践中,这限制 schema 变化为安全操作,如添加可选字段或移除现有字段。当 Debezium 捕获更新 schema,它以 Avro 格式序列化数据并将数据和 schema 更新推到 Registry。

Registry 检查新 schema 对抗现有 schema,并基于向后兼容规则接受或拒绝它。Datadog 使用 Avro 序列化特别是因为它原生支持这种 schema 协商。

自动化管道

使用异步复制运行和 schema 演进控制,Datadog 有工作管道。然而,设置每个新管道仍然需要工程师手动配置七个或更多组件跨多个系统。那是自动化改变游戏的地方。

单个 CDC 管道涉及许多移动部件。例如,你需要:

  • 通过设置 wal_level 为 logical 在 Postgres 启用逻辑复制
  • 创建有正确权限的 Postgres 用户
  • 建立复制槽和出版物
  • 部署 Debezium 实例
  • 创建有正确映射的 Kafka 主题
  • 设置心跳表用于监控
  • 配置 sink 连接器推送数据到目标

为所有手动做所有对一个管道是乏味。但为许多管道和多个数据中心做它意味着操作负担复合快速。

Datadog 使自动化成为基础原则。使用 Temporal(工作流编排引擎),他们将供应过程分解为模块化、可靠任务,并将它们缝合成高级工作流。如果步骤失败,工作流重试或干净回滚。团队不直接触摸基础设施。他们通过平台请求管道,自动化处理一切端到端。

扩展能力

这是将单个修复转变为全公司能力什么。从”复制这个 Postgres 表到搜索引擎”开始扩展到:

  • Postgres-to-Postgres 复制用于解开他们大型共享单体数据库
  • Postgres-to-Iceberg 管道用于事件驱动分析
  • Cassandra 复制支持非 SQL 数据源
  • 跨区域 Kafka 复制改善产品如 Datadog On-Call 的数据局部性

权衡

Datadog 做的每个架构选择都有成本:

  • 异步复制意味着下游系统总是稍微落后于源
  • Schema 演进约束意味着你不能自由改变数据库不考虑管道
  • 基础设施本身(Debezium、Kafka、Schema Registry、Temporal)代表许多移动部件操作、监控和维护
  • 最后,构建所有这个到平台需要专用团队拥有它

这个方法当你有真正不属于你主数据库的工作负载、当多个团队需要相同数据不同形状、当你的规模使手动管道管理不可行时有意义。Datadog 检查所有三个框。然而,如果你有几个简单数据流,开销不合理。定期批量同步或简单读副本可能是你需要的一切。不是每个问题需要平台。

本文为学习目的的个人翻译,译文仅供参考。

原文链接:How Datadog Redefined Data Replication

版权归原作者或原刊登方所有。本文为非官方译本;如有不妥,请联系删除。