三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

工业时序数据中台架构演进:从 IoTDB + MySQL 到去 DWD 层简化实践

工业时序数据中台架构演进:从 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 混合查询publicList<ProcessData>queryProcessData(LongstartTime,LongendTime){// 1. 先查 ODS 条数longodsCount=odsMapper.countByTime(startTime,endTime);// 2. 查询绑定关系条数(业务表)longbindCount=bindMapper.countByTime(startTime,endTime);// 3. 一致则返回 ODS,不一致回退 IoTDBif(odsCount==bindCount){returnodsMapper.queryByTime(startTime,endTime);}else{returniotdbClient.queryByTime(startTime,endTime);}}

关键逻辑

  • ODS 查询速度:< 100ms
  • IoTDB 查询速度:200ms ~ 800ms(取决于时间范围)
  • 通过"条数对比"决定数据源,避免不一致

四、核心技术改造

4.1 ODS 分区表改造

-- 改造前:普通表CREATETABLEfp_step_process(idBIGINTPRIMARYKEY,stove_noVARCHAR(64),create_timeDATETIME);-- 改造后:按 create_time 分区(预建 3 年分区)CREATETABLEfp_step_process(idBIGINTPRIMARYKEY,stove_noVARCHAR(64),create_timeDATETIMENOTNULL,...)PARTITIONBYRANGE(TO_DAYS(create_time))(PARTITIONp2026_q1VALUESLESS THAN(TO_DAYS('2026-04-01')),PARTITIONp2026_q2VALUESLESS THAN(TO_DAYS('2026-07-01')),PARTITIONp2026_q3VALUESLESS THAN(TO_DAYS('2026-10-01')),...);

收益

  • 查询自动剪枝,90 天内数据查询速度提升3 倍
  • 历史分区可单独删除(如删除 2024 年数据:DROP PARTITION p2024_*

4.2 IoTDB 查询兜底

// IoTDB 查询模板(按时间范围 + 设备编号)Stringsql="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=5000jdbc_url="jdbc:mysql://prod:3306/ods?rewriteBatchedStatements=true"# 3. 异常重试retry_count=3retry_delay=5s

关键优化

  • 从全表同步改为增量同步,ETL 耗时从 5 分钟降到30 秒
  • 开启 MySQL 批量写入重写,写入性能提升10 倍

五、上线效果对比

5.1 性能指标

指标重构前重构后提升
大屏查询响应3.8s800ms4.75x
ETL 同步耗时5min30s10x
调度任务数12 个4 个-67%
存储成本2TB800GB-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*FROMqrtz_triggersWHEREjob_groupLIKE'%dwd%';-- 暂停并删除DELETEFROMqrtz_triggersWHEREjob_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)
❌ 团队规模小,希望查询逻辑尽量简单


八、总结

这次架构重构的核心收获:

  1. 分层不是越多越好:DWD 层在特定场景下确实多余,直查 ODS + IoTDB 更高效
  2. 混合查询策略是关键:通过"条数对比"实现数据源自动切换,兼顾性能和一致性
  3. 分区表是基础:ODS 按时间分区后,查询性能和可维护性大幅提升
  4. 渐进式重构:先保留 DWD 双跑,验证新查询无误后再下线,降低风险

如果你也在做工业数据中台或时序数据架构选型,欢迎交流讨论。

← 返回列表