冷热数据分层与归档

讲解冷热数据识别方法、归档策略设计、基于分区的秒级归档、历史库建设与查询路由,以及归档过程中的校验与可回溯性保障。

1. 为什么需要冷热分层

一句话总结: 90% 的访问集中在新近 10% 的数据上,让热数据与冷数据共用一套昂贵存储是巨大浪费——冷热分层把"热"留在高性能层,“冷"迁到低成本层。

数据库成本随数据量线性增长,但访问热度是幂律分布:新写入的数据被高频查询,几个月前的历史数据几乎无人问津。把所有数据都放在同一套热存储上,等于为"几乎不读的老数据"持续支付高昂的存储与索引成本。

访问热度(典型幂律):
  最近 7 天数据  →  80%+ 的查询流量
  最近 90 天数据 →  15% 的查询流量
  一年前数据     →  5% 的查询流量(多为审计/合规)

存储成本(SSD vs 归档):
  热层 SSD   :每 GB 成本高、性能好
  冷层归档/对象存储:每 GB 成本可低一个数量级

冷热分层带来的收益不仅是省钱,还让热表的索引更小、锁竞争更少、备份更快。

2. 冷热识别:谁该进冷层

2.1 冷热的判断维度

维度热数据冷数据
访问频率秒级/分钟级被查几乎不查 / 偶发
时间窗最近 7~90 天超过保留窗口
变更特性可能更新只读,永不改
价值支撑线上业务合规、审计、分析

2.2 冷热识别的工程化做法

不要靠感觉,用访问统计驱动:

-- 用慢日志/审计日志统计:不同时间窗数据的查询占比
-- 例如统计表 orders 中,按 created_at 时间窗的查询命中分布
SELECT DATE_FORMAT(created_at, '%Y-%m') AS month,
       COUNT(*) AS row_count
FROM orders
WHERE created_at < DATE_SUB(NOW(), INTERVAL 90 DAY)
GROUP BY month ORDER BY month;
识别口诀:
  1. 以业务保留期为准(合规要求 3 年,就必须留 3 年)
  2. 以访问统计为准(连续 90 天无查询,进冷层)
  3. 以运维窗口为准(每晚 2 点做归档任务)

一句话: 冷热分界不是一个固定天数,而是"业务保留期 + 访问频率 + 存储成本"三个约束的交点。先定保留期,再按访问统计划冷热线。

2.3 热表与冷表的物理差异

热与冷不只是逻辑概念,它们在物理层有截然不同的形态:

维度热表冷表/归档表
存储介质SSD/NVMeHDD/对象存储/廉价 SSD
索引完整,按查询路径建精简,只保审计与检索
压缩少压缩,保随机读高压缩(列式/页压缩)
备份高频率低频甚至免备份
锁行级,最小化只读,无并发写
冷层该「牺牲什么」:
  ✅ 牺牲随机读性能(老数据访问极少)
  ✅ 牺牲更新能力(冷数据只读)
  ❌ 不能牺牲:可检索、可恢复、可审计

冷热分层不是「把热表复制一份到便宜机器」,而是按访问模式重新设计存储形态:热表为点查优化,冷表为压缩与低成本优化。

3. 归档策略设计

3.1 归档的目标与原则

归档三个目标:
  1. 热表瘦身:表变小 → 索引小、锁少、查询快
  2. 成本控制:冷数据进低成本存储
  3. 合规保留:老数据仍可查询/审计

归档五个原则:
  □ 可回溯:归档数据必须可查询、可校验
  □ 可逆:迁移出错的冷数据能回退到热层
  □ 定时:归档任务可编排、可重试、可监控
  □ 无感:归档过程不阻塞线上读写
  □ 一致:归档后主键/约束/统计信息不丢

3.2 归档策略矩阵

维度策略选项
归档对象全表归档 / 按时间窗分批
归档去向同实例冷表 / 独立历史库 / 对象存储
归档频率每日 / 每周 / 每月
保留期热层 N 天 / 冷层 M 年
触发方式定时任务 / binlog 订阅触发
一个典型策略:
  订单表 orders
    热层保留:最近 90 天(SSD,主库)
    冷层保留:90 天 ~ 5 年(历史库 orders_archive)
    过期清理:超过 5 年 → 导出到对象存储 → 删除

