PlanetScale让768台服务器像一台一样运行
时间:2026-07-18 22:00
通过数据库分片将数据与查询分布到多个主节点,并配合智能体路由器与网络负载均衡器,使768台服务器对应用呈现单一数据库视图,从而解决了写入瓶颈、数据容量限制及备份复杂度问题。
对某些人来说,这看起来像是一大堆计算机。但对于那些管理着数百万客户应用程序基础设施的人来说,每秒执行数百万次查询是家常便饭。这种规模的产品,通常需要数千台服务器协同工作。

数据库几乎总是最难扩展的基础设施组件。单个数据库服务器无法满足这样的需求,所以必须通过数据库分片,将查询和数据分散到许多服务器上。
数据库分片是扩展Postgres或MySQL数据库、处理超过几TB数据的最佳方法。接下来,看看如何从小型单节点数据库,发展到分布在四个分片上的几TB数据库,再到分布在768个服务器上、存储1PB数据的数据库。
成长的烦恼
要理解为什么分片是扩展关系数据库的必要组成部分,需要先了解一些可扩展性较差的方法的瓶颈在哪里。
先考虑一个简单的应用程序架构。
你用过的大多数应用,要么现在就是这样运行的,要么至少在其生命早期是这样。客户端设备上的软件,通过互联网连接到应用程序服务器。这个服务器位于数据中心,负责处理身份验证、页面加载以及所有服务器端逻辑。所有持久数据,比如用户账户、帖子、设置和消息,都存储在数据库服务器里,并从那里检索(这里的“数据库服务器”通常是Postgres或MySQL,但文章重点在Postgres)。
即使使用大型数据库服务器(几十个CPU核心、几百GB内存),瓶颈也会很快出现。通常,要么是高查询量导致的CPU限制,要么是大量读写导致的I/O限制(IOPS)。
通用可扩展性定律很好地总结了这一点:
简而言之,USL指出,资源争用会导致可扩展性随着资源增加而呈次线性增长;在某个点上,不一致性甚至会导致性能下降。Postgres也是如此,和任何试图在更大服务器上跨线程或进程扩展的软件系统一样。
至少在短期内,一种解决方法是利用只读副本。
在这个配置中,原始服务器作为主服务器,再加上一些副本,如上所示。
主节点会向每个副本发送连续的消息流,确保它们及时了解主节点上的数据变化。写入操作(INSERT、UPDATE、DELETE)只能发生在主数据库上。如果允许任何服务器写入,最终可能会出现数据冲突。解决这个问题需要复杂且缓慢的共识算法——虽然可行,但在大多数情况下,对性能最优并不理想。
不过,应用服务器可以向副本发送读取查询(SELECT)。由于大多数应用读取的比例远高于写入,这提供了更多的可扩展性(副本对高可用性和数据持久性也很必要,即使查询流量不需要它们)。
数据库可以通过添加副本来扩展,以处理更多流量。一个极端的例子是OpenAI,它在单个主节点上使用了50个副本。
但垂直扩展服务器(增加CPU/内存)和添加副本,只能让你走到一定地步。有些瓶颈是没法这样解决的:
1)写入仅限于一台服务器
如果写入量足够高,任何额外的只读副本都无法缓解问题。在Postgres确认写入已提交之前,它必须在预写日志(WAL)中记录这个更改,并把日志刷新到持久存储。WAL是主节点上所有连接之间的共享资源,这本质上就成了整个数据库的单一写入瓶颈,哪怕你有几十个副本。
2)副本不会增加数据容量
副本是主数据的完整拷贝,包括所有索引。添加副本,只是提供了更多执行读取的位置,并没有分布数据。
3)备份
备份是数据持久性和RPO/RTO保障的重要部分。由于节点与存储之间通信的带宽限制,把大型单体数据库备份到对象存储,可能需要几小时甚至几天。对很多依赖频繁、可验证备份的组织来说,这个时间长得让人无法接受。
处理这个问题最有效的方法,就是分片。
分片,带“d”
分片通过把数据和查询分布到许多不同的主节点上,完美解决了这三个瓶颈。对于数据来说,单个节点能存储的数据量有限,写入吞吐量也有限,分片正好解决了这个问题。对于查询来说,网络互连和CPU一次只能处理这么多查询,分片也分担了压力。
分片对超过几TB数据的所有规模都有用。比如,对于2TB的数据,可以选择一个带四个分片的设置,每个分片存储500GB,处理总查询流量的四分之一。当需要存储PB级数据(一百万GB)时,就需要更多分片了。这种情况下,可以用256个分片,每个分片有一个主节点加两个副本,每个负责存储约4TB。这需要256乘以3,也就是768台服务器!
如果没有一个好的系统,这会大大增加应用后端的复杂性。这么多事情同时发生,系统要怎么……
- 决定哪些数据发到哪个服务器?
- 决定哪些查询发到哪个服务器?
- 处理需要同时与多个分片交互的查询?
- 在这个分散的数据库里做备份?
- 监控整个系统的健康状况?
- 响应失败的服务器?
解决这些问题,可以讲很多。但本文要解决的问题是:
对于应用来说,这768台服务器,怎么看起来像一个内聚的数据库?
希望让应用服务器不再与下面这样复杂的系统交互:
相反,通过一个连接字符串与之交互,让它看起来像是在和一个大型、可扩展的数据库打交道:
而实际上,用的是几十或几百个分片。用于Postgres的Neki和用于MySQL的Vitess,就是来解决这个问题的。看看它们是怎么做到的。
智能体层
这里几个关键部分中,最重要的就是智能体层。
智能体是位于两个服务之间的中间件服务器。在我们的例子里,这两个服务是应用服务器和数据库服务器。
智能体经常和Postgres数据库一起使用。即使没有分片,它们对连接池和请求排队也很有用。对于常规(未分片)Postgres,PgBouncer是一种流行的智能体,能在较少的直接Postgres连接上复用1000个应用连接。
PgBouncer的目标很简单:接受来自大量客户端的大量连接,通过它与Postgres持续维护的较小连接池来路由这些连接。查询队列在流量激增和数据库故障转移期间特别有用,当新的主节点上线时,请求就能恢复。如果想了解更多,有关于PgBouncer的完整博客。
对Postgres进行分片,需要更复杂的智能体。最大的区别在于,除了多路复用和缓冲外,智能体还必须了解数据如何在服务器间分布,并把SQL查询路由到正确的分片。所以,常被称为路由器。
插入数据时,路由器必须知道数据的分布方式。这叫分片策略。
一种常见的方法,是根据id列的哈希值对传入行进行分片。插入这样一行数据到数据库时:
四个分片,每个被分配一个负责存储的ID范围,路由器把插入操作发送到正确的分片。插入先发送到路由器,在路由器里计算每个ID的哈希值,然后转发到正确的分片。
说到读取,有些查询很简单,路由器直接把它们转发到单个分片。
这种情况下,路由器需要做的,就是维护用户ID在哪些服务器中的内部映射,然后转发查询。根据上面的例子,这会发到第一个(最上面)分片。
有些情况就复杂多了。
这些范围ID的用户分布在多个分片中。路由器必须了解数据拓扑,制定一个计划,把查询分发到可能包含匹配结果的所有分片,把结果聚合回路由器,再把完整结果集发送给客户端。
最终,这意味着路由器本身必须内置完整的查询解析器和路由规划器。
路由器必须能在一个系统中执行查询解析、规划、连接池和缓冲。复杂的软件,要做到好可不简单。
它是怎么知道的?
每个数据库都独一无二,有自己的架构、表和查询模式。那么,路由器怎么知道哪些数据和查询该去哪?
在Neki和Vitess里,这些通过代表系统数据拓扑的JSON文件来指定。Vitess的VSchema和Neki的数据拓扑,给工程师提供了极大的灵活性,能精确描述表和查询应该怎么分布。这里是一个为用户表指定分片方案的简化示例:
这个元数据存储在路由器里,告诉路由器使用user_hash分片索引,在id列上对用户表进行分片。这个user_hash分片索引,用的是路由器的内置值哈希。对于每个传入行,它会对ID进行哈希运算,然后用这个结果把它发送到正确的分片去存储。
由于这一切都是通过文本和JSON传给路由器的,AI智能体在这个配置和优化环节就特别适合。
多个智能体,一个数据库
在跨越768台服务器的256个分片、每秒数百万次查询的规模下,没办法通过单个智能体来路由所有流量。需要很多个,可能是10个,也许是100个,具体取决于流量情况。
依然希望应用把它视为单个服务器。这就是网络负载均衡器(NLB)发挥作用的地方。
NLB的工作很简单:允许通过单个主机/IP进行连接,把每个连接分配到多个目标之一。这样,流量就在路由器之间实现了分配。一旦分配,连接在其生命周期内会一直保留在同一智能体上。
有些情况下,不需要NLB。去掉NLB会稍微增加应用服务器连接逻辑的复杂性,因为必须知道每个路由器的主机,但消除了网络跃点,能把往返延迟降到最低。
完整图片
现在,所有部件都就位了,让存储1000TB数据的768台服务器,在应用里显示为一个整体数据库。
- 应用服务器被告知“连接到位于mydb.pscale.com的数据库”
- 执行DNS查找,返回NLB的IP地址:123.152.100.4
- 应用请求连接到123.152.100.4的数据库
- 这首先通过NLB路由连接,然后路由到N个智能体之一
- 应用开始发送数据库查询,请求经过应用 -> NLB(可选) -> 智能体 -> 分片。复杂的路由逻辑对应用来说完全透明(为简化起见,NLB在下面没有显示)
这个示例展示的是扩展到1PB,但分片应该更早开始。精确建议取决于每个数据库的大小、架构和QPS,不过通常建议对超过几TB的Postgres和MySQL数据进行分片。这差不多就是前面描述的瓶颈开始出现的地方:长时间备份、写入瓶颈等等。如果面临扩展关系数据库的挑战,Neki和Vitess就是解决方案。
Vitess用于MySQL,十多年来一直用于扩展世界上最大的关系数据库。Neki由Vitess的专家维护者开发,为Postgres带来了更强大的分片系统。
其他一切呢?
只触及了Neki和Vitess等分片系统提供的所有功能的表面。还有很多有趣的细节:分片数据的最佳方法是什么?分片数据库怎么处理故障?分片数量怎么改?如何同时跨256个分片做备份?
敬请期待更多内容。
快乐分片。