几百字节的小记录怎么压?单独压缩几乎没效果,攒批又要延迟。zstd 字典把“记录之间共有的内容”提前交给压缩器。这篇量字典大小、样本数、记录大小、泛化、过期和使用代价。
方式 2000 条共 平均每条 压缩比 不压缩(紧凑 JSON) 719257 359.6 1.00× gzip-6 548649 274.3 1.31× zstd-3,无字典 577103 288.6 1.25× zstd-3,16 KiB 字典 218147 109.1 3.30× zstd-19,16 KiB 字典 213615 106.8 3.37× 对照:每 100 条攒成一批、无字典 zstd-3:174706 字节(平均每条 87.4,4.12×) 解压校验(200 条):全部一致
压缩器靠“前面出现过什么”。单条记录压缩时,前面什么都没有,记录自己的键名和公共前缀白白占字节(360 字节只压到 288.6 字节)。54 字节的行甚至压缩后更大(62.9 字节)。
字典大小 样本 100 样本 500 样本 2000 样本 5000
1KiB 132.6 131.2 130.3 130.4 (平均每条字节)
4KiB 122.6 120.7 120.6 120.6 (平均每条字节)
16KiB 113.1 109.7 109.7 109.1 (平均每条字节)
64KiB 114.6 100.9 99.6 98.8 (平均每条字节)
110KiB 114.6 100.6 97.4 97.6 (平均每条字节)字典是一段训练出来的样本片段(以及预先算好的熵编码表),压缩和解压时当作“已经见过的内容”。单条 products 记录从 288.6 字节降到 109.1 字节(3.30×),接近攒 100 条一批的 87.4 字节/条。
记录类型 平均大小 无字典 有字典 无字典压缩比 有字典压缩比 timeseries 一行 54 62.9 30.7 0.86× 1.75× products 一条 361 289.6 110.8 1.25× 3.26× nested 一个文档 1852 429.3 265.0 4.31× 6.99× pages 一页(13 KiB) 13904 4012.2 3182.0 3.47× 4.37×
1 KiB 的字典就能降到约 130 字节/条;16 KiB 109.1;64 KiB 98.8;收益递减。样本太少时大字典没有用:100 条样本训练 64 KiB 字典是 114.6,500 条样本是 100.9。
字典共 16384 字节,字典 ID 1516849344;开头 4 字节魔数 37a430ec;后面先是熵编码的表(Huffman/FSE 表),最后是一段样本里常见的原始内容
字典最后 300 字节(原始内容部分):'guwa"],"category":"fecuraka/gisezamu/hajifudu","updated_at":"2026-10-09T02:51:16Z","seller":{"id":644,"name":"shop-pola"}}{"id":1000024,"url":"https://shop.example.com/item/1000024?ref=list&page=24","title":"rebohofa teci jiwaji neze bekufu geda saru munu ritabina","price":122.58,"currency":"USD","s'
用字典压缩的帧头里记录了字典 ID:1516849344(与字典自己的 ID 1516849344 一致);无字典压缩的帧头 dict_id = 0记录越小收益越大(54 字节:0.86× 到 1.75×;361 字节:1.25× 到 3.26×;13904 字节:3.47× 到 4.37×)。字典只对训练样本相似的数据有效:用 products 的字典压 timeseries 几乎没效果(59.8 对 30.0)。
products 字典压 timeseries 一行:无字典 62.9 字节/条,products 字典 59.8 字节/条,自己的字典 30.0 字节/条 数据漂移(换了词表、新增 brand 和 warehouse 字段)的 products:无字典 313.0 字节/条,旧字典 148.8 字节/条;而同样字段不漂移的测试集:无字典 288.4,旧字典 109.0 用漂移后的数据重新训练的字典(新数据 500 条 + 少量旧数据):漂移数据 115.6 字节/条
数据的形态变了(词表变、字段增加),旧字典就变差(148.8 对未漂移的 109.0)。要定期用新数据重新训练,新字典带新的 ID,旧数据要用旧字典解。
无字典:压缩 500 条单条记录 中位 1.8 ms(平均每条 4 微秒);解压 500 条 中位 0.7 ms(平均每条 1 微秒) 16 KiB 字典:压缩 500 条单条记录 中位 0.6 ms(平均每条 1 微秒);解压 500 条 中位 0.3 ms(平均每条 1 微秒) 不带字典的解压器解压带字典的帧:失败:ZstdError: decompression error: Dictionary mismatch 带另一份字典的解压器解压带字典的帧:失败:ZstdError: decompression error: Dictionary mismatch
解压端必须有同一份字典,帧头里的字典 ID 让它知道要哪一份;不匹配就报 Dictionary mismatch。字典要和数据一起保管、版本化。