Back to Javaguide

数据冷热分离详解

docs/high-performance/data-cold-hot-separation.md

latest21.5 KB
Original Source
<!-- @include: @small-advertisement.snippet.md -->

什么是数据冷热分离?

数据冷热分离是指根据数据的访问频率业务重要性,将数据划分为冷数据和热数据,并分别存储在不同性能和成本的存储介质中的架构策略。

这种架构的核心目标有三个:

  1. 提升查询性能:热数据存储在高性能介质(如 SSD、内存)中,保障核心业务的响应速度。
  2. 降低存储成本:冷数据迁移至低成本介质(如 HDD、对象存储),大幅削减存储开支。
  3. 满足合规要求:部分行业(如金融、医疗)要求数据长期归档,冷热分离可兼顾合规与成本。

冷数据和热数据

热数据是指被频繁访问和修改、且需要快速响应的数据;冷数据是指访问频率极低、对当前业务价值较小、但需要长期保留的数据。

冷热数据的区分方法主要有两种:

  1. 时间维度区分:按照数据的创建时间、更新时间或过期时间划分。例如,订单系统将一段时间前(如 90 天或 1 年)的订单数据标记为冷数据。该方法适用于数据访问频率与时间强相关的场景,实现简单、成本低。
  2. 访问频率区分:将高频访问的数据视为热数据,低频访问的数据视为冷数据。例如,内容系统将浏览量低于阈值的文章标记为冷数据。该方法需要额外记录访问频率,适用于访问频率与数据本身特性强相关的场景。

如何选择区分策略?

  • 若业务数据天然具有时效性(如订单、日志、账单),优先选择时间维度,实现成本最低。
  • 若数据价值与时间无关(如文章、商品、用户画像),需结合访问频率进行判定。
  • 实际项目中,可将两者结合使用:以时间维度为主、访问频率为辅,覆盖更多业务场景。

冷热分离的多级分层策略

实际落地时,"冷"与"热"往往不是非此即彼的二分法,而是渐进式多级分层

层级数据特性判定规则示例存储策略
热数据高频访问、实时响应最近 30 天 + 所有未完成订单MySQL 热库(SSD)
温数据中频访问、可能被查询30~90 天前的订单MySQL 温库(HDD)
冷数据低频访问、偶发查询90 天~3 年的历史订单独立冷库或对象存储
归档数据极少访问、仅合规留存超过 3 年的订单对象存储(仅保留汇总)

实践建议:判定规则应通过配置中心动态管理,避免因业务变化导致频繁修改代码。

冷数据被访问后如何处理?

如果冷数据突然被访问(如用户查询 3 年前的订单),是否需要"热升级"?

策略适用场景优点缺点
不回迁偶发查询、查询频率极低实现简单查询速度慢
缓存层中等频率查询加速查询、不改变存储需要额外缓存组件
异步回迁高频查询、需要持续访问彻底解决性能问题实现复杂、可能产生一致性问题

推荐做法:绝大多数场景采用"不回迁 + 缓存层"的组合方案。冷数据查询时,先查缓存,命中则直接返回;未命中则查冷库并将结果写入缓存(针对偶发查询,设置 5~15 分钟的短暂 TTL 即可)。

⚠️注意:为防止恶意攻击者利用随机参数频繁查询不存在的数据导致冷库被击穿,可以在缓存层前置布隆过滤器(Bloom Filter)或在缓存中设置空值占位符,避免恶意请求穿透到冷库。详细介绍参考 Redis 常见面试题总结(下)(Redis 事务、性能优化、生产问题、集群、使用规范等)。

冷热分离的思想

冷热分离的核心思想是分层存储(Tiered Storage),根据数据的访问特性将其分配到不同层级的存储介质中。在企业级存储架构中,通常划分为以下层级:

层级数据特性典型存储介质访问延迟
Hot(热层)高频访问、实时响应NVMe SSD、内存毫秒级
Warm(温层)中频访问、近期数据SATA SSD、高速 HDD百毫秒级
Cold(冷层)低频访问、历史数据大容量 HDD、对象存储秒级
Archive(归档层)极少访问、合规留存磁带库、冰川存储分钟~小时级

这种分层思想在 IT 基础设施中被广泛应用,不仅限于数据库,还包括文件系统、对象存储、CDN 缓存等场景。

数据冷热分离的优缺点

优点:

  • 热数据查询性能优化:热数据集中在高性能存储上,表数据量大幅减少,索引效率显著提升,用户的绝大部分操作体验会更好。
  • 存储成本大幅降低:冷数据可迁移至 HDD 或对象存储,SSD 与 HDD 的单位成本差距可达 5~10 倍,对于海量数据场景节省效果显著。
  • 系统可维护性增强:热库数据量可控,备份恢复速度更快,DDL 操作(如加索引)耗时更短。

缺点:

  • 系统复杂性增加:需要额外的迁移组件、路由逻辑和监控体系,数据一致性风险增加。
  • 跨库查询效率低:若业务需要同时查询冷热数据(如年度统计报表),需进行跨库关联或数据聚合,查询性能和开发成本均会上升。
  • 迁移策略维护成本:冷热数据的判定规则需要持续调优,避免误判导致热数据被错误迁移。

冷数据迁移

冷数据如何迁移?

冷数据迁移是冷热分离的核心环节,主流方案有以下三种:

方案实现原理优点缺点适用场景
业务层代码实现写操作时判断冷热,直接路由到对应库实时性高侵入业务代码、判定逻辑复杂几乎不使用
任务调度迁移定时任务扫描热库,批量迁移符合条件的数据实现简单存在迁移延迟、扫表可能污染 Buffer Pool时间维度区分场景
Binlog 监听迁移监听数据库变更日志,实时或准实时迁移实时性好、对业务无侵入需要额外组件(如 Canal)、不适合时间维度判定访问频率区分场景(推荐)

任务调度迁移是最常用的方案,可借助 XXL-Job、Elastic-Job 等分布式任务调度平台实现。关于任务调度的方案,我也写过文章详细介绍,可以查看这篇文章:Java 定时任务详解

⚠️ 风险提示:任务调度迁移在大数据量下存在性能隐患。大范围的扫表操作(如 SELECT * FROM orders WHERE create_time < 'xxx' LIMIT 10000)会严重污染 InnoDB Buffer Pool,将真正的业务热数据挤出内存。生产环境建议

  • 使用基于主键的范围查询,避免全表扫描;
  • 控制单次迁移批量大小,分批执行;
  • 业务低峰期执行迁移任务;
  • 对于海量数据,优先考虑 Binlog 监听方案,将对热库的冲击降到最低。

典型流程如下:

实践建议:若公司有 DBA 支持,可先进行一次存量冷数据的人工迁移,将历史数据批量导入冷库;后续再通过任务调度实现增量迁移的自动化。

迁移过程中如何保证数据一致性?

数据迁移过程中,最棘手的问题是:如果数据在迁移过程中被更新,如何处理?

常见解决方案

方案实现方式优点缺点
迁移前锁定迁移前对记录加写锁,迁移完成后释放一致性强影响业务写入、吞吐量下降
版本号乐观锁迁移时记录版本,删除前校验版本是否变化无锁、性能好需要业务表增加版本字段、冲突时需重试
状态标记 + 幂等热库增加迁移状态字段,先标记再迁移可追溯、支持回滚需要改造业务表

注意:冷热库通常是不同的数据库实例INSERT(冷库)和 DELETE(热库)无法放在同一个本地事务中,需要特殊处理跨库原子性问题。

推荐方案:状态标记 + 幂等迁移

在热库表中增加 migrate_status 字段,通过状态机保证迁移的原子性和可追溯性:

sql
-- 1. 热库表增加迁移状态字段
ALTER TABLE orders ADD COLUMN migrate_status TINYINT DEFAULT 0
    COMMENT '0-未迁移 1-迁移中 2-已迁移';
java
// 2. 迁移流程(伪代码,独立冷库场景需在应用层分步执行)

// Step 1: 标记为迁移中(热库事务)
hotDb.execute("UPDATE orders SET migrate_status = 1 WHERE id = ? AND migrate_status = 0", id);

// Step 2: 读取热库数据并写入冷库(需切换数据库连接)
Order order = hotDb.query("SELECT * FROM orders WHERE id = ?", id);
coldDb.execute("INSERT IGNORE INTO orders_cold VALUES (?, ?, ...)", order.id, order.data...);

// Step 3: 标记为已迁移(热库事务)
hotDb.execute("UPDATE orders SET migrate_status = 2 WHERE id = ? AND migrate_status = 1", id);

