先谈量,再谈钱
谈成本最容易滑向两个坑:一是直接报一个「每 GB 每月多少钱」,可这个数字随厂商、地域、合约、是否自建变化,抄过去就是错的;二是只算存储、不算网络和人力,得出的结论系统性偏低。
本篇只做第一件事:把每一种可靠性配置换算成可核算的量。你要的存储是 GB·天,你要的出网流量是 GiB/月,你要的运维动作是「每一次副本重建要搬多少数据」。拿到这些量之后,乘你自己手里的单价,就是你的账单。
所有算例统一用同一组假设,方便你对号入座:
| 假设 | 取值 |
|---|---|
| 每日业务消息量 | 1000 万条 |
| 单条业务载荷 | 1 KiB(未压缩) |
| 消息格式 | v2 record batch(Kafka 4.x 默认) |
| 存储余量 | 在载荷基础上留 5%,覆盖批次头、索引文件与段尾未满部分 |
| 网络口径 | 只算集群内外出网;同机房内网不计费但计带宽 |
按这组假设,一天的业务载荷是 10,000,000 × 1024 = 10,240,000,000 字节,约 9.54 GiB。加上 5% 余量,取整记作 每天 10 GiB。后面所有数字都从这个 10 出发。
需要提醒一句:这 5% 余量的口径随批量大小变化。卷一讲过分区与批次,v2 格式的开销挂在批次上而不是消息上,你的生产端如果攒批量很大,实际余量会明显小于 5%;如果每条都单独发(linger.ms=0、批量极小),余量会超过 5%。10 GiB 这个数字在你自己的系统上要实测,不能照搬。
存储:三个乘数
Kafka 的磁盘占用不是「消息量」一个变量,而是三个乘数连乘:
占用 ≈ 每日数据量 × 保留天数 × 副本数
三个乘数对应三种完全不同的支出意图,分开看才看得清:
- 每日数据量:业务决定的,只能靠压缩和减少消息数来降。
- 保留天数:可靠性决定的,它买的是「出事之后还能回头重放多久」。
- 副本数:可用性决定的,它买的是「同时坏几台机器还不丢」。
把它们混成一句「我们每天产生多少数据」是成本失控最常见的起点。三档方案在这三个乘数上的取值不同,结果差 8 倍以上:
| 方案 | 每日数据 | 保留 | 副本 | 存储占用 |
|---|---|---|---|---|
| 基础 | 10 GiB | 3 天 | 2 | 60 GiB |
| 标准 | 10 GiB | 7 天 | 3 | 210 GiB |
| 强化 | 10 GiB | 14 天 | 3 | 420 GiB |
| 强化 + 跨集群 | 10 GiB | 14 天(主) / 3 天(备) | 3 + 3 | 420 + 90 = 510 GiB |
从 60 GiB 到 510 GiB,业务数据量一天没变。多出来的 450 GiB 全是可靠性溢价。
副本数乘的是什么
副本不是「备份」,是热备——每一份都在持续接收写入、持续占盘、持续消耗 IO。RF 从 2 到 3,存储涨 50%,但买到的东西很具体:
- RF=2:任意一台 broker 挂掉,数据仍在;但剩下的那台在重建期间如果再挂,就丢。
- RF=3:任意一台挂掉,重建期间再挂一台,仍不丢。
重建不是瞬间完成的。一台 broker 上承载的可能是几十 GiB 数据,重建这几小时到一两天,正是 RF=3 与 RF=2 拉开差距的窗口。第二章讨论过重复来源,这里换个角度:RF=2 的重复风险不在同一秒,而在这段长长的重建窗口里。
保留天数乘的是什么
保留期买的是「故障被发现之前,数据还没被删」。这条因果链经常被算错:
保留天数 ≥ 故障潜伏期 + 定位时间 + 修复与重放时间
一次消费端逻辑错误,可能三天后才被业务方发现对账不平。如果保留期是 3 天,发现的那一刻,最早出错的那批数据已经滚出磁盘,你连重放都做不到,只能靠下游对账补数据。这时候省下的 70 GiB 存储,代价是一次人工数据修复。
反过来,保留期过长也是浪费。第七章讲 outbox、第九章讲 Saga,这些链路里消息一旦被消费并确认,长期留存的价值很低。按 topic 定保留期,不要全站一个值。
5% 余量之外还有两笔
上面算的是稳态占用。还有两笔不常出现在报表里:
段文件尾部未满。 每个分区当前正在写的那个段文件,大小在 0 到 segment.bytes 之间浮动。分区多的时候这笔不小:1000 个分区、段大小 1 GiB、按平均半段 500 MiB 估,就是约 488 GiB 的「半空段」,比上表里 210 GiB 的稳态数据还多。这跟消息量无关,纯粹由分区数 × 段大小决定。分区多而消息少的集群,这条经常是主要开销。
索引文件。 .index 与 .timeindex 的大小由 log.index.interval.bytes 决定,通常远小于数据文件,但如果把间隔调得很小(排查问题时有人会这么干),索引能涨到数据文件的几个百分点。
网络:第二笔账,也是最容易漏的
存储有账单,因为硬盘有价格。网络也有账单,但它在内网里往往「看不见」,直到跨了机房或跨了云。
把流量按方向拆开,每一项的口径不同:
| 流量 | 量的算法 | 是否计费 |
|---|---|---|
| 生产入流量 | 每日数据量 | 通常不计入网费,计带宽 |
| 副本复制(leader → follower) | 每日数据量 × (RF − 1) | 内网不计费,占带宽 |
| 消费出流量 | 每日数据量 × 消费组数 | 同机房不计费;跨区/跨云计费 |
| 副本重建 | 单机承载量(一次性) | 内网,但与正常流量抢带宽 |
| 跨集群复制 | 每日数据量 | 计费,这是出网 |
| 灾备回切 | 灾备端累积量 | 计费,且量大 |
按 10 GiB/天、RF=3 算:
- 副本复制:
10 × 2 = 20 GiB/天,即正常写入流量的 3 倍在网络里流动(1 份入 + 2 份复制)。 - 消费:每多一个消费组,多 10 GiB/天。第四章讲过「多组订阅」的代价,那篇讲的是组会互相拖累,这里的账是同一条事实的另一面:组也是网络账单的乘数。
- 跨集群:主备各 10 GiB/天进出,一个月约 600 GiB 出网。
副本重建:平时不花、故障时一次性花
这笔账最容易被忽略,因为它不是月度成本,而是故障当天的一次性支出。
一台 broker 挂掉之后恢复,它上面的所有分区都要从 leader 全量拉回缺失的部分。规模是「这台机器承载的数据量」:
单机重建量 ≈ 集群总占用 ÷ broker 台数
60 GiB 的三节点集群,一台挂着要拉回约 20 GiB。这个传输是全速的,会和正常生产消费抢同一块网卡。卷一实测过 100 MiB 分区的追赶,5.2 秒完成;按同一带宽外推,20 GiB 需要十几分钟到几十分钟,取决于链路和磁盘。这段时间里:
- 该 broker 上的分区不在 ISR 内,
acks=all的生产者会感受到延迟上升。 - 如果同时有另一个 broker 抖动,
min.insync.replicas=2会拒写。 - 网卡打满会影响同机房其他服务。
所以重建成本不只是「多花了带宽」,还是「故障恢复期间系统整体变慢」的机会成本。这部分在容量规划里要留出带宽头寸:正常峰值占用不要超过链路的 60%,给重建和追赶留余量。
人力:第三笔账,通常最大
前两笔能用公式算,这一笔不能,但它常常比前两笔加起来还大。
三档方案对运维的要求完全不同:
| 动作 | 基础 | 标准 | 强化 + 跨集群 |
|---|---|---|---|
| 日常巡检 | 看盘、看 lag | 看盘、看 lag、看 ISR | 以上 + 看复制延迟、看两端 lag 差 |
| 扩缩容 | 加盘/加机 | 加盘/加机 + 分区重分配 | 以上 + 两端同时操作 |
| 版本升级 | 停机窗口可接受 | 滚动升级 | 滚动 + 主备切换演练 |
| 故障演练 | 偶尔 | 定期 | 定期 + 切换与回切 |
| 值班要求 | 工作时间 | 7×24 响应 | 7×24 + 切换决策权限 |
「强化」方案多出来的不只是 450 GiB 存储和 600 GiB 月出网,还有一支会做主备切换决策的值班队伍。跨集群方案最贵的从来不是带宽,而是「切换一次要谁签字、切过去之后数据怎么回切」——这第九章讲最终一致性时提过,跨集群的最终一致性窗口是以分钟计的。
一个诚实的判断:如果一个团队没有能力定期演练切换,那跨集群方案买到的是心理安慰,不是可用性。 真出事时不敢切或者切了回不来,比没有灾备更糟。13.3 节讨论过 MirrorMaker2 的模型,那里说的是机制,这里说的是组织能力——两者缺一不可。
边际成本曲线:多花的每一份钱买到了什么
把三档按「防住的故障」排序,能看出投入的边际效率:
| 升级动作 | 增加的量 | 买到什么 | 边际效率 |
|---|---|---|---|
| RF 2 → 3 | +150 GiB(60 → 210) | 重建窗口内再坏一台不丢 | 高,几乎必做 |
| acks=1 → all | 网络 +0(复制本来就在跑) | leader 挂时不丢未同步消息 | 极高,纯配置改动 |
| 保留 3 → 7 天 | +120 GiB | 故障发现窗口从 3 天到 7 天 | 中,取决于业务对账周期 |
| 保留 7 → 14 天 | +210 GiB | 再多一周的重放能力 | 低,多数系统用不上 |
| 加跨集群 | +90 GiB 存储 + 600 GiB/月出网 + 值班 | 机房级故障存活 | 低到中,看监管与业务等级 |
这张表的读法是:先做边际效率高的,不要先做看起来最厉害的。
acks=1 → all 这一行的「增加量」是零,因为副本复制流量本来就在发生——acks=1 时 follower 也在拉,只是 leader 不等它们确认。改成 acks=all 不增加任何网络流量,只是让 leader 多等一会儿。它唯一的代价是延迟,不是带宽,不是存储。用延迟换「不丢」,这笔交易在绝大多数业务里都划算。
反过来,跨集群那一行的边际效率最低,但它常常是第一个被写上方案的东西——因为它最显眼。
不该省的钱
有三种省钱方式,事后看基本都是负收益:
砍保留期。 保留期是故障的处理窗口。把它从 7 天砍到 1 天,省 6 天的存储,但任何一次超过 24 小时才被发现的消费错误都变成「无法重放」。而消费错误被发现的时间,取决于业务对账周期,不取决于你的意愿。
砍副本。 RF=2 在单机故障时看起来「也扛住了」,容易得出「RF=2 够用」的结论。这个结论只在「重建期间不再坏」的前提下成立。而重建期间恰恰是集群最脆弱的时候——新 broker 刚回来,磁盘和网络都在满负荷。
砍 min.insync.replicas。 把它降到 1,等于告诉 broker「只要 leader 在就允许写」。这会让 acks=all 的保护失效:leader 一个人确认了,但它挂了之后这条消息可能不存在于任何副本上。4.2 节实测过 ISR 收缩时 NotEnoughReplicasException 的行为,那个异常的存在本身就是保护,为了「写入不要失败」而调低它,是把可靠性配置换成了可用性幻觉。
可以省的钱
同样有三笔,省了不心疼:
压缩。 11.2 节专门测了压缩算法的收益与负收益场景。文本类载荷压缩比常常能到 3 到 5 倍,直接乘在「每日数据量」这个乘数上,是唯一能同时降低存储、复制网络、重建量的手段。注意它降的是磁盘和网络,不降 CPU —— 生产端与 broker 端的 CPU 会上升,这笔账要一起算。
按 topic 分层。 上面所有算例都假设「全站一个等级」。真实系统里,审计日志可能需要保留 30 天且 RF=3,而某些缓存失效通知只需要保留 1 小时、RF=1、acks=1。把它们放进同一个等级,等于给低价值数据付高可靠性的钱。先给 topic 分类,再谈成本是性价比最高的一步。
清理没有消费者的 topic。 这类 topic 会一直积攒数据直到保留期,却没有任何产出。巡检里加上「无消费组且持续增长」这一条,往往能清出一批纯浪费。
换一个量级:日增 1 亿条
把假设放大 10 倍,看哪些成本线性和哪些不线性。日增 1 亿条、单条 1 KiB,每日 100 GiB:
| 项 | 1000 万条/天 | 1 亿条/天 | 倍数 |
|---|---|---|---|
| 每日载荷 | 10 GiB | 100 GiB | 10× |
| 标准档存储(7 天 × RF3) | 210 GiB | 2100 GiB | 10× |
| 复制流量 | 20 GiB/天 | 200 GiB/天 | 10× |
| 段尾余量 | 与分区数相关 | 不变 | 1× |
| 单机重建量(3 台) | 70 GiB | 700 GiB | 10× |
两行「不变」和「10×」值得单独说。
段尾余量不随流量增长。 它由分区数乘平均段大小决定。这意味着在低流量场景里,如果分区开得很多,段尾余量可能超过数据本身的占比——一个日增 1 GiB、开了 500 个分区的集群,稳态数据只有几十 GiB,段尾却可能有上百 GiB。这类集群扩盘往往扩错了地方:真正吃盘的是空段,不是消息。
重建量线性增长,但可接受的重建时长不增长。 700 GiB 的单机重建,按 100 MiB 五秒的卷一实测外推,粗算需要接近十小时。这个时长需要和「RF=3 在重建期间再挂一台」的概率放在一起看:重建窗口越长,窗口内二次故障的概率越高。到某个规模,「加副本数」不再是选项,而是必须重新设计单机承载上限——把数据摊到更多 broker 上,让每次重建的搬运量降下来。
这也是为什么规模上去了以后不能只加盘:加盘增加的是单机承载,反而拉长重建时间,把可靠性往上加的副本数又抵消掉了。
压缩对三个乘数的影响
11.2 节测过压缩算法,这里只把结论折回成本公式。压缩改变的是「每日数据量」这一个乘数,但因为它出现在存储和网络两条链路的上游,实际影响是处处生效的。
假设文本类载荷压缩比 3:1,这个数字在 11.2 节的实测区间内(4 KB 事件、819 KB 样本上,lz4 是 2.58 倍、snappy 3.40 倍、zstd 5.69 倍、gzip 6.97 倍)。换算时按你选的算法取:
| 项 | 不压缩 | 3:1 压缩 | 变化 |
|---|---|---|---|
| 每日载荷 | 10 GiB | 3.3 GiB | −67% |
| 标准档存储 | 210 GiB | 70 GiB | −67% |
| 复制流量 | 20 GiB/天 | 6.6 GiB/天 | −67% |
| 单机重建量 | 70 GiB | 23 GiB | −67% |
| 生产端 CPU | 基准 | 上升 | 正成本 |
| broker 端 CPU | 基准 | 上升(校验时解压) | 正成本 |
三个乘数里只有一个动了,但存储、复制、重建三项支出同时降了三分之二。这是性价比最高的单项优化——前提是载荷本身可压缩。已经压缩过的二进制(图片、protobuf 的某些字段、加密后的数据)压缩收益接近零甚至为负,11.2 节有实测对照。
要一起算的是 CPU:压缩把磁盘和网络的钱换成了 CPU 的钱。在 CPU 富余、磁盘和带宽紧张的集群里这笔换得很值;反过来就不值。
自建与托管的账怎么比
同一个「标准档」方案,自建和托管的成本结构完全不同:
| 成本项 | 自建 | 托管 |
|---|---|---|
| 存储 | 硬件折旧 + 机房 + 电 | 按 GB·月单价 |
| 网络 | 带宽 + 交换机 | 出网按 GiB 计费,内网通常含在单价里 |
| 运维人力 | 全职或半职 | 显著降低,但要保留对接与巡检 |
| 升级与故障 | 自己承担 | 厂商承担 |
| 弹性 | 慢,要采购 | 快,但要为弹性付溢价 |
本篇不给「哪个更便宜」的结论,因为变量太多:团队已有多少基础设施、消息量有多大、是否有合规要求必须自建。可以给的是三个判断维度:
- 量小的时候托管几乎总是更便宜,因为自建的人力成本不随量缩小,而托管单价随量缩小。
- 量大且稳定的时候自建可能更便宜,但省下来的是钱,付出的是「必须有人懂它」。
- 跨集群灾备在两种模式下都要单独付钱,托管不会替你做切换决策——这一条上面讲人力时说过。
无论选哪种,核算表里那张表都成立:量是同一批量,只是单价不同。
一张可填的核算表
把上面的口径收成一张表,你填自己的数字就行。前四列是你的输入,后三列是算出来的量:
| 项 | 公式 | 你的值 | 单位 |
|---|---|---|---|
| 每日载荷 | 日消息数 × 平均载荷 | 10 | GiB/天 |
| 稳态存储 | 每日载荷 × 1.05 × 保留天数 × 副本数 | 210 | GiB |
| 段尾余量 | 分区数 × 平均段大小 ÷ 2 | 待填 | GiB |
| 复制流量 | 每日载荷 × (RF − 1) | 20 | GiB/天 |
| 消费流量 | 每日载荷 × 消费组数 | 30(3 组) | GiB/天 |
| 单次重建量 | 稳态存储 ÷ broker 数 | 70(3 台) | GiB |
| 跨集群出网 | 每日载荷 × 2(进 + 出) | 20 | GiB/天 |
最后一行「跨集群出网」乘 2,是因为主集群要发出去、备集群要收进来,两端都可能计费,具体看你的计费口径。
填完之后做一件事:把「稳态存储 + 段尾余量」和「单次重建量」对一下。如果单次重建量超过了一个维护窗口内能搬完的量,说明你的 broker 单机承载过重,故障恢复时间会超出预期——这时候该做的是增加 broker 数量而不是加盘。
小结
- 存储 = 每日数据量 × 保留天数 × 副本数,三个乘数对应三种不同的支出意图,不要混着谈。
- 网络要按方向拆:生产、复制、消费、重建、跨集群,五种口径不同,其中重建是一次性大额支出,要留带宽头寸。
- 人力常常比前两笔加起来大,跨集群方案尤其如此;没有演练能力时,灾备买到的是心理安慰。
- 边际效率排序:
acks=all与 RF=3 先做,保留期按业务对账周期定,跨集群最后做。 - 省钱优先省压缩与 topic 分级,不要省保留期、副本数和
min.insync.replicas。
本篇所有数字都是按公开配置语义推算的量,不是任何厂商的账单,也没有在本机跑过计费实验。你自己的单价乘上去,才是你的成本。
阅读导航:上一节 15.1 可靠性验收清单 | 下一节 15.3 libraryd-mq 卷二验收
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。