4、数据压缩与去重:LZ4/Zstd压缩算法选择、在线去重技术、压缩对加密性能的影响
数据压缩和去重,说白了就是用计算时间换存储空间。在安全存储场景里,这个交换没那么简单——你压缩完了还得加密,加密完了还得保证性能。我见过不少团队,一上来就上最高压缩比,结果IO延迟直接翻倍,业务方天天投诉。
这一章,咱们就聊聊怎么选压缩算法、怎么搞在线去重,以及压缩和加密之间那点“相爱相杀”的关系。
4.1 LZ4 vs Zstd:压缩算法的选型博弈
目前主流的压缩算法就两个阵营:LZ4和Zstd。你可能会问,为什么不提Gzip?嗯,Gzip在存储场景里基本被淘汰了——压缩比不如Zstd,速度不如LZ4,两头不讨好。
先看一张对比表,心里有个底:
| 特性 | LZ4 | Zstd |
|---|---|---|
| 压缩速度 | 极快(~500 MB/s) | 较快(~200 MB/s) |
| 解压速度 | 极快(~1.5 GB/s) | 快(~800 MB/s) |
| 压缩比(文本数据) | 2:1 ~ 3:1 | 3:1 ~ 5:1 |
| CPU占用 | 低 | 中等 |
| 适用场景 | 热数据、实时写入 | 温数据、归档、备份 |
我个人习惯这样选:写路径延迟敏感的场景,无脑上LZ4。比如数据库的WAL日志、实时监控数据的写入管道,这些地方CPU不能成为瓶颈。LZ4的压缩速度几乎是内存拷贝级别的,你几乎感觉不到它在压缩。
反过来,如果存储空间是稀缺资源,或者数据写入频率不高,Zstd是更好的选择。我在一个备份系统中遇到过,每天全量备份的数据量从3TB压缩到800GB,用的就是Zstd level 3。压缩时间多了30%,但存储成本省了70%,这笔账怎么算都划算。
核心原则:压缩算法的选择不是“哪个更好”,而是“哪个更适合你的IO模型”。LZ4保延迟,Zstd保空间,两者可以共存于同一系统——热数据用LZ4,冷数据转Zstd。
4.2 在线去重技术:实时消除冗余
去重和压缩是两回事。压缩是在一个数据块内部找重复模式,去重是在全局范围内找重复的数据块。在线去重,就是数据还在内存里、还没落盘的时候,就把重复的块干掉。
在线去重的核心流程,我画了张图:
在线去重有几个关键点,我踩过坑,给你提个醒:
- 分块策略:固定分块(4KB/8KB)实现简单,但遇到数据偏移就废了。变长分块(CDC)用内容定义边界,去重率更高。我在一个文件存储系统里,从固定4KB切块换成CDC,去重率从35%提升到52%。
- 指纹索引:这是性能瓶颈。内存里放不下全部指纹怎么办?布隆过滤器先过滤一遍,命中再查精确索引。我曾经遇到过索引太大导致GC频繁,后来改成分层索引——热区指纹放内存,冷区放SSD,问题就解决了。
- 写放大:去重需要先计算指纹、查索引,然后才决定写不写。这个流程本身就有延迟。如果每个4KB块都走一遍,CPU扛不住。我建议至少16KB起步,或者用异步去重——先写后查,后台再回收重复块。
实战技巧:在线去重不要追求100%去重率。达到70%之后,每提升1%带来的计算开销都指数级增长。设定一个阈值,比如“指纹命中率超过80%就停止查索引”,能有效保护CPU。
4.3 压缩对加密性能的影响:先压缩还是先加密?
这个问题,我直接给结论:先压缩,后加密。原因有三:
- 压缩率更高:明文数据有大量重复模式,压缩效果好。加密后的数据是伪随机序列,几乎无法压缩。你想想看,先加密再压缩,压缩算法面对一堆乱码,根本找不到规律,压缩比接近1:1,白费力气。
- 性能更好:压缩后的数据量变小,加密引擎需要处理的数据就少。虽然压缩本身有开销,但总体IO路径上的时间反而更短。我做过测试:1GB明文数据,先压缩(LZ4)再加密(AES-256-XTS),总耗时比先加密再压缩少了40%。
- 安全性不受影响:压缩不会泄露明文信息(除非你用了有漏洞的压缩算法,但LZ4和Zstd都没这个问题)。加密在压缩之后做,安全性完全由加密算法保证。
不过,这里有个例外——压缩长度泄露攻击。如果攻击者能控制部分明文内容,并且能观察到压缩后的数据长度变化,就有可能推断出其他部分的内容。CRIME和BREACH攻击就是利用这个原理。在安全存储场景里,如果你的数据包含用户输入的混合内容(比如HTTPS请求),我建议关闭压缩,或者使用固定长度的压缩块。
注意:先压缩后加密是通用原则,但如果你使用硬件加密引擎(如Intel QAT或NVMe加密盘),压缩后的数据块大小可能不匹配硬件加密的固定块大小(通常是512B或4KB)。这时候需要在压缩层做padding对齐,否则加密性能会大幅下降。
4.4 综合策略:分层压缩与去重
在实际系统中,我不会把压缩和去重混在一起做。我的做法是分层:
| 层级 | 技术 | 算法/工具 | 目标 |
|---|---|---|---|
| L1:在线去重 | 变长分块 + SHA-256指纹 | CDC + Bloom Filter | 消除全局重复块 |
| L2:快速压缩 | LZ4 | 块级压缩(16KB~64KB) | 减少IO路径延迟 |
| L3:高压缩比 | Zstd | 流式压缩(冷数据迁移时触发) | 最大化存储利用率 |
这个分层策略,我在一个对象存储项目里用过。热数据走L1+L2,写入延迟控制在200μs以内;冷数据通过后台任务走L3,压缩比从2.5:1提升到4.2:1。整体存储成本降低了55%,而且对业务几乎没有感知。
嗯,最后说一句:压缩和去重不是银弹。如果你的数据已经是压缩过的(比如图片、视频、加密文件),再压也压不出多少空间。做之前,先分析一下数据特征,别盲目上。