消息队列-⑤高性能篇
消息队列-⑤高性能篇
学习核心
消息队列-高性能篇
- 基于Kafka框架作为学习沉淀
- Kafka 高性能 核心
- 多层次
- 顺序写
- 页缓存
- 零拷贝
- 批量操作
- 数据压缩
- Kafka 高性能常见题型
- Kafka 高性能 核心
学习资料
高性能总结
从“存储设计”(多层次)、“读写”(顺序写、Page Cache)、“传输”(零拷贝)、“应用层”(批量操作、数据压缩)等方面提供了性能优化支持
- 多层次(分治+查询优化)
- Topic 划分多Partition(分治)、Partition 拆分多文件(查询优化)
- 顺序写(写入优化)
- “顺序写入磁盘”:Kafka根据自身定位和需求选择“顺序写磁盘”(其性能既接近普通内存写,又降低了数据丢失风险),顺序写入减少了磁盘寻道时间,使得磁盘IO操作更加高效,能够持续高速写入数据,从而实现高吞吐量
- 页缓存(读写优化)
- “顺序写入内存”:引入操作的系统的Page Cache进行读写优化,通过合理配置参数来充分利用Page Cache提高消息的读写性能
- 写入:消息先写入Page Cache,然后由操作系统进行异步刷盘(整个过程中可能存在消息丢失风险)
- 读取:先从Page Cache读取,如果命中则直接返回,如果没有命中则从磁盘读取然后回写Page Cache再响应
- “顺序写入内存”:引入操作的系统的Page Cache进行读写优化,通过合理配置参数来充分利用Page Cache提高消息的读写性能
- 零拷贝(传输优化)
- 零拷贝技术可以优化数据发送效率,Kafka则充分利用了Linux的这个特性来提升性能
- Kafka底层传输是通过调用sendfile()系统调用函数,进而减少【上下文切换】、【数据拷贝】带来的损耗
- 【上下文切换】:sendfile() 替换了原有的read()、wirte()(传统:2次=》零拷贝:1次)
- 【数据拷贝】:数据直接在核心态传输,只需要拷贝2次(传统:4次=》零拷贝:2次)
- 批量操作(应用层)
- 批量发送:通过“批量发送”(“发送缓冲”:将数据在缓冲中聚集起来,然后再统一发送给Broker)减少IO、网络性能消耗
- 批量消费:一次性拉取多条消息进行消费,节约网络开销和带宽
- 数据压缩(应用层)
- 可通过压缩消息来提升kafka性能,压缩算法的选择结合实际业务场景配置
- kafka压缩的环节:producer端进行消息压缩(享受发送环节的性能增长)、broker端进行消息压缩(享受更高磁盘利用率)
- kafka支持的压缩算法:gzip、snappy、lz4、zstd
多层次(分治+查询优化)
Kafka 本质是数组,数组的优势在于存储连续、方便索引,如果顺序写入性能会很高。但是如果光是一个数组,随着数据越积越多,单一磁盘存放越来越吃力,造成I/O压力过大。所以Kafka不仅仅是一个数组,实际上,Kafka充分利用分治的思想,将这一个抽象的大数组,划分为很多个小数组。
1.Kafka 多层划分核心
多层划分概念
- Topic 划分多Partition(分治):一个Topic划分多个Partition,分布在不同的Broker =》可以理解为一个大数组被划分为多个小数组,分别存放在不同的Broker
- Partition 拆分多文件(查询优化):Kafka对Partition做了进一步拆分,每个Partition对应多个不同的文件(这些文件是分离开来的:
.index
、.log
、.timeindex
).log
:记录数据(即消息本身).index
:时间索引(通过时间对.log文件做索引查询).timeindex
:偏移量索引(通过偏移量对.log文件做索引查询)
顺序写(写入优化)
1.Kafka 写入机制
基于前面的学习可知,Kafka 写入数据实际最终就是添加到每个Partition 末端(写入对应的磁盘文件中)。这种设计手段简单高效、且非常巧妙
数据落地思路 =》顺序写磁盘
为了实现数据可靠,一般有两种思路:
- 【方式1】直接写入磁盘:直接写磁盘很容易实现了数据可靠,缺点是性能很差;
- 【方式2】先写入程序内存,后续再寻求持久化:这种思路写入之后需要有完善复杂的机制同步到磁盘,增加了很高的复杂性;
上述两种方式的选择取决于“低复杂度”和“高性能”的取舍,其实可以有两者兼顾的手段:顺序写磁盘
2.顺序写入磁盘
顺序写入磁盘概念核心
一般情况下内存写入通常远快于磁盘写入,但例外的是,当磁盘顺序写入的话,其性能和内存的差距并不会太大,所以落盘场景是可以考虑磁盘顺序写的,
Kafka的写入模式专门设计成了顺序写入磁盘(此处写磁盘也不一定是直接刷盘,而是交由操作系统控制,也会存在丢失数据的风险),相对于“先写内存后刷盘”的方式来说其减少了一个消息可能遗失的环节。
顺序写的优势
- 高效的磁盘利用:
- 磁盘的顺序写入性能通常远远高于随机写入性能,这使得 Kafka 能够实现高吞吐量
- 简单的存储管理:
- 顺序写入简化了日志段的管理和消息的追加操作
- 日志文件按顺序组织,便于快速查找和读取消息
- 可靠性和一致性:
- 顺序写入有助于确保消息的可靠性,因为消息一旦写入日志文件,不会被修改
- 消费者可以通过偏移量准确地读取消息,确保消息处理的顺序和一致性
顺序写入为什么快?
磁盘写入的核心步骤是寻址、数据传输,而寻址这一过程是主要耗时点,顺序写入磁盘则是通过减少寻址时间(主要耗时的步骤得到优化)进而提升写入性能
一般而言写磁盘的性能会远远低于操作内存,但是顺序写入则不一样,顺序写入的性能通常而言可以高出随机写入3个以上的数量级,甚至接近内存写入。
首先先理解写入磁盘具体是做什么?可以简单一点把写入磁盘分为两步:1.寻址;2.数据传输,寻址需要磁头转动,是机械操作、是主要耗时的地方,而随机写入,就得每一次都去寻址,这就意味着每一次都需要机械活动,自然就非常慢,所以从磁盘的视角来看,它是很讨厌随机写入的。
如果想更深入一点理解,可以把磁盘写入拆得更细致:
- (1)磁头沿着半径机械移动,最终移动到数据所在的磁柱
- (2)盘片旋转,是磁盘对齐数据所在扇区
- (3)数据传输,也就是写入数据
(1)、(2)可以看作寻址,(3)是数据传输,在随机读写情况下,写100000次数据,就需要100000次磁头移动时间、100000次盘面旋转时间,而顺序写入情况下,只需要1次磁头移动和1次盘面旋转,即一次寻址,然后最后都是写100000次数据,但是大的耗时被缩减了,所以顺序写入的性能自然就上来了
页缓存(读写优化)
1.顺序写入内存
基于上述“顺序写”概念,顺序写入磁盘的速度已经很快了,如果能实现“顺序写入内存”速度会更快。Kafka 利用操作系统自带的Page Cache来实现一定程度顺序读写内存
Page Cache可以简单看作热点磁盘数据的内存缓存,其读写优化体现分析如下:
- 写入优化:当消息写入时,先写入Page Cache,后面由操作系统异步将其刷入磁盘,进而提升性能
- 读取优化:当读取消息时,先从Page Cache中读取,如果命中则直接返回;如果没有命中则再去磁盘中读取随后回写Page Cache、返回数据
值得一提的是,Kafka是生产消费者模式,即生产了消息,在无积压情况下,这个消息很快就会被消费,也就是说生产消费时写入了Page Cache,而很快就有消费者来触发Kafka应用程序读取对应数据,而这个时间间隔很短,PageCache命中的可能性会很高
2.Page Cache数据和磁盘同步
数据写入Page Cache之后,是需要和磁盘同步的。此时如果电脑断电或者重启,这部分数据就会丢失。数据同步有几个时机:
(1)当空间内存不够用了,也就是说低于某个阈值时,此时将Page Cache刷入并释放Page Cache
(2)当脏页在内存驻留时间超过一个阈值时
- 写入数据之后,Page Cache 会标记为脏页,即内存数据页跟磁盘数据页内容不一致
(3)用户主动调用刷脏系统,调用sync()和fsync()(理解概念即可)
- sync系统调用:实际是将所有修改过的缓冲区排入写队列,不等待磁盘操作结束
- fsync系统调用:需要传入一个文件描述符fd为参数,fsync会确保写磁盘结束才返回,安全性高
3.Page Cache 相关参数
为了充分利用 Page Cache,可以关注Kafka 一些文件相关的配置和优化策略
操作系统配置(linux下)
(1)vm.dirty_background_ratio
vm.dirty_background_ratio
参数用于控制内存中脏页的比例,确保系统及时将脏页写入磁盘,避免过多的脏页占用 Page Cache。这个值通常默认在 5% 或 10%,具体和系统型号版本有关系,Linux下具体默认值可通过 cat /proc/sys/vm/dirty_background_ratio
命令查看
对于磁盘性能比较差的服务器,应该把这个值设置的小一点,这样可以把一个比较重的IO操作,拆解为多个小的IO操作,减少单次压力:
- 较低的
vm.dirty_background_ratio
参数值可以确保脏页及时写回磁盘,避免在短时间内大量脏页写回磁盘造成的IO突发 - 较高的
vm.dirty_background_ratio
参数值可能会导致较大的IO负载
(2)vm.dirty_ratio
vm.dirty_ratio
参数用于控制内存中脏页的比例,确保系统及时将脏页写入磁盘,避免过多的脏页占用 Page Cache。它控制的是脏页占总内存的最大比例。当脏页达到这个比例时,所有后续写操作会被阻塞,直到部分脏页被写回磁盘。默认值一般为20%,具体和系统型号版本有关系,Linux下的默认配置可通过cat /proc/sys/vm/dirty_ratio
命令查看。
需注意,这是一个前台操作,影响用户进程的性能,所以尽量不要达到这个阈值,如果写压力比较大,建议适当调大这个值
日志文件清除参数
如果业务不是说数据非要一直保留,是可以开启清除策略的,这样也可以减少不必要的数据占用 PageCache,从而提高读写性能,以下是清除相关的参数:
(1)log.retention.bytes
log.retention.bytes
参数定义了每个主题在磁盘上可以占用的最大字节数。当日志文件的大小达到这个阈值时,Kafka将开始删除旧的日志文件以腾出空间。log.retention.bytes默认是-1,表示没有上限,也就是不删除,具体可以根据业务情况灵活设置这个参数,不过这个值对于业务开发者可能会比较难以设定,所以更多的是使用下面的log.retention.ms参数
(2)log.retention.ms
log.retention.ms
参数定义了日志文件保留的最长时间。当日志文件的创建时间超过这个阈值时DKafka 将删除这些过期的日志文件。这个配置单位是以毫秒为单位,默认为可以配置成整数表示的毫秒数604800000,表示7天(7天*24小时*60分钟*60秒*1000毫秒
)
(3)配置场景参考
组合使用:log.retention.bytes
和log.retention.ms
可以组合使用,Kafka 会在任意一个条件满足时删除日志文件。例如,如果日志文件超过 10 GB 或者超过7天,Kafka 就会删除旧的日志文件
独立使用:如果只设置了 log.retention.ms 而没有设置 log.retention.bytes ,日志文件将只会根据时间进行清理。反之亦然。
零拷贝(传输优化)
1.“传输”分析
常规传输为何低效?
问题分析
消息是存储在Kafka服务器,消费者消费消息时,数据要从Kafka服务端传递给消费者。具体分析流程即当有Consumer订阅王题,数据需要从磁盘读取并将数据写入网络套接字,然后在网络中进行传输,这个操作看似不复杂,但如果不很好的设计传输流程,它的效率会非常低
常规传输为何低效?
常规的数据传输方式简单又传统,但存在冗余的上文切换和数据拷贝,在高并发系统里是非常糟糕的,多了很多不必要的开销,会严重影响系统性能
如果应用程序要从磁盘读取数据发送到网络,那么会经历下图的流程:可以从系统调用和数据拷贝两个角度分析
2次系统调用:
一次是 read(),一次是 write(),每次系统调用都得先从用户态切换到内核态,等内核完成任务后,再从内核态切换回用户态,所以共发生了4次用户态与内核态的上下文切换。且上下文切换的成本并不小,一次切换需要耗时几十纳秒到几微秒,虽然时间看上去很短,但是在高并发的场景下,这类时间容易被累积和放大,从而影响系统的性能
4次数据拷贝:其中两次是 DMA 的拷贝,另外两次则是通过 CPU拷贝的
- 第1次拷贝:把磁盘上的数据拷贝到操作系统内核的缓冲区里(一些资料称之为Read Buffer),这个拷贝的过程是通过 DMA 搬运的
- 第2次拷贝:把内核缓冲区的数据拷贝到用户的缓冲区里,干是我们应用程序就可以使用这部分数据了,这个拷贝到过程是由 CPU 完成的
- 第3次拷贝:把刚才拷贝到用户的缓冲区里的数据,再拷贝到内核的socket 的缓冲区里,这个过程依然还是由CPU 搬运的
- 第4次拷贝:把内核的 socket 缓冲区里的数据,拷贝到网卡的缓冲区里,这个过程又是由 DMA 搬运的
基于上述分析,本来是搬运一份数据,结果内部却搬运了4次,过多的数据拷贝无疑会消耗 CPU资源,大大降低了系统性能
DMA(Direct Memory Access),即直接内存访问,核心就是CPU 不再参与「将数据从磁盘控制器缓冲区搬运到内核空间」的工作,这部分工作全程由 DMA 完成。但是 CPU 在这个过程中也是必不可少的,因为传输什么数据,从哪里传输到哪里,都需要 CPU 来告诉 DMA 控制器。早期 DMA 只存在在主板上,如今由于IO设备越来越多,数据传输的需求也不尽相同,所以每个IO设备里面都有自己的 DMA 控制器
优化思路分析
基于上述分析,结合传统传输方式下的低效问题分析,可以从“系统调用”和“数据拷贝”这两方面进行切入
(1)减少系统调用次数
读取磁盘数据的时候,之所以要发生上下文切换,这是因为用户空间没有权限操作磁盘或网卡,内核的权限最高,这些操作设备的过程都需要交由操作系统内核来完成,所以一般要通过内核去完成某些任务的时候,就需要使用操作系统提供的系统调用函数。
而一次系统调用必然会发生2次上下文切换:首先从用户态切换到内核态,当内核执行完任务后,再切换回用户态交由进程代码执行。
所以,要想减少上下文切换的次数,就要减少系统调用的次数
(2)减少数据拷贝次数
传统的文件传输方式会历经4次数据拷贝,其中的「从内核的读缓冲区拷贝到用户的缓冲区里,再从用户的缓冲区里拷贝到 socket 的缓冲区里」这个过程是没有必要的。因为文件传输的应用场景中,在用户空间并不会对数据「再加工」,所以数据实际上可以不用搬运到用户空间,因此用户的缓冲区是没有必要存在的。
那么有没有一种技术,可以是实现数据之间在核心态进行传输,而不需要将数据在核心态和用户态之间来回复制,最终发送给接收端呢?=》零拷贝技术
2.“零拷贝”
所谓的零拷贝是指将数据在内核空间直接从磁盘文件复制到网卡中,而不需要经由用户态的应用程序之手。这样既可以提高数据读取的性能,也能减少核心态和用户态之间的上下文切换,提高数据传输效率。
具体流程如下:
(1)sendfile开启流程(sendfile是代替了read和write),相当于只有一次系统调用 =》减少上下文切换性能损耗
(2)操作系统将数据从磁盘通过DMA加载到内核空间的缓存区
(3)操作系统将数据的描述符拷贝到Socket 缓冲区中(Socket 缓冲区仅仅会拷贝一个描述符过去,不会拷贝数据)
(4)操作系统直接将数据从内核空间的缓存区传输到网卡中,并通过网卡将数据发送给接收方(本质是通过DMA来做的,此处DMA叫SG-DMA,理解为增强版DMA)
SG-DMA是一种强大的 DMA技术,特别适用于处理复杂的数据传输需求。在现代计算和通信系统中,SG-DMA提供了高效的数据传输解决方案,显著提高了系统性能和资源利用率。通过合理使用SG-DMA,系统可以在高性能数据处理和低功耗设计中找到平衡
基于上述流程分析,使用零拷贝之后,系统调用次数从2次变成了1次(减少上下文切换性能损耗),拷贝次数从4次变成了2次(减少数据拷贝次数),显著减少了数据传输的损耗,提升性能
3.kafka哪里使用的零拷贝
Kafka 充分利用零拷贝技术,大幅提升I/O的吞吐率,这也是为什么Kafka在处理海量数据时这么快的原因之一。可以通过追溯kafka文件传输的代码,其最终是调用了Java NIO库的transferTo方法,如果linux系统支持调用sendfile系统调用,则transferTo最后实际会使用到sendfile()
系统调用函数
批量操作
除却利用底层的技术,kafka 还在应用程序层面提供了一些手段来提升性能,例如批量操作(批量生产、批量消费)。
实际上,批量操作并不是Kafka特有的技术支持,它是后端领域中非常常见的优化手段
1.批量生产
Kafka默认是消息来了就发送到Kafka服务端,试想下如果有5个线程来发送消息,那么并发度就只有5。但很多时候其实也不会去追求瞬时发送,所以这里有一个潜在的优化方向就是批量发送。即在向Kafka写入数据时,可以启用批次写入,这样可以避免在网络上频繁传输单个消息带来的延迟和带宽开销。假设网络带宽为10MB/S,一次性传输10MB的消息比传输1KB的消息10000万次显然要快得多。
配置参数(影响批量发送的核心参数)
高吞吐配置推荐:
linger.ms
:20s、batch.size
:32kb、buffer.memory
:32MB
linger.ms
:在发送一个批次消息前等待多少ms(默认是0,即不等待,即来即发),如果想增大单个批次的消息数的话可以增加linger.ms
(例如3s,5s,10s,20s等)。
batch.size
:数据量累计到多大就发送(默认为16KB),即使没有达到linger.ms
,只要批次字节数达到batch.size
阈值也会立刻发送。可以将批量大小增加到32KB或64KB以提高请求的吞吐量。每个分区的批次是独立计算的,所以不要将batch.size设置为太高的数字,否则可能会遇到高内存使用率
buffer.memory
:控制生产者用于缓存消息的总内存大小。如果生产者发送消息的速度超过了Kafka 接收的速度,这个缓冲区会被填满,在批量场景要额外注意,比如希望批量发送64KB的消息,但是buffer.memory
配置只有12KB,这显然是会阻塞生产者写入流程的,所以buffer.memory
需要配置得大一些,默认值是32MB,还是很稳的。
场景类比理解:可以理解为类似有很多件快递,一件一件发出去、运输肯定成本高,但是一批快递发在一个集装箱里,发到目的城市,那效率自然会有所提升。而这些参数可以分别对应场景中的设定
linger.ms
:一批快递等待发车的时间,超出这个时间阈值就发车batch.size
:“装车累计量”,当装车达到这个阈值就发车(如果超过阈值但发车时间还没到也是直接发车)buffer.memory
:“车总容量”,例如一辆车最多放1000个快递,但是批量预期希望单批塞2000个快递,这明显是会出错的。因此在批量场景下,一般都会将这个值配置大一点
注意事项
凡事有取舍,批量操作在带来性能优化的同时,也会付出一定的代价。在使用聚合发送需要下面几个问题:
- 吞吐量 vs 延迟:增大
batch.size
和linger.ms
可以提高吞吐量,但也会增加消息的延迟,需要根据应用需求进行权衡 - 内存使用:增大
buffer.memory
可以提高批量发送的效率,但会占用更多的内存 - 可靠性:在批量发送消息时,要注意可能的数据丢失风险(聚合发送数据一丢就是一批)。如果对消息有很高的可靠性要求,需要确保配置了适当的 acks 参数,例如 acks=all ,以确保消息的可靠交付
2.批量消费
批量消费是指一次性拉多条消息进行消费,这样可以节约网络开销和带宽
配置参数(影响批量消费的核心参数)
max.poll.records
:每次调用 poll()方法时返回的最大记录数,用于设置单次轮询获取的最大记录条数
fetch.min.bytes
:消费者在一次拉取请求中希望接收的最小数据量(以字节为单位),消费者会等待单批数据大小达到阈值时再返回
fetch.max.wait.ms
:如果指定了 fetch.min.bytes ,则该参数控制消费者等待服务器返回数据的最大时间,即使在此时间内数据量没有达到 fetch.min.bytes,也会返回当前可用的数据
消费者示例配置:
linger.ms
:500 // 每次poll最多拉取500条记录batch.size
:1024 // 最小拉取1024字节的数据buffer.memory
:500 // 最长等待500ms
注意事项
处理效率:批量消费消息可以显著提高处理效率,但需要确保处理逻辑能够快速处理这些消息,否则可能会导致消息堆积
偏移量提交:默认情况下,消费者会定期自动提交偏移量。可以通过配置 enable.auto.commit 参数来控制自动提交的行为,或者手动提交偏移量以确保消息处理的可靠性
资源管理:在批量消费时,要注意内存和 CPU 的使用情况,避免资源耗尽导致系统不稳定
数据压缩
生产者通常发送基于文本的数据,例如JSON 数据。在这种情况下,对生产者应用压缩非常重要。默认情况下,生产者消息以未压缩的方式发送。Kafka支持两种类型的压缩(producer端和broker端) 通过启用压缩,可以减少网络利用率和存储,这通常是向Kafka 发送消息时的瓶颈
压缩批次具有以下优点:
- 生产者请求的大小要小得多,通常压缩之后的数据比压缩前小3倍以上
- 因为数据变小了,通过网络传输数据的速度更快,即延迟更短,同时也拥有了更好的吞吐量
- 因为数据变小了,那么在磁盘上存储的消息也变小了,Kafka 中的磁盘利用率更高
1.何时需要压缩?
如果是追求高性能的服务,那么正常来说,都是需要开启压缩的,但它也会付出额外的CPU,需要考虑压缩带来的磁盘、带宽节省的收益是大于CPU一定程度的损失的
那怎么判断收益是否大于损失?=》这个得看业务团队的具体情况来分析:
- 如果团队CPU资源多到用不完,而瓶颈在于带宽或者磁盘存储,那显然需要开启压缩
- 消息如果比较大,可以考虑压缩
- 消息自身内容如果重复度高,可以考虑压缩(压缩本质就是针对重复片段用简单代码取代,已实现让数据更小的目的,所以重复度越高,压缩效果肯定是越好的)
- 多条消息之间内容重复度高,那么可以考虑在批量发送和消息压缩一起开启,效果会很好
- 其实一般来说,如果追求高性能的服务,那么都会开启压缩的
2.在哪个环节压缩?
producer端进行压缩
producer可以选择使用compression.type
设置来压缩消息(参数枚举:none、gzip、lz4、snappy、zstd 等压缩算法) (注意: zstd 压缩是在Kafka 2.1之后引入的),考虑测试 snappy 或 lz4 以获得最佳速度/压缩比。
为了进一步提高性能,可以搭配“批量发送”操作使用,压缩一批数据,达到更好的压缩效果
broker端进行压缩
broker也可以进行压缩,即在发送端不进行压缩(则无法享受发送环节的性能增长),但还是可以享受更高磁盘利用率。Broker压缩是主题级的,也就是说不同主题可以有不同设计,包括用不同的压缩算法。
默认情况下,broker是不开启压缩的,体现在配置上,就是主题压缩定义为compression.type=producer
,即直接继承 producer 端所发来消息的压缩方式,即无论是否压缩,无论采用哪种压缩算法,broker 都原样存储消息
Broker端的压缩有规则限制:
- 如果Broker开启了压缩,而Producer未开启压缩,那么没有歧义,就在Broker执行压缩即可
- 如果Broker和Producer端同时开启了压缩配置,则遵循如下规则:
- 如果两者压缩设置相同(例如
lz4
),则Broker不会进行重复压缩,消息批次将按原样写入日志文件 - 如果两者压缩设置不同,Broker则解压消息并重新按配置压缩(Broker将解压缩消息并将其重新压缩为其配置的格式)
- 如果两者压缩设置相同(例如
3.压缩算法对比
目前 Kafka 共支持四种主要的压缩类型:Gzip
、Snappy
、Lz4
、Zstd
:
Gzip
可能是平常工作生活中打交道是最多的,它的缺点在于CPU高、速度又不是很快Snappy
是Google的作品,性能非常棒,也没有什么明显的缺点,各方面都还可以Lz4
特点在于速度非常快,但是缺点是压缩比率很低,也就是快是快,但是压缩不够到位。Zstd是 Facebook开源的压缩算法,压缩率和压缩速度都还可以,和Snappy一样比较能打,直到 Kafka 的 2.1.0 版本才引入支持zstd
在其Github也公布了他们的压测对比数据,可以看到Zstd的压缩速度是命令前茅的,同时还可以通过--fast参数控制压缩比,压缩比稍低的时候压缩速度也就更快一些