// Step 4: 延迟删除热库数据(可选,确认冷库数据无误后执行)
hotDb.execute("DELETE FROM orders WHERE id = ? AND migrate_status = 2", id);

注意:独立冷库场景下,标准 MySQL 无法直接执行跨库 INSERT ... SELECT,必须在应用层拆分为"读取热库 → 写入冷库"两步。

方案优势

  • 幂等性INSERT IGNORE 保证冷库写入幂等,migrate_status 状态流转保证热库更新幂等。
  • 可追溯:通过状态字段可以查询迁移进度,异常时可以人工介入。
  • 可回滚:迁移失败时可以将状态重置为 0,重新迁移。
  • 渐进式删除:不立即删除热库数据,确认冷库无误后再清理,降低风险。

空间回收:InnoDB 执行 DELETE 后仅将数据页标记为删除,物理空间不会立即释放给操作系统。需在业务低峰期执行 OPTIMIZE TABLEALTER TABLE ENGINE=InnoDB 重建表,才能真正回收磁盘空间。

兜底机制

  • 定时对账:定期扫描 migrate_status = 1 超过阈值的记录,自动重置或告警。注意migrate_status 字段区分度极低,必须配合联合索引(如 idx_create_time_migrate_status)限定扫描区间,避免全表扫描。
  • 高频更新兜底:对于因频繁更新导致多次跳过的记录,设置最大重试次数,超过后强制迁移或人工介入。

冷数据如何存储?

冷数据存储方案的选型原则是:容量大、成本低、可靠性高,访问速度可适当牺牲

中小厂方案

直接使用 MySQL/PostgreSQL 即可,保持与热库相同的数据库类型,降低运维复杂度。具体实现方式:

  • 同库分表:在同一数据库中新增冷数据表(如 order_history),通过表名区分冷热数据。
  • 独立冷库:部署单独的数据库实例作为冷库,热库与冷库通过应用层路由访问。

⚠️注意:独立冷库方案涉及跨库查询,若业务存在冷热数据联合查询需求,需评估是否引入数据同步或聚合层。

大厂方案

大厂通常采用专门针对海量数据优化的存储引擎:

存储方案特点适用场景
HBase列族存储、高吞吐、支持 PB 级数据日志、用户行为、IoT 数据归档
RocksDB高性能 KV 存储、LSM-Tree 结构嵌入式场景、作为其他系统底层存储
Doris/ClickHouseOLAP 引擎、支持实时分析冷数据需要进行聚合分析的场景
Cassandra分布式、高可用、无单点故障跨地域部署、高可用要求的归档场景
对象存储(OSS/S3)成本极低、无限扩展超大规模冷数据、合规归档

TiDB 方案(推荐)

如果公司技术栈允许,可以直接使用 TiDB 这类分布式关系型数据库,原生支持冷热分离,一步到位。

TiDB 6.0 引入了 基于 SQL 接口的数据放置框架(Placement Rules in SQL) 功能,用于通过 SQL 接口配置数据在 TiKV 集群中的放置位置。

  • 热数据:通过 Placement Rules 指定存储在 SSD 节点上,保障查询性能。
  • 冷数据:指定存储在 HDD 节点上,降低存储成本。
sql
-- 创建放置策略:热数据存储在 SSD 节点
CREATE PLACEMENT POLICY hot_data
  CONSTRAINTS="[+disk=ssd]";

-- 创建放置策略:冷数据存储在 HDD 节点
CREATE PLACEMENT POLICY cold_data
  CONSTRAINTS="[+disk=hdd]";

-- 对表或分区应用放置策略
ALTER TABLE orders PLACEMENT POLICY = hot_data;
ALTER TABLE orders PARTITION p2022 PLACEMENT POLICY = cold_data;

这种方案的优势在于:业务无需感知冷热分离逻辑,数据路由由 TiDB 自动完成,大幅降低了应用层的复杂度。

完整实践Placement Rules 指定了数据存放的介质类型,但数据如何从"热分区"流转到"冷分区"仍需结合分区表(Range Partitioning)。按时间跨度创建分区,为历史分区绑定 HDD 放置策略,为当前活跃分区绑定 SSD 放置策略。随着时间推移,只需维护分区的创建与销毁,底层数据即可在不同介质间自然流转。

冷数据如何查询?

冷数据虽然访问频率低,但一旦需要查询(如审计、对账、年度报表),如何保证查询效率?

冷数据查询需求分析

