Kafka的ISR机制和像Raft这样的传统基于Quorum(法定人数)的协议之间的区别确实很微妙,但也非常重要。让我们来分析一下为什么ISR可以减少所需的副本数量。
在采用ISR模型和(f+1)个副本数的配置下,一个Kafka分区能够容忍最大f个节点失败,相比于“少数服从多数”的方式所需的节点数大幅减少。实际上,为了能够容忍f个节点失败,“少数服从多数”的方式和ISR的方式都需要相同数量副本的确认信息才能提交消息。
比如,为了容忍1个节点失败,“少数服从多数”需要3个副本和1个follower的确认信息,采用ISR的方式需要2个副本和1个follower的确认信息。在需要相同确认信息数的情况下,采用ISR的方式所需要的副本总数变少,复制带来的集群开销也就更低,“少数服从多数”的优势在于它可以绕开最慢副本的确认信息,降低提交的延迟,而对Kafka而言,这种能力可以交由客户端自己去选择。
问题是:为什么它们有这么大的区别,ISR是如何实现副本数量减少的?
根本区别在于当旧的leader失效后,如何选择新的leader。
“多数投票”系统(如 Raft)
在像Raft这样的系统中,领导者选举是一个在所有副本节点之间进行的去中心化、对称的过程。
- 固定的Quorum:为了容忍
f
个故障,必须有2f+1
个副本。这个数字是数学上必需的。 - 写入Quorum:一个写操作只有在被复制到大多数节点(
f+1
)后才被认为是“已提交”的。 - 选举Quorum:新领导者只有在获得大多数节点(
f+1
)的投票后才能当选。
2f+1
规则的奥妙在于,任何两个多数派(Quorum)都保证会重叠。这意味着,在选举新领导者时,投票给它的节点集合中必须包含至少一个接收了最后一次已提交写入的节点。这个重叠的成员确保了过时的副本不能被选为领导者,从而防止数据丢失。领导者是谁的共识是在每次选举期间由副本自己达成的。
Kafka的ISR(In-Sync Replica)模型
在Kafka中,领导者选举是一个由一个特殊节点——Controller(控制器)——管理的中心化、非对称的过程。
- 动态的“Quorum”(即ISR):Kafka不使用固定的多数派,而是使用一个动态的、与领导者完全同步的副本集合。这就是同步副本集(In-Sync Replica, ISR)。
- 更严格的写入条件:一个写操作只有在被复制到当前ISR中的所有副本后,才被认为是“已提交”的(即高水位线可以推进)。
- 中心化任命:当领导者失败时,副本们不进行投票。监控所有Broker健康状况的Controller会注意到这个故障。然后,它会查看该分区的最新已知ISR,并任命其中一个幸存的副本作为新的领导者。
正如在文档中看到的那样:
design.html:496-498
// ... existing code ...
It is also important to optimize the leadership election process as that is the critical window of unavailability. A naive implementation of leader election would end up running an election per partition for all
partitions a node hosted when that node failed. As discussed above in the section on <a href="#replication">replication</a>, Kafka clusters have a special role known as the "controller" which is
responsible for managing the registration of brokers. If the controller detects the failure of a broker, it is responsible for electing one of the remaining members of the ISR to serve as the new leader.
// ... existing code ...
为什么这能减少副本数量
Kafka的ISR模型通过改变问题的方式,有效地“取巧”了。它将达成共识的工作从单个分区转移到了中心化的Controller上。
- Raft 需要
2f+1
个副本,因为每个分区都必须能够依靠重叠Quorum的数学保证,安全地自行选举出新的领导者。 - Kafka 的一个分区只需要
f+1
个副本,因为分区副本本身不做出领导决策。它们相信Controller会任命一个新的领导者。由于ISR中的任何副本都保证拥有所有已提交的数据(得益于严格的写入条件),所以Controller的选择总是安全的。
这将容错的负担转移了。数据分区可以用 f+1
个副本容忍 f
个故障,但现在Controller本身必须是容错的。
这就引出了Raft在现代Kafka中的作用:
- 在 KRaft(Kafka Raft Metadata)模式下,Controller们自己组成一个Quorum,并使用Raft协议就集群元数据——包括每个分区的ISR成员——达成一致。
所以,总结一下:
- Raft:在每个分区的
2f+1
个对等节点之间进行去中心化选举。 - Kafka的数据分区:使用由Controller进行的中心化领导者任命,这使得它们只需要
f+1
个副本即可运行。 - Kafka的Controller(在KRaft模式下):它们之间使用Raft来确保其管理的元数据(如ISR列表)的可靠性和一致性。
通过将元数据的共识与数据复制路径分离开来,Kafka优化了常见场景(数据复制),使其在副本数量、磁盘空间和网络流量方面更加高效。