大家常说“换 Protobuf 就小了”。压缩之后还是这样吗?这篇逐字段拆开看谁赢了哪一列,再把数据模型的改动和格式的改动分开量,看收益到底来自哪里。
字段 JSON 原 →zstd MsgPack 原 →zstd Protobuf 原 →zstd 谁压缩后最小 id 160001 8886 100003 62871 60004 44014 JSON url 1135001 11989 1115003 12386 1115000 12248 JSON title 1223717 413803 1203160 436587 1203716 436512 JSON price 118460 49891 180003 55116 160004 63401 JSON currency 120001 6301 80003 5942 100000 6125 MsgPack stock 75643 31332 44645 29661 34860 26799 Protobuf rating 80001 20284 180003 31267 160004 30526 JSON tags 772005 99219 652007 127291 712004 128992 JSON category 451749 35091 411751 35938 431748 36111 JSON updated_at 460001 100143 420003 99373 440000 99867 MsgPack seller_id 77314 33137 50477 32279 36844 29016 Protobuf seller_name 280281 45325 240283 56549 260280 57243 JSON 各列压缩后合计:JSON 855401、MessagePack 985260、Protobuf 970854
逐列比较,Protobuf 只在 stock、seller_id 这类小整数列上压缩后最小;id 这类连续整数,十进制文本反而被压得很干净(JSON 8886 字节,Protobuf 44014 字节);字符串列 JSON 略优(第 3 篇讲过长度前缀的原因)。
一条记录:原模型 JSON 358 字节、新模型 JSON 240 字节;原模型 Protobuf 241 字节、新模型 Protobuf 93 字节
新模型示例:{"id":1000000,"page":0,"title":"wico biwa wasu kosekefo julomesa zanisa wegiraka","price_cents":11340,"currency":0,"stock":249,"rating10":11,"tags":[53,24,27],"category":19,"updated":1790893697,"seller_id":739,"seller_name":"shop-kujimuge"}价格存整数分、评分存整数、分类和标签存编号、时间存 Unix 秒、去掉可以由别的字段推出的 url:单条 JSON 从 358 字节降到 240 字节,Protobuf 从 241 降到 93。这些改动与格式无关,JSON 同样受益。
组合 原始 zstd-3 zstd-19 gzip-6 原模型 JSON Lines 7214161 1331194 1047098 1484671 原模型 Protobuf 4914350 1378051 1081117 1449113 新模型 JSON Lines 4955536 1169428 927076 1258389 新模型 Protobuf 2106013 1032452 918732 1042267 换数据模型的收益(zstd-3):JSON 12.2%、Protobuf 25.1%;换格式的收益(新模型,JSON → Protobuf):原始 57.5%、zstd-3 +11.7%
zstd-3 压缩后,改数据模型让 JSON 小 12.2%、Protobuf 小 25.1%;在新模型下 JSON 换成 Protobuf 只小 11.7%(zstd-19 下只小 0.9%)。换格式的收益比数据模型小。
2 万个 title 用换行拼起来 1183715 字节,zstd-3 409620、zstd-19 364005;占新模型 JSON zstd-3 总量 1169428 的 35.0% 去掉 title 以后:新模型 JSON 634906、新模型 Protobuf 526241(zstd-3 字节),title 单独压缩 409620
title 是随机拼的词,单独压缩要 409620 字节(zstd-3),占新模型 JSON 压缩后总量的 35%。内容本身的熵决定下限,换格式降不下来。
组合 不压缩 zstd-3 zstd-3+字典 原模型 JSON 359.6 288.6 109.1 原模型 Protobuf 242.6 232.7 106.5 新模型 JSON 246.7 199.9 95.3 新模型 Protobuf 103.1 112.1 79.3
新模型 Protobuf 单条 103.1 字节,无字典 zstd-3 反而是 112.1;加字典 79.3 字节。数据模型优化和字典叠加后,单条从 359.6 字节降到 79.3 字节。
新模型 列式 JSON 原始 2555549 zstd-3 782551 zstd-19 703321 gzip-6 802876 原模型 列式 JSON 原始 4954174 zstd-3 864712 zstd-19 745186 gzip-6 919905 新模型 Protobuf 原始 2106013 zstd-3 1032452 zstd-19 918732 gzip-6 1042267 新模型 JSON Lines 原始 4955536 zstd-3 1169428 zstd-19 927076 gzip-6 1258389 原模型 JSON Lines 原始 7214161 zstd-3 1331194 zstd-19 1047098 gzip-6 1484671 原模型 Protobuf 原始 4914350 zstd-3 1378051 zstd-19 1081117 gzip-6 1449113
把数据模型改好、按列排布、再用 zstd-19 压缩的 JSON(703321 字节)比任何 Protobuf 变体都小。格式不是体积的决定因素,数据本身的表示、排列和压缩级别才是。如果你要的是类型安全、版本演进、跨语言,Protobuf 仍有它的价值(第 5、6 篇),只是不要指望它自己带来多大的压缩收益。