Heath Kang

InfluxDB 与 Grafana 学习

2016 年来到上海、转行进入第一个大数据项目后,我还是个刚转行的毛头小子。第一次面对的不是“存一条业务记录”,而是持续涌入的设备数据:温度、压力、转速、振动、电流、阀门状态。它们都有一个共同点——脱离时间几乎没有意义

一台设备的温度为 82℃,单独看只是一个数字;知道它在五分钟内从 61℃ 上升到 82℃、同时冷却水流量下降,才开始接近一个可判断的工业事件。这也是当时 InfluxDB + Grafana 组合吸引我的原因:前者按时间组织和查询数据,后者把同一段时间窗口里的信号放在一个人能理解的界面里。

工业数据不是“带了 created_at 的普通表”

关系数据库当然可以保存时间戳,但工业遥测通常有几种不同的访问模式:

  • 写入持续发生,而且常以批量方式到达;
  • 查询几乎总会带时间范围,例如“过去 24 小时”;
  • 常要按设备、工厂、产线、工位筛选,并按分钟/小时聚合;
  • 原始高频数据保存期有限,但降采样后的趋势数据要保留更久;
  • 最关心的是变化、异常和相关性,而不是某一行的事务更新。

这意味着数据模型的第一列不是主键,而是时间。时序数据库会围绕时间范围、分区、压缩、保留策略和聚合来优化写入与读取;这比把海量点位硬塞进一张 OLTP 表更贴近问题本身。

InfluxDB:measurement、tag、field、timestamp

以设备遥测为例,一个点可以写成:

machine_metric,
plant=shanghai,
line=paint,
machine=robot-17
temperature=82.4,pressure=1.72,running=true
1711072800000000000

这里有四类东西:

部分例子该怎么理解
measurementmachine_metric一类观测,类似逻辑表
tagplantlinemachine用来描述来源/上下文,适合过滤和分组
fieldtemperaturepressure实际测量值
timestamp最后一列该观测发生的时间

最值得记住的不是语法,而是 tag 和 field 的取舍。在传统 InfluxDB TSM 模型中,tag 会被索引,field 通常不被索引;因此“按产线查温度”应把产线建成 tag,而温度值应是 field。查询方式应反过来指导 schema;高基数 tag 会造成过多 series,拖慢读写并增加内存压力。

当时踩过的三件事

把现场日志也当成时序点追踪

一开始,我还把现场采集和服务运行产生的日志一起写进 InfluxDB:时间、设备、级别、消息,偶尔还有异常堆栈。那时的想法是“既然日志也有时间戳,放在同一个库里查询不是更方便吗?”

后来才发现,日志和遥测指标的查询方式并不一样。温度、压力这类指标适合按时间窗口聚合;日志往往需要按关键字、短语、异常文本和上下文追踪。把大量自由文本塞进 field,虽然能保存,却不能获得面向全文检索的索引和检索体验;如果又为了筛选把日志内容、请求 ID 之类写进 tag,问题又会回到高基数。两种误用叠在一起后,写入和查询都明显变慢。

所以这是我后来才补上的分工:时序库保留数值观测、状态和聚合指标;日志走更适合文本检索的系统,例如 Elasticsearch。不是说一条日志绝不能进 InfluxDB,而是不能因为“都有时间”就忽略两类数据完全不同的访问方式。

以为 tag 越多越好

刚开始做 schema 时,我很容易把一切“看起来能帮助定位问题”的信息都放进 tag:批次号、工单号、采集请求 ID,甚至某些会频繁变化的状态值;设备序列号也没有先评估规模与查询方式,就机械地全放了进去。想法很朴素:以后过滤起来会不会更方便?

问题是每一个不同的 tag 组合都会形成新的 series。设备数、批次数和请求 ID 一起增长后,索引和内存压力远快于我当时的直觉;写入与查询开始变慢,才知道这不是“多建几个索引”就能解决的事。

后来回头看,一个更稳妥的做法是:把工厂、产线、设备型号这类稳定且常用于筛选的维度放 tag;把温度、压力、状态码和高变动 ID 放 field 或关联系统。这个教训也提醒我,监控 schema 的第一问不该是“我能采什么”,而是“我打算如何查”。

所以在设备数量可控、且确实常按设备筛选时,machine=robot-17 可以作为 tag;但每次请求的 UUID、随机 hash、完整错误栈这类几乎每点不同的值,绝不能放 tag。一个很实用的判断法是:我会经常用它过滤/分组吗?它的取值数量有上界吗? 前者是“是”、后者也是“是”时,才考虑 tag。

从旧存储引擎迁移到 TSM

