3. 实验室时序数据模型与存储选型

实验室时序数据平台的核心问题,不是“把数据存进去”,而是“用正确的模型描述数据,再选一个能长期稳定承载它的存储引擎”。如果模型设计错了,后面无论换多贵的数据库,查询都会越来越慢、标签越来越乱、磁盘越来越贵。本节围绕三件事展开:时序数据模型怎么建、标签与字段怎么分、关系型数据库与 InfluxDB、TimescaleDB、TDengine 等时序数据库如何选型。

一、时序数据模型:measurement、tag、field、timestamp

实验室时序数据最典型的四元结构是:measurement(测量/表名)、tag(标签,索引维度)、field(字段,实际数值)、timestamp(时间戳)。以一台环境试验箱为例,采集点每 10 秒上报一次温度、湿度、设定值、运行状态,可以写成:

measurement: env_chamber
tags:        device_id=CH-01, lab=LAB-A, sensor=TH-01
fields:      temp=23.6, humidity=45.2, setpoint=23.0, run_state=1
timestamp:   2025-03-11T09:30:00+08:00

这里的关键判断是:tag 是“谁/在哪/属于什么”这类低基数、用于过滤和分组的字符串维度;field 是“测到了多少”这类随时间变化、需要做聚合和计算的数值或状态量。device_id、lab、sensor 适合做 tag,因为查询常按设备、实验室、传感器筛选;temp、humidity 适合做 field,因为要对它们求平均、最大、最小、积分。run_state 虽然看起来像状态,但它是随时间变化的量,通常放 field;如果它被频繁用于过滤“只查运行中的设备”,也可以考虑单独建一个状态 measurement,而不是硬塞进 tag。

时间戳必须统一时区与精度。实验室常见做法是:采集端统一用 UTC 存储,展示层按本地时区转换;精度按采样周期决定,10 秒采样用秒级或毫秒级均可,但同一平台内应保持一致,避免同一 measurement 里混用秒和毫秒导致排序错乱。

二、标签与字段设计:基数、粒度与命名

标签设计的最大陷阱是高基数。如果把每次实验的 run_id、样品编号、操作员姓名都做成 tag,标签组合数会爆炸,索引膨胀,写入和查询都会退化。合理的做法是:把真正用于“筛选和分组”的维度做成 tag,把“每次实验不同、数量巨大”的标识放到 field 或单独的关系表中,通过外键关联。

命名要统一。建议采用小写加下划线,measurement 用“对象_指标”或“对象”命名,tag key 用 device_id、lab、sensor、unit 这类稳定语义,field key 用 temp、humidity、pressure、voltage 这类物理量名。单位不要混进 field 名,例如不要写 temp_c 和 temp_f 两个字段,而应统一用 temp,单位在元数据或 tag 中声明。

下面这张图给出一个实验室时序数据模型的典型结构:左侧是采集对象,中间是 measurement 的 tag/field 划分,右侧是写入存储后的查询视角。

实验室时序数据模型:采集对象 → tag/field 划分 → 存储查询 采集对象 环境试验箱 CH-01 温度/湿度/设定值 电池充放电台 BC-03 电压/电流/容量 振动台 VS-02 加速度/频率 每 10s 上报一次 measurement: env_chamber tag(索引维度,低基数) device_id=CH-01 lab=LAB-A sensor=TH-01 field(数值,随时间变化) temp=23.6 humidity=45.2 setpoint=23.0 run_state=1 timestamp 2025-03-11T09:30:00+08:00 存储与查询视角 按 tag 过滤 device_id='CH-01' 按时间窗口聚合 avg(temp) / 5min 降采样与保留策略 原始 30d → 1h 聚合 2y 高基数 tag 会拖慢查询

三、关系型数据库 vs 时序数据库

很多实验室最初用 MySQL 或 PostgreSQL 存时序数据,单表按时间建索引,短期可用,但数据量到亿级后会出现写入瓶颈、索引膨胀、聚合查询慢、压缩率低等问题。关系型数据库的优势是事务、关联查询、生态成熟,适合存元数据、实验记录、设备台账、报警配置;时序数据库的优势是高写入吞吐、按时间分区、列式压缩、内置降采样和保留策略,适合存高频采集值。

对比维度关系型数据库(MySQL/PostgreSQL)时序数据库(InfluxDB/TimescaleDB/TDengine)
数据模型行式表,强 Schema时间线模型,tag/field 分离
写入吞吐万级/秒,受索引影响十万级~百万级/秒
压缩率低,原始行存储高,列式+时间戳压缩
聚合查询需手写 SQL 和索引优化内置 time_bucket、降采样、连续聚合
保留策略需自行分区和清理原生 retention policy / TTL
事务与关联强,适合元数据弱,不适合复杂关联

实际工程中,常见架构是“关系型 + 时序型”混合:PostgreSQL 存设备台账、实验批次、用户权限、报警规则;时序数据库存采集值。两者通过 device_id、run_id 关联,查询时先查元数据拿到维度,再查时序库拿数值。

四、InfluxDB、TimescaleDB、TDengine 选型要点

InfluxDB 是专用时序数据库,tag/field 模型清晰,Flux/InfluxQL 查询语言成熟,适合快速搭建、写入量大、以监控和指标为主的场景。它的短板是集群版商业授权、复杂关联弱、标签基数敏感。实验室若以设备监控、环境数据采集为主,InfluxDB 上手快。

TimescaleDB 是 PostgreSQL 扩展,本质仍是 PostgreSQL,优势是 SQL 完整、事务和关联强、生态成熟,通过 hypertable 自动按时间分区,支持 continuous aggregate 连续聚合。适合已有 PostgreSQL 团队、需要同时做元数据关联和时序分析的实验室。它的写入性能不如专用时序库,但胜在“一个数据库解决大部分问题”。

TDengine 是国产时序数据库,采用“一个设备一张表”的模型,超级表 + 子表设计,写入和压缩性能突出,自带缓存、流计算和降采样,适合设备数量多、采集频率高、国产化要求强的场景。它的 SQL 方言与标准 SQL 有差异,生态和工具链相对前两者略窄,选型时需评估团队熟悉度。

选型时不要只看 benchmark 写入数字,要按实验室真实负载评估:设备数 × 测点数 × 采样频率 = 每秒写入点数;再乘以保留天数估算总数据量。例如 200 台设备、每台 20 个测点、10 秒采样,每秒约 400 点,日增约 3456 万点,一年约 126 亿点。这个量级下,压缩率和降采样能力比峰值写入更关键。
常见误区:把 run_id、sample_id 这类高基数标识做成 tag,导致时间线数量爆炸;或者把温度、湿度等数值做成 tag,导致无法聚合。正确做法是 tag 只放低基数维度,field 放数值,高基数标识放关系表或 field。

综合来看,选型可以按以下顺序判断:如果团队强依赖 SQL 和事务、需要频繁关联元数据,优先 TimescaleDB;如果追求开箱即用、监控指标为主、写入量大,优先 InfluxDB;如果设备数量极大、国产化要求高、能接受专用 SQL 方言,优先 TDengine。无论选哪个,模型设计先行:先确定 measurement、tag、field、timestamp,再决定存储引擎,才能让平台在数据量增长后仍然可查、可扩、可维护。