4. 数据存储与处理:时序数据库选型、清洗与压缩策略
大家好,我是老张。今天咱们聊聊综合能源系统里一个特别实在的话题——数据怎么存、怎么管。
你想想看,一个园区级的综合能源系统,光传感器就几百上千个。电表、气表、热表、光伏逆变器、储能BMS……每秒钟都在往外吐数据。这些数据要是存不好,后面做分析、做优化全是白搭。
我最早做项目时吃过这个亏。当时图省事,直接把数据往MySQL里塞。结果三个月后,单表数据量破亿,查询一条历史曲线要等十几秒。业主直接打电话骂人……嗯,从那以后我再也不敢轻视数据存储了。
4.1 时序数据库选型:InfluxDB vs TimescaleDB
先说结论:综合能源场景下,我个人更倾向用TimescaleDB。但这不是绝对的,得看你的具体需求。
为什么会有时序数据库这个东西?说白了,传统的关系型数据库是为“事务”设计的——你今天下了个订单,明天改了个地址,后天退了个货。但时序数据不一样,它只追加、不修改,而且天然带时间戳。用MySQL硬扛,就像用卡车运快递,能运,但效率低、成本高。
我整理了一个对比表,大家一看就明白:
| 特性 | InfluxDB | TimescaleDB |
|---|---|---|
| 底层架构 | 自研TSM引擎 | 基于PostgreSQL |
| 写入性能 | 极高(百万点/秒) | 高(十万级/秒) |
| 查询语法 | 类SQL(Flux/InfluxQL) | 标准SQL |
| 数据压缩比 | 10:1 ~ 30:1 | 5:1 ~ 15:1 |
| 与现有系统集成 | 需单独部署 | 可复用PG生态 |
| 适用场景 | 纯时序、高吞吐 | 时序+业务数据混合 |
我在项目中遇到过这样的情况:一个光伏电站监控系统,数据量不大但需要跟设备台账、运维工单做关联查询。用InfluxDB的话,得搞两套数据库,数据还得来回同步,麻烦得很。后来换成TimescaleDB,一张超表搞定所有,舒服多了。
但如果你做的是高频采集——比如电质量分析,采样率到kHz级别——那InfluxDB的写入性能确实更猛。这个得看你的实际场景。
我的选型建议:
- 纯时序、高并发写入 → InfluxDB
- 时序+业务数据混合、团队熟悉SQL → TimescaleDB
- 已有PostgreSQL基础设施 → 无脑选TimescaleDB
4.2 数据清洗与插值:别让脏数据毁了你的模型
数据存进去了,但质量怎么样?我见过太多项目,数据是存了,但一查全是坑——传感器离线、通信丢包、数值跳变……用这种数据做优化,结果可想而知。
数据清洗,我一般分三步走:
- 去重:同一时间戳的多条记录,只保留一条
- 异常值剔除:超出合理范围的数据直接扔掉
- 缺失值插值:把空缺补上,保证时间序列连续
举个例子,光伏功率数据正常情况下应该在0到额定容量之间。如果某天突然蹦出来一个负值,或者一个比额定容量大10倍的值,那肯定是传感器抽风了。我一般用3σ原则或者箱线图法来识别这些异常点。
插值这块,我常用的方法有几种:
- 线性插值:简单粗暴,适合变化平缓的数据(如温度)
- 前向填充:用上一个有效值填充,适合开关状态类数据
- 样条插值:曲线更平滑,适合功率、负荷这类波动数据
给大家看一段我常用的插值代码(Python + Pandas):
import pandas as pd
import numpy as np
# 假设df是原始数据,index是时间戳
df = df.resample('1min').mean() # 重采样到1分钟粒度
# 线性插值
df['power_linear'] = df['power'].interpolate(method='linear')
# 前向填充(最多填充5个空值)
df['status_ffill'] = df['status'].fillna(method='ffill', limit=5)
# 样条插值(order=2表示二次样条)
df['power_spline'] = df['power'].interpolate(method='spline', order=2)
小技巧:插值不是万能的。如果连续缺失超过10个点,我建议直接标记为“无效时段”,而不是强行插值。否则会引入虚假数据,影响后续分析。
4.3 数据压缩与存储策略:省钱又省心
数据量大了,存储成本是个大问题。一个中等规模的园区,一年下来光原始数据就能攒几个TB。全存着?硬盘不要钱吗?
我一般用分层存储策略:
- 热数据(最近7天):原始精度,全量存储
- 温数据(7天~3个月):降采样到1分钟粒度
- 冷数据(3个月以上):降采样到15分钟或1小时粒度
这样做的好处很明显:查询近期数据时速度快,查询历史数据时虽然精度低一点,但做趋势分析完全够用。
压缩方面,TimescaleDB自带原生压缩功能,可以按列指定压缩算法。我一般这样配置:
-- 启用压缩
ALTER TABLE measurements SET (
timescaledb.compress,
timescaledb.compress_segmentby = 'device_id',
timescaledb.compress_orderby = 'time DESC'
);
-- 创建压缩策略(每天压缩一次)
SELECT add_compression_policy('measurements', INTERVAL '7 days');
InfluxDB的压缩更激进,它默认就开启了。我实测过,同样的数据量,InfluxDB的磁盘占用只有TimescaleDB的60%左右。但代价是查询灵活性差一些——你没法做复杂的JOIN操作。
注意:压缩不是免费的午餐。压缩后的数据查询延迟会略有增加。如果你的系统对实时性要求极高(比如毫秒级响应),建议保留一部分数据不压缩,或者用缓存层来加速。
4.4 本章知识体系总览
下面这张图是我自己画的,把本章的核心逻辑串起来了。你看一眼就能明白数据从采集到存储的完整链路:
这张图把整个流程串起来了:数据从传感器进来,先做清洗,然后根据场景选数据库,再按时间维度分层存储,最后用压缩技术省空间。每一步都环环相扣,缺一不可。
好了,关于数据存储与处理,我就讲这么多。记住一句话:存得好不好,直接决定你的平台能跑多远。别在这个环节省钱省力,不然后面有你哭的。
公众号:蓝海资料掘金营,微信deep3321