Kafka 复制配置:确保数据的持久性与可用性

配置 Kafka 复制、ISR、生产者确认和机架感知,而不削弱持久性。

Kafka 复制配置:确保数据的持久性与可用性

Kafka 复制配置是集群从一堆代理变成一个在故障时值得信赖的系统的关键。这些设置本身并不复杂:复制因子、同步副本、生产者确认、领导者选举和机架放置。棘手之处在于它们只有协同工作才有意义。

一个具有三个副本的主题,如果生产者使用弱确认,仍然可能丢失已确认的数据。使用 acks=all 的生产者,如果 min.insync.replicas 对当前存活的代理数量来说过于严格,写入仍然可能失败。一个跨可用区分布的集群,如果某个热门分区的所有副本都落在同一个故障域中,仍然可能遇到问题。复制不是一个单一的复选框。

我喜欢这样简单理解 Kafka 复制:对于每个分区,Kafka 保留多个副本,选择一个副本来接受读写,并保持其他副本足够接近,以便其中一个可以接管。你的任务是决定多少个副本足够,在写入被认为成功之前必须有多少个副本跟上,以及集群是否应该优先考虑可用性而非数据安全。

一个 Kafka 主题被分成多个分区。每个分区有一个领导者副本和零个或多个追随者副本。生产者写入领导者。消费者通常从领导者读取。追随者从领导者获取记录并保持其本地日志同步。如果领导者的代理发生故障,Kafka 会从被认为是安全候选的副本中选举一个新的领导者。

这个安全候选列表就是 ISR,即同步副本。当一个副本根据 Kafka 的副本滞后规则足够紧密地跟上领导者时,它就在 ISR 中。如果追随者停止获取、落后太久或代理消失,Kafka 会将其从 ISR 中移除。当它赶上时,它可以重新加入。

这个细节很重要,因为 ISR 是使 Kafka 持久性不仅仅是空想的关键。使用 acks=all 时,领导者不会确认生产请求,直到记录已被复制到所需的同步副本。确切的要求由 min.insync.replicas 控制。如果主题的 replication.factor=3min.insync.replicas=2,Kafka 要求至少有两个同步副本,acks=all 的写入才能成功。

这种组合在生产中很常见,因为它提供了一个实用的平衡。一个代理可以故障,主题仍然可以接受强确认的写入。如果在第一个代理恢复之前第二个代理故障,使用 acks=all 的生产者应该开始看到错误,例如 NotEnoughReplicasNotEnoughReplicasAfterAppend。这在事故期间很烦人,但通常是正确的行为。Kafka 在没有足够安全副本时拒绝假装写入是持久的。

以下是正常的三代理或更大集群的典型生产基线:

default.replication.factor=3
min.insync.replicas=2
unclean.leader.election.enable=false

这些值不会自动使每个工作负载安全,但它们为你提供了一个合理的起点。default.replication.factor=3 意味着新主题获得三个副本,除非主题创建命令另有说明。min.insync.replicas=2 意味着至少有两个副本必须同步才能进行强写入。unclean.leader.election.enable=false 告诉 Kafka 不要为了保持分区可写而选举一个过时的副本作为领导者。

不要将复制因子设置得高于你的代理数量。如果只有两个代理存在,Kafka 无法将三个副本放在三个不同的代理上。在小型开发集群中,replication.factor=1 是可以的,因为便利性比容错更重要。在生产中,1 意味着单个代理丢失可能导致数据不可用,并可能永久丢失仅存储在该代理上的记录。

生产者端必须与主题端匹配。对于重要数据,使用 acks=all。还要启用幂等性,除非你有特定的理由不这样做。在现代 Kafka 客户端中,幂等生产者是减少重试导致的重复的正常选择。

acks=all
enable.idempotence=true
retries=2147483647
max.in.flight.requests.per.connection=5

不要盲目地将重试值复制到每个客户端,而不了解你的客户端版本和交付要求。重要的是,持久的 Kafka 生产通常需要重试、幂等性和 acks=all 一起使用。如果你设置 acks=1,领导者可以在追随者复制记录之前确认记录。如果该领导者在错误的时间故障,已确认的记录可能会消失。这对于某些遥测流是可以接受的。但对于支付、审计跟踪、库存移动或任何下游团队视为真相来源的数据来说是不可接受的。

当你创建主题时,有意识地设置复制选择,而不是依赖于恰好存在的代理默认值:

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

分区数量与复制是分开的。12 个分区,复制因子为 3,意味着总共有 36 个分区副本。这会产生存储、网络、文件句柄和控制器元数据成本。复制提高了持久性,但并非免费。

对于现有主题,更改 min.insync.replicas 很简单:

kafka-configs.sh --alter   --bootstrap-server broker1:9092   --entity-type topics   --entity-name orders.v1   --add-config min.insync.replicas=2

更改现有主题的复制因子取决于 Kafka 版本和工具。较新的 Kafka 版本支持 kafka-reassign-partitions.sh,并且在某些情况下,主题更改工作流程使增加更容易。在较旧的集群中,增加复制通常意味着生成并执行分区重新分配计划。减少复制更敏感,因为你正在删除副本。将其视为计划操作,而不是在嘈杂的事故中随意输入的命令。

如果主题很大或集群已经很忙,重新分配应该被限制。复制追赶从现有副本读取旧数据并将其写入新副本。这可能会从实时生产者和消费者那里窃取磁盘和网络容量。一个安全的运行手册通常包括维护窗口、前后 --describe 输出、重新分配限制和回滚计划。

