TDengine极简实战:从采集到入库,从前端到后端,体验物联网设备数据流转
作者:牛晓青背景我们的项目涉及物联网相关业务,由于一开始的年少无知,传感器数据采用了 MySQL 进行存储,经过近两年的数据累积,目前几个核心表单表数据已过亿,虽然通过索引优化、 SQL 优化以及读写分离等措施,勉强满足基本的查询,能在秒级给出数据;但是数据量还在持续增加,当面对用户多维度的统计需求,在实现上、效率上总是那么不尽如人意。。大概是2021年的6月份,一次偶然的机会,我在一个技术交流群
作者:牛晓青
背景
我们的项目涉及物联网相关业务,由于一开始的年少无知,传感器数据采用了 MySQL
进行存储,经过近两年的数据累积,目前几个核心表单表数据已过亿,虽然通过索引优化、 SQL
优化以及读写分离等措施,勉强满足基本的查询,能在秒级给出数据;但是数据量还在持续增加,当面对用户多维度的统计需求,在实现上、效率上总是那么不尽如人意。。
大概是2021年的6月份,一次偶然的机会,我在一个技术交流群中了解到涛思数据。后来花了三五天,把 TDengine
官网的文档过了一遍,并通过实际代码体验了一番,大大小小总结了14篇文章(文末附链接),这个算是第15篇。
这个关于 TDengine
的极简实战项目,重在体验物联网设备数据的流转过程,尤其关注 TDengine
的特性,因为这个项目就是为体验 TDengine
而写的,顺便学习下 Vue3.0
、 ElementPlus
的使用。
所谓极(wan)简(ju)实(xiang)战(mu),就是不涉及太复杂的业务_,我们就拿通用的物联网系统的数据流转过程为例,体验下物联网数据采集、数据传输、数据转发、数据解析、数据存储、数据查询、数据呈现的完整过程。这篇文章先介绍下整体流程,不涉及具体的编码实现,重点关注数据存储时用到的时序数据库 TDengine
的独特之处,关于前、后端的功能实现,将在后续文章进行详细说明。
作为一个数据库,本身是用来存储数据的,在使用过程中主要就是 CRUD
操作,但用到实际的物联网场景中,还需要具备高可用性、可扩展性以及便于维护的特点, TDengine
满足以上所有要求(在这之前也体验了下清华大学开源的 IoTDB
,但是并没有用明白o(╥﹏╥)o)。关于 TDengine
的时序数据写入性能,TDengine 官方自带了一个 taosdemo
的程序,我在1-TDengine安装与初体验这里面有实践,结果是开启了10个线程,建库+建表+插入数据总耗时约60s,总记录100000000条,每秒写入数据记录1681350.46条。,这里不做赘述,主要体验查询、修改、删除功能,而 TDengine
甚至都用不到我们自己在程序中进行更新、删除操作(可以在建库时设置更新、删除配置),所以重点就是查询啦:海量数据的条件查询、按时间范围查询、聚合查询、分页查询、TopN查询、降采样。
TDengine是什么?
TDengine
专为物联网、车联网等时序空间大数据设计,其核心功能是时序数据库。但为减少大数据平台的研发和运维的复杂度,更进一步降低计算资源,TDengine
还提供大数据处理所需要的消息队列、消息订阅、缓存、流式计算等功能。
为什么要用TDengine?
物联网设备无时无刻都在发出数据,与互联网相比,物联网第一个突出的特点便是数据量大,而我们目前竟然还在用关系型数据库 MySQL
存储这些设备数据,单表过亿,查询与统计遇到了瓶颈。 TDengine
对物联网、运维监控等系统产生的时序数据的特点具有深刻的洞察。所有机器、设备、传感器、以及交易系统所产生的数据都是时序的,而且很多还带有位置信息。这些数据具有明显的特征,而 TDengine
是充分利用这些特点设计出的大数据平台,以下是涛思数据总结的时序数据的特点。
- 数据是时序的,一定带有时间戳;
- 数据是结构化的;
- 数据极少有更新或删除操作;
- 无需传统数据库的事务处理;
- 相对互联网应用,写多读少;
- 用户关注的是一段时间的趋势,而不是某一特点时间点的值;
- 数据是有保留期限的;
- 数据的查询分析一定是基于时间段和地理区域的;
- 除存储查询外,还往往需要各种统计和实时计算操作;
- 数据量巨大,一天采集的数据就可以超过
100
亿条。
怎么快速上手TDengine?
那必须是官网了,官方文档写得非常全面,而且也有对应的微信视频号、B站UP等学习资源渠道。
不过在阅读官方文档前,强烈建议先看下白皮书:
https://www.taosdata.com/downloads/TDengine_White_Paper_20.pdf
当时,在阅读白皮书的过程中,看到总结的时序数据的特点、数据模型设计、存储结构设计以及数据分片设计时,在心里一遍遍地直呼内行~~,当时就下决心研究下这款优秀的时序数据库,为后续数据迁移、架构演进进行技术预研。
数据来源
至于历史数据怎么来,这不是 TDengine
要解决的问题,哈哈,因为一般需用到 TDengine
这种时序数据库的,都是数据量达到了一定程度,数据分析时的效率遇到瓶颈。
由于换工作的原因,无法使用实际的生产数据进行迁移,我这里模拟了历史数据,直接往 TDengine
中写入1亿条记录。
数据源这里使用 Node.js
模拟了一个 MQTT
客户端,当然也可以使用很多 MQTT
客户端工具: HiveMQ
, MQTTfx
, MQTTX
等。
主要功能
- 按区域检索并查看当前区域接入设备的实时数据
- 指定区域下设备各项指标的平均值以及数据总量
- 查看指定设备的历史数据,可按照时间段检索
- 查看指定设备仅1小时内的各项指标的趋势
- 导出指定设备的全量数据为
Excel
文件
技术选型
-
物联网终端技术:
- EMQX
- Node.js
-
后端技术:
- SpringBoot
- TDengine
- TDengine-alert
- MyBatisPlus
- WebSocket
- HuTool
-
前端技术:
-
Vue
- vue-router
-
ElementPlus
- Layout 响应式布局
- Cascader 级联选择器
- Card 卡片
- Dialog 对话框
- Table 表格
- Pagination 分页
- DateTimePicker 日期时间选择器
- Message 消息提示
-
Echarts
-
环境配置
CentOS 7
TDengine 2.2.0.0,两台虚拟机实例集群
EMQX 4.2.4
SpringBoot 2.4.3
Node.js 12.22.6
Vue 3.0
服务组件
经过这个项目,我加强了对以下技术点的应用与掌握。
- Node.js模拟MQTT客户端
- EMQX Webhook消息路由
- SpringBoot+MyBatisPlus服务端数据ORM
- TDengine亿级数据存储与查询
- TDengine-alert与AlertManager报警监控
- WebSocket实时消息推送
- Hutool Excel文件导出
- ECharts数据可视化图表呈现
- 统一响应封装、全局异常拦截
- Spring事件与监听
- MyBatisPlus分页
模拟场景
场景:在两个省份的多个城市、每个城市安装100个设备。
{{"太原", "大同", "阳泉", "长治", "晋城", "朔州", "晋中", "运城", "忻州", "临汾", "吕梁"},
{"西安", "铜川", "宝鸡", "咸阳", "渭南", "延安", "汉中", "榆林", "安康", "商洛"}};
数据模型设计
根据具体业务,设计超级表与子表。
-- ts, 时间戳
-- voltage, 电压
-- currente, 电流
-- temperature, 温度
-- sn, 设备序号
-- city, 时间戳
-- groupid, 分组编号
-- 超级表
create stable if not exists iot.power(ts timestamp, voltage int, currente float, temperature float) tags(sn int, city nchar(64), groupid int)
-- 子表
create table if not exists iot.device1 using iot.power tags(#{sn}, #{city}, #{groupid})
效果展示
- WebSocket服务端推送
这里用到了 Node.js
模拟的一个 MQTT
客户端,并通过 EMQX
的 WebHook
插件回调后端服务接口,借助 Spring
的事件机制,实现消息入库与 WebSocket
实时数据更新 ;
- TDengine查询效率
总量1亿条记录,分页查询毫无压力,基本都是 ms
级返回结果,这得益于 TDengine
的存储结构设计(其实,底层是分区查询的,扫描的仅是很小一部分数据,所以必须快啊)。
- TDengine压缩效率
这可以到 TDengine
的数据目录 data
,其链接目录: /var/lib/taos/vnode
,观察数据的占用空间。
数据查询
模拟历史数据,通过脚本直接往 TDengine
中写入近1亿条记录。
- 条件查询、聚合查询
select count(1) total, avg(voltage) voltage, avg(currente) currente, avg(temperature) temperature from iot.power where city='西安';
- 按时间范围查询
SELECT COUNT(1)
FROM power
WHERE sn = '1100' AND ts BETWEEN '1610168956836' AND '1641704956836';
- 分页查询
SELECT ts,voltage,currente,temperature,sn,city,groupid
FROM power
WHERE sn = '1100' AND ts BETWEEN '1610168956836' AND '1641704956836' ORDER BY ts DESC
LIMIT 10 offset 50;
- 降采样
Echarts
的图表展示,用到了 TDengine
的降采样功能,完成插值,体现数据变化趋势。
select last(voltage) voltage, last(currente) currente, last(temperature) temperature
FROM iot.power
WHERE sn = '1101' and ts > '2021-09-07 09:15:11.138' and ts < '2021-09-07 10:15:11.138' interval(60s) fill(value, 0);
小总结
这个玩具项目,基于 TDengine 2.2.0.0
、 EMQX 4.2.4
、 SpringBoot 2.4.3
、 Node.js 12.22.6
、 Vue 3.0
完成物联网设备数据的采集、转发、存储与展现。其中关于 TDengine
的查询性能相当亮眼:海量数据的条件查询、按时间范围查询、聚合查询、分页查询、TopN查询、降采样等基本都是毫秒级便给出结果;除此之外,采用两个虚拟机实例的极简集群实现 TDengine
快速扩展。更多详情可参考以下快速上手系列(#.#)
快速上手系列
- 1-TDengine安装与初体验
- 2-TDengine客户端连接,RESTful,JDBC
- 3-TDengine竟然无法修改、删除数据?
- 4-TDengine图形化管理工具TDengineGUI与IDEA插件
- 5-TDengine集成SpringBoot,MyBatis,MyBatisPlus
- 6-TDengine集群体验:2台虚拟主机
- 7-TDengine集成Grafana实现日志数据可视化
- 8-TDengine里用的好好的字段名,却被MySQL的保留字狠狠上了一课
- 9-TDengine低版本分页offset出现bug,如何平滑升级版本、迁移数据
- 10-TDengine之SpringBoot读取的时间与数据库中存储的时间相差8小时
- 11-TDengine集成EMQX:通过规则引擎实现设备数据直接入库
- 12-TDengine数据迁移:导入与导出
- 13-TDengine使用JDBC-JNI连接报错:JNI connection is NULL
- 14-TDengine安装报警模块实现报警监测Webhook回调与邮件推送
If you have any questions or any bugs are found, please feel free to contact me.
Your comments and suggestions are welcome!
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)