数据过期与冷热分离:让存储成本不再失控
大家好,我是黒漂技术佬。前面几篇我们聊了怎么往 InfluxDB 里写数据、怎么聚合查询,但有个现实问题一直绕不过去——数据越攒越多,磁盘怎么办?这篇就来解决这个"甜蜜的烦恼"。
一、时序数据为什么要过期
先算笔账。一台无人售货柜每5秒上报一次数据,假设有6个 field(温度、湿度、电流、电压、功率、开门次数),1000台设备一天就产生:
1000台 × 6字段 × (86400秒 ÷ 5秒) = 约1.04亿条/天一条记录算100字节,一天就是10GB。一个月300GB,一年3.6TB。这还是1000台设备的规模,要是5000台呢?
但仔细想想,这些数据的价值是随时间急剧衰减的:
- 最近7天的原始数据:运维要排查"昨天那台柜子为什么报了温度告警",必须看秒级精度
- 最近30天:业务方看趋势,分钟级聚合就够了,原始数据基本没人碰
- 1年前的数据:除了年底做年度报告,谁会去翻一年前的温度曲线?
所以核心思路就一句话:让数据在该消失的时候消失,该降精度的时候降精度。一直堆着原始数据,既费钱又拖慢查询。
二、InfluxDB 保留策略(Retention Policy)
什么是保留策略
保留策略(Retention Policy,简称 RP)是 InfluxDB 用来管理数据生命周期的机制。简单说,你告诉数据库"这个 bucket 里的数据保留多久",到期后数据库自动删除,不需要你写定时任务去DELETE。
在 InfluxDB 2.x 中,保留时长是绑定在 Bucket 上的。创建 Bucket 时指定即可。
创建带保留时长的 Bucket
通过 InfluxDB UI 创建时,直接填写保留时长:
Bucket名称: cabinet_raw 保留时长: 7d通过命令行(influx CLI)创建:
# 创建保留7天的Bucketinflux bucket create\--namecabinet_raw\--retention7d\--orgyour-org# 创建保留90天的Bucketinflux bucket create\--namecabinet_30d\--retention90d\--orgyour-org# 创建保留1年的Bucketinflux bucket create\--namecabinet_1y\--retention365d\--orgyour-org通过 API 创建:
curl-XPOST"http://localhost:8086/api/v2/buckets"\-H"Authorization: Token YOUR_TOKEN"\-H"Content-Type: application/json"\-d'{ "name": "cabinet_raw", "retentionRules": [ { "type": "expire", "everySeconds": 604800 } ], "orgID": "YOUR_ORG_ID" }'everySeconds: 604800就是7天(7 × 24 × 3600)。创建完成后,InfluxDB 后台会自动清理超过7天的数据。
修改已有 Bucket 的保留时长
业务跑着跑着发现7天不够用,想改成30天?没问题:
influx bucket update\--idBUCKET_ID\--retention30d注意:把保留时长从7天改到30天后,之前已经超过7天的数据并不会"复活"回来——已经被删的就是删了。但从这一刻起,新写入的数据会保留30天。
多级保留策略
实际项目里,我们不会只用一个 Bucket。聪明的做法是按数据精度建多个 Bucket,每个有不同的保留时长:
| Bucket | 数据精度 | 保留时长 | 用途 |
|---|---|---|---|
| cabinet_raw | 原始(5秒) | 7天 | 短期故障排查 |
| cabinet_10m | 10分钟聚合 | 90天 | 月度趋势分析 |
| cabinet_1h | 1小时聚合 | 1年 | 年度报表 |
这三种 Bucket 的存储量差异巨大。同样是一台设备一天的数据:
- cabinet_raw:17280条
- cabinet_10m:144条(缩了120倍)
- cabinet_1h:24条(缩了720倍)
一个1年数据量的 Bucket,存储成本可能只有原始数据的千分之一。这就是多级保留策略的威力。
三、冷热数据分离
保留策略解决的是"自动清理"的问题,但有些数据你不想删,只是不想让它占用昂贵的 InfluxDB 存储。这就需要冷热分离。
什么是冷热数据
- 热数据:最近N天的数据,查询频繁、要求毫秒级响应。放在 InfluxDB 里,享受高性能查询
- 冷数据:历史归档数据,偶尔查一次,响应慢点无所谓。导出到 MySQL 或文件,腾出 InfluxDB 空间
典型的冷热边界是30天或90天,具体看业务需求。
冷数据导出方案
方案一:导出到 MySQL
适合需要结构化查询的场景,比如"查某台设备三个月前的日报记录"。
数据流:InfluxDB → 定时任务查询聚合 → 写入 MySQL 归档表
// 伪代码思路// 1. 每天凌晨1点执行// 2. 从InfluxDB查询昨天的日报数据(聚合后)// 3. 写入MySQL的cabinet_daily_report表// MySQL归档表结构// CREATE TABLE cabinet_daily_report (// id BIGINT PRIMARY KEY AUTO_INCREMENT,// device_id VARCHAR(50),// report_date DATE,// avg_temp DECIMAL(5,2),// max_temp DECIMAL(5,2),// min_temp DECIMAL(5,2),// total_power DECIMAL(10,3),// door_open_count INT,// create_time DATETIME,// INDEX idx_device_date (device_id, report_date)// );方案二:导出到 CSV / Parquet 文件
适合超长期归档,成本低、格式通用。Parquet 是列式存储格式,压缩率高,适合大数据分析工具(Spark、Pandas)后续处理。
# 用influx CLI导出CSVinflux query\'from(bucket: "cabinet_10m") |> range(start: -1d) |> filter(fn: (r) => r["_measurement"] == "cabinet_metrics_10m") |> filter(fn: (r) => r["_field"] == "temp") |> mean()'\--orgyour-org\--file-format csv\>/archive/cabinet_temp_$(date+%Y%m%d).csv冷热分离的架构图
设备数据上报 │ ▼ ┌──────────┐ 保留7天 ┌──────────────┐ │ 热数据 │ ◄──────────── │ cabinet_raw │ ← 原始5秒精度 │ (InfluxDB)│ └──────────────┘ └──────────┘ │ │ Task降采样(每10分钟) ▼ ┌──────────────┐ 保留90天 │ cabinet_10m │ ← 10分钟聚合 └──────────────┘ │ │ Task降采样(每小时) ▼ ┌──────────────┐ 保留1年 │ cabinet_1h │ ← 1小时聚合 └──────────────┘ │ │ 定时导出(每天) ▼ ┌──────────────┐ │ MySQL / CSV │ ← 永久归档 └──────────────┘热数据用 InfluxDB 的速度,冷数据用 MySQL/文件的便宜,各取所长。
四、数据降采样实战
降采样(Downsampling)就是把高精度数据聚合成低精度数据。前面04篇讲过aggregateWindow()的用法,这里重点讲怎么用 Task 实现自动化。
Task 自动降采样
InfluxDB 2.x 的 Task 相当于定时任务,按设定的时间间隔自动执行 Flux 脚本。我们用它在 Bucket 之间搬运聚合数据。
Task 1:原始数据 → 10分钟聚合
option task = { name: "downsample_5s_to_10m", every: 10m, offset: 1m } from(bucket: "cabinet_raw") |> range(start: -11m, stop: -1m) |> filter(fn: (r) => r["_measurement"] == "cabinet_metrics") |> group(columns: ["device_id", "_field"]) |> aggregateWindow(every: 10m, fn: mean, createEmpty: false) |> set(key: "_measurement", value: "cabinet_metrics_10m") |> to(bucket: "cabinet_10m", org: "your-org")逐行解读:
every: 10m:每10分钟执行一次offset: 1m:延迟1分钟执行,避免数据还没写完就聚合,导致最后一个窗口数据不全range(start: -11m, stop: -1m):取最近11分钟到1分钟前的数据,和every对齐,保证窗口完整group(columns: ["device_id", "_field"]):按设备和字段分组,各自独立聚合aggregateWindow(every: 10m, fn: mean):10分钟窗口取平均set():改个 measurement 名字,区分原始和聚合to():写入目标 Bucket
Task 2:10分钟聚合 → 1小时聚合
option task = { name: "downsample_10m_to_1h", every: 1h, offset: 5m } from(bucket: "cabinet_10m") |> range(start: -65m, stop: -5m) |> filter(fn: (r) => r["_measurement"] == "cabinet_metrics_10m") |> group(columns: ["device_id", "_field"]) |> aggregateWindow(every: 1h, fn: mean, createEmpty: false) |> set(key: "_measurement", value: "cabinet_metrics_1h") |> to(bucket: "cabinet_1h", org: "your-org")注意这里的数据源不是cabinet_raw,而是cabinet_10m——用聚合后的数据再聚合,而不是拿原始数据直接算。这样计算量更小。
Task 管理命令
# 查看所有Taskinflux task list--orgyour-org# 查看某个Task的运行记录influx task logfind--task-id TASK_ID--orgyour-org# 手动触发一次Taskinflux task run create --task-id TASK_ID--orgyour-org# 暂停Taskinflux task update--idTASK_ID--statusinactive# 恢复Taskinflux task update--idTASK_ID--statusactive五、存储空间监控
光配了保留策略还不够,你得盯着磁盘,防止意外情况把空间吃满。
查看 InfluxDB 磁盘占用
# 查看InfluxDB数据目录大小du-sh/var/lib/influxdb2/engine/data# 按Bucket查看(需通过API)curl-s"http://localhost:8086/api/v2/buckets"\-H"Authorization: Token YOUR_TOKEN"|jq'.buckets[] | {name: .name, retention: .retentionRules[0].everySeconds}'查询数据量统计
用 Flux 统计各 measurement 的数据点数量,判断哪个 measurement 在疯涨:
// 统计过去1小时各measurement的数据点数量 from(bucket: "cabinet_raw") |> range(start: -1h) |> group(columns: ["_measurement"]) |> count() |> group() |> sort(columns: ["_value"], desc: true)磁盘告警建议
建议设置以下监控告警:
- 磁盘使用率 > 70%:发告警,检查是否有异常写入
- 磁盘使用率 > 85%:紧急告警,可能需要扩容或缩短保留时长
- 日增数据量异常波动 > 50%:可能是设备端 bug 导致疯狂重传
六、无人售货柜数据保留方案
最后给一个完整的无人售货柜数据保留方案,你可以直接参考落地:
三级 Bucket 设计
| Bucket | 精度 | 保留时长 | 写入方式 | 查询场景 |
|---|---|---|---|---|
| cabinet_raw | 5秒 | 7天 | 设备直接写入 | 故障排查、实时监控 |
| cabinet_10m | 10分钟 | 90天 | Task降采样 | 月度趋势、运营分析 |
| cabinet_1h | 1小时 | 365天 | Task降采样 | 年度报表、同比对比 |
日报归档到 MySQL
每天凌晨1点,Task 把昨天的日报数据(日均温度、最高温度、总耗电、开门次数)聚合后写入 MySQL 的cabinet_daily_report表,永久保存。
这样做的好处:
- InfluxDB 只管时序数据,不背历史包袱,查询始终快
- MySQL 管业务报表,和订单、设备台账等业务数据放一起,方便联合查询
- 成本可控:InfluxDB 的存储量被限制在7天原始 + 90天聚合 + 365天小时聚合,总量可以预估
存储容量预估
1000台设备,每台6个 field:
- cabinet_raw(7天):约70GB
- cabinet_10m(90天):约5GB(缩小14倍)
- cabinet_1h(365天):约2GB(缩小420倍)
- MySQL日报(永久):1000台 × 365天 × 100字节 ≈ 36MB/年
总计约77GB,一块500GB的 SSD 轻松搞定。如果不做降采样和过期,一年就是3.6TB——差距50倍。
数据过期和冷热分离不是可选项,是时序数据系统的必修课。配置好保留策略和降采样 Task,你的 InfluxDB 就能长期稳定运行而不会把磁盘吃满。