关于我们

质量为本、客户为根、勇于拼搏、务实创新

< 返回新闻公共列表

2026 分布式 PostgreSQL 服务器租用分片集群配置手册:Citus 协调节点/分片数/内存 对比 + 避坑避雷全攻略

发布时间:2026-10-16

2026 分布式 PostgreSQL 服务器租用分片集群配置手册:Citus 协调节点/分片数/内存 对比 + 避坑避雷全攻略

本篇结论先看:Citus 集群的钱该花在哪个节点

干过几套 Citus 集群之后,我对这个东西的判断跟大多数人第一次接触时完全不一样。新手上来就问"worker 要多大内存""CPU 要多少核",这没错,但他们几乎都会漏掉一件事:Citus 真正的资源大头不在跑查询,而在分片数决定的元数据、连接和索引开销上。你把分片数从 32 提到 128,数据一行没多,集群的负担却实打实翻了几倍——协调节点要维护的分片元信息变多、每个查询要打开的连接变多、每个 worker 上的索引对象数量乘以 4、autovacuum 的工作单元乘以 4。这些成本在你导入第一行业务数据之前就已经产生了。

所以这篇我不打算按"先讲原理再给配置"的路子写,而是把容量规划直接拆成三本账:协调节点账、worker 账、磁盘账。哪本账算错,集群就垮在哪儿。下面几条是我认为最该记牢的结论:

结论一:分片数一旦建表就锁死,改分片数等于重建表。Citus 默认 citus.shard_count = 32,很多团队建完表就再没动过这个数字,等到数据量涨到几十亿行才发现并行度不够、或者分片太碎拖垮协调节点——这时候唯一出路是停写、新建表、重新导数据。这是 Citus 项目里最常见的一次返工,没有之一。

结论二:协调节点是单点,而且它的瓶颈是连接数不是 CPU。协调节点保存全部 pg_dist_* 元数据,负责分布式计划生成和结果归并。它不存业务数据,但要给每个 shard 缓存连接,连接数量级约等于"分片数 × 并发查询数"。max_connections 默认 100,在 64 shard、20 并发的场景下,几百个后端进程直接把默认值顶穿。

结论三:索引不是全局的,是按 shard 数倍乘的。100 个 shard 上建 3 个索引,物理上就是 300 个索引对象,磁盘占用、写入放大、autovacuum 开销全按 300 份算。这条最容易被低估,很多人算磁盘只算数据,忘了索引才是吃空间的大头。

结论四:跨分片 JOIN 能不能下推,取决于分片键是否对齐(colocated)。对齐了,JOIN 在每个 worker 本地做完再汇总;没对齐,就得把数据拉回协调节点归并,这时候协调节点的 work_mem × 并发数就是瓶颈,临时文件落盘能把磁盘写穿。

结论五:扩容加 worker 会触发 shard rebalancer,它是在线拷数据,期间 IO 和 WAL 翻倍,必须留 30% 以上空闲盘。拿着 85% 使用率的盘去扩容,rebalance 跑到一半磁盘满,集群就进了一个非常难收拾的状态。

Citus 到底是什么:分片表、参考表与本地表的三种代价

先把话说直白:Citus 不是另一个数据库,它是 PostgreSQL 的一个扩展。装完之后你面对的仍然是 Postgres,能用的 SQL 语法、索引类型、扩展插件基本都还在。Citus 干的事只有一件——把一张逻辑表按你指定的分片键切成 N 个 shard,每个 shard 就是一张再普通不过的 Postgres 物理表,然后散落到不同的 worker 节点上。理解这一点很重要,因为后面所有的成本推导,都是从"每个 shard 都是一张独立表"这个事实推出来的。

分布式表:一行数据落在哪,全看分片键

分布式表是 Citus 的主力形态。建表时你指定一个分片键(比如 tenant_id 或者 user_id),Citus 对分片键做哈希,把每一行路由到某个 shard,再把 shard 分配到 worker。查询的时候,如果 WHERE 条件里带了分片键,协调节点能算出"这一行在哪个 shard",直接把 SQL 下推到那一台 worker,这种查询效率非常高,几乎等于单机查询。

麻烦在于不带分片键的查询。协调节点不知道数据在哪个 shard,只能把 SQL 广播到所有 shard 并行执行,然后自己在内存里做归并。这时候分片数就是并行度——分片数太少,并行度不够,查询慢;分片数太多,协调节点要同时维护几十上百个连接和结果集,归并成本反过来吃掉并行度的收益。这是个典型的"中间最优"问题,不存在"越多越好"。

