掌握Kafka主题配置:全面指南

关于Kafka主题分区、复制、保留、压缩及安全配置变更的实用指南。

掌握Kafka主题配置:全面指南

Kafka主题配置决定了数据的存储、复制、过期、压缩和消费方式。在开发集群中,你可以暂时使用默认配置运行Kafka,但生产环境中的主题需要更多关注。错误的分区数量可能会使繁忙的工作负载陷入困境。薄弱的复制机制可能导致代理故障演变为数据丢失。宽松的保留策略可能填满磁盘。如果键缺失或不一致,压缩功能可能会带来意外问题。

处理Kafka主题配置的有效方法不是记住每一个设置。从实际系统提出的问题开始:我需要多少并行度?数据必须保持可用多长时间?我能承受存储多少数据?代理故障时会发生什么?消费者需要完整的事件历史还是仅需每个键的最新值?

一个主题被分割成多个分区。每个分区是一个有序的日志。Kafka在分区内保持顺序,而不是在整个主题内。如果某个客户的所有事件必须按顺序处理,请使用稳定的键(如customer_id),以便这些事件落在同一个分区。如果你随机分配键,可能会获得更好的分布,但会失去按实体排序的能力。

分区数量是人们首先后悔的选择之一。更多的分区允许更多的消费者并行,因为在一个消费者组内,一个分区一次只能由一个组成员消费。如果一个主题有六个分区,一个消费者组最多可以为该主题使用六个消费者。添加第七个消费者不会增加对该主题的消费,除非有其他分配的分区。

更多的分区也会带来成本。它们会增加元数据、打开的文件、复制工作、领导者选举工作以及代理故障后的恢复时间。即使每个分区的流量不大,非常高的分区数量也会使集群操作变慢。没有通用的最佳数量。一个小型内部主题可能三个分区就足够了。一个繁忙的事件流可能需要几十个分区。一个非常大的Kafka安装可能使用更多分区,但这应该基于测量的吞吐量和运营能力,而不是习惯。

使用显式设置创建主题:

kafka-topics.sh --create   --bootstrap-server broker1:9092   --topic user-events.v1   --partitions 12   --replication-factor 3   --config min.insync.replicas=2

主题名称也应带有一些意图。像eventsdata这样的名称在集群增长后会变得无用。user-events.v1billing-invoices.v1inventory-adjustments.v1这样的名称告诉未来的操作员这个流是什么,并为你以后进行破坏性模式变更留出空间。

复制因子控制Kafka为每个分区保留的副本数量。在生产环境中,3是常见的默认值,因为它允许一个代理故障,同时仍然保留另一个可用副本。但这并不意味着你可以忽略生产者设置。如果生产者使用acks=1,Kafka可能在副本复制之前就确认记录。对于重要主题,将复制因子三与主题级别的min.insync.replicas=2以及生产者acks=all配对使用。

min.insync.replicas经常被误解。它不会创建副本。它表示对于acks=all的写入成功,需要有多少个同步副本可用。使用复制因子三和min.insync.replicas=2,主题可以容忍一个代理不可用。如果只剩下一个同步副本,Kafka应拒绝强写入,而不是接受安全副本过少的数据。

保留设置决定Kafka何时可以删除旧的日志段。基于时间的保留由主题级别的retention.ms控制。基于大小的保留由retention.bytes控制。较旧的代理级别名称如log.retention.ms是代理默认值;主题配置通常使用retention.ms

例如,将主题保留七天:

kafka-configs.sh --alter   --bootstrap-server broker1:9092   --entity-type topics   --entity-name user-events.v1   --add-config retention.ms=604800000

要限制每个分区的存储,使用retention.bytes

kafka-configs.sh --alter   --bootstrap-server broker1:9092   --entity-type topics   --entity-name user-events.v1   --add-config retention.bytes=10737418240

请记住,retention.bytes通常是每个分区的,而不是主题总大小。一个具有十二个分区和retention.bytes=10GB的主题,在复制前大约可以使用120GB,使用复制因子三后大约360GB。这种细节是导致意外磁盘警报的原因。

Kafka通过日志段删除数据,而不是逐条记录。如果你设置了较短的保留期但段很大,删除可能不会在你期望的精确分钟发生。段设置如segment.bytessegment.ms影响Kafka何时滚动到新段,只有关闭的段才有资格被删除或压缩。较小的段可以使清理更及时,但会增加开销。

cleanup.policy决定Kafka如何处理旧数据。默认是delete,它根据保留设置删除旧段。compact保留每个键的最新记录,并最终删除具有相同键的旧记录。你也可以使用delete,compact用于需要压缩和保留窗口的主题。

压缩对于状态类流很有用:用户配置文件更新、功能标志值、账户设置或按主键键控的数据库变更事件。它不适合每个事件都重要的事件历史。如果你压缩审计日志,相同键的旧事件最终可能会消失。这对于合规性或调试来说可能完全错误。

压缩也依赖于键。一个具有空或不一致键的压缩主题不会像干净的键值变更日志那样运行。如果生产者发送用户更新,有时以user_id为键,有时以电子邮件为键,Kafka会看到不同的键。它无法推断它们代表同一个用户。

压缩可以由生产者设置,主题可以定义compression.type来控制代理行为。常见值包括producergzipsnappylz4zstd,具体取决于Kafka版本。许多团队将主题保留为producer并标准化生产者压缩。lz4zstd是常见选择,但正确答案取决于CPU预算、消息形状和网络压力。

