您的位置:首页 > 文旅 > 美景 > 调优Kafka,你做到了吗?

调优Kafka,你做到了吗?

2025/1/10 16:00:01 来源:https://blog.csdn.net/weixin_44302240/article/details/139240305  浏览:    关键词:调优Kafka,你做到了吗?

调优Kafka,你做到了吗?

调优目标

在做调优之前,我们必须明确优化 Kafka 的目标是什么。通常来说,调优是为了满足系统常见的非功能性需求。在众多的非功能性需求中,性能绝对是我们最关心的那一个。不同的系统对性能有不同的诉求,比如对于数据库用户而言,性能意味着请求的响应时间,用户总是希望查询或更新请求能够被更快地处理完并返回。

对 Kafka 而言,性能一般是指吞吐量和延时。

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

总之,高吞吐量、低延时是我们调优 Kafka 集群的主要目标,一会儿我们会详细讨论如何达成这些目标。在此之前,我想先谈一谈优化漏斗的问题。

优化漏斗

优化漏斗是一个调优过程中的分层漏斗,我们可以在每一层上执行相应的优化调整。总体来说,层级越靠上,其调优的效果越明显,整体优化效果是自上而下衰减的示:

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

基础性调优

接下来,我就来分别介绍一下优化漏斗的 4 个分层的调优。

操作系统调优

在操作系统层面,你最好在挂载(Mount)文件系统时禁掉 atime 更新。atime 的全称是 access time,记录的是文件最后被访问的时间。记录 atime 需要操作系统访问 inode 资源,而禁掉 atime 可以避免 inode 访问时间的写入操作,减少文件系统的写操作数。你可以执行 mount -o noatime 命令进行设置。

至于文件系统,我建议你至少选择 ext4 或 XFS。尤其是 XFS 文件系统,它具有高性能、高伸缩性等特点,特别适用于生产服务器。值得一提的是,在去年 10 月份的 Kafka 旧金山峰会上,有人分享了 ZFS 搭配 Kafka 的案例。该报告宣称 ZFS 多级缓存的机制能够帮助 Kafka 改善 I/O 性能,据说取得了不错的效果。如果你的环境中安装了 ZFS 文件系统,你可以尝试将 Kafka 搭建在 ZFS 文件系统上。

另外就是 swap 空间的设置。我个人建议将 swappiness 设置成一个很小的值,比如 1~10 之间,以防止 Linux 的 OOM Killer 开启随意杀掉进程。你可以执行 sudo sysctl vm.swappiness=N 来临时设置该值,如果要永久生效,可以修改 /etc/sysctl.conf 文件,增加 vm.swappiness=N,然后重启机器即可。

操作系统层面还有两个参数也很重要,它们分别是 ulimit -nvm.max_map_count。前者如果设置得太小,你会碰到 Too Many File Open 这类的错误,而后者的值如果太小,在一个主题数超多的 Broker 机器上,你会碰到 OutOfMemoryError:Map failed 的严重错误,因此,我建议在生产环境中适当调大此值,比如将其设置为 655360。具体设置方法是修改 /etc/sysctl.conf 文件,增加 vm.max_map_count=655360,保存之后,执行 sysctl -p 命令使它生效。

最后,不得不提的就是操作系统页缓存大小了,这对 Kafka 而言至关重要。在某种程度上,我们可以这样说:给 Kafka 预留的页缓存越大越好,最小值至少要容纳一个日志段的大小,也就是 Broker 端参数 log.segment.bytes 的值。该参数的默认值是 1GB。预留出一个日志段大小,至少能保证 Kafka 可以将整个日志段全部放入页缓存,这样,消费者程序在消费时能直接命中页缓存,从而避免昂贵的物理磁盘 I/O 操作。

JVM 层调优

说完了操作系统层面的调优,我们来讨论下 JVM 层的调优,其实,JVM 层的调优,我们还是要重点关注堆设置以及 GC 方面的性能。

  1. 设置堆大小

如何为 Broker 设置堆大小,这是很多人都感到困惑的问题。我来给出一个朴素的答案:将你的 JVM 堆大小设置成 6~8GB。在很多公司的实际环境中,这个大小已经被证明是非常合适的,你可以安心使用。如果你想精确调整的话,我建议你可以查看 GC log,特别是关注 Full GC 之后堆上存活对象的总大小,然后把堆大小设置为该值的 1.5~2 倍。如果你发现 Full GC 没有被执行过,手动运行 jmap -histo:live <pid> 就能人为触发 Full GC。

  1. GC 收集器的选择

我强烈建议你使用 G1 收集器,主要原因是方便省事,至少比 CMS 收集器的优化难度小得多。另外,你一定要尽力避免 Full GC 的出现。其实,不论使用哪种收集器,都要竭力避免 Full GC。在 G1 中,Full GC 是单线程运行的,它真的非常慢。如果你的 Kafka 环境中经常出现 Full GC,你可以配置 JVM 参数 -XX:+PrintAdaptiveSizePolicy,来探查一下到底是谁导致的 Full GC。

使用 G1 还很容易碰到的一个问题,就是大对象(Large Object),反映在 GC 上的错误,就是“too many humongous allocations”。所谓的大对象,一般是指至少占用半个区域(Region)大小的对象。举个例子,如果你的区域尺寸是 2MB,那么超过 1MB 大小的对象就被视为是大对象。要解决这个问题,除了增加堆大小之外,你还可以适当地增加区域大小,设置方法是增加 JVM 启动参数 -XX:+G1HeapRegionSize=N。默认情况下,如果一个对象超过了 N/2,就会被视为大对象,从而直接被分配在大对象区。如果你的 Kafka 环境中的消息体都特别大,就很容易出现这种大对象分配的问题。