参考表与本地表:看着省事,代价藏在写放大和单点里

参考表用 create_reference_table 创建,它的特点是每个 worker 上都存一份完整副本。这么设计的目的很明确:让分布式表能在本地跟参考表 JOIN,不用跨网络拉数据。省市区字典表、商品类目表、组织架构表这类小而几乎不更新的数据,放参考表非常合适。

代价是写放大等于 worker 数量倍。你有 8 个 worker,改一行参考表数据,Citus 要在 8 台机器上各写一次,而且是分布式事务,任何一台失败就整体回滚。我见过有人把"偶尔会改的配置表"做成参考表,结果每次配置变更都要走一遍 8 节点的 2PC,慢不说,还把协调节点的事务槽位占住。判断标准其实就一句:这张表是不是几乎只读?是,做参考表;不是,宁可做成分布式表跟着分片键走。

本地表是第三种形态,只存在于协调节点上,worker 完全不知道它存在。它适合放那些压根不参与分布式查询的小表,比如运维用的任务记录表、临时导入的中间表。代价是它天然单点——协调节点一挂,本地表跟着不可用;而且本地表跟分布式表 JOIN 时,Citus 只能把本地表的数据广播出去或者拉回来归并,数据量一大就直接爆内存。别把本地表当"反正小就随便放"的垃圾桶,超过几百万行的表放协调节点,迟早出事。

分片数怎么定:32 个默认值为什么是返工源头

Citus 较新版本默认 citus.shard_count = 32(老版本有 32/64 两档可选)。这个默认值的问题不在于它不对,而在于它对所有规模的业务都用同一个数字,而"改分片数只能重建表"这条限制,让这个默认值变成了隐形的返工源头。

为什么改不了?因为分片数是建表时写进 pg_dist_shard 元数据的,shard 的物理表已经按哈希区间建好了。你想从 32 改到 128,哈希映射关系就全变了,之前落在 shard 3 的数据现在应该落在 shard 17——这不是改个参数的事,是把数据全部重算重分布一遍。Citus 没有提供在线的"重新分片"命令,官方推荐做法就是新建一张分片数正确的表,然后把老表数据导过去。几十亿行的表导一遍是什么工作量,做过的人都知道。

那到底定多少?我用的经验公式是分片数 ≈ worker 数 × 每节点物理核数的量级。这个公式的道理是:Citus 的并行度本质上受限于能同时跑查询任务的 CPU 核数,分片数少于总核数,worker 上的核就闲着;分片数远超总核数,多出来的分片只会变成协调节点的负担,不会带来额外并行度。举几个具体例子:2 台 16 核 worker,分片数取 32 刚好;4 台 32 核 worker,分片数应该往 128 走;如果你是 3 台 8 核 worker 的小集群,32 分片其实已经偏多了,16 更合适。

分片过多的代价是具体的、可量化的。每个查询要在协调节点上打开 32 个以上(按分片数走)的连接与文件描述符,max_connections 和 ulimit -n 会先于 CPU 爆掉。我在一套 128 分片、4 worker 的集群上见过这种情况:业务并发才 30,协调节点的 Postgres 后端进程就堆到 400 多个,max_connections 提到 1000 之后又开始撞 ulimit -n,最后是靠在协调节点前面挂 pgbouncer 才压下来。分片数定得对,这些问题根本不会出现。

还有一点容易被忽略:分片数决定的是单张表的切分粒度,不是整个集群的切分粒度。你有 10 张分布式表,每张 32 分片,那整个集群就是 320 个 shard、320 张物理表。算资源的时候要按集群总分片数算,不是按单表的 32 算。这也是为什么"表一多就卡"的现象在 Citus 里特别常见——表数量 × 分片数,是个乘法。

协调节点才是单点:连接数、元数据与并发归并三本账

协调节点的定位很特殊:它不存业务数据,却掌握着集群的全部命运。pg_dist_partition、pg_dist_shard、pg_dist_placement 这些元数据全在它身上,分布式查询计划的生成、结果集的归并、分布式事务的协调,也都由它负责。它挂了会怎样?写入全停,跨分片查询全停,但单分片的直连查询还能跑——你可以直接连到 worker 上查某一个 shard 的数据。这一点在容灾设计里经常被忽略,但它其实是个很有价值的救命通道:协调节点起不来的时候,至少能把数据读出来。

