SerpBase 快照归档:zstd 压缩与月包校验

磁盘涨得比数据快

采集跑了一年,原始快照占了二十多个 GB。数据本身不大,膨胀的是「每条响应一个文件」的存法:几十万个小 JSON,目录列表都慢。

归档目标很明确:按月打包、压缩、留校验,需要时能精确恢复到某一天。

为什么选 zstd

gzip 兼容性最好,但压缩比和速度都一般;zstd 在同等级别下压缩比更高、解压更快,Python 有现成的 zstandard 库。

对一个两千万行的 JSON 语料,zstd 通常能压到原来的十分之一左右,具体比例和字段重复度有关,先拿一个月的数据实测再定 level。

月包:一个 tar.zst 加一份清单

按月打包,每个月产出一个 snapshots-2026-09.tar.zst 和一份 snapshots-2026-09.manifest.json。

清单里记三样:文件数、总字节数、包的 sha256。回滚和审计都靠它。

import hashlib
import json
import tarfile
from pathlib import Path

import zstandard as zstd

def build_month(files: list[Path], out_path: Path):
    manifest = {"files": [], "count": len(files), "bytes": 0}

    cctx = zstd.ZstdCompressor(level=10)
    with open(out_path, "wb") as fh:
        with cctx.stream_writer(fh) as zw:
            with tarfile.open(fileobj=zw, mode="w|") as tar:
                for p in files:
                    tar.add(p, arcname=p.name)
                    manifest["files"].append(p.name)
                    manifest["bytes"] += p.stat().st_size

    digest = hashlib.sha256()
    with open(out_path, "rb") as fh:
        for chunk in iter(lambda: fh.read(1 << 20), b""):
            digest.update(chunk)
    manifest["sha256"] = digest.hexdigest()
    out_path.with_suffix(".manifest.json").write_text(
        json.dumps(manifest, ensure_ascii=False), encoding="utf-8"
    )

mode="w|" 是流式写入,不需要先把整月数据读进内存。几十万文件也扛得住。

校验:别只看文件存在

归档最容易出的问题是「文件在,内容坏了」。校验分两层:

  • 包级:sha256 和清单对得上
  • 抽检:随机解出三个文件,json.loads 能解析、request_id 字段存在
def spot_check(archive: Path, names: list[str]):
    dctx = zstd.ZstdDecompressor()
    with open(archive, "rb") as fh, dctx.stream_reader(fh) as zr:
        with tarfile.open(fileobj=zr, mode="r|") as tar:
            for member in tar:
                if member.name in names:
                    data = json.loads(tar.extractfile(member).read())
                    assert data.get("request_id"), member.name

包级校验防传输损坏,抽检防序列化错误——两层都做,归档才算完成。

恢复:按天取,不整包解

恢复某一天的数据时,不需要把整月解压到磁盘:

zstd -dc snapshots-2026-09.tar.zst | tar -xO 2026-09-15.json > day.json

-O 直接输出到标准输出,一个文件一个文件取。小团队排查问题,这个操作比「解压整包再找」快得多。

我做错的一版

第一版按文件 gzip:每条响应一个 .json.gz。压缩率不错,但文件数量翻了一倍,ls 一屏刷不完,上传对象存储时逐个请求,光网络往返就花了半天。

改成月包之后,一个月一个文件,清单落在旁边,上传、校验、恢复都变成单文件操作。

归档的单位应该是「批次」,不是「原文件」。 想清楚这点,后面的维护成本会低一个量级。

现在每月 1 号跑一次归档,清单进数据库,原始小文件删掉。一次网页搜索 1 credit,新账号 500 免费 credits,攒数据容易,管数据才是长期功课。

SerpBase 的字段结构见 SerpBase 文档。

posted @ 2026-11-02 11:27  蜘蛛人  阅读(12)  评论(0)    收藏  举报