个人技能总结-kafka部分
kafka集群部分技能总结
kafka集群架构
Kafka集群的规划和架构设计是构建高性能、高可用性和可扩展性的分布式消息系统的关键。以下是关于Kafka集群规划和架构的详细说明:
- Kafka集群的基本组成
Kafka集群主要由以下几个核心组件构成:
生产者(Producer):负责向Kafka主题(Topic)发送消息的客户端。
消费者(Consumer):订阅并处理来自特定主题的消息。
服务代理(Broker):存储已发布消息的服务器,每个Broker可以处理多个分区。
控制器(Controller):用于管理集群中的Broker节点,并确保集群的高可用性。
Kafka 基本概念:
Producer :生产者,负责将消息发送到 Broker
Consumer :消费者,从 Broker 接收消息
Consumer Group :消费者组,由多个 Consumer 组成。消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费,消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。
Broker :可以看做一个独立的 Kafka 服务节点或 Kafka 服务实例。如果一台服务器上只部署了一个 Kafka 实例,那么我们也可以将 Broker 看做一台 Kafka 服务器。
Topic :一个逻辑上的概念,包含很多 Partition,同一个 Topic 下的 Partiton 的消息内容是不相同的。
Partition :为了实现扩展性,一个非常大的 topic 可以分布到多个 broker 上,一个 topic 可以分为多个 partition,每个 partition 是一个有序的队列。
Replica :副本,同一分区的不同副本保存的是相同的消息,为保证集群中的某个节点发生故障时,该节点上的 partition 数据不丢失,且 kafka 仍然能够继续工作,- kafka 提供了副本机制,一个 topic 的每个分区都有若干个副本,一个 leader 和若干个 follower。
Leader :每个分区的多个副本中的"主副本",生产者以及消费者只与 Leader 交互。
Follower :每个分区的多个副本中的"从副本",负责实时从 Leader 中同步数据,保持和 Leader 数据的同步。Leader 发生故障时,从 Follower 副本中重新选举新的 Leader 副本对外提供服务。 - Kafka的分布式架构
Kafka是一个典型的分布式系统,其架构设计包括以下几个关键点:
分区(Partition):为了提高吞吐量,Kafka将每个主题分成多个分区,每个分区可以独立于其他分区进行读写操作。
副本(Replica):为了提高系统的可靠性和容错性,每个分区在不同的Broker上保存多个副本。
Zookeeper协调:Kafka使用Zookeeper作为分布式协调框架,以保证集群中各个组件之间的同步和一致性。 - 高可用性和负载均衡
为了实现高可用性和负载均衡,Kafka集群通常会部署多个Broker节点,并通过控制器(Controller)来管理这些Broker节点。控制器负责监控Broker的状态,当某个Broker出现故障时,控制器会自动将该Broker上的分区重新分配到其他Broker上,从而保证系统的高可用性。 - 存储和性能优化
在规划Kafka集群时,需要考虑以下几个方面的存储和性能优化:
磁盘存储:根据业务需求和消息大小,合理规划磁盘存储空间。例如,如果每天需要发送1亿条消息,每条消息保存两份以防止数据丢失,那么需要足够的磁盘空间来存储这些消息。
内存需求:每个消费者至少需要一定量的内存来接收和处理消息。例如,加入一个20分区的主题,拥有5个消费者,那么每个消费者至少需要4MB的内存来接收消息。
网络设计:为了支持高并发,Kafka采用了Reactor多路复用模型和零拷贝技术等手段来提升网络性能。 - 双/多中心部署
为了进一步提高系统的可用性和容错性,Kafka支持双中心或多中心部署。在这种部署模式下,多个数据中心之间通过高速网络连接,共同构成一个统一的Kafka集群。这样不仅可以分散单点故障的风险,还可以根据地理位置的不同,实现更高效的流量路由和负载均衡。
总结
Kafka集群的规划和架构设计需要综合考虑生产环境的周边系统、业务场景以及存储和网络资源的需求。通过合理的规划和配置,可以构建一个高性能、高可用且可扩展的分布式消息系统,满足大规模实时数据流处理的需求。
Kafka容量规划
kafka容量评估需求场景分析
集群如何每天hold住10亿+请求
拿电商平台为例, kafka 集群每天需要承载10亿+请求流量数据,一天24小时,对于平台来说,晚上12点到凌晨8点这8个小时几乎没多少数据涌入的。这里我们使用「二八法则」来进行预估,也就是80%的数据(8亿)会在剩余的16个小时涌入,且8亿中的80%的数据(约6.4亿)会在这16个小时的20%时间 (约3小时)涌入。
通过上面的场景分析,可以得出如下:
QPS计算公式 = 640000000 ÷ (3 * 60 * 60) = 6万,也就是说高峰期集群需要抗住每秒6万的并发请求。
假设每条数据平均按20kb(生产端有数据汇总)来算, 那就是 1000000000 * 20kb = 18T, 一般情况下我们都会设置3 个副本, 即54T, 另外 kafka 数据是有保留时间周期的, 一般情况是保留最近3天的数据, 即 54T * 3 = 162T
场景总结:
要搞定 10亿+ 请求,高峰期要支撑 6万QPS , 需要大约 162 T 的存储空间
kafka容量评估之物理机数量
物理机 OR 虚拟机
一般对于Kafka, Mysql, Hadoop 等集群自建的时候, 都会使用物理机来进行搭建, 性能和稳定性相对虚拟机要强很多。
物理机数量计算
在第一步中我们分析得出系统高峰期的时候要支撑6万QPS, 如果公司资金和资源充足的情况下, 我们一般会让高峰期的QPS控制在集群总承载QPS能力的30%左右, 这样的话可以得出集群能承载的总QPS能力约为20万左右, 这样系统才会是安全的。
场景总结:
根据经验可以得出 每台物理机支撑4万QPS 是没有问题的, 从QPS角度分析, 我们要支撑 10亿+ 请求,大约需要 5台物理机 , 考虑到消费者请求, 需要增加约1.5倍机器, 即 7台物理机 。
kafka容量评估之磁盘
机械硬盘 OR 固态硬盘SSD
主要区别:
1、SSD就是固态硬盘,它的优点是速度快,日常的读写比机械硬盘快几十倍上百倍。缺点是单位成本高,不适合做大容量存储。
2、HDD就是机械硬盘,它的优点是单位成本低,适合做大容量存储,但速度远不如SSD。
首先SSD硬盘性能好, 主要是指的随机读写能力性能好, 非常适合Mysql这样的集群, 而SSD的顺序读写性能跟机械硬盘的性能是差不多的 。
在上一篇 [kafka三高架构设计剖析] 中我们了解到 Kafka 写磁盘是顺序追加写的, 所以对于 kafka 集群来说,我们使用普通机械硬盘就可以了。
每台服务器需要多少块硬盘
根据第一二步骤计算结果, 我们需要7台物理机, 一共需要存储162T数据,大约每台机器需要存储23T数据, 根据以往经验一般服务器配置11块硬盘, 这样每块硬盘大约存储2T的数据就可以了, 另外为了服务器性能和稳定性, 我们一般要保留一部分空间, 保守按每块硬盘最大能存储3T数据 。
场景总结:
要搞定 10亿+ 请求, 需要 7台物理机 , 使用 普通机械硬盘 进行存储, 每台服务器 11块 硬盘, 每块硬盘存储 2T 数据
kafka容量评估之内存
Kafka 写磁盘流程及内存分析
Kafka 读写数据的流程主要都是基于os cache, 所以基本上 Kafka 都是基于内存来进行数据流转的, 这样的话要分配尽可能多的内存资源给os cache 。
kafka的核心源码基本都是用 scala 和 java (客户端)写的, 底层都是基于 JVM 来运行的, 所以要分配一定的内存给 JVM 以保证服务的稳定性。对于 Kafka 的设计,并没有把很多的数据结构存储到 JVM 中, 所以根据经验,给 JVM 分配6~10G就足够了。
Topic 会对于多个 partition ,一个 partition 会对应多个 segment , 一个 segment 会对应磁盘上4个log文件。假设我们这个平台总共100个 Topic , 那么总共有 100 Topic * 5 partition * 3 副本 = 1500 partition 。对于 partition 来说实际上就是物理机上一个文件目录, .log就是存储数据文件的, 默认情况下一个.log日志文件大小为1G 。
如果要保证这1500个 partition 的最新的 .log 文件的数据都在内存中, 这样性能当然是最好的, 需要 1500 * 1G = 1500 G内存, 但是我们没有必要所有的数据都驻留到内存中, 我们只保证25%左右的数据在内存中就可以了, 这样大概需要 1500 * 250M = 1500 * 0.25G = 375G内存, 通过第二步分析结果,我们总共需要7台物理机, 这样的话每台服务器只需要约54G内存, 外加上面分析的JVM的10G, 总共需要64G内存 。 还要保留一部分内存给操作系统使用,故我们选择128G内存的服务器是非常够用了 。
场景总结:
要搞定 10亿+ 请求, 需要 7台物理机 , 每台物理机内存选择 128G内存 为主, 这样内存会比较充裕 。
kafka容量评估之CPU压力
CPU Core分析
我们评估需要多少个 CPU Core,主要是看 Kafka 进程里会有多少个线程,线程主要是依托多核CPU来执行的 , 如果线程特别多,但是 CPU核很少,就会导致CPU负载很高,会导致整体工作线程执行的效率不高,性能也不会好 。 所以我们要保证CPU Core的充足, 来保障系统的稳定性和性能最优
cpu数 支撑情况分析
4 可以支撑几十个线程, 在高峰期cpu几乎会被打满
8 可以比较宽裕的支撑几十个线程繁忙的工作
16 建议核数(2 cpu * 8), 基本可以支撑100~200个线程的工作
32 如果资源充裕, 性能会更好 (4 cpu * 8)
KAFKA配置文件详解
server.properties配置参数说明
系统 相关
每一个broker在集群中的唯一标示,要求是正数。在改变IP地址,不改变broker.id的话不会影响consumers
broker.id =1
kafka数据的存放地址,多个地址的话用逗号分割 /tmp/kafka-logs-1,/tmp/kafka-logs-## 每当创建新partition时,都会选择在包含最少 partitions的路径下进行。注:这个目录下不能有其他非kafka的目录,不然会导致kafka集群无法启动
log.dirs = /tmp/kafka-logs
提供给客户端响应的端口
port =9092
消息体的最大大小,单位是字节
message.max.bytes =1000000
broker 处理消息的最大线程数,一般情况下不需要去修改
num.network.threads =3
broker处理磁盘IO 的线程数 ,数值应该大于你的硬盘数
num.io.threads =8
一些后台任务处理的线程数,例如过期消息文件的删除等,一般情况下不需要去做修改
background.threads =4
等待IO线程处理的请求队列最大数,若是等待IO的请求超过这个数值,那么会停止接受外部消息,算是一种自我保护机制
queued.max.requests =500
broker的主机地址,若是设置了,那么会绑定到这个地址上,若是没有,会绑定到所有的接口上,并将其中之一发送到ZK,一般不设置
host.name
打广告的地址,若是设置的话,会提供给producers, consumers,其他broker连接,具体如何使用还未深究
advertised.host.name
广告地址端口,必须不同于port中的设置
advertised.port
socket的发送缓冲区,socket的调优参数SO_SNDBUFF
socket.send.buffer.bytes =100*1024
socket的接受缓冲区,socket的调优参数SO_RCVBUFF
socket.receive.buffer.bytes =100*1024
socket请求的最大数值,防止serverOOM,message.max.bytes必然要小于socket.request.max.bytes,会被topic创建时的指定参数覆盖
socket.request.max.bytes =10010241024
LOG 相关##
topic的分区是以一堆segment文件存储的,这个控制每个segment的大小,文件超过指定大小会重新创建一个文件,会被topic创建时的指定参数覆盖
log.segment.bytes =102410241024
这个参数会在日志segment没有达到log.segment.bytes设置的大小,也会强制新建一个segment,会被topic创建时的指定参数覆盖
log.roll.hours =24*7
日志清理策略 选择有:delete和compact 主要针对过期数据的处理,或是日志文件达到限制的额度,会被 topic创建时的指定参数覆盖
log.cleanup.policy = delete
数据存储的最大时间 超过这个时间 会根据log.cleanup.policy设置的策略处理数据,也就是消费端能够多久去消费数据
log.retention.bytes和log.retention.minutes任意一个达到要求,都会执行删除,会被topic创建时的指定参数覆盖
log.retention.minutes=7days
指定日志每隔多久检查看是否可以被删除,默认1分钟
log.cleanup.interval.mins=1
topic每个分区的最大文件大小,一个topic的大小限制 = 分区数*log.retention.bytes 。-1没有大小限制
log.retention.bytes和log.retention.minutes任意一个达到要求,都会执行删除,会被topic创建时的指定参数覆盖
log.retention.bytes=-1
日志清除程序检查日志是否满足被删除的频率(以毫秒为单位) log.cleanup.policy中设置的策略
log.retention.check.interval.ms=5minutes
是否开启日志压缩
log.cleaner.enable=false
日志压缩运行的线程数
log.cleaner.threads =1
日志压缩时候处理的最大大小
log.cleaner.io.max.bytes.per.second=None
日志压缩去重时候的缓存空间 ,在空间允许的情况下,越大越好
log.cleaner.dedupe.buffer.size=50010241024
日志清理时候用到的IO块大小 一般不需要修改
log.cleaner.io.buffer.size=512*1024
日志清理中hash表的扩大因子 一般不需要修改
log.cleaner.io.buffer.load.factor =0.9
当没有日志要清理时,休眠的时间
log.cleaner.backoff.ms =15000
日志清理的频率控制,越大意味着更高效的清理,同时会存在一些空间上的浪费,会被topic创建时的指定参数覆盖
log.cleaner.min.cleanable.ratio=0.5
对于压缩的日志保留的最长时间,也是客户端消费消息的最长时间,同log.retention.minutes的区别在于一个控制未压缩数据,一个控制压缩后的数据。会被topic创建时的指定参数覆盖
log.cleaner.delete.retention.ms =1day
对于segment日志的索引文件大小限制,会被topic创建时的指定参数覆盖
log.index.size.max.bytes =1010241024
当执行一个fetch操作后,需要一定的空间来扫描最近的offset大小,设置越大,代表扫描速度越快,但是也更好内存,一般情况下不需要搭理这个参数
log.index.interval.bytes =4096
log文件"sync"到磁盘之前累积的消息条数
因为磁盘IO操作是一个慢操作,但又是一个"数据可靠性"的必要手段,所以此参数的设置,需要在"数据可靠性"与"性能"之间做必要的权衡.
如果此值过大,将会导致每次"fsync"的时间较长(IO阻塞);如果此值过小,将会导致"fsync"的次数较多,这也意味着整体的client请求有一定的延迟.
物理server故障,将会导致没有fsync的消息丢失.
log.flush.interval.messages=None
检查是否需要固化到硬盘的时间间隔
log.flush.scheduler.interval.ms =3000
仅仅通过interval来控制消息的磁盘写入时机,是不足的.
此参数用于控制"fsync"的时间间隔,如果消息量始终没有达到阀值,但是离上一次磁盘同步的时间间隔达到阀值,也将触发.
log.flush.interval.ms = None
文件在索引中清除后保留的时间 一般不需要去修改
log.delete.delay.ms =60000
控制上次固化硬盘的时间点,以便于数据恢复 一般不需要去修改
log.flush.offset.checkpoint.interval.ms =60000
TOPIC 相关##
是否允许自动创建topic ,若是false,就需要通过命令创建topic
auto.create.topics.enable =true
一个topic ,默认分区的replication个数 ,不得大于集群中broker的个数
default.replication.factor =1
每个topic的分区个数,若是在topic创建时候没有指定的话 会被topic创建时的指定参数覆盖
num.partitions =1
实例 --replication-factor 3 --partitions 1 --topic replicated-topic :名称为replicated-topic的topic有一个分区,分区被复制到三个broker上。
复制(Leader、replicas) 相关##
partition leader与replicas之间通讯时,socket的超时时间
controller.socket.timeout.ms =30000
partition leader与replicas数据同步时,消息的队列尺寸
controller.message.queue.size=10
replicas响应partition leader的最长等待时间,若是超过这个时间,就将replicas列入ISR(in-sync replicas),并认为它是死的,不会再加入管理中
replica.lag.time.max.ms =10000
如果follower落后与leader太多,将会认为此follower[或者说partition relicas]已经失效
通常,在follower与leader通讯时,因为网络延迟或者链接断开,总会导致replicas中消息同步滞后
如果消息滞后太多,leader将认为此follower网络延迟较大或者消息吞吐能力有限,将会把此replicas迁移到其他follower中.
在broker数量较少,或者网络不足的环境中,建议提高此值.
replica.lag.max.messages =4000
follower与leader之间的socket超时时间
replica.socket.timeout.ms=30*1000
leader复制时候的socket缓存大小
replica.socket.receive.buffer.bytes=64*1024
replicas每次获取数据的最大大小
replica.fetch.max.bytes =1024*1024
replicas同leader之间通信的最大等待时间,失败了会重试
replica.fetch.wait.max.ms =500
fetch的最小数据尺寸,如果leader中尚未同步的数据不足此值,将会阻塞,直到满足条件
replica.fetch.min.bytes =1
leader 进行复制的线程数,增大这个数值会增加follower的IO
num.replica.fetchers=1
每个replica检查是否将最高水位进行固化的频率
replica.high.watermark.checkpoint.interval.ms =5000
是否允许控制器关闭broker ,若是设置为true,会关闭所有在这个broker上的leader,并转移到其他broker
controlled.shutdown.enable =false
控制器关闭的尝试次数
controlled.shutdown.max.retries =3
每次关闭尝试的时间间隔
controlled.shutdown.retry.backoff.ms =5000
是否自动平衡broker之间的分配策略
auto.leader.rebalance.enable =false
leader的不平衡比例,若是超过这个数值,会对分区进行重新的平衡
leader.imbalance.per.broker.percentage =10
检查leader是否不平衡的时间间隔
leader.imbalance.check.interval.seconds =300
客户端保留offset信息的最大空间大小
offset.metadata.max.bytes
ZooKeeper 相关##
zookeeper集群的地址,可以是多个,多个之间用逗号分割 hostname1:port1,hostname2:port2,hostname3:port3
zookeeper.connect = localhost:2181
ZooKeeper的最大超时时间,就是心跳的间隔,若是没有反映,那么认为已经死了,不易过大
zookeeper.session.timeout.ms=6000
ZooKeeper的连接超时时间
zookeeper.connection.timeout.ms =6000
ZooKeeper集群中ZK follower可落后与leader多久。
zookeeper.sync.time.ms =2000
Consumer配置
Producer配置
KAFKA常用运维命令总结
- TopicCommand
1.1.Topic创建
bin/kafka-topics.sh --create --bootstrap-server localhost:9092 --replication-factor 3 --partitions 3 --topic test
--replication-factor 3 副本数量,注意不能大于broker数量;如果不提供,则会用集群中默认配置
--partitions 3 分区数量,当创建或者修改topic的时候,用这个来指定分区数;如果创建的时候没有提供参数,则用集群中默认值; 注意如果是修改的时候,分区比之前小会有问题
1.2.删除Topic
bin/kafka-topics.sh --bootstrap-server localhost:9092 --delete --topic test
bin/kafka-topics.sh --bootstrap-server localhost:9092 --delete --topic "create_topic_byhand_zk."
.表示任意匹配除换行符 \n 之外的任何单字符。要匹配 . ,请使用 . 。
··:匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 。
删除任意Topic (慎用)
bin/kafka-topics.sh --bootstrap-server localhost:9092 --delete --topic ".?" 更多的用法请参考正则表达式
1.3.Topic分区扩容
zk方式(不推荐)
bin/kafka-topics.sh --zookeeper localhost:2181 --alter --topic topic1 --partitions 2
kafka版本 >= 2.2 支持下面方式(推荐)
单个Topic扩容
bin/kafka-topics.sh --bootstrap-server broker_host:port --alter --topic test_create_topic1 --partitions 4
批量扩容 (将所有正则表达式匹配到的Topic分区扩容到4个)
bin/kafka-topics.sh --topic ".?" --bootstrap-server 172.23.248.85:9092 --alter --partitions 4
1.4.查询Topic描述
1.查询单个Topic
bin/kafka-topics.sh --topic test --bootstrap-server xxxx:9092 --describe --exclude-internal
2.批量查询Topic(正则表达式匹配,下面是查询所有Topic)
bin/kafka-topics.sh --topic ".?" --bootstrap-server xxxx:9092 --describe --exclude-internal
支持正则表达式匹配Topic,只需要将topic 用双引号包裹起来
1.5.查询Topic列表
1.查询所有Topic列表
sh bin/kafka-topics.sh --bootstrap-server xxxxxx:9092 --list --exclude-internal
2.查询匹配Topic列表(正则表达式)
sh bin/kafka-topics.sh --bootstrap-server xxxxxx:9092 --list --exclude-internal --topic "test_create_.*" - ConfigCommand
2.1 查询配置
展示关于Topic的动静态配置
1.查询单个Topic配置(只列举动态配置)
sh bin/kafka-configs.sh --describe --bootstrap-server xxxxx:9092 --topic test_create_topic
或者
sh bin/kafka-configs.sh --describe --bootstrap-server 172.23.248.85:9092 --entity-type topics --entity-name test_create_topic2.
查询所有Topic配置(包括内部Topic)(只列举动态配置)
sh bin/kafka-configs.sh --describe --bootstrap-server 172.23.248.85:9092 --entity-type topics
3.查询Topic的详细配置(动态+静态)
只需要加上一个参数--all
2.2 增删改 配置 –alter
--alter
删除配置: --delete-config k1=v1,k2=v2
添加/修改配置: --add-config k1,k2
选择类型: --entity-type
类型名称: --entity-name
Topic添加/修改动态配置
--add-config
sh bin/kafka-configs.sh --bootstrap-server xxxxx:9092 --alter --entity-type topics --entity-name test_create_topic1 --add-config file.delete.delay.ms=222222,retention.ms=999999
Topic删除动态配置
--delete-config
sh bin/kafka-configs.sh --bootstrap-server xxxxx:9092 --alter --entity-type topics --entity-name test_create_topic1 --delete-config file.delete.delay.ms,retention.ms
其他配置同理,只需要类型改下--entity-type
类型有: (topics/clients/users/brokers/broker- loggers)
哪些配置可以修改 请看最后面的附件:ConfigCommand 的一些可选配置 - 副本扩缩、分区迁移、跨路径迁移 kafka-reassign-partitions
- Topic的发送kafka-console-producer.sh
4.1 生产无key消息
生产者
./bin/kafka-console-producer.sh --broker-list 192.168.173.20:19092,192.168.173.22:19093,192.168.173.22:19094 --topic test --producer.config config/producer.properties
4.2 生产有key消息
加上属性--property parse.key=true
生产者
bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test --producer.config config/producer.properties --property parse.key=true
默认消息key与消息value间使用“Tab键”进行分隔,所以消息key以及value中切勿使用转义字符(\t)
5. Topic的发送kafka-console-producer.sh
-
新客户端从头消费--from-beginning (注意这里是新客户端,如果之前已经消费过了是不会从头消费的)
下面没有指定客户端名称,所以每次执行都是新客户端都会从头消费
sh bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --from-beginning -
正则表达式匹配topic进行消费--whitelist
消费所有的topic
sh bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --whitelist '.'
消费所有的topic,并且还从头消费
sh bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --whitelist '.' --from-beginning
3.显示key进行消费--property print.key=true
sh bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --property print.key=true -
指定分区消费--partition 指定起始偏移量消费--offset
sh bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --partition 0 --offset 100 -
给客户端命名--group
注意给客户端命名之后,如果之前有过消费,那么--from-beginning就不会再从头消费了
sh bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --group test-group
启动多个进程代表启动多个消费者在test-group组内
查看消费组消费情况 -
添加客户端属性--consumer-property
这个参数也可以给客户端添加属性,但是注意 不能多个地方配置同一个属性,他们是互斥的;比如在下面的基础上还加上属性--group test-group 那肯定不行
sh bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --consumer-property group.id=test-consumer-group -
添加客户端属性--consumer.config
跟--consumer-property 一样的性质,都是添加客户端的属性,不过这里是指定一个文件,把属性写在文件里面, --consumer-property 的优先级大于 --consumer.config
sh bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --consumer.config config/consumer.properties -
Kafka消息堆积判读和处理
查看消费组消息堆积情况
bin/kafka-consumer-groups.sh -bootstrap-server 192.168.173.20:19092,192.168.173.22:19093,192.168.173.22:19094 --describe --group test-group
LogStartOffset:表示一个Partition的起始位移,初始为0,虽然消息的增加以及日志清除策略的影响,这个值会阶段性的增大。
ConsumerOffset:消费位移,表示Partition的某个消费者消费到的位移位置。 HighWatermark:简称HW,代表消费端所能“观察”到的Partition的最高日志位移,HW大于等于ConsumerOffset的值。
LogEndOffset:简称LEO, 代表Partition的最高日志位移,其值对消费者不可见。
重点关注 LAG=HW-consumer_offset 为0代表没有消息堆积
消息堆积处理方式
一般情况是增加分组 增加消费线程数
增加消费者的数量可以显著提高消息的消费速度,从而减少消息堆积的可能性。此外,可以通过异步消费(例如使用线程池)来进一步提升消费效率。
Kafka通过分区(Partition)来管理消息,每个分区是一个有序的消息队列。合理设置分区数是关键。如果分区数设置过少,可能会导致消息堆积;而过多的分区则可能增加系统的复杂性。因此,需要根据实际业务需求和系统负载情况来调整分区数。
批量处理消息可以提高系统的吞吐量。在消费过程中,尽量减少每次消费的时间,避免不必要的网络调用和数据库读取操作。
在发送和接收消息时启用消息压缩(如Gzip),可以有效减少传输的数据量,从而提高消息处理的效率
- kafka-preferred-replica-election重新选举
6.1 kafka-preferred-replica-election 是一个用于重新选举Kafka集群中优先副本(Preferred Replica)的命令。这个命令主要用于在创建主题时尽量将分区均匀分配到所有broker上,并且将副本也均匀分配在不同的broker上
该命令通常通过以下方式使用:
./kafka-preferred-replica-election.sh --zookeeper
其中,--zookeeper 参数指定了Zookeeper的地址,例如 localhost:2181 或者具体的Zookeeper主机名和端口。
运行此命令可以强制对所有分区进行优先副本选举,以确保负载均衡。如果启用了自动负载均衡(auto.leader.rebalance.enable ),控制器会定期检查不平衡情况并自动重新平衡领导者,但为了避免不必要的集群负载,只有在检测到显著的负载不均时才会进行自动重新平衡。
此外,Kafka Manager等工具也可以用来执行优先副本选举,只需指定Zookeeper列表和Kafka版本即可添加Cluster。
需要注意的是,在某些情况下,手动运行 kafka-preferred-replica-election 可能会遇到一些异常,这需要根据具体错误信息进行调试和解决。
总之,kafka-preferred-replica-election 是一个重要的工具,用于维护Kafka集群的高可用性和负载均衡,建议定期运行以确保系统的稳定运行
6.3 设置配置文件批量指定topic和分区进行Leader重选举
先配置leader-election.json文件
{
"partitions": [
{
"topic": "test_create_topic4",
"partition": 1
},
{
"topic": "test_create_topic4",
"partition": 2
}
]
}
./kafka-preferred-replica-election.sh --zookeeper localhost:12913/kafka --path-to-json-file /path/to/your/json/file.json - 持续批量推送消息kafka-verifiable-producer.sh
单次发送100条消息--max-messages 100
一共要推送多少条,默认为-1,-1表示一直推送到进程关闭位置
sh bin/kafka-verifiable-producer.sh --topic test_create_topic4 --bootstrap-server localhost:9092 --max-messages 100
每秒发送最大吞吐量不超过消息 --throughput 100
推送消息时的吞吐量,单位messages/sec。默认为-1,表示没有限制
sh bin/kafka-verifiable-producer.sh --topic test_create_topic4 --bootstrap-server localhost:9092 --throughput 100
发送的消息体带前缀--value-prefix
sh bin/kafka-verifiable-producer.sh --topic test_create_topic4 --bootstrap-server localhost:9092 --value-prefix 666
注意--value-prefix 666必须是整数,发送的消息体的格式是加上一个 点号. 例如: 666.
其他参数:
--producer.config CONFIG_FILE 指定producer的配置文件
--acks ACKS 每次推送消息的ack值,默认是-1 - 持续批量拉取消息kafka-verifiable-consumer
持续消费
sh bin/kafka-verifiable-consumer.sh --group-id test_consumer --bootstrap-server localhost:9092 --topic test_create_topic4
单次最大消费10条消息--max-messages 10
sh bin/kafka-verifiable-consumer.sh --group-id test_consumer --bootstrap-server localhost:9092 --topic test_create_topic4 --max-messages 10 - 生产者压力测试kafka-producer-perf-test.sh
- 发送1024条消息--num-records 100并且每条消息大小为1KB--record-size 1024 最大吞吐量每秒10000条--throughput 100
./kafka-producer-perf-test.sh --topic test --record-size 100 --num-records 100000 --throughput -1 --producer-props bootstrap.servers=192.168.173.20:19092,192.168.173.22:19093,192.168.173.22:19094
主要关注吞吐量
你可以通过LogIKM查看分区是否增加了对应的数据大小
从LogIKM 可以看到发送了1024条消息; 并且总数据量=1M; 1024条*1024byte = 1M; - 用指定消息文件--payload-file发送100条消息最大吞吐量每秒100条--throughput 100
先配置好消息文件batchmessage.txt
然后执行命令
发送的消息会从batchmessage.txt里面随机选择; 注意这里我们没有用参数--payload-delimeter指定分隔符,默认分隔符是\n换行;
bin/kafka-producer-perf-test.sh --topic test --num-records 100 –throughput 100 --producer-props bootstrap.servers=192.168.173.20:19092,192.168.173.22:19093,192.168.173.22:19094 --payload-file config/batchmessage.txt
验证消息,可以通过 LogIKM 查看发送的消息 - 消费者压力测试kafka-consumer-perf-test.sh
消费100条消息--messages 100
./kafka-consumer-perf-test.sh --broker-list 192.168.173.20:19092,192.168.173.22:19093,192.168.173.22:19094 --topic=test --fetch-size 10000 -messages 100000 --threads 1
主要关注吞吐量
sh bin/kafka-consumer-perf-test.sh -topic test_create_topic4 --bootstrap-server localhost:9090 --messages 100 - 删除指定分区的消息kafka-delete-records.sh
删除指定topic的某个分区的消息删除至offset为1024
先配置json文件offset-json-file.json
{"partitions":
[{"topic": "test1", "partition": 0,
"offset": 1024}],
"version":1
}
在执行命令
sh bin/kafka-delete-records.sh --bootstrap-server 172.23.250.249:9090 --offset-json-file config/offset-json-file.json
验证 通过 LogIKM 查看发送的消息
从这里可以看出来,配置"offset": 1024 的意思是从最开始的地方删除消息到 1024的offset; 是从最前面开始删除的 - 查看Broker磁盘信息
查询指定topic磁盘信息--topic-list topic1,topic2
sh bin/kafka-log-dirs.sh --bootstrap-server xxxx:9090 --describe --topic-list test2
查询指定Broker磁盘信息--broker-list 0 broker1,broker2
sh bin/kafka-log-dirs.sh --bootstrap-server xxxxx:9090 --describe --topic-list test2 --broker-list 0
如果你觉得通过命令查询磁盘信息比较麻烦,你也可以通过 LogIKM查看 - 消费者组管理 kafka-consumer-groups.sh
- 查看消费者列表--list
sh bin/kafka-consumer-groups.sh --bootstrap-server xxxx:9090 --list
先调用MetadataRequest拿到所有在线Broker列表
再给每个Broker发送ListGroupsRequest请求获取 消费者组数据 - 查看消费者组详情--describe
DescribeGroupsRequest
查看消费组详情--group 或 --all-groups
查看指定消费组详情--group
sh bin/kafka-consumer-groups.sh --bootstrap-server xxxxx:9090 --describe --group test2_consumer_group
undefined
查看所有消费组详情--all-groups
sh bin/kafka-consumer-groups.sh --bootstrap-server xxxxx:9090 --describe --all-groups
查看该消费组 消费的所有Topic、及所在分区、最新消费offset、Log最新数据offset、Lag还未消费数量、消费者ID等等信息
查询消费者成员信息--members
所有消费组成员信息
sh bin/kafka-consumer-groups.sh --describe --all-groups --members --bootstrap-server xxx:9090
指定消费组成员信息
sh bin/kafka-consumer-groups.sh --describe --members --group test2_consumer_group --bootstrap-server xxxx:9090
查询消费者状态信息--state
所有消费组状态信息
sh bin/kafka-consumer-groups.sh --describe --all-groups --state --bootstrap-server xxxx:9090
指定消费组状态信息
sh bin/kafka-consumer-groups.sh --describe --state --group test2_consumer_group --bootstrap-server xxxxx:9090 - 删除消费者组--delete
DeleteGroupsRequest
删除消费组--delete
删除指定消费组--group
sh bin/kafka-consumer-groups.sh --delete --group test2_consumer_group --bootstrap-server xxxx:9090
删除所有消费组--all-groups
sh bin/kafka-consumer-groups.sh --delete --all-groups --bootstrap-server xxxx:9090
PS: 想要删除消费组前提是这个消费组的所有客户端都停止消费/不在线才能够成功删除;否则会报下面异常 - 重置消费组的偏移量 --reset-offsets
能够执行成功的一个前提是 消费组这会是不可用状态;
下面的示例使用的参数是: --dry-run ;这个参数表示预执行,会打印出来将要处理的结果;
等你想真正执行的时候请换成参数--excute ;
下面示例 重置模式都是 --to-earliest 重置到最早的;
请根据需要参考下面 相关重置Offset的模式 换成其他模式;
重置指定消费组的偏移量 --group
重置指定消费组的所有Topic的偏移量--all-topic
sh bin/kafka-consumer-groups.sh --reset-offsets --to-earliest --group test2_consumer_group --bootstrap-server xxxx:9090 --dry-run --all-topic
重置指定消费组的指定Topic的偏移量--topic
sh bin/kafka-consumer-groups.sh --reset-offsets --to-earliest --group test2_consumer_group --bootstrap-server xxxx:9090 --dry-run --topic test2
重置所有消费组的偏移量 --all-group
重置所有消费组的所有Topic的偏移量--all-topic
sh bin/kafka-consumer-groups.sh --reset-offsets --to-earliest --all-group --bootstrap-server xxxx:9090 --dry-run --all-topic
重置所有消费组中指定Topic的偏移量--topic
sh bin/kafka-consumer-groups.sh --reset-offsets --to-earliest --all-group --bootstrap-server xxxx:9090 --dry-run --topic test2
--reset-offsets 后面需要接重置的模式
先配置cvs文档
格式为: Topic:分区号: 重置目标偏移量```cvs
test2,0,100
test2,1,200
test2,2,300
执行命令
sh bin/kafka-consumer-groups.sh --reset-offsets --group test2_consumer_group --bootstrap-server xxxx:9090 --dry-run --from-file config/reset-offset.csv
5. 删除偏移量delete-offsets
<font color=red>能够执行成功的一个前提是 消费组这会是不可用状态;</font>
偏移量被删除了之后,Consumer Group下次启动的时候,会从头消费;
sh bin/kafka-consumer-groups.sh --delete-offsets --group test2_consumer_group2 --bootstrap-server XXXX:9090 --topic test2
15. Kafka副本同步原理
引用链接: https://segmentfault.com/a/1190000021873842
follower副本同步的过程大致就是向leader发起获取数据请求,leader给出响应并返回数据,然后follower副本更新自己的HW和LEO值,并且follower的请求数据过程中,leader也会更新自己的HW和LEO,在这里注意一下,leder副本除了维护自己的HW和LEO值以外,还维护了一份各个follower副本的LEO值,这里我们就暂时叫他RemoteLEO。
再总结一下,follower副本的同步过程无非就是从leader副本获取数据写入log,然后更新HW和LEO的值。
HW、LEO更新机制
假设我们新的kafka集群刚刚建立,没有任何生产者,没有消息,follower此时向leader发起fetch数据的请求,leader发现没有数据会将该请求暂时存在purgatory(用于临时存放暂时无法被处理的请求,但是这些请求有超时设置,如果超时则强制完成)中。Leader和follower的初始状态如下
LEO、HW更新关键点
Leader
Leader LEO:消息写入底层log后便发生更新
Leader RemoteLEO:需要比较本地的RemoteLEO和fetch offset的值,两者取较小
Leader HW:需要比较RemoteLEO和LEO的值,两者取较小
更新顺序:有数据写入底层日志LEO更新,其次会尝试更新RemoteLEO,再尝试更新HW
Follower
Follower LEO:取决于response中是否有日志数据
Follower HW:response中的HW和LEO进行比较,两者取较小
KAFKA面试问题总结(你不知道的事情)
为什么选择Kafka作为消息队列?
目前市面上比较主流的开源消息队列包括 Kafka、RocketMQ、RabbtiMQ 等,要从中选择出一个最合适的消息队列,则需要根据公司的业务,并结合功能、性能、可靠性、可用性以及社区和生态等方面进行综合考量。
从这些角度来说,Kafka 很好的满足了这些需求:
功能丰富:Kafka 提供了丰富的功能,其特性包括消息堆积、可靠性、消息顺序性、幂等性和消息回溯等,可以很好的满足各种业务场景的需求。
高性能:作为为大数据而生的消息队列,Kafka 在处理高吞吐量和大规模数据传输方面表现非凡,具有很高的性能表现。
可靠性:Kafka 通过多副本机制保证消息的可靠性,包括生产者端、Broker 和消费者端,确保消息不丢失并提供高度的数据一致性。
高可用性:Kafka 通过分区多副本机制提升容灾能力,即使集群中的某个 Broker 失效,系统仍然能够保持高可用性。
社区和生态:Kafka 拥有活跃的社区和丰富的生态系统,用户可以及时获取支持和反馈,同时有大量开源运维工具和其他大数据组件对 Kafka 提供支持和集成。
1. 功能
消息队列通常用于两个业务场景:
应用解耦:在许多情况下,多个系统可能需要同一份数据。如果由发送方直接调用所有依赖系统,随着系统数量的增加,发送方代码需要频繁修改。而使用消息队列可以将发送方和接收方解耦。发送方将数据放入消息队列,依赖方根据实际需求按需进行订阅消费即可。
流量削峰:传统的跨服务调用通常是实时的,即上游发起请求,下游需要立即处理并响应。然而,在某些特殊的业务场景中 —— 例如秒杀或抢购 —— 可能会出现流量激增的情况,此时下游服务可能无法承受如此大的请求量,导致大量请求失败。此时,消息队列就可以作为缓冲区来协调生产和消费速度。当下游的消费能力跟不上时,上游的消息可以暂存在消息队列中,避免直接导致下游服务崩溃。
而作为一个老牌的消息队列,Kafka 的很多特性都可以很好的支持这两种场景:
消息堆积:Kafka 可以处理大量的消息堆积,适用于流量削峰场景。
可靠性:Kafka 支持消息的持久化存储,并且还有着多副本机制来保证消息的可靠性,即使在节点故障的情况下也不会丢失消息。
单分区消息顺序性:Kafka 保证在单个分区内消息的顺序性,适用于需要顺序处理的场景。
单分区单会话的幂等:Kafka 支持生产者的幂等性,即在同一会话中,同一分区的消息即使重复发送也只会被处理一次,确保消息处理的准确性。
消息回溯:Kafka 允许消费端根据需求回溯到任意时间点重新消费消息,适用于数据恢复和重新处理的场景。
2. 性能
作为一款为大数据而生的消息队列,Kafka 在处理高吞吐量和大规模数据传输方面表现非凡,它的单机 TPS 在几十万级别,而集群环境可以轻易达到百万级别。
不过,与 RabbitMQ 或 RocketMQ 相比,Kafka 其实在某些功能方面是有所欠缺的,例如 Kafka 没有死信队列和延迟消息,因此在使用时一些功能无法做到开箱即用。然而 Kafka 的性能优势可以在一定程度上弥补这些不足,毕竟虽然我们总会希望有一款消息队列功能与性能兼顾,但是有时候二者确实不可兼得,这种情况下我们还是会以性能优先。
3. 可靠性
消息队列的可靠性通常指的是对消息不丢失的保障程度,这是消息队列最重要的指标之一。Kafka 则从生产者、Broker 和消费者三个方面一起保证了其可靠性。
3.1. 生产者
Kafka 在生产者端提供了可选的三种 ACK 策略,每种策略都对应着不同的可靠级别,我们可以根据自己的业务需要进行选择:
0:生产者发送过来的数据,不需要等数据落盘应答。该方式可靠性差,效率高。
1(默认):生产者发送过来的数据,Leader 副本收到数据后应答(不包含 ISR 集合中的其他副本)。该方式可靠性中等,效率中等。
-1:生产者发送过来的数据,Leader 和 ISR 集合里面的所有节点收齐数据后应答。该方式可靠性高,效率低。
在生产环境中,0 方式基本不用; 1 方式一般用于传输普通日志,允许丢个别数据;-1 方式一般用于对可靠性要求比较高的场景。
关于 ISR 集合,你可以先简单的认为它是一个由 Leader 副本和有效 Follower 副本共同组成一个集合。
3.2. Broker
当消息到达 Broker 时,首先会存储到 Page Cache,然后通过操作系统的刷盘机制将数据持久化到硬盘上。此外,在集群环境中,Kafka 还会通过数据同步机制,将数据同步到分布在多个节点的副本中,并保证它们的一致性。这些措施都提高了消息的可靠性。
3.3. 消费者
消费者端的可靠性通过消息的 Commit 机制来保证。与生产者端一样,Kafka 也提供了两种 Commit 策略供我们根据业务场景进行选择:
手动 Commit:消费者在处理完消息后再提交,从而确保消息处理的可靠性。
自动 Commit 则可以简化操作,但可能在某些情况下导致消息的重复处理或丢失。
4. 高可用
可用性是指系统无故障运行的时间百分比,通常用几个 "9" 来衡量(如 99.9%、99.99%)。Kafka 通过引入分区多副本(Replica)机制提升容灾能力,从而实现高可用性。
简单来说,在集群环境下,Kafka 会为同一个分区内创建多个副本,并将这些副本分布在不同的 Broker 节点上。其中,这些副本又会分为处理所有的读写请求 Leader 副本和从 Leader 副本同步消息 Follower 副本,它们最终会在集群中形成一主多从的关系。
Kafka 使用的索引是怎么设计的?
Kafka 中使用了稀疏索引;
index 索引的第一列是offset 的相对文件的位置,第二列是这条数据在文件中的物理偏移量。
kafka如何确定当前能读到哪一条消息?
标识共有7条消息,offset (消息偏移量)分别是0~6
0 代表这个日志文件的开始
HW(High Watermark) 为4,0~3 代表这个日志文件可以消费的区间,消费者只能消费到这四条消息
LEO 代表即将要写入消息的偏移量 offset
分区 ISR 集合中的每个副本都会维护自己的 LEO,而 ISR 集合中最小的LEO 即为分区的 HW
kafka发送消息的分区策略有哪些
1.轮询:依次将消息发送该topic下的所有分区,如果在创建消息的时候 key 为 null,Kafka 默认采用这种策略。
2.key 指定分区:在创建消息是 key 不为空,并且使用默认分区器,Kafka 会将 key 进行 hash,然后根据hash值映射到指定的分区上。这样的好处是 key 相同的消息会在一个分区下,Kafka 并不能保证全局有序,但是在每个分区下的消息是有序的,按照顺序存储,按照顺序消费。在保证同一个 key 的消息是有序的,这样基本能满足消息的顺序性的需求。但是如果 partation 数量发生变化,那就很难保证 key 与分区之间的映射关系了。
3.自定义策略:实现 Partitioner 接口就能自定义分区策略。
4.指定 Partiton 发送
Kafka 的可靠性是怎么保证的?
1.acks
这个参数用来指定分区中有多少个副本收到这条消息,生产者才认为这条消息是写入成功的,这个参数有三个值:
1.acks = 1,默认为1。生产者发送消息,只要 leader 副本成功写入消息,就代表成功。这种方案的问题在于,当返回成功后,如果 leader 副本和 follower 副本还没有来得及同步,leader 就崩溃了,那么在选举后新的 leader 就没有这条消息,也就丢失了。
2.acks = 0。生产者发送消息后直接算写入成功,不需要等待响应。这个方案的问题很明显,只要服务端写消息时出现任何问题,都会导致消息丢失。
3.acks = -1 或 acks = all。生产者发送消息后,需要等待 ISR 中的所有副本都成功写入消息后才能收到服务端的响应。毫无疑问这种方案的可靠性是最高的,但是如果 ISR 中只有leader 副本,那么就和 acks = 1 毫无差别了。
2.消息发送的方式
第6问中我们提到了生产者发送消息有三种方式,发完即忘,同步和异步。我们可以通过同步或者异步获取响应结果,失败做重试来保证消息的可靠性。
3.手动提交位移
默认情况下,当消费者消费到消息后,就会自动提交位移。但是如果消费者消费出错,没有进入真正的业务处理,那么就可能会导致这条消息消费失败,从而丢失。我们可以开启手动提交位移,等待业务正常处理完成后,再提交offset。
4.通过副本 LEO 来确定分区 HW
分区再分配是做什么的?解决了什么问题?
分区再分配主要是用来维护 kafka 集群的负载均衡
既然是分区再分配,那么 kafka 分区有什么问题呢?
问题1:当集群中的一个节点下线了
如果该节点的分区是单副本的,那么分区将会变得不可用
如果是多副本的,就会进行 leader 选举,在其他机器上选举出新的 leader
kafka 并不会将这些失效的分区迁移到其他可用的 broker 上,这样就会影响集群的负载均衡,甚至也会影响服务的可靠性和可用性
问题2:集群新增 broker 时,只有新的主题分区会分配在该 broker 上,而老的主题分区不会分配在该 broker 上,就造成了老节点和新节点之间的负载不均衡。
为了解决该问题就出现了分区再分配,它可以在集群扩容,broker 失效的场景下进行分区迁移。
分区再分配的原理就是通化控制器给分区新增新的副本,然后通过网络把旧的副本数据复制到新的副本上,在复制完成后,将旧副本清除。 当然,为了不影响集群正常的性能,在此复制期间还会有一系列保证性能的操作,比如复制限流。
Kafka Partition 副本 leader 是怎么选举的?
常用选主机制的缺点:
split-brain (脑裂):
这是由ZooKeeper的特性引起的,虽然ZooKeeper能保证所有Watch按顺序触发,但是网络延迟,并不能保证同一时刻所有Replica“看”到的状态是一样的,这就可能造成不同Replica的响应不一致,可能选出多个领导“大脑”,导致“脑裂”。
herd effect (羊群效应):
如果宕机的那个Broker上的Partition比较多, 会造成多个Watch被触发,造成集群内大量的调整,导致大量网络阻塞。
ZooKeeper负载过重:
每个Replica都要为此在ZooKeeper上注册一个Watch,当集群规模增加到几千个Partition时ZooKeeper负载会过重。
优势:
Kafka的Leader Election方案解决了上述问题,它在所有broker中选出一个controller,所有Partition的Leader选举都由controller决定。 controller会将Leader的改变直接通过RPC的方式(比ZooKeeper Queue的方式更高效)通知需为此作为响应的Broker。
没有使用 zk,所以无 2.3 问题;也没有注册 watch无 2.2 问题 leader 失败了,就通过 controller 继续重新选举即可,所以克服所有问题。
Kafka partition leader的选举:
由 controller 执行:
从Zookeeper中读取当前分区的所有ISR(in-sync replicas)集合
调用配置的分区选择算法选择分区的leader
上面五种分区算法都是选择PreferredReplica(优先副本选举)作为当前Partition的leader。区别仅仅是选择leader之后的操作有所不同。
分区数越多越好吗?吞吐量就会越高吗?
般类似于这种问题的答案,都是持否定态度的。
但是可以说,在一定条件下,分区数的数量是和吞吐量成正比的,分区数和性能也是成正比的。
那么为什么说超过了一定限度,就会对性能造成影响呢?原因如下:
1.客户端/服务器端需要使用的内存就越多
服务端在很多组件中都维护了分区级别的缓存,分区数越大,缓存成本也就越大。
消费端的消费线程数是和分区数挂钩的,分区数越大消费线程数也就越多,线程的开销成本也就越大
生产者发送消息有缓存的概念,会为每个分区缓存消息,当积累到一定程度或者时间时会将消息发送到分区,分区越多,这部分的缓存也就越大
2.文件句柄的开销
每个 partition 都会对应磁盘文件系统的一个目录。在 Kafka 的数据日志文件目录中,每个日志数据段都会分配两个文件,一个索引文件和一个数据文件。每个 broker 会为每个日志段文件打开一个 index 文件句柄和一个数据文件句柄。因此,随着 partition 的增多,所需要保持打开状态的文件句柄数也就越多,最终可能超过底层操作系统配置的文件句柄数量限制。
3.越多的分区可能增加端对端的延迟
Kafka 会将分区 HW 之前的消息暴露给消费者。分区越多则副本之间的同步数量就越多,在默认情况下,每个 broker 从其他 broker 节点进行数据副本复制时,该 broker 节点只会为此工作分配一个线程,该线程需要完成该 broker 所有 partition 数据的复制。
4.降低高可用性
在第 7 问我们提到了分区再分配,会将数据复制到另一份副本当中,分区数量越多,那么恢复时间也就越长,而如果发生宕机的 broker 恰好是 controller 节点时:在这种情况下,新 leader 节点的选举过程在 controller 节点恢复到新的 broker 之前不会启动。controller 节点的错误恢复将会自动地进行,但是新的 controller 节点需要从 zookeeper 中读取每一个 partition 的元数据信息用于初始化数据。例如,假设一个Kafka 集群存在 10000个partition,从 zookeeper 中恢复元数据时每个 partition 大约花费 2 ms,则 controller 的恢复将会增加约 20 秒的不可用时间窗口
kafka 为什么这么快?
顺序读写磁盘分为顺序读写与随机读写,基于磁盘的随机读写确实很慢,但磁盘的顺序读写性能却很高,kafka 这里采用的就是顺序读写。
Page Cache为了优化读写性能,Kafka 利用了操作系统本身的 Page Cache,就是利用操作系统自身的内存而不是JVM空间内存。
零拷贝Kafka使用了零拷贝技术,也就是直接将数据从内核空间的读缓冲区直接拷贝到内核空间的 socket 缓冲区,然后再写入到 NIC 缓冲区,避免了在内核空间和用户空间之间穿梭。
分区分段+索引Kafka 的 message 是按 topic分 类存储的,topic 中的数据又是按照一个一个的 partition 即分区存储到不同 broker 节点。每个 partition 对应了操作系统上的一个文件夹,partition 实际上又是按照segment分段存储的。通过这种分区分段的设计,Kafka 的 message 消息实际上是分布式存储在一个一个小的 segment 中的,每次文件操作也是直接操作的 segment。为了进一步的查询优化,Kafka 又默认为分段后的数据文件建立了索引文件,就是文件系统上的.index文件。这种分区分段+索引的设计,不仅提升了数据读取的效率,同时也提高了数据操作的并行度。
批量读写Kafka 数据读写也是批量的而不是单条的,这样可以避免在网络上频繁传输单个消息带来的延迟和带宽开销。假设网络带宽为10MB/S,一次性传输10MB的消息比传输1KB的消息10000万次显然要快得多。
批量压缩Kafka 把所有的消息都变成一个批量的文件,并且进行合理的批量压缩,减少网络 IO 损耗,通过 mmap 提高 I/O 速度,写入数据的时候由于单个
Partion是末尾添加所以速度最优;读取数据的时候配合 sendfile 进行直接读取。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!