协调节点上有三本账要算清楚。

第一本账是元数据账。每个 shard 在 pg_dist_shard 里有一条记录,在 pg_dist_placement 里按副本数有多条记录。几千个 shard 看起来元数据表不大,但这些表是查询计划的必经之路——每次生成分布式计划都要查一遍。分片数上万之后,光是计划生成的耗时就能从毫秒级涨到几十毫秒,对高并发小查询的场景是实打实的损耗。

第二本账是连接数账,这是最容易爆的一本。Citus 在协调节点上会缓存到各 shard 的连接,避免每个查询都重新建连。连接数的量级约等于分片数 × 并发查询数:64 个 shard、20 个并发查询,协调节点上的 Postgres 后端进程就是几百个量级。Postgres 的 max_connections 默认 100,完全不够用。而且连接数不是你想调多大就多大——每个后端进程在 Linux 上要吃掉文件描述符,Postgres 自身的 max_files_per_process 和系统的 ulimit -n 都会成为上限。我的做法是:协调节点 max_connections 直接给到 800–1500,系统 ulimit -n 给到 65535 以上,然后在前面加一层 pgbouncer 做连接复用,让应用侧的连接数和 Citus 内部的连接数解耦。

第三本账是并发归并账。不带分片键的查询、跨分片 JOIN、ORDER BY + LIMIT、GROUP BY,这些都要在协调节点上归并。归并用的内存是 work_mem,而且注意——不是按查询数算,是按"查询数 × 每个查询归并的分片数"算。一个跨 64 个 shard 的排序查询,如果每个 shard 的结果集都要在协调节点上参与排序,那么 64 份数据 × work_mem 就是这一条查询的归并内存需求。并发 20 个这样的查询,内存需求直接乘 20。work_mem 给大了 OOM,给小了排序落盘写临时文件——这就是协调节点上那块盘必须单独规划的原因。

所以协调节点的配置思路跟 worker 完全不同:CPU 中等偏上就够(计划生成是单线程的),内存要足(并发归并吃内存),连接数要给足(这是硬指标),磁盘要快但不一定要大(主要放临时文件和 WAL)。我一般会建议协调节点单独一台物理机,别跟 worker 混部——混部的时候,worker 上的大查询一跑,CPU 和 IO 争抢,协调节点的计划生成和归并就跟着慢,整个集群的响应都会抖。

worker 节点的内存账:shared_buffers 与 work_mem 的放大系数

worker 是真正干活的地方,内存账要分成三块算:shared_buffers、每连接的 work_mem、autovacuum 用的 maintenance_work_mem。

shared_buffers 按 Postgres 惯例给物理内存的 25%。这条老规矩在 Citus 上依然适用,但要注意一个前提:worker 上跑的不是"一个数据库",而是"N 个 shard 对应的 N 张物理表"。这些表共享同一个 buffer pool,所以 shared_buffers 的总量不需要按分片数放大——它需要覆盖的是"热数据总量",不是"表数量"。真正会随分片数变化的是另外两项。256GB 内存的 worker,shared_buffers 给 64GB 是合理起点;如果你的热数据远超 64GB,那该加内存而不是硬调 shared_buffers(超过 40% 之后收益递减,还会挤压 OS page cache)。

work_mem 的放大系数是分片数。这是 worker 内存账里最容易算错的一项。Postgres 的 work_mem 是"每个排序/哈希操作"的上限,一个查询里可能有多个这样的操作,所以单机 Postgres 上就有"一个查询可能用 N 倍 work_mem"的说法。到了 Citus 上,情况更狠:一条广播查询会在每个 shard 上各自执行一次排序/哈希,而同一个 worker 上可能落着多个 shard——假设 128 个 shard 平均分布在 4 台 worker 上,每台 32 个 shard,那么一条广播查询在这一台 worker 上就是 32 个并行的排序操作,每个都能用满 work_mem。32 × 并发查询数 × work_mem,这才是 worker 的真实内存需求。work_mem 默认 4MB 看着小,乘以 32 再乘以 20 并发就是 2.5GB——还好;但如果你按单机经验把它调到 64MB,同样场景就是 40GB,加上 shared_buffers 的 64GB,一台 128GB 的机器就开始 OOM 了。

我的建议是:worker 上的 work_mem 不要照搬单机经验值,按"总分片数 / worker 数"反推。计算公式大概是 work_mem ≤ (可用内存 - shared_buffers) / (每 worker 分片数 × 预期并发 × 每查询排序操作数)。算出来偏小是正常的,宁可让排序落盘(前提是临时文件空间规划好了),也不要 OOM——Postgres OOM 被系统杀掉的后果比慢查询严重得多。

maintenance_work_mem 的账要跟 autovacuum_max_workers 一起算。Citus 上每个 shard 都是一张独立的表,有自己的 visibility map、自己的 dead tuple、自己的 autovacuum 工作单元。这是关键:autovacuum 的最小工作单位是表,而 Citus 里表 = shard。100 个 shard 就是 100 张待清理的表,autovacuum 要挨个排队处理。而每个 worker 节点上同时只能跑 autovacuum_max_workers(默认 3)个 autovacuum 进程,每个进程吃一份 maintenance_work_mem。分片数多的时候,autovacuum 的排队会严重滞后,dead tuple 清不掉,表膨胀,查询变慢——这个连锁反应在高写入的 Citus 集群上非常常见。解法是把 autovacuum_max_workers 提到 6–8,同时把 autovacuum_naptime 调小,并给足 maintenance_work_mem。调高的同时,内存也要给这些进程留出来——8 个 worker 进程各占一份 maintenance_work_mem,这部分必须提前从总内存里扣掉。

磁盘账:索引按分片数倍乘、WAL 独立盘与 rebalance 冗余

磁盘这块,Citus 的账比单机 Postgres 复杂好几倍,得一项一项拆。

第一项:主数据。这部分跟单机一样,按行数和行宽估算,没什么特别的。数据总量不会因为分片而变化,32 个 shard 和 128 个 shard 装的数据是一样多的。

第二项:索引,这是按分片数倍乘的。前面提过,这里展开算:假设你有一张订单表,建了 3 个索引(主键、租户+时间、状态)。分 100 个 shard,那物理上就是 300 个索引对象,每个索引都是一棵独立的 B-tree,有自己的内部碎片、自己的填充率浪费。索引的磁盘占用、写入时的索引维护开销、autovacuum 清理索引的成本,全部按 300 份计算。更麻烦的是写入放大:一行数据写入,要维护这一行所在 shard 上的 3 个索引;批量导入 1000 万行,索引维护的 IO 量是单机情况下的同样倍数(因为索引结构的总量一样),但随机 IO 的模式会更碎——因为每个 shard 的索引都是小树,缓存命中率反而可能比单机大索引更好,这块算是为数不多的加分项。总体上我按"索引空间 = 数据空间 × 索引数量 × 0.5~0.8"来估,再乘以一个分片碎片系数。

第三项:WAL,建议独立盘。Citus 的分布式事务会产生比单机更多的 WAL——2PC 的 prepare/commit 记录、参考表的多副本写入、rebalance 期间的数据拷贝,都会往 WAL 里灌。而且 WAL 是顺序写,跟数据文件的随机 IO 混在一块盘上会互相干扰。做法是把 pg_wal 单独挂一块盘(或者独立的 NVMe 分区),容量按"日均 WAL 产生量 × 保留天数"给,一般 200–500GB 足够,但 IOPS 和顺序写带宽要给足。max_wal_size 也别用默认值,高并发写入场景给到 16–32GB 能明显减少 checkpoint 抖动。

第四项:临时文件空间。排序、哈希 join 超出 work_mem 就会往 base/pgsql_tmp 落盘。协调节点上这块的压力最大(归并都在那儿做),worker 上也有(shard 本地的排序)。一个跑飞的查询写几十上百 GB 临时文件是常有的事。别把临时文件跟数据盘放一起——写满了数据盘直接宕库。可以让 temp_tablespaces 指向独立的表空间,或者在协调节点上单独挂一块盘给 pgsql_tmp。

第五项:rebalance 冗余空间,30% 起步。往集群里加 worker 之后,Citus 的 shard rebalancer 会把一部分 shard 从老节点搬到新节点。这个搬运是在线拷贝——源端读、网络传、目标端写,期间磁盘 IO 和 WAL 量都会翻倍(搬过去的 shard 在目标节点上要写一遍完整数据和索引,源端同时还在正常服务)。如果你的盘使用率已经到 85%,rebalance 跑到一半就会把目标节点写满。我给客户的硬规矩是:常态磁盘使用率不超过 70%,扩容前确认目标节点有 30% 以上空闲。这条不遵守,扩容就会变成事故。

