实测 zstd 级别 -5 到 22:从级别 3 到 19 体积小 21% 但压缩慢 130 倍;解压速度与级别无关(约 1700–2000 MiB/s);级别 22 需要 128.7 MiB 压缩内存;和 gzip、lz4、snappy、xz、brotli 的压缩比—速度对照;多线程只在较低级别加速。
自己写解析器拆开 LZ4 block 和 Snappy 的真实输出:没有熵编码,字面量原样存放,偏移最大 65535;products 上 LZ4 的 338418 字节里元数据占 58%;速度优先的压缩器压缩比比 zstd-3 低 40% 以上;短输入的固定开销。
窗口决定压缩器能往回看多远:重复相隔 1 MiB 要 windowLog 21 才找得到;windowLog 27 也找不到相隔 16 MiB 的重复,要开长距离匹配;压缩端声明了大窗口,解压端不准备足够内存就会失败;每个 4 KiB 对象单独压缩比整体压缩大 62%。
实测 zstd 字典:单条 360 字节的商品记录无字典只有 1.25×、16 KiB 字典 3.30×;54 字节的行无字典反而变大(0.86×);1 KiB 字典就能从 288.6 降到约 130 字节/条;数据漂移后旧字典变差,重新训练恢复;解压端没有同一份字典会失败。
在 HTTP 场景里比较 gzip、brotli、zstd:brotli q5 在英文 HTML 样板上比 gzip 小 27%(应来自内置字典)、在乱码词上与 gzip 相同;requests 装了 brotli 和 zstandard 后会发 gzip, deflate, br, zstd;随机数据再压变大;brotli q11 现压缩每页 11 ms;慢网络压缩划算、快网络现压缩高级别不划算。
3 种编码 × 7 种压缩的矩阵:products 上最小的是 JSON + brotli q11(是未压缩 JSON 的 14.1%);zstd-3 压缩后 Protobuf 比 JSON 大 3.5%;timeseries 上 JSON + zstd-19 比 Protobuf + zstd-19 小;压缩级别越高,编码格式之间的差距越小甚至反转;解码速度 Protobuf 快 3–5 倍。
压缩过的 JSON 与 Protobuf:12 列里 JSON 压缩后有 8 列更小;把价格存整数分、分类存编号、去掉可推出的字段这类数据模型优化,收益(JSON 少 12.2%,Protobuf 少 25.1%)比换格式大;最小的是改了数据模型再按列排的 JSON(703321 字节,zstd-19)。
爬虫结果落盘的 8 种方式:每条单独 zstd 的 sqlite BLOB 只省 12%,加 16 KiB 字典 34%,每 100 条一块 24.6%,整个文件一个 zstd 帧 18.5%;整个帧点查 7657.5 微秒对 5.5 微秒;关系列聚合 1.1 ms 对逐行 json.loads 58.5 ms;1500 个小文件按块多占 17.8%;sqlite 删了一半数据文件不缩小。
在独立的 redis-server 上测队列里的任务体:10 万个 376 字节的任务,JSON 每任务占 392.9 字节、Protobuf 265.8、JSON + zstd 字典 52.8;入队出队吞吐不降;50 个任务打包成一个元素每任务只占 36.1 字节;页面缓存用 zstd 字典从 68.7 MiB 降到 14.9 MiB。
六个场景的选型,每个都附测得的数字和前提:单条小消息(Protobuf + 字典 106.5 字节,JSON + 字典 109.1)、批量归档(xz-9 988040 最小)、批量热数据(Protobuf + lz4 读 8.2 ms)、HTTP 动态响应(zstd-3 体积乘耗时最小)、缓存(zstd + 字典省 79%)、不值得压缩的情况。
在 Scrapy 2.19 上给引擎的每一层挂钩子,记录一个请求的完整路径(29 个事件);默认队列是后进先出、不是严格深度优先;CONCURRENT_REQUESTS 从 1 到 24 总耗时 7.34 s 降到 0.34 s;回调返回字符串和整数也被当成 item;DontCloseSpider 之后 spider_idle 每约 5 秒重复一次。
用自己写的 TCP 抓包代理看一次 gRPC Unary 调用的 HTTP/2 帧:请求是 HEADERS 加 DATA,响应是 HEADERS、DATA 和带 grpc-status 的 trailers;消息是 5 字节前缀加 protobuf;首次调用请求头 82 字节,第二次因为 HPACK 只有 15 字节。