还有一次记忆很深的升级。当时我会把它模糊地说成“从 memory tree 升级到 disk tree”,后来查资料才知道这并不准确:0.9 时代旧的 b1/bz1 引擎基于 BoltDB;0.10 起,新写入的 shard 使用 tsm1(Time-Structured Merge Tree)。两者都落盘,真正变化的是时序数据在 WAL、内存 cache、不可变 TSM 文件和压缩/compaction 之间的组织方式。

迁移并不是简单替换一个二进制文件。旧 shard 可以暂时继续按旧格式被读取,但若要把存量数据转换为 TSM,需要用 influx_tsm 处理;完整转换是离线操作,还要先备份、安排停写窗口、检查配置与保留策略,并在转换后核对查询结果。对当时的我来说,这些步骤比“升级版本”本身花的功夫多得多。

不过效果也很直观:旧数据转过去后,磁盘占用下降,写入吞吐和查询表现都有明显改善。TSM 的关键不是把数据简单放到磁盘,而是按时间组织不可变文件、在后台压缩合并,让冷数据能有更好的压缩率,也让持续写入不会一直被旧式存储结构拖住。具体提升幅度会随数据、硬件和写入模式变化;我只记得那次迁移之后,系统终于不再那么容易被高频写入压得喘不过气。

为什么它适合工业大数据:时间窗口就是第一语言

工业监控很少问“给我所有温度”;更常问:

-- 表意伪查询:过去一小时,每分钟每台机器的平均温度
SELECT mean(temperature)
FROM machine_metric
WHERE time > now() - 1h AND plant = 'shanghai'
GROUP BY time(1m), machine

这里同时体现了三个能力:范围裁剪、按时间桶聚合、按设备维度分组。再配合保留策略和连续查询/任务,可形成分层数据:原始秒级数据保存 7 天,分钟聚合保存 90 天,小时聚合保存一年。它不只是省磁盘,更是在控制“我愿意为多老、多细的数据付出多少查询成本”。

但“时序数据库”不是万能标签。设备主数据、工单、权限、复杂事务仍更适合关系数据库;日志全文检索也另有工具。InfluxDB 擅长的是大量按时间追加、按维度筛选、按窗口聚合的数值观测

Grafana 的价值不只是画折线

InfluxDB 回答“数据在哪里、怎样查询”;Grafana 回答“人在故障发生时怎样读懂它”。一个好的 dashboard 不应是把所有指标塞成图墙,而是把一个判断过程压缩进一个页面:

总览:产线是否健康?
定位:哪台设备、从什么时候开始偏离?
关联:温度上升时,压力、流量、运行状态发生了什么?
处置:阈值是否触发告警,谁需要知道?

Grafana 的时间选择器、可复用 query、dashboard variable 很适合这个过程。把 $plant$line$machine 做成变量,同一套 dashboard 就能在不同产线与设备间复用;把温度、压力、运行状态对齐到同一时间范围,异常才有上下文。单值面板适合“现在是否超阈值”,time series 适合趋势与突变,table 适合按设备排序找异常者。

插件该在什么情况下使用

Grafana 的插件体系可以分成三类,不要混为一谈:

插件类型解决的问题工业场景示例
Data source从哪里取数据InfluxDB、SQL、OPC/工业网关、云监控
Panel如何呈现数据状态灯、仪表盘、地理位置、流程图
App打包成一套领域体验预置数据源、dashboard、页面与操作入口

Grafana 官方也按 data source、panel、app 三种类型定义插件:数据源负责连接外部系统,面板扩展可视化,app 可以把两者与自定义页面组合起来。

这使它特别适合作为工业项目的“观察层”:底层可以是 InfluxDB,也可以逐步接入关系库、日志、云监控;上层仍保持同一套变量、链接和告警体验。插件的正确用途是填补协议或呈现能力的缺口,而不是为了让 dashboard 更花哨。引入插件前要确认维护状态、权限边界、网络访问范围和升级兼容性。

这次实践留给我的三点学习

第一,监控系统的核心不是采集,而是指标命名与维度设计。如果 machinelineplant 的定义在采集端各自为政,任何数据库和 dashboard 都救不了查询体验。

第二,图表不是结论。图表的职责是把人带到下一个可验证的问题:这条曲线何时变化?哪些维度共同变化?是否超过业务阈值?是否需要行动?当 dashboard 能缩短从“发现异常”到“定位原因”的路径时,它才真正进入了生产系统。

第三,按时间写入不等于应该放进时序库。先分清数据是用来做窗口聚合、全文追踪,还是事务关联;再选存储和索引,往往比在同一个库里继续堆 schema 更重要。

References

Notion 相关零散笔记

  • TSDB
  • ClickHouse
  • Storage
  • 常用运维命令

相关文档