第六项:高可用副本。Citus 社区版通常用 Postgres 原生流复制做 HA,主副本各占一份完整磁盘——也就是说,磁盘需求直接 ×2。有些团队会用 citus.shard_replication_factor(MX / 企业版形态)做分片级多副本,那个是按副本数乘,但副本可以分散在不同 worker 上,磁盘压力是平摊的。选型的时候要分清楚你用的是哪种,两种混着算会把磁盘账算错一倍。

三档服务器配置对比:10 亿行以内 / 10–50 亿行 / 50 亿行以上

把上面三本账合起来,就能给出三档可直接落地的配置。表格里的价格一律写「预估价格,以咨询为准」——分布式数据库集群的报价跟 CPU 型号、内存容量、磁盘类型(NVMe 还是 SATA SSD)、副本份数、带宽与线路都有关,必须按实际配置单核算,我不给拍脑袋的确定数字。

规模块 协调节点配置 worker 配置与数量 磁盘规划 适用业务
10 亿行以内
单表 32 分片
预估价格,以咨询为准
16 核 / 64G 内存
系统盘 480G SSD
max_connections 800
挂 pgbouncer
可做主备两台
2 台 × 16 核 32 线程
128G 内存
shared_buffers 32G
work_mem 8–16M
总核数 32 ≈ 分片数 32
每 worker 2T NVMe
WAL 独立 200G
临时文件独立 200G
常态使用率 ≤70%
流复制副本 ×2
SaaS 多租户、单租户数据量中等、日增千万行以内的订单/日志分析
10–50 亿行
核心表 64–128 分片
预估价格,以咨询为准
32 核 / 128–256G 内存
系统盘 960G NVMe
max_connections 1500
ulimit -n 65535+
建议独立物理机
4 台 × 32 核 64 线程
256–512G 内存
shared_buffers 64–128G
autovacuum_max_workers 6–8
总核数 128 ≈ 分片数 128
每 worker 4–8T NVMe
WAL 独立 400G
临时文件独立 500G
预留 30% 空闲盘
流复制副本 ×2
广告投放明细、用户行为埋点、物联网时序数据、多租户 CRM 与计费
50 亿行以上
核心表 128–256 分片
预估价格,以咨询为准
32–48 核 / 256–512G 内存
系统盘 960G NVMe ×2 RAID1
max_connections 2000+
双机 HA + 元数据实时备份
pg_dist_* 定期导出
8 台起 × 32–64 核
512G–1T 内存
shared_buffers 128–256G
分片按表拆分,避免单表 256 片
预留横向扩容位
每 worker 8–16T NVMe
WAL 独立 500G+
临时文件独立 1T
常态使用率 ≤60%
副本 ×2 + 异地灾备
金融风控明细、运营商级话单、跨区域电商主数据、PB 级日志检索

看这张表要注意一点:协调节点的配置不是"worker 的一半",它是独立的一套逻辑。协调节点的内存主要喂给并发归并和连接缓存,CPU 够用就行;worker 的内存主要喂给 shared_buffers 和 shard 本地排序,核数决定分片数上限。拿同一套模板去配两种节点,怎么配都不对。

一万网络怎么配:分片集群的三档落地组合

方案落到具体机器上,我一般就这么给客户配,都是能直接下单的档位。

#1 一万网络「裸金属 E5-2698v4 ×2 双路机型」——小规格 Citus 集群的协调节点首选。这档是官网明示价 ¥3999 起(以官网实时价为准),双路 E5-2698v4 合计 40 核,配 64–128G 内存完全够协调节点用。为什么协调节点我反而推荐上一代平台?因为协调节点的瓶颈是连接数和内存,不是单核性能,把预算省下来加内存、加 pgbouncer、把 ulimit -n 调上去,收益比换新 CPU 大得多。10 亿行以内的集群,协调节点用这个档位性价比最高。

#2 一万网络「大内存裸金属定制(512G / 1T DDR4 ECC)」——10–50 亿行档的 worker。这个档位的价格属于预估价格,需按具体内存容量和磁盘配置询价,以咨询为准。worker 是真正吃内存的地方:shared_buffers 按 25% 算,512G 内存的机器能给到 128G,正好覆盖中等规模集群的热数据;同时留足空间给"每 worker 分片数 × 并发 × work_mem"。一万网络这边做裸金属定制有个实际好处——内存和磁盘能按你的分片数反推着配,不用迁就云主机的固定规格,这对 Citus 这种"配置跟着分片数走"的场景挺关键。

