Kafka 综合运用:怎么在实践中保证 Kafka 高性能?
如何选择压缩算法?
在选择压缩算法的时候,首先要考虑的就是压缩比和压缩速率。压缩比主要是为了节省网络带宽和磁盘存储空间,而压缩速率主要影响吞吐量。
一般来说,压缩比越高,压缩速率越低;压缩比越低,压缩速率越高。
操作系统交换区
在现代操作系统中,基本都支持交换区,也叫做 swap 分区。当操作系统发现可用的物理内存不足的时候,就会把物理内存里的一部分页淘汰出来,放到磁盘上,也就是放到 swap 分区。
你也可以把 swap 分区看作是“虚拟内存”。那么你可以想到,如果触发了这种交换,性能就会显著下降。交换越频繁,下降越快。
在 Linux 中有一个参数,叫做 vm.swappniess,它控制住了使用交换区的积极性。比如说 vm.swappniess 在大多数 linux 发行版上默认值都是 60,也就是比较积极地使用交换区。而追求性能的中间件,如消息队列、数据库等都会尽量避免触发交换,也就是把 vm.swappniess 调小。
有一种说法是当内存使用率超过 40% 的时候就开始使用 swap 分区,但是这种说法其实不够准确,因为是否交换在 Linux 2.6 以后的版本后还参考了别的因素。
优化生产者
优化 acks
在消息不丢失那一节课里面。在尝试优化 acks 的时候,如果追求性能,那么就应该把 acks 设置为 0。(可能会丢消息)
如果追求消息不丢失,那么就应该把 acks 设置为 all
优化批次
优化批次有两个好处,对于生产者本身来说,它发送消息的速率更快;对于 Kafka 来说,同样数量的消息,批次越大,性能越好。
所以当你的发送者遇到瓶颈之后,就可以尝试调大批次的参数来进一步提高发送性能。和批次有关的有两个参数:linger.ms 和 batch.size。前者是凑够一个批次的最大等待时间,后者是一个批次最大能有多少字节。你可以先简单介绍你的优化手段,关键词是调大批次。
之前遇到过一个生产者发送消息的性能问题。后来我们经过排查之后,发现是因为发送性能太差,导致发送缓冲池已经满了,阻塞了发送者。这个时候我们注意到其实发送速率还没有达到 broker 的阈值,也就是说,broker 其实是处理得过来的。在这种情况下,最直接的做法就是加快发送速率,也就是调大 batch.size 参数,从原本的 100 调到了 500,就没有再出现过阻塞发送者的情况了。
调大批次究竟能有多大的优化效果和调整前后批次大小、消息平均大小、borker 负载有关。好的时候 TPS 可以翻倍,差的时候可能也就是提升 10% 不到。所以最好亲自动手试一试业务调整这个参数性能究竟能提升多少。
启用压缩
为了进一步提高 Kafka 的吞吐量,开启了 Kafka 的压缩功能,使用了 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 的两个方向就是调整文件系统,使用 XFS,并且禁用 atime。atime 是指文件最后的访问时间,而本身 Kafka 用不上。
Kafka 也是一个磁盘 IO 密集的应用,所以可以从两个方向优化磁盘 IO。一个是使用 XFS 作为文件系统,它要比 EXT4 更加适合 Kafka。另外一个是禁用 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。
这些参数都是跟机器有关的,需要通过不断测试来确认这些参数的最佳值。如果记不住细节,那就记住都调大。尤其是后三个,调大它们的效果,就是为了让从分区一批次同步尽可能多的数据。
Kafka 的主从分区同步也可以优化。首先调整从分区的同步数据线程数量,比如说调整到 3,这样可以加快同步速率,但是也会给主分区和网络带宽带来压力。其次是调整同步批次的最小和最大字节数量,越大则吞吐量越高,所以都尽量调大。最后也可以调整从分区的等待时间,在一批次中同步尽可能多的数据。
优化 JVM
Kafka 是运行在 JVM 上的,所以理论上来说任何优化 Java 性能的措施,对 Kafka 也一样有效果。如果你是面试 Java 岗位,那么这个点会非常适合你,因为你可以同时展示你对 JVM 的理解,让你赢得竞争优势。
优化 JVM 首先就是考虑优化 GC,即优化垃圾回收。而优化 GC 最重要的就是避免 full GC。full GC 是指整个应用都停下来等待 GC 完成。它会带来两方面影响。一方面是发送者如果设置 acks 为 1 或者 all,都会被阻塞,Kafka 吞吐量下降。
基本的思路就是调大 JVM 的堆,并且在堆很大的情况下,启用 G1 垃圾回收器。
之前我们的 Kafka 集群还出过 GC 引发的性能问题。我们有一个 Kafka 的堆内存很大,有 8G,但是垃圾回收器还是用的 CMS。触发了 full GC 之后,停顿时间就会很长,导致 Kafka 吞吐量显著下降,并且有时候还会导致 Kafka 认为主分区已经崩溃,触发主从选举。
在这种情况下,有两个优化思路,一个是考虑优化 CMS 本身,比如说增大老年代,但是这个治标不治本,可以缓解问题,但是不能根治问题。所以综合之下我选了另外一个方向,直接切换到 G1 回收器。G1 回收器果然表现得非常好,垃圾回收频率和停顿时间都下降了。
- 点赞
- 收藏
- 关注作者
评论(0)