Kafka核心技术与实战——10 | 生产者压缩算法面面观

    • 压缩(compression)
      • 它秉承了用时间去换空间的经典 trade-off 思想
      • 具体来说就是用 CPU 时间去换磁盘空间或网络 I/O 传输量
      • 希望以较小的 CPU 开销带来更少的磁盘占用或更少的网络 I/O 传输
    • 怎么压缩?
      • Kafka 的消息层次都分为两层:消息集合(message set)以及消息(message)
        • 一个消息集合中包含若干条日志项(record item),而日志项才是真正封装消息的地方
        • Kafka 底层的消息日志由一系列消息集合日志项组成
        • Kafka 通常不会直接操作具体的一条条消息,它总是在消息集合这个层面上进行写入操作
      • V2 版本主要是针对 V1 版本的一些弊端做了修正
        • 第一,就是把消息的公共部分抽取出来放到外层消息集合里面,这样就不用每条消息都保存这些信息了
          • 再对每条消息都执行 CRC 校验就有点没必要了,不仅浪费空间还耽误 CPU 时间
          • 因此在 V2 版本中,消息的 CRC 校验工作就被移到了消息集合这一层。
        • 第二,V2 版本还有一个和压缩息息相关的改进,就是保存压缩消息的方法发生了变化
          • 之前 V1 版本中保存压缩消息的方法是把多条消息进行压缩然后保存到外层消息的消息体字段中;
          • 而 V2 版本的做法是对整个消息集合进行压缩。显然后者应该比前者有更好的压缩效果
        • 压缩效果图
      • 何时压缩?
        • 生产者端
          • 生产者程序中配置 compression.type 参数即表示启用指定类型的压缩算法
            Properties props = new Properties(); props.put("bootstrap.servers", "localhost:9092"); props.put("acks", "all"); props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer"); props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer"); // 开启 GZIP 压缩 props.put("compression.type", "gzip"); Producer<String, String> producer = new KafkaProducer<>(props);
          • 这里比较关键的代码行是 props.put(“compression.type”, “gzip”),它表明该 Producer 的压缩算法使用的是 GZIP
        • Broker 端
          • 其实大部分情况下 Broker 从 Producer 端接收到消息后仅仅是原封不动地保存而不会对其进行任何修改,但这里的“大部分情况”也是要满足一定条件的。有两种例外情况就可能让 Broker 重新压缩消息
          • 情况一:Broker 端指定了和 Producer 端不同的压缩算法
            • 可一旦你在 Broker 端设置了不同的 compression.type 值,就一定要小心了,因为可能会发生预料之外的压缩 / 解压缩操作,通常表现为 Broker 端 CPU 使用率飙升
          • 情况二:Broker 端发生了消息格式转换
            • 所谓的消息格式转换主要是为了兼容老版本的消费者程序
            • Kafka 集群中同时保存多种版本的消息格式非常常见。为了兼容老版本的格式,Broker 端会对新版本消息执行向老版本格式的转换
            • 一般情况下这种消息格式转换对性能是有很大影响的,除了这里的压缩之外,它还让 Kafka 丧失了引以为豪的 Zero Copy 特性
            • 所谓“Zero Copy”就是“零拷贝”,说的是当数据在磁盘和网络进行传输时避免昂贵的内核态数据拷贝,从而实现快速的数据传输
            • 因此如果 Kafka 享受不到这个特性的话,性能必然有所损失,所以尽量保证消息格式的统一吧,这样不仅可以避免不必要的解压缩 / 重新压缩,对提升其他方面的性能也大有裨益
      • 何时解压缩?
        • 通常来说解压缩发生在消费者程序中
          • Kafka 会将启用了哪种压缩算法封装进消息集合中,这样当 Consumer 读取到消息集合时,它自然就知道了这些消息使用的是哪种压缩算法
        • Producer 端压缩、Broker 端保持、Consumer 端解压缩
          • 除了在 Consumer 端解压缩,Broker 端也会进行解压缩。注意了,这和前面提到消息格式转换时发生的解压缩是不同的场景
          • 每个压缩过的消息集合在 Broker 端写入时都要发生解压缩操作,目的就是为了对消息执行各种验证。我们必须承认这种解压缩对 Broker 端性能是有一定影响的,特别是对 CPU 的使用率而言
      • 各种压缩算法对比
        • 在 Kafka 2.1.0 版本之前,Kafka 支持 3 种压缩算法:GZIP、Snappy 和 LZ4。从 2.1.0 开始,Kafka 正式支持 Zstandard 算法(简写为 zstd)。它是 Facebook 开源的一个压缩算法,能够提供超高的压缩比(compression ratio)
        • 看一个压缩算法的优劣,有两个重要的指标
          • 一个指标是压缩比,原先占 100 份空间的东西经压缩之后变成了占 20 份空间,那么压缩比就是 5,显然压缩比越高越好
          • 另一个指标就是压缩 / 解压缩吞吐量,比如每秒能压缩或解压缩多少 MB 的数据。同样地,吞吐量也是越高越好
        • 但对于 Kafka 而言,它们的性能测试结果却出奇得一致
          • 即在吞吐量方面:LZ4 > Snappy > zstd 和 GZIP
          • 而在压缩比方面,zstd > LZ4 > GZIP > Snappy、
          • 具体到物理资源,使用 Snappy 算法占用的网络带宽最多,zstd 最少,这是合理的,毕竟 zstd 就是要提供超高的压缩比
          • 在 CPU 使用率方面,各个算法表现得差不多,只是在压缩时 Snappy 算法使用的 CPU 较多一些,而在解压缩时 GZIP 算法则可能使用更多的 CPU
      • 最佳实践
        • 首先来说压缩。何时启用压缩是比较合适的时机呢?
          • 你现在已经知道 Producer 端完成的压缩,那么启用压缩的一个条件就是 Producer 程序运行机器上的 CPU 资源要很充足。如果 Producer 运行机器本身 CPU 已经消耗殆尽了,那么启用消息压缩无疑是雪上加霜,只会适得其反。
          • 除了 CPU 资源充足这一条件,如果你的环境中带宽资源有限,那么我也建议你开启压缩。事实上我见过的很多 Kafka 生产环境都遭遇过带宽被打满的情况。这年头,带宽可是比 CPU 和内存还要珍贵的稀缺资源,毕竟万兆网络还不是普通公司的标配,因此千兆网络中 Kafka 集群带宽资源耗尽这件事情就特别容易出现。如果你的客户端机器 CPU 资源有很多富余,我强烈建议你开启 zstd 压缩,这样能极大地节省网络资源消耗。
        • 其次说说解压缩。其实也没什么可说的。一旦启用压缩,解压缩是不可避免的事情。这里只想强调一点:我们对不可抗拒的解压缩无能为力,但至少能规避掉那些意料之外的解压缩。就像我前面说的,因为要兼容老版本而引入的解压缩操作就属于这类。有条件的话尽量保证不要出现消息格式转换的情况。
      • 小结
        • 我们主要讨论了 Kafka 压缩的各个方面,包括 Kafka 是如何对消息进行压缩的、何时进行压缩及解压缩,
        • 还对比了目前 Kafka 支持的几个压缩算法
        • 最后我给出了工程化的最佳实践
        • 分享这么多内容,我就只有一个目的:就是希望你能根据自身的实际情况恰当地选择合适的 Kafka 压缩算法,以求实现最大的资源利用率
      • 核心观点
        • 1、broker端要进行解压缩校验,保证传输过程中的数据完整
        • 2、保持kafka版本的一致,有效规避broker端的解压缩操作
        • 3、学一个东西最重要的还是要用,如果只是参加一些培训课程很难全面的理解
posted @ 2020-05-11 11:10  怡情养性长智  阅读(417)  评论(0编辑  收藏  举报