4. 分区归档:最优雅的路径

4.1 时间分区 + 秒级搬迁

如果表按时间做了 RANGE 分区,归档就可以用分区操作实现"秒级搬移”:

-- 按天分区的订单表
CREATE TABLE orders (
  id BIGINT,
  created_at DATETIME,
  amount DECIMAL(10,2),
  PRIMARY KEY (id, created_at)
) PARTITION BY RANGE (TO_DAYS(created_at)) (
  PARTITION p20260629 VALUES LESS THAN (TO_DAYS('2026-06-30')),
  PARTITION p20260630 VALUES LESS THAN (TO_DAYS('2026-07-01')),
  PARTITION pFuture  VALUES LESS THAN MAXVALUE
);

-- 归档 30 天前的分区:先交换到归档表,再 DROP
ALTER TABLE orders EXCHANGE PARTITION p20260530 WITH TABLE orders_archive;
ALTER TABLE orders DROP PARTITION p20260530;
对比:
  DELETE 1000 万行老数据   → 分钟级,产生大量 redo/binlog
  DROP 一个分区            → 秒级,直接释放数据文件
  所以:能用分区,就绝不用 DELETE 归档。

4.2 分区归档的自动化流程

每日归档流水线:
  1. 识别到期分区(created_at < 今天 - 90 天)
  2. EXCHANGE 到归档表(同库,结构一致)
  3. 校验归档表行数 = 原分区行数
  4. DROP 原分区,回收热层空间
  5. 归档表导出 → 历史库/对象存储 → 本地归档表清空

一句话总结: 时间分区让归档从"逐行 DELETE"进化为"分区级秒搬",代价是建表时就要按时间设计分区并保证分区键进主键。

4.3 非分区表的归档兜底方案

历史遗留表没有分区怎么办?不要急着 DELETE,用「时间窗分批 + 分批删除」:

-- 分批归档:每次取 1 万条老数据搬到归档表
INSERT INTO orders_archive (id, created_at, amount)
SELECT id, created_at, amount FROM orders
WHERE created_at < DATE_SUB(NOW(), INTERVAL 90 DAY)
LIMIT 10000;

-- 确认归档成功后,分批删除原表(加 LIMIT 防大锁)
DELETE FROM orders
WHERE created_at < DATE_SUB(NOW(), INTERVAL 90 DAY)
LIMIT 10000;
分批规则:
  □ 每批 5000~10000 行,批间 sleep 0.1~1s
  □ 跑在业务低峰期,观察主从延迟与磁盘 IO
  □ 用主键排序保证分批可断点续跑

这是「没有分区时的妥协方案」:分批 + LIMIT 控制锁与复制延迟。治本方案仍是先给大表补上时间分区,之后归档走 EXCHANGE/DROP 秒级完成。

5. 历史库与查询路由

5.1 归档数据放哪:三种形态

形态位置查询方式适用
同库冷表同实例归档表直接 SQL数据量适中
独立历史库单独实例/低配机器应用路由量大、隔离热库
对象存储S3/OSS + 分析引擎湖仓查询海量、低频分析
推荐演进:
  初期(量小)  → 同库冷表,SQL 无感
  中期(量大)  → 独立历史库,应用层路由
  远期(海量)  → 对象存储 + 数仓,走分析链路

5.2 查询路由设计

归档后热表查不到老数据,必须让查询能"路由"到正确的层:

统一查询入口 QueryRouter:
  SELECT ... FROM orders WHERE created_at >= 90 天内
    → 路由到热表 orders
  SELECT ... FROM orders WHERE created_at < 90 天前
    → 路由到历史库 orders_archive
  SELECT ... 不按时间过滤
    → 先热表,miss 再查冷层(或提示改查历史)
# 应用层按时间路由(伪代码)
def query_orders(start, end):
    if start >= ARCHIVE_CUTOFF:          # 全热窗
        return query_hot(start, end)
    if end < ARCHIVE_CUTOFF:             # 全冷窗
        return query_archive(start, end)
    hot = query_hot(start, ARCHIVE_CUTOFF)     # 跨界:两段都查
    old = query_archive(ARCHIVE_CUTOFF, end)
    return merge(hot, old)

一句话: 路由是归档后必须补的一课。设计上优先"按时间窗双表查询",业务无感知;实在做不到,再提供显式的历史查询接口。

6. 归档的数据校验与可回溯性

归档最怕"数据悄悄丢"。每次归档都要做三重校验:

-- 1. 行数校验:归档前后行数必须一致
SELECT COUNT(*) FROM orders
WHERE created_at < '2026-06-30';           -- 迁移前
SELECT COUNT(*) FROM orders_archive;        -- 迁移后(应相等)

-- 2. 主键校验:两边主键集合一致,无缺失无重复
SELECT COUNT(*) FROM (
  SELECT id FROM orders
  WHERE created_at < '2026-06-30'
  EXCEPT
  SELECT id FROM orders_archive
) AS missing;                                 -- 期望 0 行

-- 3. 抽样校验:抽查若干行字段值一致
SELECT * FROM orders_archive
WHERE created_at >= '2026-06-01' AND created_at < '2026-06-30'
LIMIT 100;
可回溯保障:
  □ 每次归档写审计表:归档时间、表名、时间窗、行数、MD5
  □ 归档数据设只读权限,防误改
  □ 冷层保留"回迁脚本":出错时可反向 EXCHANGE 回热表
  □ 定期做归档恢复演练(从对象存储拉回并校验)

一句话总结: 归档动作本身是"高风险数据搬迁",必须把校验(行数、主键、抽样)与审计固化到流程里,并保证冷层数据可回迁、可恢复演练。

6.1 归档审计表设计

每轮归档都应落一条审计记录,保证「可追溯、可对账」:

CREATE TABLE archive_audit (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  archive_batch VARCHAR(64) NOT NULL,        -- 批次号
  src_table VARCHAR(128) NOT NULL,           -- 源表
  dst_table VARCHAR(128) NOT NULL,           -- 目标
  time_start DATETIME NOT NULL,              -- 归档时间窗起点
  time_end   DATETIME NOT NULL,              -- 归档时间窗终点
  rows_moved BIGINT NOT NULL,                -- 迁移行数
  checksum_md5 VARCHAR(64),                  -- 全量行指纹
  status ENUM('ok','failed','rollback') NOT NULL,
  finished_at DATETIME NOT NULL,
  KEY idx_batch (archive_batch)
);

-- 对账:本批迁移行数与源表应删行数相等
SELECT batch, rows_moved, time_start, time_end
FROM archive_audit WHERE status = 'ok' ORDER BY id DESC LIMIT 10;
审计的价值:
  1. 归档出错时可定位到具体批次回滚
  2. 审计/合规审查时能证明数据去向
  3. 每月对账:所有已归档批次与历史库总量吻合

归档是「不可见的数据搬移」,审计表就是让搬运过程「可见」的账本。没有审计的归档,等于裸奔。

7. 避坑清单

坑表现对策
没做分区就归档DELETE 大表归档,锁库半天建表就按时间 RANGE 分区
冷热线拍脑袋归档过早影响查询,过晚成本不减访问统计 + 保留期双驱动
归档只搬不校验数据悄悄丢失行数/主键/抽样三重校验
归档后查不到老数据业务线报错"数据不见了"提前设计查询路由/双表查询
归档表无索引冷层查询全表扫按查询路径重建冷层索引
冷数据永不回迁误归档无法恢复保留回迁脚本 + 恢复演练
忘了保留期合规提前删了审计所需数据先确认法律/合规保留期

8. 总结

环节要点
识别访问统计 + 业务保留期,划出冷热分界线
策略定归档对象、去向、频率、保留期
归档时间分区 + EXCHANGE/DROP,秒级搬移
历史库同库冷表 → 独立历史库 → 对象存储渐进
路由按时间窗双表查询,业务无感
校验行数 + 主键 + 抽样 + 审计表 + 恢复演练

冷热分层与归档是一场**“用时间换空间、用低频换低成本”的治理。它的价值不止省钱:热表更瘦、索引更小、备份更快、查询更稳。真正要做好,核心是把分区设计、访问统计、三重校验、查询路由**这四件事在一开始就纳入表结构设计——而不是等数据膨胀到不可收拾再补救。

延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「database」更多文章

  1. Supabase 平台与 PostgreSQL 边缘函数实践
  2. PostgreSQL 事件触发器与审计日志实现
  3. Kubernetes 上 PostgreSQL 运维与 CloudNativePG 实战