Kafka调优

概述

调优是为了满足系统常见的非功能性需求。在众多的非功能性需求中,性能绝对是我们最关心的那一个。不同的
系统对性能有不同的诉求,比如对于数据库用户而言,性能意味着请求的响应时间,用户总是希望查询或更新请求能够被更快地处理完并返回。对 Kafka 而言,性能一般是指吞吐量和延时

  • 吞吐量:也就是 TPS,是指 Broker 端进程或 Client 端应用程序每秒能处理的字节数或消息数,这个值自然是越大越好
  • 延时:它表示从 Producer 端发送消息到 Broker 端持久化完成之间的时间间隔,也就是从Producer 发送消息到 Consumer 成功消费该消息的总时长,和 TPS 相反,我们通常希望延时越短越好

为了达到高吞吐低时延,将从以下4个方面进行调优:

  1. 应用程序层:它是指优化 Kafka 客户端应用程序代码。比如,使用合理的数据结构、缓存计算开销大的运算结果,抑或是复用构造成本高的对象实例等。这一层的优化效果最为明显,通常也是比较简单的
  2. 框架层:它指的是合理设置 Kafka 集群的各种参数。毕竟,直接修改 Kafka 源码进行调优并不容易,但根据实际场景恰当地配置关键参数的值,还是很容易实现的
  3. JVM 层:Kafka Broker 进程是普通的 JVM 进程,各种对 JVM 的优化在这里也是适用的。优化这一层的效果虽然比不上前两层,但有时也能带来巨大的改善效果
  4. 操作系统层:对操作系统层的优化很重要,但效果往往不如想象得那么好。与应用程序层的优化效果相比,它是有很大差距的

优化细节

操作系统调优

  • 禁用atime更新:挂载文件系统时使用mount -o noatime,避免记录文件最后访问时间,减少写操作。

  • 选择合适的文件系统:推荐使用ext4或XFS,XFS在高性能和高伸缩性方面表现更佳。

  • 设置swap空间:将swappiness值设置为1-10,防止OOM Killer随机杀掉进程。临时设置命令:sudo sysctl vm.swappiness=N,永久设置需修改/etc/sysctl.conf文件。

  • 调整重要参数:设置ulimit -nvm.max_map_count,例如将vm.max_map_count设置为655360,避免因打开文件过多或映射数过大导致的错误。

  • 优化页缓存:确保页缓存至少能容纳一个日志段的大小,默认值为1GB,以提升消费者程序的读取性能。

JVM 层调优

  • 调整堆大小:根据实际情况设置堆内存大小,以确保足够的内存分配给Kafka。

    一般JVM 堆大小设置成 6~8GB;如果需要精准值,可以查看 GC log,特别是关注 Full GC 之后堆上存活对象的总大小,然后把堆大小设置为该值的 1.5~2 倍。如果你发现 Full GC 没有被执行过,手动运行jmap -histo:live < pid > 就能人为触发 Full GC

  • 设置G1垃圾收集器:使用G1垃圾收集器,调整-XX:+G1HeapRegionSize=N参数增大区域大小,避免大对象分配问题。

  • 优化GC参数:通过合理配置GC参数,减少Full GC的频率和停顿时间。

Broker 端调优

  • 版本一致性:保持客户端和Broker的版本一致,确保可以利用Zero Copy等性能优化技术。

  • 合理配置参数:根据生产环境设置合适的Broker参数值,如num.network.threadsnum.io.threadslog.segment.bytes等。

应用层调优

  • 优化客户端代码:使用合理的数据结构、缓存计算结果、复用对象实例、不要频繁地创建 Producer 和 Consumer 对象实例等,提高客户端应用程序的性能。

  • 资源使用问题:用完及时关闭,如 Socket 连接、ByteBuffer 缓冲区等。不及时关闭的话,势必造成资源泄露。

  • 合理利用多线程来改善性能:Kafka 的 Java Producer 是线程安全的,你可以放心地在多个线程中共享同一个实例;而 Java Consumer 虽不是线程安全的:1.每个线程维护专属的 KafkaConsumer 实例,负责完整的消息获取、消息处理流程 2. 消费者程序使用单或多线程获取消息,同时创建多个消费线程执行消息处理逻。