TDengine 应用案例 — IoT 设备监控

分类:15.应用案例 | 篇章:01 IoT 设备监控

在这里插入图片描述
免费详情

TDengine 通过 InfluxDB 兼容协议、JDBC、连接器等方式与主流数据生态对接。本文汇总 Telegraf、Kafka Connect、Flink、Spark、Logstash 等工具的集成方式。

IoT 设备监控是 TDengine 最典型的应用场景。本文以一个虚拟的智能水表系统为例,介绍从数据建模、写入、查询、可视化的端到端方案。

场景速查表

维度参数
设备数10 万~100 万
采集频率每分钟 1 次
数据量10~100 GB/天
保留期5 年
查询模式单设备明细 + 区域聚合

详细解析

1. 业务背景

智能水表系统:

  ① 设备:城市供水公司的居民/商业水表
     - 10 万台水表
     - 分布在 100 个小区
     - 5 个城市区
     - 每台 1 个 SIM 卡通过 4G 上传
  
  ② 数据:
     - 流量(瞬时/累计)
     - 压力
     - 温度
     - 电池电量
     - GPS 位置
  
  ③ 需求:
     - 实时监控(异常告警)
     - 历史查询(账单结算)
     - 数据分析(漏水检测)
     - 报表(月度/季度)

2. 数据建模

-- 1. 创建数据库(保留 5 年)
CREATE DATABASE iot 
  PRECISION 'ms' 
  KEEP 1825 
  DURATION 30 
  REPLICA 3 
  VGROUPS 24
  CACHEMODEL 'last_value';


-- 2. 超级表设计:一个设备类型一个超级表
USE iot;

CREATE STABLE water_meter (
  ts TIMESTAMP,
  flow_instant FLOAT,       -- 瞬时流量 m³/h
  flow_total DOUBLE,        -- 累计流量 m³
  pressure FLOAT,           -- 压力 MPa
  temperature FLOAT,        -- 水温 °C
  battery TINYINT,          -- 电池 %
  signal_strength TINYINT,  -- 信号强度
  status TINYINT            -- 状态码
) TAGS (
  device_id BINARY(20),     -- 设备 ID(唯一)
  install_date BINARY(10),  -- 安装日期
  community NCHAR(50),      -- 小区
  district NCHAR(20),       -- 区
  city NCHAR(20),           -- 城市
  meter_type TINYINT,       -- 居民1/商业2/工业3
  lat DOUBLE,               -- 纬度
  lon DOUBLE                -- 经度
);


-- 3. 设计要点:
--   - Tag 不变信息:设备元数据
--   - Tag 用于过滤/分组
--   - Column 用于时序数据
--   - 子表自动按 device_id 创建

3. 数据写入

# 设备数据上报(MQTT → taosX → TDengine 流程)

# 应用直接写入示例(Python WebSocket)
import taosws

conn = taosws.connect("taosws://root:taosdata@localhost:6041/iot")

# 自动建子表写入
stmt = conn.statement2()
stmt.prepare("""
  INSERT INTO ? USING water_meter 
  TAGS (?, ?, ?, ?, ?, ?, ?, ?) 
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""")

def write_meter_data(device_id, meta, readings):
  subtable = f"d_{device_id}"
  stmt.set_tbname(subtable)
  stmt.set_tags([
    device_id, meta['install_date'], 
    meta['community'], meta['district'], 
    meta['city'], meta['type'],
    meta['lat'], meta['lon']
  ])
  
  for r in readings:
    stmt.add_row([
      r['ts'], r['flow_instant'], r['flow_total'],
      r['pressure'], r['temperature'], 
      r['battery'], r['signal'], r['status']
    ])
  
  stmt.execute()


# Schemaless 写入(更简单)
conn.execute_schemaless([
  f'water_meter,device_id=d001,community=COM_A,city=Beijing '
  f'flow_instant=2.5,flow_total=12345.6,pressure=0.35,battery=85i {ts}'
])

4. 查询场景

-- 1. 实时监控大盘
SELECT 
  COUNT(DISTINCT tbname) AS active_devices,
  AVG(battery) AS avg_battery,
  COUNT(CASE WHEN status > 0 THEN 1 END) AS abnormal_count 
FROM water_meter 
WHERE ts > NOW - 5m;


-- 2. 异常设备列表(电池低)
SELECT tbname, device_id, community, LAST(battery) AS battery 
FROM water_meter 
WHERE ts > NOW - 1d 
PARTITION BY tbname 
HAVING battery < 20 
ORDER BY battery ASC;


-- 3. 单设备明细
SELECT * FROM water_meter 
WHERE device_id = 'd001' 
  AND ts BETWEEN '2026-06-01' AND '2026-06-30' 
ORDER BY ts;


-- 4. 月度账单(最后值 - 第一个值)
SELECT 
  device_id, 
  community,
  LAST(flow_total) - FIRST(flow_total) AS month_usage_m3 
FROM water_meter 
WHERE ts >= '2026-06-01' AND ts < '2026-07-01' 
PARTITION BY device_id, community;


-- 5. 区域用水趋势(按小时)
SELECT _wstart, district, SUM(flow_instant) AS total_flow 
FROM water_meter 
WHERE ts > NOW - 7d 
PARTITION BY district 
INTERVAL(1h);


-- 6. 漏水检测(夜间流量 > 0 持续 1h)
SELECT device_id, community, COUNT(*) AS leak_minutes 
FROM water_meter 
WHERE ts > NOW - 1d 
  AND HOUR(ts) BETWEEN 2 AND 5 
  AND flow_instant > 0.1 
PARTITION BY device_id, community 
HAVING leak_minutes > 60;


-- 7. TOP 10 用水大户
SELECT device_id, community, 
       LAST(flow_total) - FIRST(flow_total) AS usage 
FROM water_meter 
WHERE ts > NOW - 30d 
PARTITION BY device_id, community 
ORDER BY usage DESC LIMIT 10;

5. 性能优化

-- 1. TSMA 加速大范围聚合
CREATE TSMA tsma_water_1h ON water_meter 
FUNCTION(
  AVG(flow_instant), 
  SUM(flow_instant), 
  LAST(flow_total), 
  FIRST(flow_total)
) INTERVAL(1h);

CREATE TSMA tsma_water_1d ON water_meter 
FUNCTION(
  AVG(flow_instant), 
  SUM(flow_instant), 
  LAST(flow_total), 
  FIRST(flow_total)
) INTERVAL(1d);


-- 2. Tag 索引(按小区查很常用)
CREATE INDEX idx_community ON water_meter (community);

-- 3. CACHEMODEL 加速 LAST 查询
-- ALTER DATABASE iot CACHEMODEL 'last_value';

6. 流计算实时分析

-- 1. 实时异常告警流
CREATE STREAM stream_alert TRIGGER AT_ONCE INTO alert_stream AS 
SELECT 
  _wstart AS ts, 
  district, 
  COUNT(CASE WHEN status > 0 THEN 1 END) AS abnormal 
FROM water_meter 
PARTITION BY district 
INTERVAL(1m);


-- 2. 区域用水实时统计
CREATE STREAM stream_district_flow INTO district_flow_1h AS 
SELECT 
  _wstart, district, 
  SUM(flow_instant) AS total_flow,
  AVG(flow_instant) AS avg_flow,
  COUNT(DISTINCT tbname) AS device_count 
FROM water_meter 
PARTITION BY district 
INTERVAL(1h);


-- 3. 计费基础数据流
CREATE STREAM stream_monthly INTO monthly_usage AS 
SELECT 
  _wstart, device_id, community, 
  LAST(flow_total) - FIRST(flow_total) AS usage 
FROM water_meter 
PARTITION BY device_id, community 
INTERVAL(1n);   -- 月窗口

7. 数据订阅消费

# Java 后端订阅异常告警,触发推送
import taosws

consumer = taosws.Consumer({
  'td.connect.websocket.scheme': 'ws',
  'td.connect.user': 'root',
  'td.connect.pass': 'taosdata',
  'group.id': 'alert_pusher',
  'auto.offset.reset': 'latest',
})

# 订阅异常流结果
consumer.subscribe(['alert_topic'])

while True:
  msg = consumer.poll(1)
  if msg is None:
    continue
  for record in msg:
    for row in record:
      if row['abnormal'] > 0:
        send_alert(row['district'], row['abnormal'])

8. 容量规划

资源估算(100 万设备):

  原始数据:
    - 单条记录 ~80 字节
    - 1 分钟 1 条
    - 单设备每日 = 1440 × 80 = 115 KB
    - 100 万设备每日 = 110 GB
    - 5 年 = 200 TB(未压缩)

  压缩后:
    - TDengine 压缩比 ~10x
    - 实际存储 ~20 TB

  集群规模:
    - 5 节点 × 4 TB SSD = 20 TB(REPLICA 3 后 ~7 TB 可用)
    - 需 10~15 节点
    - CPU:每节点 16 核
    - 内存:每节点 64 GB

代码示例

完整 IoT 平台架构

设备(MQTT) ─→ MQTT Broker ─→ taosX MQTT Connector ─→ TDengine
                                                         │
                                                         ├─→ Stream (实时分析)
                                                         │       │
                                                         │       └─→ TMQ → 告警推送
                                                         │
                                                         ├─→ Grafana (大盘)
                                                         │
                                                         ├─→ Web 后台 (REST)
                                                         │
                                                         └─→ 计费系统 (定时 SQL)

Grafana Dashboard 关键 Panel

-- Panel 1: 活跃设备数
SELECT NOW AS time, COUNT(DISTINCT tbname) AS value 
FROM water_meter WHERE ts > NOW - 5m;

-- Panel 2: 各区用水趋势
SELECT _wstart, district, SUM(flow_instant) AS value 
FROM water_meter 
WHERE ts >= $from AND ts < $to 
PARTITION BY district 
INTERVAL($interval);

-- Panel 3: 异常设备数
SELECT _wstart, COUNT(DISTINCT tbname) AS value 
FROM water_meter 
WHERE status > 0 AND ts >= $from AND ts < $to 
INTERVAL($interval);

-- Panel 4: 电池低预警 TOP 10
SELECT tbname, community, LAST(battery) AS value 
FROM water_meter 
PARTITION BY tbname, community 
HAVING value < 30 
ORDER BY value LIMIT 10;

性能考量

写入性能

设备数QPS推荐配置
10 万~1700/秒3 节点
100 万~17000/秒5~10 节点
1000 万~17万/秒20+ 节点

查询性能

查询响应(10亿行)
单设备月数据< 100ms
区域日聚合(带 TSMA)< 500ms
区域月聚合(带 TSMA)< 1s
全网月度账单几秒

FAQ

Q1: 子表用 device_id 命名好吗?

是常见做法。要求:合法标识符。建议加前缀如 d_xxx

Q2: GPS 怎么存?

  • 简单:lat/lon 作 Tag
  • 复杂:用 GEOMETRY 类型 + ST_* 函数

Q3: 历史数据 5 年怎么处理?

KEEP 1825 自动管理。冷数据可通过多级存储(hot/warm/cold)放在低成本盘。

Q4: 设备状态变化怎么记?

  • 写入 status 列
  • 用 STATE_WINDOW 分析状态时长
  • 状态变化事件单独超级表(events)

Q5: 怎么处理设备掉线?

  • 客户端缓存离线数据,恢复后批量补传
  • 服务端宽容处理乱序时间戳
  • 定期检查未上报设备(LAST(ts) < NOW - 1h)

参考

系统构架篇

数据模型

存储引擎

查询引擎

数据写入

数据订阅

预聚合

索引

SQL 语句

客户端与连接器

运维

安全

生态

关于 TDengine

TDengine 专为物联网IoT平台、工业大数据平台设计。其中,TDengine TSDB 是一款高性能、分布式的时序数据库(Time Series Database),同时它还带有内建的缓存、流式计算、数据订阅等系统功能;TDengine IDMP 是一款AI原生工业数据管理平台,它通过树状层次结构建立数据目录,对数据进行标准化、情景化,并通过 AI 提供实时分析、可视化、事件管理与报警等功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TDengine (老段)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值