数据压缩:空间换时间

441 阅读7分钟

前言

数据压缩不仅能节省存储空间,还可以用于提升网络传输性能。这种使用压缩来提升系统性能的方法,不仅限于在消息队列中使用,我们日常开发的应用程序也可以使用。

比如,我们的程序要传输大量的数据,或者要在磁盘、数据库中存储比较大的数据,这些情况下,都可以考虑使用数据压缩来提升性能,还能节省网络带宽和存储空间。

数据压缩

使用场景

在使用压缩之前,需要考虑当前这个场景是否适合使用数据压缩。

比如,进程之间通过网络传输数据,这个数据是不是需要压缩呢?

  • 不压缩直接传输需要的时间是:传输未压缩数据的耗时。
  • 使用数据压缩需要的时间是:压缩耗时 + 传输压缩数据耗时 + 解压耗时。

到底是压缩快,还是不压缩快呢?影响的因素非常多,比如数据的压缩率、网络带宽、收发两端服务器的繁忙程度等等。

压缩和解压的操作都是计算密集型的操作,非常耗费 CPU 资源

  • 如果你的应用处理业务逻辑就需要耗费大量的 CPU 资源,就不太适合再进行压缩和解压。
  • 如果你的系统的瓶颈是磁盘的 IO 性能,CPU 资源又很闲,这种情况就非常适合在把数据写入磁盘前先进行压缩。

压缩它的本质是资源的置换,是一个时间换空间,或者说是 CPU 资源换存储资源的游戏。

还有一点,如果你的系统读写比严重不均衡,你还需要考虑,每读一次就解压一次是否划算。

权衡之后,使用了数据压缩确实可以提升系统的性能,那我们就需要选择合适的压缩算法了。

压缩算法

压缩算法可以分为有损压缩和无损压缩。有损压缩主要是用来压缩音视频,它压缩之后是会丢失信息的。我们这里讨论的全都是无损压缩,也就是说,数据经过压缩和解压过程之后,与压缩之前相比,是 100% 相同的。

目前常用的压缩算法包括:ZIP,GZIP,SNAPPY,LZ4 等等。选择压缩算法的时候,主要需要考虑数据的压缩率和压缩耗时

一般来说,压缩率越高的算法,压缩耗时也越高。如果是对性能要求高的系统,可以选择压缩速度快的算法,比如 LZ4;如果需要更高的压缩比,可以考虑 GZIP 或者压缩率更高的 XZ 等算法。

压缩样本对压缩速度和压缩比的影响也是比较大的,同样大小的一段数字和一段新闻的文本,即使是使用相同的压缩算法,压缩率和压缩时间的差异也是比较大的。所以,有的时候在选择压缩算法的之前,用系统的样例业务数据做一个测试,可以帮助你找到最合适的压缩算法。

在这里,不会讲压缩算法的实现机制,因为压缩算法都很复杂,一般来说也不需要我们来实现某种压缩算法,如果你感兴趣的话,可以去学习一下最经典压缩算法:哈夫曼编码(霍夫曼编码,Huffman Coding)。

压缩分段

大部分的压缩算法,他们的区别主要是对数据进行编码的算法,但是压缩的流程和压缩包的结构大致是一样的。所以在压缩过程中,你最需要了解的就是如何选择合适的压缩分段大小

在压缩时,给定的被压缩数据它必须有确定的长度,或者说,是有头有尾的,不能是一个无限的数据流,如果要对流数据进行压缩,那必须把流数据划分成多个帧,一帧一帧的分段压缩

主要原因是:压缩算法在开始压缩之前,一般都需要对被压缩数据从头到尾进行一次扫描,扫描的目的是确定如何对数据进行划分和编码,一般的原则是重复次数多、占用空间大的内容,使用尽量短的编码,这样压缩率会更高。

被压缩的数据长度越大,重码率会更高,压缩比也就越高。这个很好理解,比如我们这篇文章,可能出现了几十次“压缩”这个词,如果将整篇文章压缩,这个词的重复率是几十次,但如果我们按照每个自然段来压缩,那每段中这个词的重复率只有二三次。显然全文压缩的压缩率肯定高于分段压缩。

当然,分段也不是越大越好,实际上分段大小超过一定长度之后,再增加长度对压缩率的贡献就不太大了,这是一个原因。

另外,过大的分段长度,在解压缩的时候,会有更多的解压浪费。比如,一个 1MB 大小的压缩文件,即使你只是需要读其中很短的几个字节,也不得不把整个文件全部解压缩,造成很大的解压浪费

所以,你需要根据你的业务,选择合适的压缩分段,在压缩率、压缩速度和解压浪费之间找到一个合适的平衡

压缩/解压

确定了如何对数据进行划分和压缩算法之后,就可以进行压缩了,压缩的过程就是用编码来替换原始数据的过程。压缩之后的压缩包就是由这个编码字典和用编码替换之后的数据组成的。

解压的时候,先读取编码字典,然后按照字典把压缩编码还原成原始的数据就可以了。

Kafka 处理压缩的方式

首先,Kafka 是否开启压缩,这是可以配置,它也支持配置使用哪一种压缩算法。原因我们在上面说过,不同的业务场景是否需要开启压缩,选择哪种压缩算法是不能一概而论的。所以,Kafka 的设计者把这个选择权交给使用者。

在开启压缩时,Kafka 选择一批消息一起压缩,每一个批消息就是一个压缩分段。使用者也可以通过参数来控制每批消息的大小。(调整每批消息的大小进而去控制压缩分段)

在 Kafka 中,生产者生成一个批消息发给服务端,在服务端中是不会拆分批消息的。那按照批来压缩,意味着,在服务端也不用对这批消息进行解压,可以整批直接存储,然后整批发送给消费者。最后,批消息由消费者进行解压。

在服务端不用解压,就不会耗费服务端宝贵的 CPU 资源,同时还能获得压缩后,占用传输带宽小,占用存储空间小的这些好处,这是一个非常聪明的设计。

在使用 Kafka 时,如果生产者和消费者的 CPU 资源不是特别紧张,可以选择开启压缩,从而节省网络带宽和服务端的存储空间,提升总体的吞吐量。

注意事项

正常情况下,在 Broker 端不会解压消息,但是如果触发了某些条件也是会进行解压和压缩操作的!反映到系统层面就会看到 Broker 端 CPU 使用率飙升。

  • Broker 端指定了和 Producer 端不同的压缩算法。
  • Broker 端发生了消息格式(V2 → V1,兼容老版本)转换。

同时这些操作也会使 Kafka 丧失零拷贝的优势,进一步损失性能。所以,我们在生产环境中要尽量保证消息格式的统一

总结

数据压缩,它本质上是用 CPU 资源换取存储资源,或者说是用压缩解压的时间来换取存储的空间,这个买卖是不是划算,需要你根据自己的情况先衡量一下。

参考

摘自 极客时间 - 李玥老师的《消息队列高手课》 <- 推荐大家阅读~

《消息队列高手课》学习笔记 Day21