news 2026/5/4 4:21:15

别再只写CRUD了!用TDengine+SpringBoot+Druid搞个物联网数据中台原型(附完整源码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
别再只写CRUD了!用TDengine+SpringBoot+Druid搞个物联网数据中台原型(附完整源码)

从零构建工业物联网数据中台:TDengine与SpringBoot深度整合实战

在智能制造浪潮下,工厂设备每秒钟产生的传感器数据量正呈指数级增长。我曾为某汽车零部件厂商设计数据采集系统时发现,传统关系型数据库在处理高频时序数据时,写入性能往往成为瓶颈,查询响应时间随着数据量增加而线性下降。这正是时序数据库TDengine的用武之地——其独创的"一个设备一张表"存储模型和列式压缩算法,使得处理千万级数据点如同儿戏。本文将带您用SpringBoot+Druid搭建一个具备生产级可靠性的物联网数据中台原型,完整代码已托管GitHub(文末获取)。

1. 智能工厂场景建模与数据库设计

1.1 时序数据特征分析

工业设备数据具有三个典型特征:

  • 高频率采样:温度传感器通常以0.5-1秒间隔上报
  • 结构化简单:多为时间戳+数值型指标的组合
  • 强时效性:近期数据访问频率远高于历史数据
-- TDengine超级表定义示例 CREATE STABLE factory_data ( ts TIMESTAMP, temperature FLOAT, humidity FLOAT, vibration FLOAT ) TAGS ( device_id NCHAR(32), workshop NCHAR(16), production_line NCHAR(16) );

1.2 分表策略设计

根据产线物理分布,我们采用三级分表策略:

TAGS维度分表规则示例数据分布特点
workshopworkshop_1按车间物理隔离
production_lineline_A同车间不同生产线
device_typemotor_sensor同类设备聚合
// 设备注册时动态创建子表 public void createDeviceTable(Device device) { String sql = String.format( "CREATE TABLE %s USING factory_data TAGS('%s', '%s', '%s')", device.getTableName(), device.getId(), device.getWorkshop(), device.getProductionLine() ); jdbcTemplate.execute(sql); }

2. SpringBoot数据接入层实现

2.1 高并发写入接口设计

采用异步批处理架构提升吞吐量:

@RestController @RequestMapping("/api/v1/telemetry") public class TelemetryController { @Autowired private BatchingService batchingService; @PostMapping public ResponseEntity<?> receiveData( @RequestBody List<DeviceData> dataPoints) { // 异步处理避免阻塞HTTP线程 batchingService.addToBatch(dataPoints); return ResponseEntity.accepted().build(); } }

2.2 数据校验与转换

建立数据质量检查机制:

public class DataValidator { private static final Map<String, Range<Float>> PARAM_RANGES = Map.of( "temperature", Range.between(-20f, 120f), "humidity", Range.between(0f, 100f), "vibration", Range.between(0f, 10f) ); public boolean validate(DeviceData data) { Range<Float> range = PARAM_RANGES.get(data.getMetricType()); return range.contains(data.getValue()); } }

3. 生产级连接池配置

3.1 Druid优化配置参数

# 连接池核心配置 spring.datasource.druid.initial-size=5 spring.datasource.druid.max-active=50 spring.datasource.druid.min-idle=5 spring.datasource.druid.max-wait=3000 # 保活策略 spring.datasource.druid.time-between-eviction-runs-millis=60000 spring.datasource.druid.min-evictable-idle-time-millis=300000 spring.datasource.druid.test-while-idle=true spring.datasource.druid.validation-query=SELECT SERVER_STATUS() # 监控配置 spring.datasource.druid.stat-view-servlet.enabled=true spring.datasource.druid.web-stat-filter.enabled=true

3.2 连接泄漏检测

@Configuration public class DruidConfig { @Bean public FilterRegistrationBean<Filter> statFilter() { FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>(); bean.setFilter(new WebStatFilter()); bean.addUrlPatterns("/*"); bean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.css,/druid/*"); return bean; } @Bean public ServletRegistrationBean<Servlet> statViewServlet() { ServletRegistrationBean<Servlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*"); bean.addInitParameter("loginUsername", "admin"); bean.addInitParameter("loginPassword", "admin"); return bean; } }

4. 时序数据高级分析功能

4.1 滑动窗口聚合查询

-- 每5分钟统计各产线平均温度 SELECT AVG(temperature) AS avg_temp, WSTART AS window_start, WEND AS window_end FROM factory_data WINDOW(5m) GROUP BY production_line;

4.2 设备异常检测

public List<AbnormalDevice> detectAbnormalDevices() { String sql = """ SELECT device_id, STDDEV(temperature) AS temp_stddev, AVG(temperature) AS temp_avg FROM factory_data WHERE ts > NOW - 1h GROUP BY device_id HAVING STDDEV(temperature) > 5.0 """; return jdbcTemplate.query(sql, (rs, rowNum) -> new AbnormalDevice( rs.getString("device_id"), rs.getFloat("temp_avg"), rs.getFloat("temp_stddev") )); }

5. 性能优化实战技巧

5.1 写入批处理模板

@Repository public class BatchInsertRepository { @Autowired private JdbcTemplate jdbcTemplate; public int[] batchInsert(List<DeviceData> dataList) { return jdbcTemplate.batchUpdate( "INSERT INTO ? USING factory_data TAGS(?,?,?) VALUES(?,?,?,?)", new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { DeviceData data = dataList.get(i); ps.setString(1, data.getTableName()); ps.setString(2, data.getDeviceId()); // 其他参数设置... } @Override public int getBatchSize() { return dataList.size(); } } ); } }

5.2 查询缓存策略

@Configuration @EnableCaching public class CacheConfig { @Bean public CacheManager cacheManager() { CaffeineCacheManager cacheManager = new CaffeineCacheManager(); cacheManager.setCaffeine(Caffeine.newBuilder() .expireAfterWrite(5, TimeUnit.MINUTES) .maximumSize(1000)); return cacheManager; } } @Cacheable(value = "device_stats", key = "#deviceId") public DeviceStats getDeviceStats(String deviceId) { // 复杂统计查询逻辑 }

在真实产线环境中部署时,建议将TDengine的WAL日志目录挂载到高性能SSD存储,我们曾通过这个调整将写入延迟从15ms降低到3ms。完整项目源码包含Docker Compose部署文件,可通过GitHub仓库获取。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/4 4:15:27

从弹簧秤到汽车悬挂:聊聊单自由度无阻尼振动那些意想不到的工程应用

从弹簧秤到汽车悬挂&#xff1a;聊聊单自由度无阻尼振动那些意想不到的工程应用 当你用弹簧秤称量一袋水果时&#xff0c;指针会在数字附近轻微摆动几次才稳定下来&#xff1b;老式座钟的钟摆以恒定节奏左右摇摆&#xff1b;汽车驶过减速带时车身会有规律地上下起伏——这些看似…

作者头像 李华
网站建设 2026/5/4 4:09:48

基于神经网络的代码密集分析:从原理到工程实践

1. 项目概述&#xff1a;从“dense-analysis/neural”看现代代码分析工具的演进最近在GitHub上看到一个名为“dense-analysis/neural”的项目&#xff0c;光看这个名字&#xff0c;就让我这个老码农心里一动。“dense-analysis”直译是“密集分析”&#xff0c;而“neural”自然…

作者头像 李华