分区
分区概述
主题划分为多个分区,根据分区规则将消息存到不同分区。配置合理的分区规则, 实现负载均衡和水平扩展。
多个订阅者可以从一个或多个分区同时消费数据,以支撑海量数据处理。
消息是追加到分区的,多个分区顺序写磁盘的效率比随机写内存效率高,是kafka高吞吐保证。
副本机制
Producer和Consumer都只会与Leader角色的分区副本相连,所以kafka需要以集群的组织形式提供主题下的消息高可用。kafka支持主备复制,所以消息具备高可用和持久性。
一个分区有多个副本,这些副本保存在不同broker上。每个分区的副本中都会有一个作为Leder。当一个broker失败时,Leader在这台broker上的分区都会变得不可用。kafaka会自动转移Leader,再从其他副本中选一个新的Leader。
通常情况下,增加分区可以提供kafka集群的吞吐量,但应该意识到集群的总分区数或是单台服务器上的分区数过多时,会增加不可用及延迟的风险。
红色:leader
绿色:flower
Leader选举
会在Zookeeper上针对每个topic维护一个称为ISR(in-sync-replicat,已同步的副本)集合,显然还有其他一些副本未来得及同步。
只有这个ISR里面的才有资格成为Leader(先使用ISR里面的第一个,如果不行以此类推,因为ISR里都是同步副本,消息最完整且各个几点是一样的)。
通过ISR,kafka需要的冗余度较低,可容忍的失败数比较高。假设topic有n+1个副本,kafka可以容忍n个不可用。当然如果,ISR里都不可用,也可以选择其他副本,只是数据不一致
分区重新分配
集群里添加broker,复制配置文件,修改broker id为唯一,最后启动节点将它加入集群中。
但是新的kafka节点并不会自动分配数据,所以无法分担集群负载,除非新建topic。
我们想手动将部分分区移到新添加的kafka节点上,kafka提供相关工具来重新分布某个topic分区。
集群配置
配置文件
- 复制出多个kafka目录,构成集群
- 修改每个kafka中config/server.properties
- broker.id:修改为唯一
- listeners:修改端口为位移
- port:修改为唯一
- log.dirs=/opt/kafka/kafka-01/logs依此修改每个kafka日志路径
- 清除复制过来的日志 rm -rf logs/*
创建主题
#创建主题,3个分区,每个分区3个副本
./kafka-topics.sh --zookeeper 192.168.0.191:2181 --create --topic jpy-par --partitions 3 --replication-factor 3
#查看主题,分区0,leader在broke.id=1上,isr=1,0,2,0和2是fllower,
#主题是在新增节点之后创建,所以负载均衡了
[root@localhost bin]# ./kafka-topics.sh --zookeeper 192.168.0.191:2181 --describe --topic jpy-par
Topic: jpy-par PartitionCount: 3 ReplicationFactor: 3 Configs:
Topic: jpy-par Partition: 0 Leader: 1 Replicas: 1,0,2 Isr: 1,0,2
Topic: jpy-par Partition: 1 Leader: 2 Replicas: 2,1,0 Isr: 2,1,0
Topic: jpy-par Partition: 2 Leader: 0 Replicas: 0,2,1 Isr: 0,2,1
新增分区
#添加分区
./kafka-topics.sh --zookeeper 192.168.0.191:2181 --alter --topic jpy-par --partitions 4
#查看主题,节点1上有2个leader,分担了分区0和3的数据,负载不均衡
[root@localhost bin]# ./kafka-topics.sh --zookeeper 192.168.0.191:2181 --describe --topic jpy-par
Topic: jpy-par PartitionCount: 4 ReplicationFactor: 3 Configs:
Topic: jpy-par Partition: 0 Leader: 1 Replicas: 1,0,2 Isr: 1,0,2
Topic: jpy-par Partition: 1 Leader: 2 Replicas: 2,1,0 Isr: 2,1,0
Topic: jpy-par Partition: 2 Leader: 0 Replicas: 0,2,1 Isr: 0,2,1
Topic: jpy-par Partition: 3 Leader: 1 Replicas: 1,2,0 Isr: 1,2,0
添加节点
添加新节点(复制文件,修改properties)之后查看主题,可见新添加的节点并没有分配之前主题的分区
[root@localhost bin]# ./kafka-topics.sh --zookeeper 192.168.0.191:2181 --describe --topic jpy-par
Topic: jpy-par PartitionCount: 4 ReplicationFactor: 3 Configs:
Topic: jpy-par Partition: 0 Leader: 1 Replicas: 1,0,2 Isr: 1,0,2
Topic: jpy-par Partition: 1 Leader: 2 Replicas: 2,1,0 Isr: 2,1,0
Topic: jpy-par Partition: 2 Leader: 0 Replicas: 0,2,1 Isr: 0,2,1
Topic: jpy-par Partition: 3 Leader: 1 Replicas: 1,2,0 Isr: 1,2,0
重新分配
- 定义reassign.json文件,确定要重启分配分区的主题
{"topics":[{"topic":"jpy-par"}],
"version":1
}
- 使用kafka-reassign-partitions.sh工具生成reassign plan
#生成执行方案,generate
bin/kafka-reassign-partitions.sh --zookeeper 192.168.0.191:2181 --topics-to-move-json-file reassign.json --broker-list "0,1,2,3" --generate
#输出
Current partition replica assignment
{"version":1,"partitions":[{"topic":"jpy-par","partition":0,"replicas":[1,0,2],"log_dirs":["any","any","any"]},{"topic":"jpy-par","partition":1,"replicas":[2,1,0],"log_dirs":["any","any","any"]},{"topic":"jpy-par","partition":2,"replicas":[0,2,1],"log_dirs":["any","any","any"]},{"topic":"jpy-par","partition":3,"replicas":[1,2,0],"log_dirs":["any","any","any"]}]}
Proposed partition reassignment configuration
{"version":1,"partitions":[{"topic":"jpy-par","partition":0,"replicas":[1,0,2],"log_dirs":["any","any","any"]},{"topic":"jpy-par","partition":1,"replicas":[2,1,3],"log_dirs":["any","any","any"]},{"topic":"jpy-par","partition":2,"replicas":[3,2,0],"log_dirs":["any","any","any"]},{"topic":"jpy-par","partition":3,"replicas":[0,3,1],"log_dirs":["any","any","any"]}]}
第一个json:当前分区副本分配情况
第二个json:重新分配候选方案,只是方案,并未执行
-
将第二个json保存在result.json中
-
执行分配策略
#执行,需要时间,--execute
bin/kafka-reassign-partitions.sh --zookeeper 192.168.0.191:2181 --reassignment-json-file result.json --execute
#输出当前分配策略,不是执行之后的
Current partition replica assignment
{"version":1,"partitions":[{"topic":"jpy-par","partition":0,"replicas":[1,0,2],"log_dirs":["any","any","any"]},{"topic":"jpy-par","partition":1,"replicas":[2,1,0],"log_dirs":["any","any","any"]},{"topic":"jpy-par","partition":2,"replicas":[0,2,1],"log_dirs":["any","any","any"]},{"topic":"jpy-par","partition":3,"replicas":[1,2,0],"log_dirs":["any","any","any"]}]}
Save this to use as the --reassignment-json-file option during rollback
Successfully started partition reassignments for jpy-par-0,jpy-par-1,jpy-par-2,jpy-par-3
#查看执行情况,verify
bin/kafka-reassign-partitions.sh --zookeeper 192.168.0.191:2181 --reassignment-json-file result.json --verify
#输出
Status of partition reassignment:
Reassignment of partition jpy-par-0 is complete.
Reassignment of partition jpy-par-1 is complete.
Reassignment of partition jpy-par-2 is complete.
Reassignment of partition jpy-par-3 is complete.
Clearing broker-level throttles on brokers 0,1,2,3
Clearing topic-level throttles on topic jpy-par
#再次查看主题详情,出现了问题,0节点还是没有作为leadr,于是手动创建执行计划,参考下面技巧
bin/kafka-topics.sh --zookeeper 192.168.0.191:2181 --describe --topic jpy-par
#输出
Topic: jpy-par PartitionCount: 4 ReplicationFactor: 3 Configs:
Topic: jpy-par Partition: 0 Leader: 1 Replicas: 1,0,2 Isr: 2,1,0
Topic: jpy-par Partition: 1 Leader: 2 Replicas: 2,1,3 Isr: 2,1,3
Topic: jpy-par Partition: 2 Leader: 3 Replicas: 3,2,0 Isr: 2,0,3
Topic: jpy-par Partition: 3 Leader: 1 Replicas: 0,3,1 Isr: 1,0,3
技巧
- 使用 kafka-reassign-partitions.sh 工具生成的reassign plan只是一个建议,方便大家而已。自己完全可以编辑一个reassign plan,然后执行它
{"version":1,"partitions":[{"topic":"jpy-par","partition":0,"replicas":[0,1,2],"log_dirs":["any","any","any"]},{"topic":"jpy-par","partition":1,"replicas":[1,2,3],"log_dirs":["any","any","any"]},{"topic":"jpy-par","partition":2,"replicas":[2,3,0],"log_dirs":["any","any","any"]},{"topic":"jpy-par","partition":3,"replicas":[3,0,1],"log_dirs":["any","any","any"]}]}
- verfiy查看执行情况时,可以在语句后面加上 | grep -c "progress"就知道有多少分区还没完成,输出为0的时候就是完成了。
修改副本因子
-
不能通过kafka-topoics.sh来修改副本数
-
通过kafka-reassign-partitions.sh,配置配置topic的副本,保存为json文件
- 配置副本json文件
{“version”:1, “partitions”:[ {“topic”:”yqtopic01″,”partition”:0,”replicas”:[0,1,2]}, {“topic”:”yqtopic01″,”partition”:1,”replicas”:[0,1,2]}, {“topic”:”yqtopic01″,”partition”:2,”replicas”:[0,1,2]} ]}
- 执行脚本
./kafka-reassign-partitions.sh -zookeeper 127.0.0.1:2181 –reassignment-json-file result.json –execute
分区分配策略
多个策略以逗号隔开。
分配分区的条件:
-
同一个消费组内消费者的新增、关闭或崩溃
-
订阅的主题新增分区。
RangeAssignor(默认)
对每个主题而言,先按照分区序号排序,然后将消费者排序。分区数/消费者数=n,分区数%消费者数=m,前m个消费者每个分配n+1分区,后面的每个消费者分配n个分区
-
例:2个消费者C0,C1,都订阅了主题t0,t1,每个主题4个分区。那所有分区为:t0p0、t0p1、t0p2、t0p3、t1p0、t1p1、t1p2、t1p3
最终分配结果:4/2=2,4%2=0,每个消费这分配2个分区(针对每个主题而言)
#分配均匀 消费者C0:t0p0、t0p1、t1p0、t1p1 消费者C1:t0p2、t0p3、t1p2、t1p3
-
例:假设每个主题3个分区,那所有分区为:t0p0、t0p1、t0p2、t1p0、t1p1、t1p2,前1个消费者分配2个分区,后面的分配1个分区,分配结果:
#分配不均 C0:t0p0、t0p1、t1p0、t1p1 C1:t0p2、t1p2
总结:如果将类似的情形扩大,有可能会出现部分消费者过载的情况
RoundRobinAssignor
将消费组内所有消费者以及消费者所订阅的所有topic的partition按照字典序排序,然后通过轮询方式逐个将分区以此分配给每个消费者
-
同一个消费组内所有的消费者的订阅信息都是相同的,那此策略的分区分配会是均匀的。
例:2个消费者C0,C1,都订阅了主题t0和t1,每个主题都有3个分区,那所有分区为:t0p0、t0p1、t0p2、t1p0、t1p1、t1p2,分配结果
#分配均匀 消费者C0:t0p0、t0p2、t1p1 消费者C1:t0p1、t1p0、t1p2
-
同一个消费组内的消费者所订阅的信息是不相同的,分配的时候就不是完全的轮询分配,有可能会导致分区分配的不均匀。
例:3个消费者C0,C1,C2,共同订阅了主题t0,t1,t2,分别有1,2,3个分区,整个消费组订阅了t0p0、t1p0、t1p1、t2p0、t2p1、t2p2这6个分区。
具体而言:C0订阅t0,C1订阅t0和t1,C2订阅t0,t1和t2,分配结果:
#分配不均 消费者C0:t0p0 消费者C1:t1p0 消费者C1:t1p1、t2p0、t2p1、t2p2
总结:RoundRobinAssignor策略也不完美,完全可以将t1p1分配给C1
StickyAssignor
它主要有两个目的:1. 分区的分配要尽可能的均匀;2. 分区的分配尽可能的与上次分配的保持相同。
当两者发生冲突时,第一个目标优先于第二个目标。
-
假设消费组内有3个消费者:C0、C1和C2,都订阅了4个主题:t0、t1、t2、t3,并且每个主题有2个分区,整个消费组订阅了t0p0、t0p1、t1p0、t1p1、t2p0、t2p1、t3p0、t3p1这8个分区。最终的分配结果如下:
消费者C0:t0p0、t1p1、t3p0 消费者C1:t0p1、t2p0、t3p1 消费者C2:t1p0、t2p1
这样初看上去似乎与采用RoundRobinAssignor策略所分配的结果相同,但事实是否真的如此呢?再假设此时消费者C1脱离了消费组,那么消费组就会执行再平衡操作,进而消费分区会重新分配。
-
如果采用RoundRobinAssignor策略,分配结果如下:
#重新轮询分配 消费者C0:t0p0、t1p0、t2p0、t3p0 消费者C2:t0p1、t1p1、t2p1、t3p1
总结:所有分区按照C0,C2重新轮询
-
如果采用StickyAssignor策略,分配结果如下:
消费者C0:t0p0、t1p1、t3p0、t2p0 消费者C2:t1p0、t2p1、t0p1、t3p1
总结:保留了上一次分配中对于消费者C0和C2的所有分配结果,将C1的负担分配给了C0和C2(去掉C1之后,C0本来分配了3个,C2分配2个,所有按从C2开始轮询分配)
-
自定义分配策略
实现PartitionAssignor
继承AbstractPartitionAssignor
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY