工业时序数据中台架构演进:从 IoTDB + MySQL 到去 DWD 层简化实践
·
工业时序数据中台架构演进:从 IoTDB + MySQL 到去 DWD 层简化实践
在钢铁行业全流程数据采集项目中,我们经历了一次彻底的数据架构重构。本文分享从传统 ODS/DWD/D1 分层到去 DWD 简化为 ODS 直查 IoTDB 的实战经验。
一、项目背景:钢铁全流程数据采集的挑战
1.1 业务场景
轧钢生产线包含 7 大工艺:入炉、加热、锻坯、压延、锯切、收集、出坑。每支钢坯从入炉到收集需要经历数小时,期间产生大量设备采集数据:
- 秒级传感器数据:温度(℃)、压力(bar)、速度(m/s)、辊缝(mm)
- 工艺步骤数据:开始/结束时间、操作员、设备状态
- 质量检测数据:坯料编号、炉号、支号、规格
1.2 技术栈选型
| 层级 | 技术选型 | 存储内容 |
|---|---|---|
| 时序数据源 | IoTDB 1.3.x | 秒级设备采集数据 |
| 关系型数据库 | MySQL 8.0 | 业务数据、工艺步骤、质量记录 |
| ETL 工具 | RestCloud / NiFi | 数据抽取、转换、加载 |
| 后端服务 | Java + Spring Boot | 业务接口、大屏展示 |
| 前端 | Vue 3 + Element Plus | 大屏、管理页面 |
二、初始架构:ODS/DWD 双层存储
2.1 架构图
IoTDB (时序原始数据)
│
│ ETL 30s 聚合
▼
MySQL ODS 层 (原始数据快照)
│
│ 清洗、关联、归一化
▼
MySQL DWD 层 (明细数据仓库)
│
│ 业务查询
▼
Java 后端 → 前端大屏
2.2 存在的问题
经过 6 个月的生产运行,我们发现这套架构存在 4 个致命问题:
问题1:查询链路过长
- 前端请求 → DWD → ODS → IoTDB(三层跳转)
- 平均查询响应时间:1.2s → 3.8s
- 大屏 5 秒刷新一次,频繁超时
问题2:数据一致性差
- ODS 和 DWD 同步依赖定时调度,存在 30s~2min 延迟
- 工艺状态查询时,ODS 已更新但 DWD 未同步,导致"状态显示异常"
问题3:维护成本高
- ODS 保留 90 天,DWD 保留 2 年,双份存储成本高
- 需求变更时,需同时修改 ODS 抽取 SQL 和 DWD 查询逻辑
- 调度任务复杂,排查问题需跨 3 个数据层
问题4:DWD 价值低
- DWD 层的清洗逻辑(去重、补全、归一化)在业务侧重复实现
- 前端大屏需要原始 ODS 数据,DWD 层反而成为"中间商"
三、架构重构:去 DWD 直查 IoTDB
3.1 新架构设计
IoTDB (时序原始数据,永久存储)
│
│ ETL 30s 聚合(仅 ODS 需要)
▼
MySQL ODS 层 (原始数据快照,保留90天)
│
│ 直接查询 + IoTDB 兜底
▼
Java 后端 → 前端大屏
核心原则:
- ODS 是唯一关系型数据源,保留最近 90 天热数据
- IoTDB 是长期存储,ODS 无数据时自动回退查询
- DWD 层彻底下线,关闭相关调度任务
3.2 查询策略
// 伪代码:ODS + IoTDB 混合查询
public List<ProcessData> queryProcessData(Long startTime, Long endTime) {
// 1. 先查 ODS 条数
long odsCount = odsMapper.countByTime(startTime, endTime);
// 2. 查询绑定关系条数(业务表)
long bindCount = bindMapper.countByTime(startTime, endTime);
// 3. 一致则返回 ODS,不一致回退 IoTDB
if (odsCount == bindCount) {
return odsMapper.queryByTime(startTime, endTime);
} else {
return iotdbClient.queryByTime(startTime, endTime);
}
}
关键逻辑:
- ODS 查询速度:< 100ms
- IoTDB 查询速度:200ms ~ 800ms(取决于时间范围)
- 通过"条数对比"决定数据源,避免不一致
四、核心技术改造
4.1 ODS 分区表改造
-- 改造前:普通表
CREATE TABLE fp_step_process (
id BIGINT PRIMARY KEY,
stove_no VARCHAR(64),
create_time DATETIME
);
-- 改造后:按 create_time 分区(预建 3 年分区)
CREATE TABLE fp_step_process (
id BIGINT PRIMARY KEY,
stove_no VARCHAR(64),
create_time DATETIME NOT NULL,
...
) PARTITION BY RANGE (TO_DAYS(create_time)) (
PARTITION p2026_q1 VALUES LESS THAN (TO_DAYS('2026-04-01')),
PARTITION p2026_q2 VALUES LESS THAN (TO_DAYS('2026-07-01')),
PARTITION p2026_q3 VALUES LESS THAN (TO_DAYS('2026-10-01')),
...
);
收益:
- 查询自动剪枝,90 天内数据查询速度提升 3 倍
- 历史分区可单独删除(如删除 2024 年数据:
DROP PARTITION p2024_*)
4.2 IoTDB 查询兜底
// IoTDB 查询模板(按时间范围 + 设备编号)
String sql = "SELECT * FROM root.steel.rolling.* " +
"WHERE time >= {startTime} AND time <= {endTime} " +
"AND device = '{deviceNo}' " +
"ORDER BY time DESC LIMIT 1000";
优化点:
- IoTDB 查询始终带时间范围,避免全表扫描
- 大屏最新一条数据:
ORDER BY time DESC LIMIT 1,耗时 < 50ms - 分页查询:避免
OFFSET,改用时间戳游标
4.3 ETL 抽取优化
# RestCloud ETL 配置优化
# 1. 增量抽取(基于 IoTDB 最新时间戳)
SELECT * FROM process_data
WHERE time > '${last_sync_time}'
# 2. 批量写入(JDBC Batch)
batch_size = 5000
jdbc_url = "jdbc:mysql://prod:3306/ods?rewriteBatchedStatements=true"
# 3. 异常重试
retry_count = 3
retry_delay = 5s
关键优化:
- 从全表同步改为 增量同步,ETL 耗时从 5 分钟降到 30 秒
- 开启 MySQL 批量写入重写,写入性能提升 10 倍
五、上线效果对比
5.1 性能指标
| 指标 | 重构前 | 重构后 | 提升 |
|---|---|---|---|
| 大屏查询响应 | 3.8s | 800ms | 4.75x |
| ETL 同步耗时 | 5min | 30s | 10x |
| 调度任务数 | 12 个 | 4 个 | -67% |
| 存储成本 | 2TB | 800GB | -60% |
5.2 稳定性提升
- 查询链路简化:从 3 层减少到 2 层,故障点减少
- 数据一致性:通过"条数对比"策略,不一致率从 5% 降到 0.1%
- 排障效率:不再需要跨 ODS/DWD 两层排查,定位时间从 2h 降到 20min
六、踩坑实录
坑1:IoTDB 时间戳对齐失败
现象:ODS 条数 = 100,但 IoTDB 查询返回 98 条。
根因:IoTDB 和 MySQL 服务器时间不同步(相差 2 秒),导致边缘数据落在不同分区。
解决方案:
- ETL 抽取时以 IoTDB 时间戳为准,MySQL 只做映射
- 增加 2 秒缓冲窗口:
WHERE time >= start - 2s AND time <= end + 2s
坑2:DWD 关闭后遗留任务干扰
现象:去 DWD 后,某些历史报表仍然报 DWD 表不存在。
根因:未彻底清理 Quartz 调度任务,凌晨仍触发 DWD 同步 Job。
解决方案:
-- 查询遗留任务
SELECT * FROM qrtz_triggers WHERE job_group LIKE '%dwd%';
-- 暂停并删除
DELETE FROM qrtz_triggers WHERE job_name = 'dwd_sync_job';
坑3:大屏分页 vs 最新一条不一致
现象:大屏列表页第 1 条数据 ≠ 大屏"最新一条"卡片数据。
根因:列表页查 ODS(分页),最新一条查 IoTDB(兜底),两个数据源时间窗口不一致。
解决方案:
- 统一数据源:列表页和详情页都走 ODS + IoTDB 混合查询
- 增加数据源一致性校验:
odsCount == bindCount才用 ODS
七、适用场景与边界
7.1 适合去 DWD 的场景
✅ 业务查询以时间范围为主(如:最近 7 天工艺数据)
✅ 原始数据粒度细(秒级),DWD 聚合价值低
✅ 团队运维能力强,能handle 混合查询复杂度
7.2 不建议去 DWD 的场景
❌ 业务查询以维度关联为主(如:按产品型号汇总)
❌ 数据需要多源关联清洗(如:IoTDB + ERP + MES)
❌ 团队规模小,希望查询逻辑尽量简单
八、总结
这次架构重构的核心收获:
- 分层不是越多越好:DWD 层在特定场景下确实多余,直查 ODS + IoTDB 更高效
- 混合查询策略是关键:通过"条数对比"实现数据源自动切换,兼顾性能和一致性
- 分区表是基础:ODS 按时间分区后,查询性能和可维护性大幅提升
- 渐进式重构:先保留 DWD 双跑,验证新查询无误后再下线,降低风险
如果你也在做工业数据中台或时序数据架构选型,欢迎交流讨论。
更多推荐
所有评论(0)