4. 农业数据存储:时序数据库选型、数据湖架构与冷热分层
数据存不好,后面所有分析都是空中楼阁。农业数据尤其特殊——传感器每几分钟就上报一次,一年下来几亿条记录是常事。我见过不少团队,前期图省事把数据全塞进关系库,结果三个月后查询慢得像蜗牛爬。
这一章,咱们就聊聊农业数据到底该怎么存。核心就三件事:选对时序库、搭好数据湖、做好冷热分层。
4.1 时序数据库选型:InfluxDB vs TimescaleDB
农业数据里,90% 以上是时序数据。温度、湿度、光照、土壤 EC 值……这些数据天生带时间戳。用传统关系库存不是不行,但查询性能和维护成本会让你头疼。
我个人习惯,先看场景再选库。两个主流选手:InfluxDB 和 TimescaleDB。
| 对比维度 | InfluxDB | TimescaleDB |
|---|---|---|
| 底层架构 | 自研 TSM 存储引擎,专为时序优化 | 基于 PostgreSQL,时序数据存为 hypertable |
| 写入性能 | 极高,单节点可达百万点/秒 | 较高,依赖 PG 的批量写入能力 |
| 查询语法 | 类 SQL 的 Flux/InfluxQL,学习成本中等 | 标准 SQL,熟悉 PG 就能上手 |
| 数据压缩 | 内置高压缩比,磁盘占用低 | 依赖 PG 压缩插件,需手动配置 |
| 生态集成 | Telegraf + Grafana 是黄金搭档 | 与 PG 生态无缝对接,支持 GIS 扩展 |
| 适用场景 | 纯时序、高频写入、监控告警 | 时序+业务数据混合、复杂分析 |
4.2 数据湖架构:不止是存数据
数据湖这个概念,说白了就是把所有原始数据一股脑存下来,不提前做太多加工。农业数据湖里,除了时序数据,还有气象预报、卫星影像、农事记录、市场行情……格式五花八门。
我建议用分层架构来组织数据湖。别把所有数据扔一个桶里,那叫数据沼泽。
数据湖分层架构(以 MinIO + Delta Lake 为例)
Bronze 层(原始数据层)
├── 传感器原始 JSON
├── 气象站 CSV 文件
└── 无人机影像 TIFF
Silver 层(清洗数据层)
├── 清洗后的时序表(去重、补缺)
├── 标准化气象数据
└── 地块边界 GeoJSON
Gold 层(业务数据层)
├── 作物生长指标宽表
├── 灌溉决策特征表
└── 产量预测模型输入表
为什么要分三层?
- Bronze 层:保留原始数据,方便回溯和重算。万一清洗逻辑错了,还能从这层重新跑。
- Silver 层:做数据清洗、去重、格式统一。这层的数据已经可以用于常规分析。
- Gold 层:面向具体业务场景,比如模型训练、报表生成。数据经过聚合和特征工程,查询效率最高。
4.3 数据分区策略:让查询快 10 倍
数据量一大,全表扫描就是灾难。分区是解决这个问题的第一手段。农业时序数据,最自然的分区键就是时间。
我常用的分区策略:
- 按天分区:适合查询跨度在几天内的场景,比如查看最近一周的温室温度变化。
- 按月分区:适合月度统计报表,比如每月用水量汇总。
- 按年分区:适合长期趋势分析,比如对比不同年份的同一作物生长周期。
但光按时间分区还不够。农业数据还有一个天然维度——空间。比如地块 ID、大棚编号。我建议采用复合分区:先按时间范围分区,再按空间维度做二级分区。
-- TimescaleDB 示例:按时间分区 + 按地块哈希分布
CREATE TABLE sensor_data (
time TIMESTAMPTZ NOT NULL,
plot_id INT NOT NULL,
sensor_type TEXT,
value DOUBLE PRECISION
);
-- 创建 hypertable,按时间自动分区
SELECT create_hypertable('sensor_data', 'time');
-- 添加空间分区:按 plot_id 哈希分 4 个分区
SELECT add_dimension('sensor_data',
create_hypertable_index('sensor_data', 'plot_id',
number_partitions => 4));
分区粒度怎么定?一个经验法则:每个分区数据量控制在 1000 万到 5000 万行之间。分区太小,管理成本高;分区太大,查询剪枝效果差。我一般先估算一年的数据量,再反推分区粒度。
4.4 冷热数据分层:省钱又高效
农业数据有个特点:越新的数据,访问越频繁。昨天的数据可能每小时查一次,去年的数据可能一年都查不了一次。把所有数据都放在高性能存储上,成本扛不住。
冷热分层的核心思路:热数据放 SSD,冷数据放 HDD 或对象存储。
| 层级 | 时间范围 | 存储介质 | 访问频率 | 压缩策略 |
|---|---|---|---|---|
| 热数据 | 最近 7 天 | SSD (NVMe) | 秒级/分钟级 | 无压缩或轻压缩 |
| 温数据 | 7 天 ~ 6 个月 | HDD (SATA) | 小时级/天级 | 中等压缩 |
| 冷数据 | 6 个月以上 | 对象存储 (S3/MinIO) | 月级/年级 | 高压缩 + 列式存储 |
具体怎么做?我分享一个在 TimescaleDB 里的实现思路:
-- 创建数据保留策略:热数据保留 7 天
SELECT add_retention_policy('sensor_data', INTERVAL '7 days');
-- 创建压缩策略:超过 7 天的数据自动压缩
ALTER TABLE sensor_data SET (
timescaledb.compress,
timescaledb.compress_segmentby = 'plot_id',
timescaledb.compress_orderby = 'time DESC'
);
SELECT add_compression_policy('sensor_data', INTERVAL '7 days');
-- 冷数据迁移:超过 6 个月的数据转存到对象存储
-- 这部分需要自定义脚本,TimescaleDB 不内置
-- 我一般用 pg_cron 定时任务 + COPY TO S3 实现
4.5 整体架构一览
说了这么多,咱们用一张图把整个存储架构串起来。你想想看,从传感器数据产生,到最终支撑决策,数据是怎么流动的?
这张图把咱们刚才讲的内容串起来了。数据从传感器、气象站、农事系统流入数据湖,经过 Bronze → Silver → Gold 三层加工,同时写入时序数据库供实时查询。时序库内部再做冷热分层,热数据放 SSD 保证查询速度,冷数据转存对象存储降低成本。最终,清洗好的数据喂给决策应用和模型训练。
总结一下核心原则:
- 时序数据优先选专用库,别硬扛在关系库里
- 数据湖一定要分层,Bronze 层只追加不修改
- 分区策略要结合时间和空间两个维度
- 冷热分层是省钱的关键,别舍不得把冷数据搬走
嗯,数据存储这块就聊到这儿。记住,存储架构不是一锤子买卖,随着数据量增长,分区策略和冷热边界都需要动态调整。我一般每季度会 review 一次存储方案,看看需不需要调整分区粒度或者冷热阈值。毕竟,农业数据不会等你准备好了才增长。