业务跑了一段时间之后,几乎所有团队都会遇到同一个问题:数据越积越多,成本越来越高,但真正被频繁访问的可能只有很小一部分。一个典型的观测结果是——全站数据里,最近 7 天产生的数据往往承担了 80% 以上的访问量,而一年前的数据可能几十天都没人碰一次(经验规律,具体比例随业务类型变化)。如果所有数据都平等地放在最贵、最快的存储上,等于让最冷的数据占着最热的资源。
冷热数据分层要解决的正是这个错配:把数据按"访问频率"和"访问延迟要求"分到不同性价比的存储介质上,热数据用高性能 SSD 保证响应,冷数据用大容量 HDD 或对象存储压低成本,中间再加一层缓存或温层做过渡。做得好,可以在不影响用户体验的前提下把存储成本降下一大截;做得不好,则可能出现"热数据被误判为冷、被挪走之后性能暴跌"的尴尬。
这篇文章会给出设计分层架构的完整思路:怎么划分冷热、每层该用什么介质、数据怎么流转、数据库和文件两类数据分别怎么做、缓存层怎么配合、如何在不下线的情况下迁移数据、以及怎么监控分层是否有效。最后给一个中小团队可以直接落地的参考架构。文中涉及阈值的地方都注明为经验值,需要按自己的业务校准。
一、先给数据"量体温":冷热怎么划分
分层的第一步不是选硬件,而是搞清楚数据实际的访问特征。至少要看三个维度:
- 访问频率:单位时间内被读取/写入的次数。这是最核心的指标。
- 访问新鲜度:数据产生后多久还会被访问。很多数据是"刚产生时热、之后迅速冷却"。
- 延迟敏感度:业务能接受多长的响应时间。同样频率,交易数据要求毫秒级,报表查询可能接受秒级。
按这几个维度,通常把数据分成四层:
| 层级 | 访问特征 | 典型数据 | 介质 | 延迟量级(经验值) | 相对成本 |
|---|---|---|---|---|---|
| 热数据(Hot) | 秒级/毫秒级高频访问 | 会话、购物车、实时行情、订单最近记录 | 本地 NVMe、内存缓存 | 微秒–毫秒 | 最高 |
| 温数据(Warm) | 每天/每周访问 | 近月订单、活跃用户资料、近期日志 | SATA/SAS SSD、性能型云盘 | 毫秒–十几毫秒 | 中高 |
| 冷数据(Cold) | 每月几次或更少 | 历史订单、已归档日志、旧备份 | 大容量 HDD、低保真云盘 | 十几毫秒–秒级 | 低 |
| 归档(Archive) | 几乎不访问,合规要求保留 | 多年的审计记录、冷备份、历史快照 | 对象存储归档层、磁带 | 秒–分钟 | 最低 |
要特别提醒:"冷热"是会变化的。 一次大促、一次对账、一次数据回溯,都可能让原本很冷的数据突然变热。所以分层规则里要留"回温"通道——冷数据能被重新召回,而不是"挪出去就再也不好取"。
二、分层的三种落地层次
"分层"不是单一动作,它在系统里有三个不同的层级,往往需要同时做:
第一层:缓存层。 在内存或本地 SSD 里放一份热点数据的副本,挡住绝大部分读请求。Redis、Memcached,或 Nginx 的 proxy_cache 都属于这一层。它的目的不是减少存储成本,而是降低对后端存储的压力,顺带提升响应速度。
第二层:存储介质分层。 在持久化存储这一侧,根据数据冷热分配到不同介质:本地 NVMe / 性能云盘 / 容量云盘 / 对象存储。这是成本优化的主战场。
第三层:数据生命周期管理。 对数据本身做归档和淘汰:数据库历史表归档、日志按时间轮转归档、对象存储配置生命周期规则自动降冷。这一层决定"数据留多久、放哪层、何时删除"。
三层配合起来才完整。只做缓存不做归档,存储依然无限膨胀;只做归档不做缓存,热数据体验会受影响。
三、数据库数据的分层
数据库是分层里最需要谨慎处理的部分,因为数据库往往直接关系业务正确性。
做法一:热点表 + 历史表分离。 把"最近 N 天的数据"留在主表(高频查询),更早的数据归档到历史表或历史库:
-- 以 MySQL 为例(示意思路,字段按实际业务调整)
-- 1. 建历史表,结构与主表一致
CREATE TABLE orders_history LIKE orders;
-- 2. 分批迁移旧数据(避免一次锁表,每批 1000 行)
INSERT INTO orders_history
SELECT * FROM orders WHERE created_at < DATE_SUB(NOW(), INTERVAL 90 DAY) LIMIT 1000;
-- 3. 确认迁移成功后再删除主表旧数据
DELETE FROM orders WHERE created_at < DATE_SUB(NOW(), INTERVAL 90 DAY) LIMIT 1000;
做法二:分区表(Partition)。 让数据库自己按时间分区,把不同分区的数据放在不同表空间/磁盘上。查询带上时间条件时可以直接"分区裁剪",只扫描相关分区,性能好且易管理:
-- MySQL 8.0 按范围分区示例
ALTER TABLE orders
PARTITION BY RANGE (TO_DAYS(created_at)) (
PARTITION p2025q4 VALUES LESS THAN (TO_DAYS('2026-01-01')),
PARTITION p2026q1 VALUES LESS THAN (TO_DAYS('2026-04-01')),
PARTITION p2026q2 VALUES LESS THAN (TO_DAYS('2026-07-01')),
PARTITION pmax VALUES LESS THAN MAXVALUE
);
-- 归档时直接换出分区,比逐行删除高效得多
-- ALTER TABLE orders EXCHANGE PARTITION p2025q4 WITH TABLE orders_archive_2025q4;
做法三:冷热分库。 把冷数据迁到配置低一些的独立实例(大容量 HDD 或容量型云盘),主库只保留热数据。查询历史数据时走冷库,通过业务层路由。这个方法成本收益明显,但要处理好跨库查询和一致性。
做法四:分析类数据外移。 把明细数据同步到列式存储或数据仓库做分析,主库不再承担报表压力。
数据库分层的风险提示:迁移和归档数据是不可逆的数据变更。执行 DELETE、DROP PARTITION、EXCHANGE PARTITION 前,必须确认归档数据已经完整落盘到目标位置并校验通过;操作要分批、在低峰期进行,并提前做好备份。回滚方式:保留归档目标表/库,必要时从归档侧反向导入;若删除前未备份也未归档,数据将无法恢复。
四、文件与对象数据的分层
文件类数据(日志、图片、附件、备份、数据集)的分层通常更容易,因为它们大多是"追加写、很少改"。
日志:本地只保留最近 7–15 天用于排查,更早的压缩后归档到对象存储,配置生命周期自动降冷。日志轮转用 logrotate,归档用 rsync 或对象存储工具:
# logrotate 配合归档脚本(/etc/logrotate.d/myapp)
# /var/log/myapp/*.log {
# daily
# rotate 14 # 本地保留 14 份
# compress # 压缩旧日志
# missingok
# notifempty
# postrotate
# /usr/local/bin/archive-old-logs.sh # 归档更早的日志到对象存储
# endscript
# }
# 归档脚本示例:把压缩后的旧日志同步到对象存储,校验成功再删除本地
#!/bin/bash
set -euo pipefail
SRC=/var/log/myapp
DST=s3://my-bucket/logs/myapp/
for f in "$SRC"/*.gz; do
[ -e "$f" ] || continue
aws s3 cp "$f" "$DST" --storage-class STANDARD_IA
# 校验对象存在后删除本地
aws s3 ls "$DST$(basename "$f")" >/dev/null && rm -f "$f"
done
静态资源与用户上传:热资源走 CDN(边缘缓存),源站放对象存储。CDN 本身就是一层"热数据缓存",命中率高时可以替源站挡住绝大部分流量。
备份:最近 3–7 天的备份放性能型存储以便快速恢复,更早的备份放归档层。恢复时间要求(RTO)决定了备份能不能放到最冷的层。
风险提示:归档脚本里用到了
rm -f,删除前必须确认对象已成功上传并校验存在。脚本应加set -euo pipefail,任何一步失败就退出,避免"没传上去却把本地删了"。回滚方式:从对象存储重新拉回;若本地删除且对象上传失败,则数据丢失。
五、缓存层的设计与失效策略
缓存是热数据的第一道防线,设计时有几个关键点:
- 缓存什么:高读低写、可容忍短时不一致的数据。会话、用户资料、榜单、详情页适合;涉及金额的实时数据要谨慎。
- 缓存多久(TTL):太短命中率低,太长数据陈旧。经验上热点列表类可以 30 秒到几分钟,详情类可以几十分钟,配合主动失效。
- 怎么失效:主动删除(写时更新)比等 TTL 到期更及时。要注意"缓存穿透、缓存击穿、缓存雪崩"三个经典问题,分别用空值缓存、互斥锁、随机 TTL 来缓解。
- 容量怎么定:命中率不是越高越好,超过一定比例后收益递减。经验上把命中率做到 80%–95% 就足够,剩下交给后端存储。
六、迁移与流转:怎么在线搬数据
分层架构的真正难度在"数据怎么从热层流到冷层,且不中断服务"。几条实用原则:
- 先复制、后切换、再清理。分三步走,每一步都可验证、可回退,避免"一步到位"造成不可逆损失。
- 分批做,别一次搬完。按时间或 ID 范围分小批,控制单次影响。
- 错峰执行,并限制迁移速度(rsync 的
--bwlimit、数据库的并发控制),别把线上 IO 打满。 - 校验一致性。迁移后用行数、校验和、抽样比对确认数据完整。
- 留回温通道。冷数据目录/表要能被业务按需查询,而不是"归档即失联"。
一个可复用的迁移校验流程:
# 文件类迁移:先 dry-run,再分批同步,最后校验大小与数量
rsync -avn --delete /data/hot/ /data/warm/ # 1. 干跑确认
rsync -av --bwlimit=51200 /data/hot/ /data/warm/ # 2. 限速同步
find /data/hot -type f | wc -l; find /data/warm -type f | wc -l # 3. 数量比对
du -sb /data/hot; du -sb /data/warm # 4. 体积比对
七、一个可落地的参考架构
给中小团队一个不太复杂的分层方案(层数和介质按预算调整):
- 缓存层:Redis(热 key)+ Nginx/CDN(静态资源)。
- 热层:数据库主库和核心表放本地 NVMe 或性能型云盘,只保留最近 90 天活跃数据。
- 温层:近 1 年的历史数据放容量型云盘或大容量 SSD,用历史表/历史库承载。
- 冷层:超过 1 年的数据放对象存储标准层,业务按需查询。
- 归档层:合规要求长期保留的放对象存储归档层,用生命周期规则自动降冷与清理。
- 备份:近 7 天备份放性能存储,更早的转归档。
配合的自动化:数据库按时间分区 + 定期归档脚本;日志用 logrotate + 对象存储同步;对象存储配置生命周期规则(如 30 天转低频、180 天转归档、按合规周期删除);监控采集各层容量、命中率、冷热分布。
八、怎么验证分层有没有效果
分层的目标是"成本下降但不伤体验",所以监控要同时盯两头:
- 成本侧:各层存储用量、单位数据成本、对象存储请求与流量费用趋势。
- 性能侧:缓存命中率、P95/P99 延迟、冷数据回温查询的响应时间。
- 正确性侧:归档数据的完整性校验、恢复演练(能不能从冷层按预期恢复)。
- 流转侧:归档任务是否按时执行、失败告警、积压情况。
一个常见的误区是"分完层就不管了"。数据热度会漂移,业务会变化,分层规则要定期复盘,观察"冷层里是不是有被频繁访问的数据""热层是不是塞了太多根本用不上的数据",据此调整阈值。
常见问题
Q1:数据量不大(几百 GB),需要做分层吗?
不一定。数据量小、成本压力不大时,统一放在性能足够的盘上更简单,分层反而增加复杂度。分层主要解决"数据量持续增长 + 成本敏感"的问题。
Q2:冷数据放对象存储后,业务还能查吗?
能,但延迟会明显上升,归档层甚至需要先"解冻"才能读。所以要在设计时就明确哪些查询允许变慢,必要时在业务层做"异步查询 + 结果通知"。
Q3:怎么判断一个数据是冷是热?
最可靠的是看访问日志统计:按数据标识聚合最近 30 天的访问次数。没有埋点的话,可以先按"数据的年龄"做粗略规则(最近 90 天算热),再逐步细化。
Q4:数据库归档会不会影响主键和自增?
要注意归档后主键的唯一性和外键关系,历史表建议结构一致但可去掉外键约束以提升写入效率,查询时靠业务逻辑关联。归档前务必在测试环境演练。
Q5:缓存命中率越高越好吗?
不是。命中率提升到一定程度后,边际收益递减,而缓存容量和一致性成本持续上升。经验上 80%–95% 是合理区间,重点优化"最热的那部分"而不是追求 100%。
总结
- 分层的本质是把数据按"访问频率 + 延迟要求"匹配到不同性价比的存储,避免冷数据占着最贵的资源。
- 落地要同时做三层:缓存层挡热点、存储介质分层控成本、生命周期管理管归档与淘汰。
- 数据库分层优先用分区表 + 历史表/历史库,操作要分批、错峰、先归档校验再删除。
- 文件类数据分层的标准路径是:本地留近期用于排查,旧数据压缩归档到对象存储并配置生命周期自动降冷。
- 迁移遵循"先复制、后切换、再清理",加限速、分批、校验,并给冷数据留回温通道。
- 分层要定期复盘,数据热度会漂移,规则需要按监控数据持续校准,成本与体验两头都要盯。
企业QQ咨询




