将 Postgres 和 MySQL 扩展至 768 台服务器看起来像一台

将 Postgres 和 MySQL 扩展至 768 台服务器看起来像一台

将关系型数据库扩展到每秒数百万查询和 PB 级数据,需要超越垂直扩展和只读副本,采用分片架构。通过实现复杂的代理层,组织可以将数据分布在数百台服务器上——例如 768 台服务器的集群——同时向应用层呈现单一、统一的数据库接口。

垂直扩展和只读副本的局限性

垂直扩展(增加 CPU 和内存)以及添加只读副本是有效的初始扩展策略,但它们最终会遇到三个关键瓶颈:

  • 写入瓶颈: 在 Postgres 中,所有写入必须记录在写前日志(WAL)并在主服务器上刷新到持久存储。这会形成单点写入瓶颈,添加只读副本无法解决。
  • 数据容量: 副本是主数据的完整拷贝。增加副本可以提升读取吞吐量,但不会增加系统可存储的总数据量。
  • 备份延迟: 将庞大的单体数据库备份到对象存储可能需要数小时甚至数天,受限于带宽,这对于严格的恢复点目标(RPO)和恢复时间目标(RTO)是不可接受的。

通过数据库分片解决扩展问题

分片将数据和查询分布到多个独立的主服务器上。这消除了单节点写入吞吐量和存储容量的限制。例如,使用 256 个分片,每个分片拥有一个主节点和两个副本,总计 768 台服务器,即可存储 1 PB 数据。

为了防止这种复杂性泄漏到应用代码中,使用代理层使分布式系统表现为单一数据库。

路由代理的作用

虽然像 PgBouncer 这样的简单代理只负责连接池和请求排队,分片则需要一个“路由”代理。路由器必须了解数据拓扑——数据如何在分片之间分布——并相应地路由 SQL 查询。

  • 数据分布: 路由器使用分片策略,通常是对 ID 列进行哈希,以确定哪一个分片应存储特定行。
  • 简单读取: 对于针对特定 ID 的查询,路由器直接将请求转发到对应的分片。
  • 复杂读取: 对于范围查询(例如 WHERE id BETWEEN 3 AND 5),路由器解析查询,识别所有相关分片,分发查询,聚合结果,并将最终集合返回给客户端。

定义数据拓扑

路由器使用 JSON 配置文件来定义表的分片方式。例如,配置可能指定 user 表在 id 列上使用特定的哈希索引进行分片。该元数据使路由器能够通用地处理多种模式和查询模式。

高可用性与流量分配

要处理每秒数百万查询,单个代理不足以支撑。需要在网络负载均衡器(NLB)后部署一整套代理。NLB 将传入连接分配给特定代理,直至连接生命周期结束,确保应用只需一个连接字符串(例如 mydb.pscale.com)。

请求流程如下: App ServerNLBRouter ProxyDatabase Shard

行业视角与权衡

虽然通过 Vitess(针对 MySQL)和 Neki(针对 Postgres)等工具进行分片是实现大规模的成熟路径,但社区也指出了若干技术挑战和替代方案:

  • 分布式 SQL 的复杂性: 批评者指出,分片会带来序列、外键、分布式事务和跨分片连接的困难。这些操作的性能特征往往与单分片查询不同。
  • “向上扩展”论点: 有些工程师认为,在采用分片的运营复杂性之前,应先最大化单台服务器的容量并优化缓存层。
  • 替代架构: 有人建议使用同步多写主系统(如 Oracle RAC),通过高速 RDMA 网络进行锁协调,能够在不需要手动分片或代理路由的情况下实现关系型数据库的水平扩展。

"当你再也无法检查系统的关键状态时,那就是你的公司应该上 NASDAQ 并准备为几百名工程师支付 30 万美元薪水的时刻。"

推荐方案概述

PlanetScale 建议在数据量超过几 TB 时,转向 Postgres 和 MySQL 的分片架构。在此规模下,单体主库的瓶颈以及备份窗口的限制通常使分片成为持续增长的最可行路径。

SUMMARY: PlanetScale 解释了如何使用 Neki 和 Vitess 的代理层将 Postgres 和 MySQL 数据库在数百台服务器上进行分片,同时为应用保持单一连接字符串。

TITLE: 将 Postgres 和 MySQL 扩展至 768 台服务器看起来像一台

Sources