#3 一万网络「NVMe 大容量存储型定制 + 多节点部署」——50 亿行以上档。同样是预估价格,以咨询为准。这个档位的核心诉求是磁盘:索引按分片数倍乘之后,实际占用往往比"数据量 × 1.5"的粗算高出不少,再加上 WAL 独立盘、临时文件盘、流复制副本 ×2、以及 rebalance 要留的 30% 冗余——算完你会发现磁盘需求是裸数据的 5–8 倍。一万网络在华南、华东、华北多个节点都有资源,做跨机房的流复制副本和异地灾备时能直接落地,不用再去第三家找机房。

顺带说一句服务层面的事。一万网络深耕 IDC 19 年(成立于 2007 年),深圳南山总部,自营机柜最快 1 分钟上架,7×24 中文工单平均 5 分钟响应,硬件故障 10 分钟自动迁移。对 Citus 这种"协调节点一挂就全停"的架构来说,硬件故障的替换速度比什么都重要——协调节点宕机之后,你唯一的退路是直连 worker 查单分片数据,那是个非常难受的状态。另外系统盘快照(每日 3 份、30 秒回滚)建议一定开上,pg_dist_* 元数据一旦损坏,重建的代价远高于恢复的代价。

六个真实翻车点:Citus 分片集群避坑清单

坑一:拿默认 32 分片建了核心大表,数据量涨上来才发现并行度不够。
为什么坑:分片数建表即固定,改分片数只能重建表。业务跑起来之后重建表意味着停写、双写迁移、切表,几十亿行的表这一套流程下来,几天时间搭进去了,还得承担切换风险。
怎么避:建表前先按"分片数 ≈ worker 数 × 每节点物理核数"算一遍,并且按三年后的规模算,不是按上线时的规模算。宁可上线时分片数略微偏多,也不要一年后推倒重来。真已经踩了坑的,用 create_distributed_table 建新表 + 逻辑复制或双写做在线迁移,别指望原地改。

坑二:协调节点 max_connections 保持默认 100。
为什么坑:Citus 在协调节点上缓存到每个 shard 的连接,连接数 ≈ 分片数 × 并发查询数。64 shard、20 并发就是几百个后端进程,默认 100 直接把应用连接挤爆,报错信息还都是"too many connections"这种看不出根因的提示,排查方向很容易跑偏到应用侧连接池。
怎么避:协调节点 max_connections 按"分片数 × 预期并发 × 1.5"给,起步 800,大集群 1500–2000;系统 ulimit -n 同步提到 65535 以上;应用侧一律走 pgbouncer(transaction 模式)。监控系统里加一条"协调节点后端进程数"的告警,超过 70% 就扩容或限流。

坑三:把会频繁更新的表做成了参考表。
为什么坑:参考表在每个 worker 上都有一份完整副本,写放大等于 worker 数量倍,而且走的是分布式事务(2PC)。8 个 worker 的集群改一行配置,要在 8 台机器上各写一次并协调提交,慢且占协调节点事务槽位。
怎么避:判断标准就一条——这张表是不是几乎只读。字典表、类目表、区域表这种改动以周/月为单位的,做参考表;配置表、状态表、任何会被业务流程高频更新的表,宁可做成按分片键分布的分布式表,或者干脆留在协调节点做本地表(前提是数据量小且不参与 JOIN)。

坑四:跨分片 JOIN 没做 colocation,全靠协调节点归并。
为什么坑:分片键不对齐时,Citus 没法把 JOIN 下推到 worker 本地执行,只能把数据拉回协调节点做归并。协调节点的内存(work_mem × 并发)和临时文件空间会瞬间成为瓶颈,一个大 JOIN 能写满临时盘。
怎么避:建表时就用同一个分片键(同一个类型、同一个语义),让相关的表天然 colocated;Citus 支持 colocation 组,明确指定比让它自动推断更保险。上线前用 EXPLAIN 检查关键查询的执行计划,看到"Router"或每个 worker 本地 JOIN 是健康的,看到大量数据回协调节点就要改表结构。

