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 扩展
适用场景 纯时序、高频写入、监控告警 时序+业务数据混合、复杂分析
我的建议:如果项目刚起步,团队 SQL 能力强,优先选 TimescaleDB。它既能存时序数据,又能直接关联大棚、地块等业务表,省去数据搬运的麻烦。我去年帮一个智慧茶园项目做选型,他们最终选了 TimescaleDB,因为需要把传感器数据和茶叶加工批次数据做关联分析,InfluxDB 在这块就吃力了。

4.2 数据湖架构:不止是存数据

数据湖这个概念,说白了就是把所有原始数据一股脑存下来,不提前做太多加工。农业数据湖里,除了时序数据,还有气象预报、卫星影像、农事记录、市场行情……格式五花八门。

我建议用分层架构来组织数据湖。别把所有数据扔一个桶里,那叫数据沼泽。

数据湖分层架构(以 MinIO + Delta Lake 为例)

Bronze 层(原始数据层)
  ├── 传感器原始 JSON
  ├── 气象站 CSV 文件
  └── 无人机影像 TIFF

Silver 层(清洗数据层)
  ├── 清洗后的时序表(去重、补缺)
  ├── 标准化气象数据
  └── 地块边界 GeoJSON

Gold 层(业务数据层)
  ├── 作物生长指标宽表
  ├── 灌溉决策特征表
  └── 产量预测模型输入表

为什么要分三层?

  • Bronze 层:保留原始数据,方便回溯和重算。万一清洗逻辑错了,还能从这层重新跑。
  • Silver 层:做数据清洗、去重、格式统一。这层的数据已经可以用于常规分析。
  • Gold 层:面向具体业务场景,比如模型训练、报表生成。数据经过聚合和特征工程,查询效率最高。
避坑指南:我曾经在一个智慧大棚项目里,把所有数据直接写入 Gold 层,结果模型迭代时需要回看原始传感器值,发现原始数据已经被清洗逻辑覆盖了。从那以后,我坚持 Bronze 层数据只追加、不修改。

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 实现
一个小技巧:冷数据虽然不常查,但偶尔还是要用的。我习惯在对象存储上保留一份 Parquet 格式的副本,配合 Presto/Trino 做即席查询。这样既省钱,又不完全丧失查询能力。

4.5 整体架构一览

说了这么多,咱们用一张图把整个存储架构串起来。你想想看,从传感器数据产生,到最终支撑决策,数据是怎么流动的?

农业数据存储架构总览 传感器数据源 气象/遥感数据 农事记录/业务 Bronze 层:原始数据(JSON/CSV/TIFF) Silver 层:清洗数据(去重/标准化/补缺) Gold 层:业务数据(特征宽表/模型输入) 时序数据库 InfluxDB / TimescaleDB 冷热分层 热:SSD(7天) 温:HDD(7天~6月) 冷:对象存储(6月+) 决策应用 / 模型训练 / 报表 灌溉决策 · 产量预测 · 异常告警

这张图把咱们刚才讲的内容串起来了。数据从传感器、气象站、农事系统流入数据湖,经过 Bronze → Silver → Gold 三层加工,同时写入时序数据库供实时查询。时序库内部再做冷热分层,热数据放 SSD 保证查询速度,冷数据转存对象存储降低成本。最终,清洗好的数据喂给决策应用和模型训练。

总结一下核心原则:

  • 时序数据优先选专用库,别硬扛在关系库里
  • 数据湖一定要分层,Bronze 层只追加不修改
  • 分区策略要结合时间和空间两个维度
  • 冷热分层是省钱的关键,别舍不得把冷数据搬走

嗯,数据存储这块就聊到这儿。记住,存储架构不是一锤子买卖,随着数据量增长,分区策略和冷热边界都需要动态调整。我一般每季度会 review 一次存储方案,看看需不需要调整分区粒度或者冷热阈值。毕竟,农业数据不会等你准备好了才增长。