Postgres的JSON字段存OSS方案

59 阅读2分钟

Postgres 存储 JSON 字段(可能是 jsonb)数据量巨大,增长很快,长期占用 DB 存储,影响性能与成本。解决办法是 冷热分离 ——把大 JSON 存到对象存储(OSS/S3),数据库里只存元信息与引用。


方案设计

1. 存 OSS,不存 JSON 本体

  • OSS(阿里云 OSS、MinIO、S3 兼容存储等)擅长存储大对象、冷热数据。

  • Postgres 只保存:

    • JSON 的 存储路径/Key(例如 oss://bucket/path/uuid.json)。
    • JSON 的必要 摘要信息(比如大小、MD5、时间戳、业务主键)。
  • 查询时通过 OSS SDK/API 取数据,不必压在 DB 内。

2. 表结构改造

原来:

CREATE TABLE data (
    id BIGSERIAL PRIMARY KEY,
    biz_id TEXT,
    payload JSONB,
    created_at TIMESTAMPTZ DEFAULT now()
);

改造后:

CREATE TABLE data (
    id BIGSERIAL PRIMARY KEY,
    biz_id TEXT,
    payload_url TEXT,          -- 存 OSS Key 或 URL
    payload_md5 TEXT,          -- 校验用
    payload_size BIGINT,       -- 记录大小
    created_at TIMESTAMPTZ DEFAULT now()
);

3. 写入流程

  1. 应用接收 JSON。

  2. 生成唯一 key(如 UUID + 日期)。

  3. JSON 压缩(gz 或 zstd)后上传 OSS。

  4. OSS 返回 URL/etag,写入 Postgres payload_url 和相关元信息。

  5. 不再把大 JSON 放进 DB。

4. 读取流程

  • 如果业务需要全量 JSON:

    应用查 DB 拿到 payload_url → SDK 从 OSS 下载 → 解压后使用。

  • 如果业务只需要部分字段:

    • 可以额外存储一些 索引字段 在 DB,避免每次读 OSS。

    • 比如 JSON 有 user_id, order_type,就可以拆出来单独建字段/索引。

5. 优化点

  • 压缩:JSON 存 OSS 前可 gzip/zstd,大幅节省空间。
  • 冷热分层:热数据(近 7 天)可以保留在 DB;超过一定时间归档到 OSS。
  • 自动迁移:写一个定时任务,将老数据迁出 DB,只保留 URL。
  • 查询场景考虑:如果经常需要对 JSON 内部字段过滤/聚合,最好在写入时 同步存索引字段,不然每次都得拉 OSS,很慢。

优缺点对比

✅ 优点

  • 大幅降低 Postgres 存储压力,避免膨胀。

  • 成本低:OSS 比 DB 存储便宜。

  • 可以配合归档策略(冷热分层)。

⚠️ 缺点

  • 查询复杂度高:不能直接 WHERE payload->>'field' = 'xxx'。
  • 延迟:读大 JSON 需要额外 OSS IO。
  • 需要应用层适配写入/读取逻辑。

可选方案

  • pg_largeobject / TOAST 压缩:Postgres 自带,但仍然放在 DB,不解决膨胀问题。
  • TimescaleDB + 外部存储插件:有些扩展支持外部文件存储。
  • FDW(Foreign Data Wrapper)+ OSS:例如 postgres_fdw_s3,可直接把 OSS 当外部表。

要不要我帮你写一个 迁移脚本 Demo?比如:把 payload 里的 JSON 导出到 OSS,DB 字段替换成 oss_url,支持批量迁移老数据。