坑五:磁盘使用率到 85% 才想起来扩容加 worker。
为什么坑:加 worker 会触发 shard rebalancer,它是在线拷贝数据,期间源端正常服务、目标端写入全量数据和索引,磁盘 IO 和 WAL 量翻倍。盘快满的时候 rebalance 跑到一半写满,shard 搬到一半的状态非常难回滚,集群可能长时间处于不可用边缘。
怎么避:把"常态磁盘使用率 ≤70%"写成硬指标并做告警,到 70% 就启动扩容,别等到 85%。扩容前先确认目标节点有 30% 以上空闲盘,rebalance 期间避开业务高峰,并提前调大 max_wal_size 防止 checkpoint 抖动叠加。

坑六:work_mem 照搬单机经验值,worker 直接 OOM。
为什么坑:Citus 上一条广播查询会在同一台 worker 上的每个 shard 各起一次排序/哈希,每个都能用满 work_mem。单机上调 64MB 很安全,Citus 上"每 worker 32 个 shard × 20 并发 × 64MB"就是 40GB,加上 shared_buffers 轻松突破内存上限。Postgres 被 OOM killer 杀掉的后果是实例崩溃恢复,比慢查询严重得多。
怎么避:按 work_mem ≤ (可用内存 - shared_buffers) / (每 worker 分片数 × 预期并发 × 每查询排序操作数) 反推。算出来偏小就让它落盘——前提是把 temp_tablespaces 指向独立盘,或者单独挂一块盘给 pgsql_tmp。同时监控临时文件产生量,异常增长往往意味着有 SQL 需要优化索引或改写。

FAQ:Citus 分片集群服务器租用 8 问

Q1:Citus 集群最少要几台服务器?能不能协调节点和 worker 混部?
技术上 1 台也能跑(协调节点 + 1 个 worker 同机),生产环境我不建议这么干。最低可用形态是 3 台:1 台协调节点 + 2 台 worker,worker 之间做流复制互为副本。混部的问题在于资源争抢——worker 上的大查询会抢 CPU 和 IO,协调节点的计划生成和结果归并跟着变慢,整个集群的响应都会抖,而且协调节点一旦被拖垮,全集群停摆。预算实在紧张的话,可以接受协调节点和 worker 混部在小规格集群上,但要把 CPU 核数、内存、磁盘 IO 都按两者之和给足,并且严格限制 worker 的并发度。

Q2:分片数定多少合适?32 个默认值能用吗?
32 是默认值,不是推荐值。合理量级是"worker 数 × 每节点物理核数":2 台 16 核 worker 取 32、4 台 32 核 worker 取 128、3 台 8 核 worker 取 16 更合适。关键在于分片数建表即固定,改分片数只能重建表,所以定这个数要按三年后的规模算,不是按上线时的规模。分片过多的代价是协调节点要为每个查询打开几十上百个连接和文件描述符,max_connections 和 ulimit -n 会先于 CPU 爆掉——这是 Citus 最常见的性能故障模式之一。

Q3:协调节点挂了,数据会丢吗?业务能撑多久?
数据不会丢,协调节点不存业务数据,只存 pg_dist_* 元数据。但它挂掉的后果很直接:写入全停,跨分片查询全停。有个常被忽略的救命通道——单分片的直连查询还能跑,你可以直接连到 worker 上查某个 shard 的数据,紧急取数不受影响。所以协调节点必须做 HA(主备 + 元数据实时备份),pg_dist_* 定期导出到异地。恢复时间取决于你的备机切换速度,这也是为什么我强调硬件故障的替换速度——一万网络这类自营机柜能做到 10 分钟自动迁移,差距就在这个环节。

Q4:索引的空间到底要按几倍算?
粗略公式是"索引空间 ≈ 数据空间 × 索引个数 × 0.5~0.8",再乘一个分片碎片系数。但要记住索引不是全局的,是按 shard 数倍乘的:100 个 shard 建 3 个索引就是 300 个独立索引对象,每个都是一棵独立的 B-tree,有自己的内部碎片。所以分片数越多,索引的实际占用会略高于粗算值。更实际的做法是建完表导入一小批真实数据,量一下实际占用,再线性外推——Citus 的索引占用跟数据分布、键的离散度关系很大,纯理论估算误差不小。

Q5:worker 的内存怎么估?256G 够不够?
看分片数和并发。三块账:shared_buffers 按物理内存 25%(256G 机器给 64G);work_mem 按"每 worker 分片数 × 并发 × 排序操作数"反推,分片多的时候给 8–16M 就够,别照搬单机的 64M;maintenance_work_mem 给 autovacuum 用,配合 autovacuum_max_workers 提到 6–8,留出相应内存。128 分片、4 台 worker、20 并发的中等集群,256G 是够用的;如果分片数上到 256、并发更高,或者热数据远超 shared_buffers,就该往 512G 走。

