【八股消消乐】Kafka集群 full GC 解决方案
😊你好,我是小航,一个正在变秃、变强的文艺倾年。
🔔本专栏《八股消消乐》旨在记录个人所背的八股文,包括Java/Go开发、Vue开发、系统架构、大模型开发、具身智能、机器学习、深度学习、力扣算法
等相关知识点,期待与你一同探索、学习、进步,一起卷起来叭!
目录
- 题目
- 答案
- 优化生产者
- 优化acks
- 优化批次
- 启用压缩
- 优化broker
- 优化 swap
- 优化网络读写缓冲区
- 优化磁盘 IO
- 优化主从同步
- 优化JVM
题目
💬技术栈:RocketMQ、Kafka、RabbitMQ
🔍简历内容:为解决xx业务高峰期响应时间长、客户端超时问题,通过优化acks、批次并将压缩算法从 Snappy 更换为 LZ4,提高生产者发送效率。经排查,kafka 集群触发了 full GC 之后,停顿时间就会很长,导致 Kafka 吞吐量显著下降,有时候还会导致 Kafka 认为主分区已经崩溃触发主从选举,通过调大 JVM 的堆,并且在堆很大的情况下,启用 G1 垃圾回收器解决了问题。
🚩面试问:Kafka 还有一些参数也对性能有影响,你能介绍一下你是如何优化的吗?
💡建议暂停思考10s,你有答案了嘛?如果你有不同题解,欢迎评论区留言、打卡。
答案
简历准备:
- 你维护的业务在使用消息队列的时候,后面优化措施中提到的
参数取值
都是多少? - 你们公司
消息队列的各个参数有没有被调过
?为什么调? - 你
是否遇到过和消息队列有关的 Bug
?如果有,那么怎么解决的? - 你
维护的业务使用消息队列时的 QPS 是多少
?
场景准备:高并发的消息队列使用场景,要求高效发送、高效消费,不然就会有问题,比如说出现消息积压或者生产者阻塞
的问题。
整理思路:从消息队列的生产者、broker 和消费者这三方出发。
优化生产者
优化acks
场景:有一个系统在一个高并发场景下会发送消息到 Kafka,结果发现这个接口在业务高峰的时候响应时间很长,客户端经常遇到超时的问题
。
排查后:写这段代码的人直接复制了已有的发送消息代码
,而原本人家的业务追求的是消息不丢,所以 acks 设置成了 all
。实际上这个业务并没有那么严格的消息不丢的要求,完全可以把 acks 设置为 0。
效果:这么一调整,整个接口的响应时间就显著下降了
,客户端那边也很少再出现超时的问题。
不过
追求消息不丢失的业务场景
就不能把 acks 设置为 0 或者 1,这时候就只能考虑别的优化手段,比如说优化批次。
优化批次
场景:生产者发送消息的性能问题。
排查后:因为发送性能太差,导致发送缓冲池已经满了
,阻塞了发送者。这个时候我们注意到其实发送速率还没有达到 broker 的阈值
,也就是说,broker 其实是处理得过来的。
解决方案:在这种情况下,最直接的做法就是加快发送速率
,也就是调大 batch.size 参数
,从原本的 100 调到了 500,就没有再出现过阻塞发送者的情况了。
当然,批次也不是说越大越好。
原因:批次大了的话,生产者这边丢失数据的可能性就比较大
。而且批次大小到了一个地步之后,性能瓶颈就变成了 broker 处理不过来了
,再调大批次大小是没有用的。最好的策略,还是通过压测来确定合适的批次大小
。
发送者被阻塞也可能是因为缓冲池太小
。
解决方案:调大缓冲池
。
原因:topic、分区太多,每一个分区都有一块缓冲池装着批量消息,导致缓冲池空闲缓冲区不足,这一类不是因为发送速率的问题导致的阻塞,就可以通过调大缓冲池来解决。
所以发送者阻塞要仔细分析,如果是发送速率的问题
,那么调大发送缓冲区是治标不治本的。如果发送速率没什么问题,确实就是因为缓冲池太小引起的
,就可以调大缓冲池。如果现实中,也比较难区别这两种情况,就可以考虑先调大批次试试,再调整缓冲池。
启用压缩
场景:Kafka 默认是不启用压缩的,为了进一步提高 Kafka 的吞吐量,我也开启了 Kafka 的压缩功能,使用了 LZ4 压缩算法
。【为了进一步提高 Kafka 的吞吐量,我将压缩算法从 Snappy 换到了 LZ4
。】
一定要做性能测试
优化broker
优化 swap
Kafka 是一个非常依赖内存的应用,所以可以调小 vm.swappniess 参数来优化内存。
为了优化 Kafka 的性能,可以调小 vm.swappiness
。比如说调整到 10,这样就可以充分利用内存
;也可以调整到 1,这个值在一些 linux 版本上是指进行最少的交换,但是不禁用交换
。目前我们公司用的就是 10。
为什么不直接禁用 swap 呢?
物理内存总是有限的,所以直接禁用的话容易遇到内存不足的问题。我们只是要尽可能优化内存,如果物理内存真的不够,那么使用交换区也比系统不可用好
。
优化网络读写缓冲区
Kafka 也是一个网络 IO 频繁的应用
,所以调整网络有关的读写缓冲区,效果也会更好。对应的参数有6个。
- net.core.rmem_default 和 net.core.wmem_default:Socket 默认读写缓冲区大小。
- net.core.rmem_max 和 net.core.wmem_max:Socket 最大读写缓冲区。
- net.ipv4.tcp_wmem 和 net.ipv4.tcp_rmem:TCP 读写缓冲区。它们的值由空格分隔的最小值、默认值、最大值组成。可以考虑调整为 4KB、64KB 和 2MB。
回答模板:调大读写缓冲区
。Scoket 默认读写缓冲区可以考虑调整到 128KB
;Socket 最大读写缓冲区可以考虑调整到 2MB
,TCP 的读写缓冲区最小值、默认值和最大值可以设置为 4KB、64KB 和 2MB
。不过这些值究竟多大,还是要根据 broker 的硬件资源来确定。
优化磁盘 IO
Kafka 也是一个磁盘 IO 密集的应用,所以可以从两个方向优化磁盘 IO。
(1)使用 XFS 作为文件系统
,它要比 EXT4 更加适合 Kafka。相比于 EXT4,XFS 性能更好。在同等情况下,使用 XFS 的 Kafka 要比 EXT4 性能高 5% 左右
。XFS还有别的优点,例如扩展性更好,支持更多、更大的文件。
(2)禁用 Kafka 用不上的 atime 功能
。
优化主从同步
总结: 都调大,它们的效果,就是为了让从分区一批次同步尽可能多的数据
。
从分区和主分区数据同步的过程受到了几个参数的影响。
- num.replica.fetchers:
从分区拉取数据的线程数量,默认是1
。你可以考虑设置成 3。 - replica.fetch.min.bytes:可以通过调大这个参数来
避免小批量同步数据
。 - replica.fetch.max.bytes:这个可以调大,比如说调整到 5m,但是不要小于 message.max.byte,也就是不要小于消息的最大长度。
- replica.fetch.wait.max.ms:如果主分区没有数据或者数据不够从分区的最大等待时间,可以考虑同步调大这个值和 replica.fetch.max.bytes。
首先调整从分区的同步数据线程数量
,比如说调整到 3,这样可以加快同步速率,但是也会给主分区和网络带宽带来压力。
其次是调整同步批次的最小和最大字节数量
,越大则吞吐量越高,所以都尽量调大。
最后也可以调整从分区的等待时间
,在一批次中同步尽可能多的数据。
不过调大到一定地步之后,瓶颈就变成了从分区来不及处理。或者调大到超过了消息的并发量,那么也没意义了。
Kafka 这种机制可以看作是典型的批量拉数据模型
。在这个模型里面,要着重考虑的就是多久拉一次,没有怎么办,一次拉多
少?在实现这种模型的时候,让用户根据自己的需要来设定参数是一个比较好的实践。
优化JVM
Kafka 是运行在 JVM 上
的,所以理论上来说任何优化 Java 性能的措施,对 Kafka 也一样有效果。
优化 JVM:
(1)首先就是考虑优化 GC,即优化垃圾回收
。而优化 GC 最重要的就是避免 full GC
。full GC 是指整个应用都停下来等待 GC 完成
。它会带来两方面影响。一方面是发送者如果设置 acks 为 1 或者 all,都会被阻塞,Kafka 吞吐量下降
。
(2)如果 full GC 时间太长
,那么主分区可能会被认为已经崩溃了,Kafka 会重新选择主分区
;而如果是从分区,那么它会被挪出 ISR
,进一步影响 acks 设置为 all 的发送者
。
基本的思路就是调大 JVM 的堆,并且在堆很大的情况下,启用 G1 垃圾回收器
。
之前我们的 Kafka 集群还出过 GC 引发的性能问题
。我们有一个 Kafka 的堆内存很大,有 8G,但是垃圾回收器还是用的 CMS
。触发了 full GC 之后,停顿时间就会很长,导致 Kafka 吞吐量显著下降
,并且有时候还会导致 Kafka 认为主分区已经崩溃,触发主从选举
。
优化思路:
(1)一个是考虑优化 CMS 本身
,比如说增大老年代,但是这个治标不治本,可以缓解问题,但是不能根治问题。
(2)直接切换到 G1 回收器
。G1 回收器果然表现得非常好,垃圾回收频率和停顿时间都下降了。
往期精彩专栏内容,欢迎订阅:
🔗【八股消消乐】20250711:浅尝Kafka性能优化
🔗【八股消消乐】20250630:消息队列优化—重复消费
🔗【八股消消乐】20250629:消息队列优化—消息丢失
🔗【八股消消乐】20250627:消息队列优化—消息积压
🔗【八股消消乐】20250625:消息队列优化—消息有序
🔗【八股消消乐】20250624:消息队列优化—延迟消息
🔗【八股消消乐】20250623:消息队列优化—系统架构设计
🔗【八股消消乐】20250622:Elasticsearch查询优化
🔗【八股消消乐】20250620:Elasticsearch优化—检索Labubu
🔗【八股消消乐】20250619:构建微服务架构体系—保证服务高可用
🔗【八股消消乐】20250615:构建微服务架构体系—链路超时控制
🔗【八股消消乐】20250614:构建微服务架构体系—实现制作库与线上库分离
🔗【八股消消乐】20250612:构建微服务架构体系—限流算法优化
🔗【八股消消乐】20250611:构建微服务架构体系—降级策略全总结
🔗【八股消消乐】20250610:构建微服务架构体系—熔断恢复抖动优化
🔗【八股消消乐】20250609:构建微服务架构体系—负载均衡算法如何优化
🔗【八股消消乐】20250608:构建微服务架构体系—服务注册与发现
🔗【八股消消乐】20250607:MySQL存储引擎InnoDB知识点汇总
🔗【八股消消乐】20250606:MySQL参数优化大汇总
🔗【八股消消乐】20250605:端午节产生的消费数据,如何分表分库?
🔗【八股消消乐】20250604:如何解决SQL线上死锁事故
🔗【八股消消乐】20250603:索引失效与优化方法总结
🔗【八股消消乐】20250512:慢SQL优化手段总结
🔗【八股消消乐】20250511:项目中如何排查内存持续上升问题
🔗【八股消消乐】20250510:项目中如何优化JVM内存分配?
🔗【八股消消乐】20250509:你在项目中如何优化垃圾回收机制?
🔗【八股消消乐】20250508:Java编译优化技术在项目中的应用
🔗【八股消消乐】20250507:你了解JVM内存模型吗?
🔗【八股消消乐】20250506:你是如何设置线程池大小?
🔗【八股消消乐】20250430:十分钟带背Duubo中大厂经典面试题
🔗【八股消消乐】20250429:你是如何在项目场景中选取最优并发容器?
🔗【八股消消乐】20250428:你是项目中如何优化多线程上下文切换?
🔗【八股消消乐】20250427:发送请求有遇到服务不可用吗?如何解决?
📌 [ 笔者 ] 文艺倾年
📃 [ 更新 ] 2025.7.11
❌ [ 勘误 ] /* 暂无 */
📜 [ 声明 ] 由于作者水平有限,本文有错误和不准确之处在所难免,本人也很想知道这些错误,恳望读者批评指正!