Broker 端调优

我们继续沿着漏斗往上走,来看看 Broker 端的调优。

Broker 端调优很重要的一个方面,就是合理地设置 Broker 端参数值,以匹配你的生产环境。不过,后面我们在讨论具体的调优目标时再详细说这部分内容。这里我想先讨论另一个优化手段,即尽力保持客户端版本和 Broker 端版本一致。不要小看版本间的不一致问题,它会令 Kafka 丧失很多性能收益,比如 Zero Copy。下面我用一张图来说明一下。

图中蓝色的 Producer、Consumer 和 Broker 的版本是相同的,它们之间的通信可以享受 Zero Copy 的快速通道;相反,一个低版本的 Consumer 程序想要与 Producer、Broker 交互的话,就只能依靠 JVM 堆中转一下,丢掉了快捷通道,就只能走慢速通道了。因此,在优化 Broker 这一层时,你只要保持服务器端和客户端版本的一致,就能获得很多性能收益了。

应用程序层调优

应用程序层的调优主要集中在 Kafka 客户端的使用上,包括 Producer 和 Consumer 的优化。以下是一些关键的建议:

  1. 复用 Producer 和 Consumer 实例

    • 创建 Producer 和 Consumer 实例的成本较高,尤其是在高吞吐量的场景下频繁创建和销毁实例会导致性能问题。因此,尽量复用这些实例,并确保它们在整个应用程序生命周期中保持活跃。
  2. 合理使用多线程

    • 在高并发的应用中,合理使用多线程可以显著提升性能。对于 Producer,可以通过增加 max.in.flight.requests.per.connection 参数来提高并发度。对于 Consumer,可以使用多线程来处理消息,但要注意避免线程安全问题。通常,使用独立的线程池来处理消息是一个不错的选择。
  3. 批量发送

    • Producer 可以通过批量发送消息来提高吞吐量。可以调整 batch.size 参数来控制批量的大小。合理设置这个参数可以显著减少网络开销,提高消息发送的效率。
  4. 异步发送

    • Kafka Producer 默认是异步发送消息的,这种方式可以极大地提高吞吐量。确保 acks 参数设置为 1all,以平衡数据可靠性和性能。
  5. 调整缓冲区大小

    • Producer 和 Consumer 都有缓冲区设置。对于 Producer,可以调整 buffer.memory 参数来增加缓冲区大小,从而提高吞吐量。对于 Consumer,可以通过调整 fetch.min.bytesfetch.max.wait.ms 参数来优化数据的获取。
  6. 压缩

    • 使用消息压缩可以减少网络带宽的消耗,提高传输效率。Kafka 支持几种压缩算法,如 gzipsnappylz4。可以通过设置 compression.type 参数来启用压缩。
  7. 优化序列化和反序列化

    • 使用高效的序列化和反序列化方法可以减少 CPU 和内存的消耗。Kafka 提供了多种序列化方式,如 StringSerializerByteArraySerializer 等。根据你的数据类型选择合适的序列化方式。

调优吞吐量和延时的具体参数设置

为了更具体地调优 Kafka 的吞吐量和延时,你可以参考以下参数设置和最佳实践:

吞吐量优化
  1. Producer 端参数

    • batch.size:增大批量大小,默认值是 16KB,可以根据实际情况适当增大,比如 32KB 或 64KB。
    • linger.ms:设置为一个较大的值(如 5ms 或 10ms),可以增加批量发送的机会。
    • acks:设置为 1all,确保消息可靠性。
    • compression.type:启用压缩(如 snappylz4),减少网络带宽消耗。
  2. Consumer 端参数

    • fetch.min.bytes:设置为一个较大的值(如 1MB),确保每次获取足够多的数据。
    • fetch.max.wait.ms:设置为一个较大的值(如 500ms),增加批量获取的机会。
    • max.partition.fetch.bytes:根据你的消息大小和吞吐量需求,适当增大该值。
  3. Broker 端参数

    • num.network.threadsnum.io.threads:根据你的硬件配置适当增加线程数。
    • log.segment.bytes:设置为较大的值(如 1GB 或更大),减少日志段切换的频率。
    • log.retention.byteslog.retention.hours:根据你的存储需求和保留策略进行调整。
延时优化
  1. Producer 端参数

    • acks:设置为 1 可以减少等待时间,但可能会降低数据可靠性。
    • linger.ms:设置为 0 可以减少批量发送的延时,但可能会降低吞吐量。
    • max.in.flight.requests.per.connection:适当增加该值,可以提高并发度。
  2. Consumer 端参数

    • fetch.min.bytesfetch.max.wait.ms:设置为较小的值,可以减少获取数据的延时。
    • session.timeout.msheartbeat.interval.ms:根据你的消费速率和稳定性需求进行调整。
  3. Broker 端参数

    • num.replica.fetchers:适当增加该值,可以提高副本同步的效率。
    • replica.lag.time.max.ms:设置为一个较小的值,可以减少副本延时。
    • log.flush.interval.messageslog.flush.interval.ms:根据你的延时需求进行调整,确保消息尽快被持久化。

总结

通过上述几个层面的调优,你可以显著提升 Kafka 集群的性能,满足高吞吐量和低延时的需求。记住,调优是一个持续的过程,你需要根据实际的生产环境不断调整参数,进行性能测试和监控,才能找到最优的配置组合。

希望这些调优建议对你有所帮助。如果你有任何问题或需要进一步的指导,请随时告诉我。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com