Q6:跨分片 JOIN 很慢,是服务器配置不够吗?
多半不是硬件问题,是分片键没对齐。Citus 只有在分片键 colocated 时才能把 JOIN 下推到 worker 本地执行;不对齐就得把数据拉回协调节点归并,这时候瓶颈是协调节点的 work_mem × 并发和临时文件落盘速度。先用 EXPLAIN 看执行计划确认是不是归并路径,如果是,改表结构(让相关表用同一个分片键、同一个 colocation 组)比加内存有效得多。加内存只是让归并没那么快爆,治不了根。

Q7:扩容加 worker 要注意什么?会不会影响线上业务?
加 worker 之后会触发 shard rebalancer,它是在线拷贝数据,理论上不阻塞业务,但期间源端正常服务 + 目标端写全量数据和索引,磁盘 IO 和 WAL 量都会翻倍,业务延迟会明显上升。最关键的一条硬规矩:目标节点必须有 30% 以上空闲盘,否则 rebalance 跑到一半写满,shard 处于半搬状态很难回滚。操作上建议避开业务高峰、提前调大 max_wal_size、分批 rebalance 并监控进度。常态磁盘使用率控制在 70% 以内,扩容时才不会手忙脚乱。

Q8:Citus 的高可用怎么做?社区版和企业版差别大吗?
社区版通常用 Postgres 原生流复制做高可用——每个 worker 挂一个(或多个)流复制备机,主副本各占一份完整磁盘,所以磁盘需求直接乘 2。这种方式的好处是成熟稳定、故障切换明确,代价是磁盘成本高、故障切换后 Citus 元数据需要相应更新。企业版形态(含 MX 特性)可以用 citus.shard_replication_factor 做分片级多副本,副本分散在不同 worker 上,磁盘压力平摊,且 worker 能直接处理查询。选型时一定要分清自己用的是哪种,两种机制的磁盘账差一倍,混着算必然出错。

本篇结论:Citus 分片集群的服务器配置取舍

回到开头那个判断:Citus 的资源大头不在查询,而在分片数决定的元数据与连接开销。这句话落到配置上,就是三个反直觉的结论。第一,协调节点不是"小一号的 worker",它的瓶颈是连接数和并发归并内存,CPU 中等就够,内存和 max_connections 必须给足,而且它必须做 HA——它一挂,写入和跨分片查询全停,只剩下直连 worker 查单分片这一条救命通道。第二,分片数是个"建表即锁死"的数字,定它是按三年后的规模算,按"worker 数 × 每节点核数"估,一旦定错就是重建表的返工。第三,磁盘账要按"索引 × 分片数 + WAL 独立 + 临时文件独立 + 30% rebalance 冗余 + 副本 ×2"算,最后的需求往往是裸数据的 5–8 倍,只算数据量的估算一定翻车。

真要给一句操作建议的话:10 亿行以内的集群,把钱花在协调节点的内存和连接数上;10–50 亿行,把钱花在 worker 的内存和磁盘上;50 亿行以上,把钱花在磁盘冗余和跨节点灾备上。三档的瓶颈各不相同,用同一套模板套下去,钱花了却没花在刀刃上。

数据来源:一万网络分布式数据库服务器租用页

本文涉及的 Citus 与 PostgreSQL 机制说明,参考 Citus 官方文档与 PostgreSQL 官方手册中关于 citus.shard_count、create_reference_table、pg_dist_* 系统表、colocation、shard rebalancer 的公开描述;硬件规格与租用方案参考一万网络官网 https://www.idc10000.net/ 的裸金属服务器、大内存定制、存储型定制与多节点部署相关页面。

文中标注「预估价格,以咨询为准」的档位均为按配置推算的区间,非官方明示报价;标注具体数字的档位(如裸金属 E5-2698v4×2 ¥3999 起)以官网实时价为准。具体以签约时最新报价与合同为准。


上一篇:2026 海外云服务器租用入门档带宽口径对比实测:马德里 A 型 10-200M/1T 与悉尼 A 型 1G/1.5T 避雷全攻略

下一篇:2026 悉尼/圣保罗/雅典/孟买云服务器租用同表配置对比:1 系 99 元与 2 系 299 元同配价差 避雷全攻略