数据分层存储并不是一个新概念,但真正把它做对的企业并不多。多数系统的做法是所有数据一股脑塞进同一套存储,头一两年没什么问题,等数据量涨到几十亿行、日志堆到几十TB,就会发现数据库越来越慢、存储账单越来越贵。冷热分层的核心思想其实很朴素:经常被访问的数据放在贵的、快的介质上,很少被访问的数据搬到便宜的、慢的介质上,让每一份数据都待在它该待的地方。

一、冷热数据怎么划分:三个维度定边界
分层的第一步是回答“哪些数据是热的,哪些是冷的”。这个问题没有统一答案,通常要结合三个维度综合判断。
第一个维度是访问频率。这是最直接的判断依据,比如电商订单表,最近三个月的订单会被用户频繁查询,属于典型的热数据;一年前的订单很少有人翻看,访问频率可能只有万分之一,归入冷数据没有争议。实际操作中可以通过审计日志或数据库自带的统计信息(如MySQL的performance_schema、MongoDB的索引访问统计)拿到真实的访问分布。
第二个维度是业务价值。有些数据虽然很少被查询,但一旦被查就是高价值场景,比如风控系统的历史交易记录,平时沉默,出险时必须秒级响应。这类数据不能简单按访问频率降级,而要单独评估其SLA要求。
第三个维度是时间维度。日志类、流水类数据天然具有时间衰减性,绝大多数访问集中在最近7天到30天,超过一年的数据访问概率趋近于零。这类数据最适合按时间做自动化分层,实现成本最低。
二、分层架构设计与介质选型
确定分层边界之后,接下来要为每一层选择合适的存储介质和产品。下面是一个典型的三层结构,可以直接对照落地。
| 层级 | 典型介质 | 数据特征 | 成本水平 |
|---|---|---|---|
| 热数据层 | SSD、内存、本地NVMe | 高频读写、毫秒级响应 | 高 |
| 温数据层 | SAS机械盘、普通云盘 | 偶发访问、秒级可接受 | 中 |
| 冷数据层 | 对象存储、磁带归档 | 几乎不访问、仅合规保留 | 极低 |
热数据层追求的是性能。以MySQL为例,可以把热表放在NVMe盘的实例上,配合足够的buffer pool,让热点数据尽量驻留内存。如果单表已经撑不住,可以按用户ID或时间做水平拆分,把热数据的单表规模控制在合理范围内。
温数据层是一个容易被忽视的过渡层。很多团队从热直接跳到冷,结果冷数据偶尔被访问时体验极差。温层适合存放“访问频率下降但还没到彻底沉默”的数据,比如半年内的历史订单。这一层用大容量机械盘或者低配云盘就够了,成本能降到热层的三分之一左右。
冷数据层首选对象存储,比如S3、OSS、COS这类产品,单价可以低到热层数据库的十分之一以下。如果数据有合规留存要求(如金融行业要求交易记录保留五年以上),还可以开启归档存储类型,单价更低,代价是取回需要几分钟到几小时的解冻时间。设计时要明确告知业务方这个取回延迟,避免关键时刻掉链子。
三、数据流转方案:怎么把数据自动搬到该去的层
分层架构搭好只是第一步,更关键的是让数据在各层之间自动流转,而不是靠人肉写脚本定期搬运。下面介绍几种常见场景下的落地做法。
第一种是基于数据库自身的分区和分区交换。MySQL的分区表配合ALTER TABLE ... EXCHANGE PARTITION可以快速把过期分区剥离出来,再通过数据导出工具落到对象存储,整个过程对线上业务几乎无感知。示例如下:
-- 按月分区的订单表,将过期分区快速剥离 ALTER TABLE orders EXCHANGE PARTITION p202301 WITH TABLE orders_archive_202301; -- 归档表数据导出到对象存储后,直接删除归档表释放空间 DROP TABLE orders_archive_202301;
第二种是利用Elasticsearch的索引生命周期管理(ILM)。日志场景下可以按天或按周创建索引,通过ILM策略定义四个阶段:热阶段使用SSD节点承接写入,温阶段迁移到机械盘节点并收缩副本数,冷阶段冻结索引释放堆内存,最后删除或快照归档。整套流程配置一次即可全自动运行,是日志分层的首选方案。
{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": { "max_size": "50gb", "max_age": "7d" }
}
},
"warm": {
"min_age": "7d",
"actions": {
"shrink": { "number_of_shards": 1 },
"forcemerge": { "max_num_segments": 1 }
}
},
"cold": {
"min_age": "30d",
"actions": {
"freeze": {}
}
},
"delete": {
"min_age": "365d",
"actions": { "delete": {} }
}
}
}
}第三种是应用层双写加查询路由。对于订单这类核心业务数据,可以在写入时同步写热库、异步写冷库(可以是另一个精简表结构的实例),查询服务根据时间条件自动路由:查最近三个月走热库,查更早的数据走冷库,冷库查不到再触发对象存储取回。这种方案对存储选型最灵活,但要在应用层维护路由逻辑,复杂度相对高一些。
四、落地时的几个坑
第一个坑是跨层查询体验割裂。用户查一个一年前的订单,如果直接告诉他“请等10分钟解冻”,体验会很糟糕。建议的做法是冷数据查询接口做成异步任务,同时在前端给出明确提示,或者对少量高价值冷数据在温层保留一份可查询的索引。
第二个坑是分层边界一拍脑袋决定。正确的做法是先跑一到两个月的数据访问统计,拿到真实的访问分布曲线再定阈值,而且阈值要支持动态调整——业务变化后,原来的热数据可能变温,原来的温数据可能因为活动运营突然变热。
第三个坑是只搬数据不留元数据。冷数据落到对象存储后,一定要在热库或独立的元数据库里保留一份索引记录,包括文件路径、时间范围、业务主键映射等信息,否则几百TB的冷数据就是一堆没人敢删也没人能用的黑盒。同时归档文件建议采用Parquet这类列式格式,既节省空间又方便后续用Spark等引擎做离线分析。
总的来说,冷热分层不是一次性工程,而是一个持续运营的机制。从访问统计入手,先做日志和流水这类时间特征明显的数据,积累经验后再推广到核心业务表,循序渐进地把存储成本降下来,同时保住线上业务的响应速度。