在大数据领域待久了,你会发现一个很有意思的现象:一提到数据压缩,很多人第一反应是"省磁盘空间"。但真正在一线跑过批处理、调过数仓任务的人心里都清楚,压缩这步棋走得好不好,直接影响的是整条数据链路的处理效率。它不是存储层面的小优化,而是从磁盘IO、网络传输、CPU消耗到查询响应时间全链路的那根杠杆。尤其在数据量上了PB级之后,压缩策略选错了,集群跑不动,任务超时,成本飙升,这些问题全都会冒出来。
这篇东西我想从实战角度把大数据场景下的数据压缩讲透,不堆理论,核心聚焦在"压缩到底怎么提升数据处理效率"这件事上,包括压缩算法的选型逻辑、文件格式层面的配合、以及那些文档里不会写但线上一定会踩的坑。适合正在做数仓建设、搞实时计算、或者天天被集群性能和存储成本折磨的朋友参考。
1. 压缩在大数据链路里的真实价值,远不止省空间
1.1 存储成本只是表面,IO才是真正的命门
先聊一个容易被忽略的底层逻辑。大数据处理引擎,比如Spark、Hive、Flink,瓶颈绝大多数时候不在CPU,而在IO。磁盘读写速度跟内存带宽、CPU处理速度相比,差距是几个数量级的。你从磁盘读1GB数据进内存做计算,和读200MB压缩数据再做解压,哪个更快?很多人直觉上觉得解压要额外花CPU时间,肯定更慢。但实际测试下来,绝大多数场景里读压缩数据再解压,整体耗时远小于直接读原始数据。
原因很简单。现代压缩算法的解压速度是以GB/s为单位的,而磁盘IO,尤其是机械盘或者网络存储,吞吐量往往只有几百MB/s。你用几分之一的CPU开销,换掉了好几倍的磁盘IO等待,这笔账怎么算都划算。所以在大数据场景里,压缩的核心价值第一是降低IO压力,第二才是省存储空间。
我见过不少团队为了省那点CPU,线上的Hive表全部用明文文本格式存储,结果就是跑一次全量扫描,动辄几个小时,集群磁盘带宽打满,其他任务全被拖死。后来把表改成列式格式加压缩,同样的数据量,扫描时间缩到原来的四分之一,CPU也没见飙升多少。这个案例很典型,后面我会细讲文件格式和压缩的配合。
1.2 数据压缩在不同计算场景下的收益差异
压缩带来的效率提升,在不同场景下的表现差异其实非常大,这直接影响你的技术选型。搞清楚了这条,你就明白为什么不能一个压缩策略打天下。
- 扫描型分析场景(如全表聚合、数仓ETL):这类任务要读大量数据,属于典型的IO密集型。压缩能显著减少磁盘IO和网络传输量,收益最大。像Spark读Parquet,用Snappy压缩和用明文存储比,扫描效率可能相差3到5倍。
- 点查型场景(如按主键过滤少量记录):这类任务IO量本身就小,瓶颈往往在索引构建、谓词下推、任务调度延迟上。压缩的影响相对小,但好的列式压缩配合页面级裁剪,依然能减少不必要的解压开销。
- 实时写入型场景(如Kafka消息、实时数仓写入):这里压缩的收益体现在网络带宽和存储上,但要格外注意压缩带来的CPU消耗和延迟。Kafka如果开启压缩,生产者侧和消费者侧的CPU都会上升,选型不当会直接影响实时链路稳定性。
- 冷热数据分层场景:热数据强调查询性能,通常选低压缩比的快速算法;冷数据强调存储成本,选高压缩比的算法更划算。这里压缩策略本质上是在帮你做成本治理。
这四种场景我都实际碰过,方案差别很大。下面我在算法选型部分会展开讲。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 主流压缩算法选型,先搞清楚它们的脾气
2.1 大数据场景下常见的压缩算法全景
大数据生态里常见的压缩算法,掰着指头数就那么几个:Snappy、LZ4、Gzip、Zstandard(一般叫zstd)、LZO、Bzip2。它们各有各的脾气,不能光看压缩率高就选哪个。我自己整理过一个简单的选型参照表,先贴出来给各位一个整体印象。
| 算法 | 压缩比 | 压缩速度 | 解压速度 | CPU消耗 | 典型适用场景 |
|---|---|---|---|---|---|
| Snappy | 低(约2x) | 极快 | 极快 | 低 | 数仓ODS层、交互式查询的默认选择 |
| LZ4 | 低(约2x) | 极快 | 极快 | 低 | 实时计算链路、Kafka消息压缩 |
| Zstandard | 中高(约3-5x) | 快/可调 | 很快 | 中 | 冷数据压缩、需要均衡的场景 |
| Gzip | 高(约4-6x) | 慢 | 较快 | 高 | 长期归档、小文件存储 |
| LZO | 中(约2-3x) | 快 | 快 | 中 | 需要支持split的旧版Hadoop生态 |
| Bzip2 | 极高(约6-10x) | 极慢 | 慢 | 极高 | 极少用,不推荐在计算链路上使用 |
这个表里的压缩比是大概区间,因为实际压缩效果跟数据特征关系很大。文本型数据压缩率高,数值型、尤其是已经经过编码的数据,压缩率会明显下降。订单ID、用户ID这种高基数维度,压缩率一般不会太好看。
2.2 Snappy为什么能成为离线数仓的默认选项
Snappy在Hadoop生态里几乎是事实标准,Hive、Spark、HBase、Parquet默认压缩经常就是它。它能坐稳这个位置,核心原因是它在"压缩足够快、CPU够省、压缩率不差"三者之间找到了最佳实践平衡。
数据压缩里有三个指标是互斥的:CPU开销、压缩率、压缩/解压速度。Snappy牺牲了压缩率,换来了极低的CPU开销和极高的吞吐。在大数据场景下,这恰恰是最理性的取舍。因为数据量以TB/PB级增长,CPU资源被你的一次全表扫描任务霸占好几分钟,和磁盘IO霸占好几分钟,前者对集群的杀伤力更大。
有个很直观的教训可以分享。我见过有团队为了省存储,把线上Parquet表的压缩全部改成Gzip。结果存储确实省了一半还多,但是跑数仓核心ETL的时候,Spark任务的CPU直接飙到80%以上,任务运行时长大了一倍多。那个月的计算资源账单涨得比省下的存储费用高多了。后来把热表的压缩换回Snappy,冷表保留Gzip,问题才解决。所以选压缩算法,一定不能孤立看存储节省率,要放回整个计算链路里看。
2.3 谁适合用Zstandard,谁适合用LZ4
Zstandard是近年来上升势头最猛的压缩算法,Facebook开源的那个。它最聪明的地方是把压缩等级做成了参数,从1到22可调。等级低的时候速度接近LZ4,等级高的时候压缩率逼近甚至超过Gzip。这种灵活性让它可以同时覆盖"需要速度"和"需要压缩率"两种场景。
我现在的做法是:热数据用Zstandard等级3,压缩速度和CPU开销接近Snappy,压缩率却比Snappy高20%到30%。冷数据用Zstandard等级9或者更高,压缩率直逼甚至超过Gzip,解压速度却比Gzip快很多。这样一套算法吃两头,不用维护两套压缩策略,省心不少。
LZ4则是把"极致速度"打在基因里的选手,压缩和解压速度比Snappy更快,CPU消耗更低,但压缩率也更差一些。它最适合的场景是实时链路,尤其是Kafka和Flink。Kafka消息压缩开启LZ4或Zstandard,网络传输量能降很多,同时不会给生产者和消费者带来明显的CPU压力。实际踩坑经验是,实时链路千万别用Gzip或高等级Zstandard,生产者端的压缩延迟会明显推高消息端到端延迟,这在实时数仓里是不可接受的。
2.4 LZO和Bzip2,能不用就别用
LZO在旧版的Hadoop生态里地位很高,因为它是早期少数支持文件切分(split)的压缩格式,配合Gzip不支持切分的问题,一度是Hive表的首选。但今天Hadoop已经通过其他机制解决了压缩文件的切分问题,LZO的生态支持反而成了包袱,还需要单独安装native库,维护成本高。新版集群不太建议再碰。
Bzip2的压缩率是真的高,但压缩和解压速度是真的慢。在大数据场景下,计算效率优先级远高于存储成本,所以Bzip2基本没有用武之地。除非你有那种极冷极小、几乎不会被读的数据,否则完全不需要考虑。我自己印象里做了这么多年,没有一次线上表用Bzip2是合理的。
3. 文件格式和压缩的配合,决定了效率的上限
3.1 列式存储和压缩率之间是乘法关系
聊压缩不能不聊文件格式。很多人问,为什么我明明用了压缩,数据量还是不小?这里的关键可能不在压缩算法,而在文件格式。
大数据处理里主流的存储格式是Parquet和ORC,两种都是列式存储。列式存储对压缩有一个天然优势:同一列的数据类型相同,数据特征接近,压缩算法能获得更好的压缩率。最典型的例子是日期列或者枚举列,大量重复值聚在一起,用字典编码加压缩,能把数据压到极小的体积。
举一组粗粒度的参考数据:同样的数据,用明文CSV存储可能是100GB,转成Parquet不压缩,差不多能到60GB左右(因为类型编码和列裁剪起了作用),再配合Snappy,能压缩到25GB上下,配合Zstandard,能压到15到18GB。这个差距是非常可观的。而查询性能的提升更夸张,扫描同样的逻辑数据量,Parquet+Zstandard和CSV明文对比,经常有5到10倍的性能差距。
3.2 Parquet和ORC内部的压缩模块设计
Parquet和ORC在压缩的实现上有个共同思路:不是把整个文件塞进一个压缩流里,而是分块压缩。Parquet的数据按Row Group组织,每个Row Group内部再按列分成Column Chunk,每个Column Chunk内部再划分成Page。压缩的粒度是Page级。也就是说,读数据的时候不需要解压整个文件,只需要定位并解压需要的Page就行。
ORC的设计更激进,引入了Index Data,每个Row Group都带有行索引和统计信息(比如最大值最小值)。查询引擎做条件过滤时,可以先读索引信息,直接把完全不符合条件的Row Group整个跳过,压缩数据连解压都不用解。这种谓词下推加数据跳过,是ORC在分析性能上非常能打的核心原因之一。
这就带来一个很重要的实践启示:你用了列式格式和压缩,但如果查询写得不讲究,比如select *满天飞,或者过滤条件没落到文件裁剪层,压缩带来的效率提升会大打折扣。压缩配合列裁剪、谓词下推,三者叠加才是完整的性能优化链路。
3.3 从文件格式层面看压缩率的魔术空间
这里多聊一个细节。列式格式里除了通用的压缩算法,还内置了层编码,比如字典编码(Dictionary Encoding)、增量编码(Delta Encoding)、游程编码(RLE)和位打包(Bit Packing)。这些编码先对列数据做一次"预压缩",然后才交给下层的压缩算法做真正的压缩。两者叠加,效果远胜于单纯用通用算法压整个文件。
举一个我优化过的实际案例。有一张用户行为日志表,里面的action_type列只有四种取值。原本用文本格式存储,这一列每行撑死了10个字节。迁移到Parquet后,字典编码先把四种取值映射成字典ID,然后用bit packing,每个值只需2个bit。再配合Zstandard压缩,这个列最终的体积不到原始文本的百分之二。这种收益,你在选择压缩算法时根本想象不到,但文件格式会自动帮你做掉。
所以处理大数据效率问题的正确姿势是:文件格式选对(Parquet或ORC)+ 编码机制理解透 + 压缩算法按场景配。三者层层递进,少一环都是在浪费数据效率的潜能。
4. 压缩是提升了效率,还是拖慢了处理?关键是你在哪一层做压缩
4.1 中间层压缩与磁盘IO的博弈
我接触过的很多大数据从业者有一个误解,以为只要加了压缩,任务就一定会变快。其实不一定。讨论这个问题必须确定压缩到底发生在链路哪个环节。
假设你有一个Spark任务做ETL,从一张压缩表读数据,经过处理,再写一张压缩表。这里的写入侧和读取侧的权衡逻辑完全不同。读取侧,压缩省的是磁盘IO,前面说了绝大多数场景是划算的。写入侧就没那么简单。Shuffle阶段的中间数据要不要压缩,就是个典型的权衡问题。
Spark Shuffle过程中,map端输出的中间结果如果开启压缩,可以大幅减少磁盘IO和网络传输,因为shuffle数据要先落盘,再被reduce端拉取。但压缩也意味着额外的CPU开销和序列化开销。什么时候压缩收益最大?数据量大、reduce端拉取数据量多的时候。这是一个经典的"Squeeze中段IO"的思路。
我自己测试过,在Shuffle数据量超过几十GB的任务里,开启Shuffle压缩,总任务时间通常能缩短15%到30%。但如果任务本身shuffle量很小,开启压缩反而可能增加1%到3%的CPU时间,但不明显。所以Spark默认开启shuffle压缩(Snappy),这个默认值是合理的。在数据量大的业务里,不要轻易关掉它。
4.2 RDD序列化与压缩的关系,一个容易被忽视的点
还有一个很少被讲透的细节:对象的序列化方式决定压缩能获得多大的收益。Spark里如果用Java原生的序列化,对象序列化后的二进制流里会带大量类名、内部结构描述等元信息,这些内容本身就是压缩率极高的"水分"。如果先用Kryo序列化,对象体积会比Java序列化小很多,再进行压缩,整体能压到什么程度,跟数据字段本身的熵值关系就很大。
实操中我的建议顺序是:先使用Kryo做序列化,再叠加压缩。Kryo可以把纯Java序列化对象体积缩小到原来的三分之一以下,再加压缩等于双重压缩,收益是非常可观的。有些框架使用者只盯着压缩算法选型,忽略序列化这层,本质上是捡了芝麻丢了西瓜。
我在做Spark Streaming转写数据到HDFS时,就遇到过这类问题。同一个DStream,默认JavaSerializer加Snappy压缩出来的文件,比切换到Kryo加LZ4压缩后的大了近一半。而Kryo加LZ4的CPU开销反而更低,因为解压量小了。这就很能说明问题:压缩的优化,往往依赖它与相邻组件的联动设计。
4.3 列式格式下压缩和查询计划如何联动
要真正弄明白"压缩提升效率"的上限在哪,得稍微深入一下查询引擎内部的执行计划。Spark或Presto读取Parquet/ORC时,不只是简单地把文件读进来然后解压。查询引擎会先解析文件的元数据(footer),拿到每个Row Group的统计信息和列Chunk的位置。然后结合查询语句里的过滤条件,做三件事:行组裁剪(Row Group pruning)、列裁剪(Projection pushdown)、谓词下推。这些动作发生在解压之前。如果元数据层面能砍掉90%的Row Group,那这90%的数据从头到尾根本不会被解压。这就是我前面说的,压缩的真正效率是"那部分压根不用读"的效率,而不是省了那部分磁盘IO的搬运成本。
所以从实操角度看,我可以给你几个提效关键词:文件大小尽量接近或等于文件系统块大小(如Parquet推荐512MB到1GB一个文件),因为大文件能最大化行组裁剪收益;避免小文件泛滥,因为小文件让元数据开销占比过高,解压反而成了额外负担;分区字段选择低基数的列(如日期、地区),让分区裁剪能在文件列表层过滤掉大量文件。
这些原则如果拿捏好,集群CPU占用率会明显下降,任务的并发度也能降下来,等于同样的资源能支撑更多任务。
5. 实操踩坑记录和调优思路
5.1 压缩算法选型时最容易翻车的四个场景
前面对着讲原理,这一段我集中梳理一下实际项目中最高频的翻车点。每一个我都亲历过,或者帮人排查过,写出来给各位避坑。
- 全链路统一用Gzip。存储省了,CPU炸了。这是离线数仓最常见的翻车姿势。Gzip压缩率确实好,但压缩速度慢到令人发指,如果写入链路是实时入仓,甚至会造成写入堆积。分析型任务读取Gzip压缩的表也并非不行,但现在热数据表一概建议低CPU消耗算法,冷数据表再考虑Gzip或者高等级Zstandard。
- 使用不支持切分的高压缩比算法。在旧Hadoop版本里,某些压缩格式(特别是Gzip做文件级压缩)是不支持文件内切分的,会导致一个大文件只能由一个Map任务处理,完全毁掉并行度。现在的解决手段一般是在文件格式内部做分块(Parquet/ORC天然解决),或者升级版本让压缩流支持切分。这里最容易踩坑的是传统Hive表用Gzip压缩的纯文本文件。
- 在实时链路上选择高压缩等级。有的朋友图压缩率高,把Kafka和Flink的压缩等级调得极高。结果就是生产者侧CPU升高,消息发送延迟增加,端到端实时性变差。实时场景最忌讳的就是在线程关键路径上加高成本操作,压缩也不例外。
- 忽略压缩对文件大小的影响。在HDFS上,小文件是众矢之的。但如果你没意识到,同样的数据,使用高压缩率算法后,文件数量可能不变,但总文件体积变小了,会导致HDFS上单个Block里的文件数变少,Spark读取时的分区数变少,从而降低并行度。
5.2 调整压缩等级的正确姿势
Zstandard最灵活的玩法是那个"等级旋钮"。但我看到很多人在线上把等级当成摆设,默认等级用到底。实际上不同等级应该配合不同的数据存储分层。
我自己有一个调优方法论:同一份数据,用不同等级各压一遍,对比三组数据,压缩后的体积、压缩耗时、解压耗时。等级不用多,2、3、6、9、12选几个有代表性的就行。做完对比后,你心里就有数了。不要拍脑袋定等级,数据特征对压缩率的影响比算法差异还大。同一个Zstd等级,压订单表还是压日志表,结果天差地别。
以主流服务器的CPU规模来看,Zstandard等级3的压缩速度大约在300到500MB/s,等级9大约能压到80到150MB/s,等级12就在50MB/s上下甚至更低。如果数据写入吞吐达到几百MB/s,你每个等级差一点,CPU就可能会成为瓶颈。这需要实测才能找到既能压得住CPU、存储又合算的点,不要想当然。
5.3 监控和验收压缩效果时看哪些指标
压缩做得好不好,不能只看"存储降了多少"。我自己在验收个人数据管道的压缩效果时,会盯四个指标:
- 任务总耗时变化。同一个任务,改压缩前后对比,这是最直接的效率指标。
- 集群CPU利用率。很多团队只关心存储下降,不看CPU曲线,这是大忌。
- 磁盘IO吞吐量。压缩后如果随机读的IO量明显下降,说明压缩在IO层面起了作用。
- 压缩/解压吞吐率。观察任务跑的实时日志里,cotask的压缩耗时占比和吞吐曲线。通过对比,才能真正掌握集群能不能承受所选压缩策略开销。
某些云厂商的EMR和托管Hadoop集群,管理控制台上能看到这些指标,做压缩改造前先截图存证,改造后做对比,用数据说话,远比感觉有效。不管是跟领导汇报成本节省,还是自己下次复盘,都有据可循。
5.4 实测:压缩改造后,处理效率提升了多少
分享一个我自己做过的真实调优案例。有一张埋点日志表,每天新增约2TB原始日志,按天分区。改造前,数据以明文文本格式直接落HDFS,凌晨的ETL任务要扫描全表,跑一个核心指标计算要将近4个小时,集群在凌晨时段几乎被这个任务吸干,其他任务排队严重。
改造动作分三步:第一步,写入链路改成Parquet格式加Snappy压缩;第二步,对部分需要保留完整明细的冷分区,用Zstandard等级9做一次重写压缩;第三步,查询侧优化,把无关字段的读取去掉,让列裁剪生效。改造后效果非常明显,同样的核心指标计算,任务时间从4小时缩短到约1小时10分钟。磁盘占用从每天2TB降到大约350GB每天,既算省了存储成本,也极大缓解了凌晨任务排队的问题。这就是压缩带来的效率提升的真实样本。
还有一点很关键。团队当时最担心的就是CPU会不会成为新瓶颈。实际跑下来的数据是,CPU利用率不但没有升高,反而因为处理的数据量大大降低,整体CPU消耗比原来还低。道理也不复杂:当一个任务的瓶颈在磁盘IO时,把IO量降下来以后,CPU空闲自然就变多了,即使多花了一点解压开销,也是绝对划算的买卖。
6. 不同引擎里的压缩参数配置参考
6.1 Spark和Hive的压缩设置
Spark和Hive的压缩配置相对成熟,但版本之间略有差异,这里列举一份基于Spark 3.x和Hive 3.x的常用配置,供参考。
Spark侧需要关注的几个参数:
- spark.sql.parquet.compression.codec:设置Parquet文件写入压缩格式,默认snappy,可改为zstd等
- spark.sql.orc.compression.codec:设置ORC写入压缩格式,默认zlib(其实就是deflate),建议调成snappy或zstd
- spark.io.compression.codec:设置RDD内部数据(shuffle、cache等)的压缩格式,默认snappy,可改为lz4或zstd
- spark.io.compression.zstd.level:设置Zstandard压缩等级,默认1,可适当调高到3或5
- spark.rdd.compress:是否压缩RDD分区数据,默认false,内存不紧张可开启,能降低内存占用和GC压力
Hive侧对应的是:
- hive.exec.compress.output:控制Hive最终输出结果是否压缩
- mapreduce.output.fileoutputformat.compress.codec:设置输出压缩编码器
- hive.exec.orc.compression.strategy:针对ORC文件的压缩策略,可选SPEED或COMPRESSION
一个很实际的提醒是:如果Hive和Spark共用一个表(比如同一份Parquet数据被两个引擎读写),两套引擎里配置要一致,否则会出现写入端压缩和读取端识别不一致的兼容性问题,偶尔会报文件损坏错误。
6.2 Flink和Kafka的压缩设置
实时链路里,Flink和Kafka的搭配是主流。Kafka侧的压缩配置在Broker端和Producer端都有。Producer端建议开启压缩并选择合适的压缩类型,代码里配置:
- compression.type=lz4或zstd,zstd压缩率高但CPU开销稍大,对延迟敏感型应用选lz4更稳
- linger.ms可以适当调高配合压缩,单位时间攒更多消息再批量压缩发送,压缩率更高
Flink作业里要注意的是,如果从Kafka读压缩消息,反序列化时CPU消耗会比不压缩时高。所以实时作业的并行度和资源配置要留出解压的CPU余量。有些Flink作业在Kafka开启压缩后,Source算子的CPU上升明显,如果并行度不调整,反而可能导致数据处理延迟增加。我自己遇到过类似问题,一开始以为是Flink本身慢,排查后确认是Kafka压缩带来的CPU开销,调大并行度就解决了。
另外,Flink的Checkpoint数据也支持压缩。如果你用RocksDB做状态后端,可以配置state.backend.rocksdb.compaction.style和block压缩相关选项。状态量大的作业,开启RocksDB的block压缩能明显降低磁盘占用,但会增加一点点CPU开销。具体要不要开,需要结合状态大小和磁盘压力来判断。
6.3 使用压缩算法时还要考虑"文件可分割性"
前面提到过LZO在旧生态里地位高是因为支持切分,这个问题的本质是:Hadoop/Spark处理一个大文件时,文件能否按块切分给多个任务并行处理。如果是不可切分的压缩格式,比如老的Gzip压缩文件,一个几百GB的Gzip文件只能由一个Map任务从头解压到尾,并行度为1。这是效率毁灭性的问题。
如果你还在用老版本Hadoop处理这种文件,唯一的办法是先做一次预处理,把大Gzip文件重写成可切分的格式。新版本中,Parquet和ORC本身就按Block/RowGroup切分,不存在这个顾虑。所以如果你的数据文件还是不可切分压缩的文本格式,压缩的效率提升很可能会被单并发解压直接抹平,甚至变成负优化。遇到这种情况,先改文件格式,再谈压缩。
7. 冷热分层下的压缩策略设计
7.1 热数据、温数据、冷数据各用什么方案
大数据平台的数据天然有时效性。日志数据近3天被频繁查询,近3个月偶尔被跑一次,一年前的数据基本没人查,但合规要求要保留。面对不同的数据温度,压缩策略应该分层设计。
我自己目前常用的一套组合拳是:
- 热数据(近1到7天):Parquet + Zstandard等级1到3,或者Snappy。目标是查询快、CPU省、写入不拖后腿。
- 温数据(近1到3个月):Parquet + Zstandard等级6到9。压缩率明显提升,查询少但依然要求随时可用。
- 冷数据(3个月以上):ORC/Parquet + Zstandard等级12以上,或Gzip。存放在冷存储介质上,最大化降低存储成本。查询频率极低,多付出一些解压CPU完全可接受。
整体思路就是,把CPU用在刀刃上。热数据查询频繁,CPU开销不能大,压缩次要;冷数据几乎不读,CPU花费本来就少,所以压缩率优先。
7.2 数据压缩与存储成本治理的联动
数据压缩在大数据成本治理里是个非常有效的抓手。现在很多公司的存储是上云的对象存储或者云厂商的HDFS,存储成本是按实际占用空间计费的。通过压缩率较高的Zstandard或Gzip对冷数据进行重压缩,可以直接在账单上看到明显的存储成本下降。
有朋友问过我,压缩本身也要耗费计算资源去跑压缩任务,是不是不划算?这需要算一笔账。以云厂商为例,跑一个压缩任务的成本通常远小于节省的存储月费,而且是一次性投入,持续省钱。比如你的冷数据有500TB,用Zstd等级9重新压缩,体积从500TB降到180TB,每月的存储费用直接省了一多半。而跑一次压缩任务的计算成本,折合下来可能只是省下费用的零头。这类成本治理动作,很多公司每个季度做一次,效果立竿见影。
7.3 数据压缩率和查询效率的平衡点
凡事都有度,压缩率拉到极致并不总是好事。Bzip2的压缩率最高,但查询慢到不能用,原因就是解压成了瓶颈。所以你要找到一个压缩率和查询效率的平衡点,这个点定义上不是固定的,受到查询频率、集群CPU规模、数据量、存储成本等多个因素影响。
我给一个省心的参考方案:如果你的集群CPU不紧张、磁盘有压力,用Zstandard等级6左右,它在压缩率和CPU消耗之间相当均衡,绝大多数场景不会翻车。如果你的集群CPU紧张、磁盘还好,那用Snappy或LZ4保平安。如果磁盘告急、CPU有空闲,上Zstandard等级12或者Gzip都行。在线的经验法则是:先满足查询性能SLA,再追求存储成本优化。主次顺序反了,一般都会在线上吃苦头。
8. 关于数据压缩未来的扩展思考
技术选型并不是一劳永逸的事。压缩算法和存储格式的生态也在持续演进,比如Parquet的新版本对嵌套数据、向量化读取的原生支持越来越好,Zstandard的硬件加速(配合Intel QAT或Zstd-N)也在进入服务器生态,未来解压的CPU成本可能进一步下降。到那时候,高压缩率算法的用武之地会更广,很多现在的"CPU换空间不划算"的结论,可能也要重新评估。
但我个人的体会是,技术演进的方向不会改变那条底层逻辑:大数据场景里的核心矛盾一直是IO与CPU的失衡,压缩是调节这个失衡的旋钮之一。谁先把这个旋钮用明白了,谁就能让自己的数据处理管道跑得更快、更稳、更省钱。
最后分享一个实操中非常有用的习惯:每次新建表结构的时候就问自己三句话。这份数据被读的频率高吗?查询时允许的延迟是多少?存储成本是我现在的主要矛盾还是次要矛盾?把这三个问题想清楚,压缩算法、文件格式这些选择就自然浮出水面了。不用追求最优,只需要每次都不踩大坑,长期下来数据处理效率就会稳定地跑在健康水位上。