你可以像这样检查主题:

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

查看输出中的三个字段:LeaderReplicasIsrReplicas 是分配的集合。Isr 是当前跟上的集合。如果 Replicas1,2,3Isr1,2,则代理 3 对于该分区落后或不可用。如果许多分区显示 ISR 中缺少代理,请检查该代理的磁盘、网络、进程健康和日志。如果只有少数热门分区受到影响,则领导者可能过载或该分区流量异常高。

不洁领导者选举需要特别小心。如果分区的所有同步副本都消失了,Kafka 有两个选择。它可以使分区不可用,直到安全副本返回,或者它可以选举一个不同步的副本,并冒着丢失在旧领导者上确认的记录的风险。unclean.leader.election.enable=false 选择安全。true 选择可用性,但冒着数据丢失的风险。

有些工作负载可能可以接受不洁选举:短暂的点击流数据、可丢弃的指标,或者上游系统可以重放所有内容的数据管道。对于大多数业务数据,请保持禁用。分区不可用很痛苦,但静默数据丢失更糟糕,因为消费者可能继续运行,好像什么都没发生。

机架感知复制有助于应对不同类型的故障。如果你的代理分布在具有共享电源/网络路径的机架、区域或主机上,请告诉 Kafka 每个代理的位置:

broker.rack=zone-a

在每个代理上设置正确的值。Kafka 将尝试跨机架分布副本,以便单个区域故障不太可能移除分区的每个副本。这不是魔法。你仍然需要每个区域有足够的代理、足够的磁盘和仔细的分区放置。但如果没有 broker.rack,Kafka 无法知道两个代理共享同一个故障域。

持续监控复制。最有用的早期警告信号是副本不足的分区、离线分区、ISR 收缩事件以及与副本不足相关的生产错误。在基于 Prometheus 的设置中,团队通常监控 Kafka 代理指标以了解副本不足的分区和离线分区,然后将这些警报与代理磁盘、网络和 JVM 指标配对。

一个好的事故问题是:ISR 收缩是因为代理故障、复制跟不上,还是网络不可靠?修复方法不同。故障的代理需要服务恢复。慢速代理可能需要磁盘更换、I/O 调查或更少的分区领导者。网络问题可能表现为重复断开和获取器延迟,即使 CPU 和磁盘看起来正常。

滚动代理重启是复制设置显示其价值的另一个地方。一次重启一个代理。在重启下一个代理之前,等待分区恢复健康的 ISR。如果你使用 min.insync.replicas=2 过快重启代理,生产者可能会开始失败,因为同步的副本太少。这种失败是预期的,但你可以通过耐心和监控来避免。

实用清单很短。对于大多数生产主题,使用复制因子 3。对于重要数据,使用 min.insync.replicas=2 和生产者 acks=all。保持不洁领导者选举禁用,除非数据明确可丢弃。通过机架感知跨故障域分布副本。监控 ISR 健康,而不仅仅是代理正常运行时间。并在真实故障为你做之前,在受控窗口中通过重启代理来测试你的假设。

在审查期间有帮助的一个细节是用简单的语言区分持久性和可用性。持久性问的是:“在 Kafka 说写入成功后,在已确认记录面临风险之前可以发生多少次故障?”可用性问的是:“生产者和消费者现在还能使用分区吗?”强设置有时会降低可用性,因为 Kafka 会拒绝写入,而不是接受弱复制的数据。这不是 Kafka 的失败。这是 Kafka 遵守你配置的契约。

例如,想象一个主题,复制因子为 3,min.insync.replicas=2,生产者使用 acks=all。代理 1 是领导者,代理 2 和 3 是追随者。如果代理 3 宕机,ISR 变为 1,2。写入仍然成功,因为两个副本同步。如果代理 2 在代理 3 返回之前宕机,ISR 变为只有 1。写入失败。一些团队首先在生产中看到这种情况,并问为什么领导者还活着但 Kafka 宕机了。答案是主题对于某些读取仍然可用,但对于强确认的写入不安全。

你还应该考虑消费者恢复。复制保护代理端的记录副本。它不会自动保护消费者偏移量免受每个工作流错误的影响。消费者偏移量也存储在 Kafka 中,通常在 __consumer_offsets 中,因此该内部主题也需要健康的复制。如果用户主题配置得当,但内部主题在早期集群构建时使用了弱复制,故障转移行为可能仍然比预期的更差。作为生产就绪审查的一部分,检查内部主题复制。

在多租户集群中,并非每个主题都值得相同的配置。一个高容量、低业务价值的可丢弃指标主题可能使用较短的保留期并容忍较弱的保证。计费主题则不应该。错误在于让意外的默认值决定这种区别。将主题分类写下来:关键事件流、可重放遥测、压缩状态主题、临时开发主题。然后将每个类映射到复制、ISR、保留和生产者设置。

在事故期间,除非每个人都理解权衡,否则避免更改持久性设置只是为了消除错误。将 min.insync.replicas2 降低到 1 可能会让生产者继续工作,但这意味着已确认的写入可以只存在于一个代理上。启用不洁领导者选举可能会恢复分区可用性,但过时的副本可能会丢失记录。有时业务可能会选择这种权衡。这应该是一个有意识的事故决策,而不是一个隐藏的操作员捷径。