你可以像这样检查主题配置:

kafka-configs.sh --describe   --bootstrap-server broker1:9092   --entity-type topics   --entity-name user-events.v1

并像这样检查分区放置:

kafka-topics.sh --describe   --bootstrap-server broker1:9092   --topic user-events.v1

使用这两个命令。主题配置告诉你保留、压缩和ISR规则。主题描述告诉你领导者、副本和ISR状态。一个主题可能有完美的配置,但由于副本不同步而仍然不健康。

有些更改很容易。保留、压缩策略、min.insync.replicas和其他几个主题配置可以动态更改。有些更改需要更加谨慎。你可以增加分区数量,但不能通过简单命令安全地减少。增加分区也会改变未来记录的键分布,因为分区计算有更多的目标分区。现有记录保持原样;相同键的新记录在增加后可能会根据分区器进入不同的分区。如果跨更改的严格按键排序很重要,请仔细规划。

复制因子更改是操作工作。为现有主题增加副本意味着Kafka必须将现有数据复制到新代理。这可能会产生大量I/O。使用重新分配工具,监控进度,并在需要时进行限流。除非你已经知道集群有足够的备用容量,否则不要在高峰流量期间开始大规模重新分配。

对于一个正常的生产事件主题,一个实用的起点可能如下所示:

kafka-topics.sh --create   --bootstrap-server broker1:9092   --topic payments-authorized.v1   --partitions 24   --replication-factor 3   --config min.insync.replicas=2   --config retention.ms=1209600000   --config cleanup.policy=delete

这意味着:足够的分区用于并行,三个副本用于可用性,两个同步副本用于强写入,十四天保留,并且没有压缩,因为每个支付授权事件都很重要。

对于状态主题,形状不同:

kafka-topics.sh --create   --bootstrap-server broker1:9092   --topic user-preferences.v1   --partitions 12   --replication-factor 3   --config min.insync.replicas=2   --config cleanup.policy=compact

该主题应以用户ID为键。重建状态的消费者可以读取压缩日志,并最终看到每个用户的最新值。他们不应期望每个历史偏好更改都永远保留。

最佳的主题配置操作起来很无聊。它有足够的分区,但不会无缘无故地成千上万。它的复制与数据的价值相匹配。它的保留与恢复和合规需求相匹配。它仅在键有意义时使用压缩。它在代码或文档中描述,以便另一个工程师无需猜测即可重新创建。

一个有用的审查习惯是在选择主题设置之前写下消费者的故事。谁读取这个主题?他们需要从头重放吗?完全重建需要多长时间?源系统可以重新发布旧数据吗?如果消费者宕机三天,Kafka是否仍应保留错过的记录?这些答案比默认的七天设置更诚实地驱动保留策略。

考虑一个读取支付事件的欺诈检测消费者。如果它宕机六小时,你几乎肯定希望它从Kafka中追赶。如果它宕机三十天,你可能期望从支付数据库进行单独的回填过程。该主题可能需要两周的保留,而不是永久。安全审计主题可能有不同的要求,也许将数据发送到对象存储进行长期保留,而Kafka只保留热重放窗口。

消息大小也属于主题讨论范围。Kafka可以在配置后处理更大的记录,但大消息会影响生产者、代理、消费者、复制和获取内存。如果团队开始将多兆字节的JSON blob或编码文件放入主题,不要仅仅提高max.message.bytes就完事。询问负载是否属于对象存储,并在Kafka中引用。Kafka通常最适合移动事件,而不是充当blob存储。

模式演化不是主题配置设置,但它塑造了主题设计。带有版本后缀的主题名称,如orders.v1,在不可避免的破坏性变更时为你提供了逃生舱。如果消费者和生产者遵循模式策略,兼容的变更可以保留在同一个主题中。破坏性变更不应偷偷放入同一个主题,因为一个团队控制生产者。Kafka解耦系统,但前提是契约得到尊重。

最后,记录主题所有权。每个生产主题都应该有一个拥有团队、预期的生产者、预期的消费者、保留原因和数据敏感性说明。这听起来像是行政工作,直到凌晨两点磁盘满了,没有人知道是否可以缩短、删除、压缩或限流主题。好的主题配置部分是技术性的,部分是运营记忆。

发布主题前的最后检查是运行一个故障场景。如果一个代理消失,生产者还能写入吗?如果消费者组周末宕机,保留策略能覆盖缺口吗?如果生产者发送了错误数据,消费者能安全地跳过、隔离或重放吗?如果主题增长速度是预期的两倍,哪个限制保护集群:保留时间、保留字节、配额还是警报?

配额值得一提,因为仅靠主题配置无法保护共享集群免受嘈杂生产者的影响。Kafka支持客户端配额,可以限制生产和获取速率。如果多个团队共享一个集群,配额可以防止意外的重放或失控的生产者压垮代理。它们应与警报配对,以便团队知道他们被限流,而不是默默责怪Kafka。

不要忘记删除策略。一些集群在代理级别禁用主题删除以防止意外。这可能是明智的,但这意味着废弃的主题必须通过受控的清理过程来处理。每月或每季度进行一次主题清单审查可以回收大量磁盘空间,尤其是在开发和测试集群中,实验会留下旧主题。