Topic 最佳实践
提前规划分区数(partitions)
Kafka 的分区本质是吞吐能力的扩展单位,每个分区只能被一个消费者线程独占,所以分区越多并发处理能力越高。
但分区太多会增加延迟、rebalance 时长、控制器压力、段文件数量……像齿轮咬得太密会卡住。
所以一次性把分区确定好,够用就行。增加分区会打乱消息顺序,消费者也会发生 rebalance 引发抖动。
如何估算合理的分区数,大致有如下两种方式:
根据消息 QPS(每秒条数)估算分区数
实际上,可以根据业界经验来快速估算分区数,这种是最简单快速的方式。
每个分区基本上支持 2k–10k QPS。
- 小消息(<1KB):每分区大概能处理 5k–10k QPS
- 中消息(5–10KB):1k–5k QPS
- 大消息(>50KB):< 1k QPS
根据这个数据,衡量下自己的业务 QPS 也能快速给出需要的分区数。
根据服务并发数确定分区数
实际上,一个分区同一时刻只能被一个线程消费,根据这点也能快速确定需要的分区数。
比如:服务需要同时开 8 个消费者线程,那你的 partitions 至少得是 ≧ 8。
根据关键服务确定分区数
因为每个消费者组独立消费,消费者组之间互相不影响。比如,如下几个服务同时订阅一个 topic,但是每个服务的重要程度不一,而且实例数也不相同:
订单服务有 8 个实例
库存服务有 4 个实例
风控服务有 6 个实例
每个服务都有自己的消费组,就各自独占整个 topic。
所以,只需要考虑最需要并发的那个组的并发能力。就是说最忙的订单服务想 10 个线程并发,那么 topic 至少需要 10 个分区。其他服务即使有 20 个实例,那只是竞争,但不会互抢分区。
根据数据量(吞吐 MB/s)估算分区数
需要的分区数 = 峰值总吞吐量(MB/s) / 单分区可承载吞吐(MB/s)
根据行业经验,单分区正常能跑 1–10MB/s,所以取中位数 6MB/s 就很安全。
比如:
- 峰值每秒 50MB
- 单分区吞吐估计 6MB/s
partitions = 50 / 6 ≈ 9
实际会取整提升一点:12 分区。
不过,百分之九十的业务场景根本达不到峰值 50MB/s,能达到这个量级的大多都是电商业务和金融场景。
不过,就小公司而言。不管啥业务,3 个分区足以~
至少设置 3 个副本数(replication-factor)
副本数就是分区的备份数,至少 3 个副本才能防止数据高可用(前提是别只在同一台一起上设置副本数)。
行业准则:
- prod 环境:至少 3 副本
- 准生产:2 副本可接受
- 不允许只有 1,消息丢得像漏水的桶
设置 min.insync.replicas 确保消息可靠性
副本数是“几份拷贝”,而 min.insync.replicas(ISR 最小同步副本数)是“至少写入几份才算成功”。
最佳实践组合:
- replication.factor = 3
- min.insync.replicas = 2
- producer 设置:acks = all
这样,即使某个节点挂掉,消息也不会丢。
cleanup.policy:delete/compact 按用途选择
Kafka 的日志清理像是图书管理员的整理习惯。
- 按时间/容量删旧日志(默认,用于大多数业务)
- 按 key 保留最后一条(适用于用户状态、配置状态、KV 表等)
如果你不知道 compact 是干什么的,那就无脑用 delete。
合理设置 retention.ms / retention.bytes
千万别让 topic 的 log 数据像雪球一样,越滚越大,一定要定时清理。
常见配置策略:
- 高频业务事件(如订单事件):3–7 天
- 日志类 topic:1–3 天
- 需要追历史的事件溯源(如金融交易流水):30 天 或者用数据湖做归档
如果你需要几个月的日志数据,建议还是考虑 Elastic 吧
避免无限数量的小 topic
Kafka 不是为了存几千个 topic 设计的。每个 topic ✖️ 每个 partition ✖️ 每个 replica 都要对应一个 log segment 目录,这会让 broker “目录爆炸”。
行业经验:
- 中小型集群:topic 控制在 200–500 以内
- 大型集群:几千 topic 也行,但要有强壮机器与良好规划
- 不要给每个用户/租户单独建一个 topic(多半会后悔)
一定要禁用自动创建 topic
每个 broker 在运行之前都要在 service.properties 增加如下设置,别问为什么,照做就完事了(这是经典的运维自保策略):
auto.create.topics.enable=false
topic 名字一定要有清晰的命名规范
名称里带上业务含义与版本信息,可以像考古层一样一眼看懂。
常见格式:
{业务域}.{事件名}[.版本]
order.order_paid
user.profile_updated.v1
保持 key 一致,不破坏分区路由
首先要记住:不同的分区,消息消费顺序是完全乱序的。
如果你想同一个订单的消息保持顺序,就永远用相同的 key,例如订单号。千万不要用随机 key,会打乱顺序。
生产者将消息投递到 topic 时,会根据 key 的 hash 值分配具体的分区。以订单消息为例,如果将消息 ID 固定为订单号。订单创建消息、支付成功消息、接单消息等都会落入同一个消息分区。
在同一个消息分区内,消息的消费顺序时一定的,这样就有效的避免了消息乱序问题。
反过来,如果消息 ID 使用随机 key。可能会出现订单创建消息落入 P1 分区,订单支付消息落入 P2 分区。
比如消息组A订阅订单创建消息。消费组B订阅订单支付成功消息。消息虽然是按照时间顺序写入各自的分区了,但是每个分区在吞吐量上可能存在差异,你没法保证消费组A消费的订单创建消息一定在消费组B之前。
不要频繁修改 topic 的关键配置
Kafka 的“双刃剑之一”就是过度灵活。分区数、retention 等都能改,但频繁修改会带来如下副作用:
- rebalance 暴增
- topic layout 改变造成延迟升高
- 消费者逻辑依赖顺序时发生意外
能不改的尽量不要改,事前规划永远比事后修补好。
对关键 topic 开启监控与告警
至少监控:
- lag(消费延迟)
- 副本是否同步(UnderReplicatedPartitions)
- 分区 leader 分配是否均衡
- topic 大小
生产 Kafka 没监控就像开高铁但不看仪表盘。
对“无谓消息”避免发送
消息一旦进 topic 就会占用磁盘、带宽和 CPU,这不是免费的午餐。
策略包括:
- 避免发送重复事件(可在生产端去重)
- 避免发送超大 payload(>1MB)
- 消息压缩:lz4/snappy 是行业常用
扩展:实际生产中的典型 topic 配置示例
订单事件示例:
partitions=12
replication.factor=3
min.insync.replicas=2
cleanup.policy=delete
retention.ms=604800000 ## 7天
compression.type=lz4
状态类 topic(KV 状态快照):
partitions=8
replication.factor=3
min.insync.replicas=2
cleanup.policy=compact