首先需要明确:业务是否真的需要查询冷数据?

  • 不需要:可将冷数据完全移出业务库,仅保留归档(如对象存储),需要时人工提取。
  • 需要:需设计合理的查询方案,平衡性能与成本。

冷数据查询优化方案

优化手段实现方式适用场景
冷库独立只读实例冷库部署只读副本,避免冷查询影响热库高频冷查询场景
查询路由应用层根据时间范围自动路由到热库或冷库跨冷热查询场景
预聚合定期对冷数据生成月度/季度报表,查询时直接查聚合结果统计分析场景
列式存储冷库采用 ClickHouse、Doris 等 OLAP 引擎大规模分析查询

跨冷热查询的处理

若查询范围同时涉及冷热数据(如"查询近 2 年的订单"),有两种处理方式:

  1. 拆分查询:分别查询热库和冷库,应用层合并结果。
  2. 限制范围:提示用户缩小查询范围,避免跨库查询。

防雪崩预警:若业务包含全局分页排序(如 ORDER BY create_time LIMIT 10000, 20),应用层必须从冷热库各拉取 10000 + 20 条记录进行内存归并,偏移量较大时极易引发 OOM强制要求

  • 限制查询时间范围,避免大跨度跨库查询;
  • 或引流至底层同步的宽表(如 ClickHouse)进行计算;
  • 严禁在应用层执行大深度的归并分页。

应用层如何路由冷热数据?

方案实现方式优点缺点
硬编码代码中直接判断路由实现简单维护成本高、规则变更需改代码
配置中心路由规则存入配置中心(如 Nacos、Apollo)动态调整、无需重启需要额外组件支持
Proxy 层引入 ShardingSphere、ProxySQL 等中间件业务无感知架构复杂度高

推荐做法:中小规模采用配置中心方案,大规模采用Proxy 层方案。

⚠️ 风险提示:引入 Proxy 层后,所有跨冷热库的聚合计算(如全局排序、GROUP BY 归并分页)都会压在 Proxy 节点的内存与 CPU 上。需严格限制此类操作的最大返回行数,否则极易导致 Proxy 节点 OOM(内存溢出)

冷热分离 vs 数据归档 vs 分区表

这三个概念容易混淆,需要区分清楚:

对比维度冷热分离数据归档分区表
数据是否可访问冷数据仍在业务访问路径上归档数据通常移出业务库所有分区均可访问
存储介质冷热数据可跨实例、跨存储通常迁移到低成本存储同一实例内
实现复杂度中等
典型场景订单、日志等有时效性的数据合规留存、数据备份单表数据量大但无需分离存储

分区表的局限性:MySQL 分区表可以按时间分区,但所有分区仍在同一个实例中,无法实现存储介质的分离。如果目标是降低存储成本,分区表无法替代冷热分离。

典型业务场景

说明:以下存储策略仅供参考,实际选型需结合数据量、查询需求、团队技术栈和成本预算综合考虑。

订单系统

阶段数据范围存储策略说明
热数据最近 90 天 + 未完成订单MySQL 热库(SSD)高频访问,保障查询性能
冷数据90 天~3 年MySQL 冷库(HDD)或 TiDB可能需要查询,保持关系型存储
归档数据超过 3 年对象存储 / HBase / 仅保留汇总表极少查询,优先考虑成本

日志系统

阶段数据范围存储策略说明
热数据近 7 天Elasticsearch 热节点实时检索、高频查询
温数据7~30 天Elasticsearch 温节点偶发查询,降低存储成本
冷数据30 天以上Elasticsearch 冷节点 / 压缩归档至对象存储 / ClickHouse根据查询需求选择,ClickHouse 适合分析场景

内容系统

阶段数据范围存储策略说明
热数据发布后 3 个月内 + 高阅读量MySQL 热库频繁被访问
冷数据3 个月后 + 低阅读量MySQL 冷库 / HBase / 对象存储访问频率低,可迁移至低成本存储

选型建议

  • 需要支持事务或复杂查询:优先选择 MySQL 冷库或 TiDB
  • 需要大规模聚合分析:优先选择 ClickHouse 或 Doris
  • 仅需偶尔查询明细:可选择对象存储(如 OSS/S3),查询时临时加载
  • 数据量极大且访问极低:HBase 或对象存储是性价比最高的选择

案例分享

<!-- @include: @article-footer.snippet.md -->