TDengine 在住建行业工地管理系统落地的“操作手册”_taos-jdbcdriver-程序员宅基地

技术标签: 后端  数据库  tdengine  Case Study  

 作者:必和必拓研发部

小T导读:湖南必和必拓科技发展有限公司定位于智慧城市建设与行业信息化,当前聚焦智慧住建、智慧环保、智慧司法以及智能制造四大领域,既包含各类电子产品安装施工及维保,也包括自有软件研发及部署。

随着业务规模的逐渐扩大,传统数据库越来越难以满足某些业务场景对查询、分析、统计的进一步需求。为了打破当下困局,湖南必和必拓想到了三种解决方案,在进行了严谨的分析后,我们决定采用物联网数据库,经过选型对比,时序数据库TDengine成为其首选方案。

如何在TDengine上进行数据建模、集群搭建、告警模块搭建?如何将数据更平滑地迁移到TDengine?在这些操作中,可能会遇到什么问题?又该如何解决?从湖南必和必拓的实践经验出发,本文将从代码层面一一解答。

业务场景及痛点

在一些业务场景中,我们需要将长沙市在建工地的扬尘数据(温度、湿度、pm2.5、pm10、pm100、噪声、风向、风速)存储在数据库中,以便为业务提供查询、分析和统计的操作。

但近来出现了一个难题,我们共有监测点107个,每分钟上送1条数据,每年就预计有“107*60*24*365 = 56,239,200”条数据,也就是5600多万条。目前已存储两年的数据,数据量总计约1亿多条。这些数据一直都被存储在MySQL数据库中,庞大的数据量使得查询速度越发缓慢,甚至部分页面出现超时问题。

解决方案1:MySQL数据库分库分表

如果我们将数据库分散到不同的表上,单表的索引大小就得到了控制,对索引以及表结构的变更会变得更加方便和高效。当数据库实例的吞吐量达到性能的瓶颈时,我们需要扩展数据库实例,让每个数据库实例承担其中一部分数据库的请求,分解总体的大请求量的压力。

弊端:

  • 分库分表需要提前对数据做好规划。如果按照时间对表进行水平划分,随着监控点增加,后面的表数据量可能越来越大,容易出现数据热点问题;如果按照监测点hash取模对表进行水平划分,当监测点增加,进行扩展就会比较困难。例如:之前是mod4,后面是mod6,则需要对之前的历史数据重新进行处理。

  • 在对数据进行统计分析时,可能需要进行多表的聚合查询,查询速度会受到影响。

解决方案2:使用华为云物联网平台

华为云物联网基于物联网资产模型,整合物联网数据集成、清洗、存储、分析、可视化,为物联网数据开发者提供一站式服务,能够有效降低开发门槛,缩短开发周期,快速实现物联网数据价值变现。

弊端:前期需设备厂商针对平台接口进行适配,接入以后零代码的方式确实在配置上会比较方便,但是平台的费用以及实时流式计算按次收费的方式,整体费用过高。

解决方案3:使用物联网数据库

  • InfluxDB:高性能的时序数据库,可以高效的存储和查询时序数据。可惜的是,目前社区版集群功能不开源。

  • TDengine:TDengine是一个简单快捷、高性能的时序数据库,提供高性能的同时也极大降低了安装、部署和维护的成本。

‍应用之后,TDengine能解决之前令我们较为头疼的一些问题,包括前文中描述的问题,它有以下5点主要优势:

  1. 安装简单。下载rpm包,一个命令安装完毕即可运行。

  2. 数据库开源,支持集群。

  3. 充分考虑时序数据的特点,以超级表为模型,将每个监测点的数据单独存储在一张表中,提高了插入和查询速度。

  4. 有丰富的函数,支持窗口查询和连续查询。

  5. 自带TDengineAlert模块,和AlertManager联合使用,可以推送告警信息

综合考虑以上解决方法,我们发现,使用TDengine后,硬件成本和开发维护成本大大降低,写入和查询速度比OpenTSDB等还要高出一个级别。于是,TDengine成为了我们的首选解决方案。

接下来,我将把我们在探索TDengine时的一些重要操作、问题点以及解决办法等经验传递给大家。


 

关于TDengine安装

官网下载安装包,文中使用的是2.0.20.12,即:TDengine-server-2.0.20.12-Linux-x64.tar.gz。安装包中包含安装命令, 解压后直接使用即可。

[root@hnbhbt ~]# tar -zxvf TDengine-server-2.0.20.12-Linux-x64.tar.gz
[root@hnbhbt ~]# cd TDengine-server-2.0.20.12
[root@hnbhbt ~]# ./install.sh
1. 启动TDengine
[root@hnbhbt ~]# systemctl start taosd
2. 检查TDengine状态
[root@hnbhbt ~]# systemctl status taosd
3. 输入以下命令进入TDengine命令行
taos
出现如下显示后表示进入成功
Welcome to the TDengine shell from Linux, Client Version:2.0.20.12
Copyright (c) 2020 by TAOS Data, Inc. All rights reserved.
taos>
卸载: 需要手动删除配置文件以及日志
[root@hnbhbt ~]# sudo rm -rf /var/log/taos/
[root@hnbhbt ~]# sudo rm -rf /var/lib/taos/
[root@hnbhbt ~]# sudo rm -rf /etc/taos/taos.cfg

注: 安装过程中需要配置TDengine的FQDN为hostname,通过Linux命令可以查看当前机器的hostname,配置相应内容即可,官方建议尽量不要使用localhost, 文中配置的是hnbhbt.com

数据建模

表设计

-- 创建数据库
create database db_transfer_platform keep 730 replica 3;
-- 使用数据库
use db_transfer_platform;
-- 创建超表
create stable if not exists s_dust_history_data(ts timestamp,measured_value double,id int)tags(device_code nchar(30),channel_number nchar(30),signal_code nchar(30),status bool);
-- 删除超表
drop stable if exists s_dust_history_data;
-- ts 时间戳;measured_value 测量值;id 平台主键;device_code 设备编号;channel_number 通道序号;signal_code 信号编号;status 在线状态

注:ts默认为主键,不能为空;列名避免取关键值,否则无法插入

数据插入

-- example:创建子表,插入数据,查询数据 
-- 子表名称[监测信号_设备编号] eg pm_25_1003055表示设备编号为1003055的pm2.5的监测表
create table if not exists eg_pm_25_1003055 using s_dust_history_data tags('1003055','1','18113001',true);
create table if not exists eg_pm_10_1003055 using s_dust_history_data tags('1003055','2','18114001',false);
insert into eg_pm_25_1003055 (ts,measured_value,id) values("2021-07-29 21:43:22",7.0,123);
insert into eg_pm_10_1003055 (ts,measured_value,id) values("2021-07-29 21:43:22",6.0,124);
insert into eg_pm_25_1003055 (ts,measured_value,id) values("2021-07-29 21:43:22",9.0,123);

注:

1.全列模式写入速度会远快于指定列,建议尽可能采用全列写入方式

2.批量插入数据,写入速度更快(最多支持同时插入32000条左右,和SQL长度有关)

3.对于重复的数据,忽略后面插入的数据

数据查询

select * from s_dust_history_data where ts > '2021-07-29 00:00:00' and ts < '2021-07-30 00:00:00' and signal_code = '18113001';

Spring Boot 整合 TDengine

当用户需要查询工地设备扬尘数据时,扬尘数据将通过定时任务从第三方接口中定时拉取保存至我们系统的数据库。由于扬尘数据的量较大,为千万甚至上亿级别,而系统配置相关数据的数据量较小,因此我们选择将扬尘数据和系统配置数据分离——扬尘数据使用 TDengine 进行存储,系统配置数据采用 MySQL 进行存储。

本文使用 MybatisPlus 作为持久层框架,同时接入 TDengine 和 MySQL,做双数据源配置。下文中将重点讲述 TDengine,MySQL 相关内容将省略。

1. taos-jdbcdriver 的选择

根据官网找到 taos-jdbcdriver 的对应版本,本文使用的 taos-jdbcdriver 版本是 2.0.22。

<dependency>
    <groupId>com.taosdata.jdbc</groupId>
    <artifactId>taos-jdbcdriver</artifactId>
    <version>2.0.22</version>
    <scope>runtime</scope>
</dependency>

【问题】最开始使用的是较高的版本,结果出现无法正常访问的情况,最终降低 taos-jdbcdriver 版本后得以解决。

2. 双数据源配置

在 Spring Boot yml 中配置双数据源,使用 druid 连接池,将 TDengine 设置为主数据库,MySQL 设置为副数据库。

spring:
  datasource:
    dynamic:
      druid:
        initial-size: 10
        min-idle: 10
        max-active: 100
        max-wait: 60000
        test-while-idle: false
      primary: td-engine
      strict: false
      datasource:
        td-engine:
          driver-class-name: com.taosdata.jdbc.TSDBDriver
          url: jdbc:TAOS://address:port/dbname
          username: dbusername
          password: dbpassword
        mysql:
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: ...
          username: ...
          password: ...

3. 代码编写

核心逻辑是第三方数据的定时拉取以及在本系统进行的查询,此处主要介绍TDengine的mapper写法。

官方提到TDengine全列模式写入速度会远快于指定列,建议尽可能采用全列写入方式;而扬尘数据的种类多样,因此我们采用动态表名 + 批量插入的方式。每次拉取数据时,以设备和扬尘数据种类为维度创建tags,同时对该维度的数据进行数据插入。

Mapper
a. 创建Tags
<insert id="createTags" parameterType="TagsEntity">
    create table if not exists #{tags名称}
        using 超级表名称 tags
    (#{tagsColumn1Value},
    ...
    #{tagsColumnNValue})
</insert>
b. Tags插入单条数据
<insert id="insert" parameterType="TagsEntity">
    insert into #{tags名称}
        (#{tagsColumn1},
        ...
        #{tagsColumnN})
        values
        (#{tagsColumn1Value},
        ...
        #{tagsColumnNValue})
</insert>

需要注意的是,TDengine 在进行全列写入时,必须为数据表的每个列都显式地提供数据,并且一次插入的数据是有上限的,否则会报错,建议在正式插入之前测试一下全列写入的上限。

<insert id="batchInsert" parameterType="TagsEntity">
    insert into #{tags名称}
    values
    <foreach collection="list" item="data">
        (#{data.tagsColumn1Value},
        ...
        #{data.tagsColumnNValue})
    </foreach>
</insert>
JavaBean

如需查询扬尘数据,创建一个和超级表绑定的实体类即可,里面应该包含超级表本身的属性以及 tags 的属性,可以直接使用 MyBatisPlus 的持久层方法进行查询。

@Data
@TableName(tbname)
public class SuperTableEntity {
    private String colunm1;
    ...
}

【问题一】在编写完代码后尽管查询不会出错,但插入和创建 tags 会发生错误,后来发现此处是 DNS 解析异常,由于 TDengine 配置了 FQDN,因此应该在 hosts 中配置映射关系,之后才能进行正常的插入和创建操作。

【问题二】在进行第三方数据同步时,由于第一次是同步历史数据,因此插入 TDengine 的数据量较大。尽管 TDengine 本身支持的全列模式写入已经提升了不少效率,但如果代码中的批量插入继续使用普通 for 循环,插入效率仍然较低,这里可以通过切割数据和多线程的方式进行实现。

private void runner(List<Entity> list, int dealSize) throws Exception {
    if (!CollectionUtils.isEmpty(list)) {
        //数据总的大小
        int count = list.size();
        //每个线程数据集
        List<Entity> threadList = null;
        //线程池
        int runSize = (count / dealSize) + 1;
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                runSize,
                350,
                30L, TimeUnit.SECONDS,
                new SynchronousQueue<>());
        
        CountDownLatch countDownLatch = new CountDownLatch(runSize);

        for (int i = 0; i < runSize; i++) {
            //计算每个线程执行的数据
            int startIndex = (i * dealSize);
            if ((i + 1) == runSize) {
                threadList = list.subList(startIndex, count);
            } else {
                int endIndex = (i + 1) * dealSize;
                threadList = list.subList(startIndex, endIndex);
            }
            //TODO 这里写自己的线程任务
            executor.execute(myThread);
        }
        //计数
        countDownLatch.await();
        //关闭线程池
        executor.shutdown();
    }
}

在使用双数据源时,Controller 和 Dao 与一般的 Spring Boot + MyBatis 无异,主要在于 Service,由于设置的 primary 数据库是 TDengine,所以持久层默认会访问 TDengine 数据库,如果要切换,需要在 service 的 class 或方法上添加注解 @DS(databaseAlias),括号中填写 yml 中配置的数据库别名,如 td-engine、mysql 等,通过这种方式来选择需要访问的数据库。primary 数据库可以省略 @DS。

@DS("td-engine")
public TestEntity get() {
    return testDao.get();
}

TDengine 数据迁移

当我们需要将扬尘数据从公司本地服务器迁移数据至华为云服务器,文中使用 TDengine 自带的 dump 工具进行数据迁移。

参考文档: https://www.taosdata.com/blog/2020/03/09/1334.html

DUMP命令帮助信息
[root@hnbhbt ~]# taosdump --help
关键参数:
-h: 主机名称
-o: 导出文件路径
-i: 导入文件路径
-u: TDengine用户名
-p: TDengine密码
-A: 导出所有数据库的数据
-B: 导出指定数据库(可选多个数据库)
-S: 开始时间时间戳
-E: 结束时间时间戳
-T: 指定导出数据时,启动的线程数。建议设置成机器上core的2倍
1. 数据导出
[root@hnbhbt ~]# taosdump -h hostname -o /dump/data-210824 -u username -p password -A -S 1598889600000 -E 1628956800000 -T 500 -N 100
2. 数据导入
[root@hnbhbt ~]# taosdump -u username -p password -i ~/data-210824 -T 4

【问题】此处在配置 hostname 时应该填写 FQDN,如果不填写或者填写服务器 IP,将会产生错误。

集群搭建

参考网址:

TDengine 集群安装、管理 文档 | 涛思数据

使用 TDengine 进行报警监测 博客: 使用 TDengine 进行报警监测 | 涛思数据

常见问题 文档 | 涛思数据

一篇文章说清楚 TDengine 的 FQDN  一篇文章说清楚TDengine的FQDN - 涛思数据TDengine - 博客园

Linux 配置 ntp 时间服务器(全)Linux配置ntp时间服务器(全) - 大墨垂杨 - 博客园

可扫描二维码下载相关附件

告警模块搭建

参考网址:

Alertmanager 配置概述  

Alertmanager配置概述 - prometheus-book

深入 Alertmanager 概念与配置介绍 

https://www.cnblogs.com/gered/p/13496950.html#autoid-5-0-0

alertmanager 配置文件详解(四) alertmanager配置文件详解(四) - SoulChild随笔记

java 利用钉钉机器人向钉钉群推送消息 

java利用钉钉机器人向钉钉群推送消息 - 农名工进城 - 博客园

Alertmanager 企业微信配置 Alertmanager 企业微信配置 - 简书

可扫描二维码下载相关附件

小结

应市质安站的监管要求以及蓝天办对空气污染治理的督查要求,市内所有工地的空气质量数据及噪音数据均需接入政府监管平台、纳入管理中。监控数据的特点就是时序、结构化、简单但量大,在处理这类数据上,TDengine 可以说是量身定做。搭载 TDengine 后,监控数据上报后的实时展示、历史回溯都非常快,加上其本身轻量的特点,对于缩减项目开发运维成本也非常有帮助,完美满足了业务的需求。


想了解更多TDengine的具体细节,欢迎大家在GitHub上查看相关源代码哦。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/taos_data/article/details/121263563

智能推荐

解决win10/win8/8.1 64位操作系统MT65xx preloader线刷驱动无法安装_mt65驱动-程序员宅基地

文章浏览阅读1.3w次。转载自 http://www.miui.com/thread-2003672-1-1.html 当手机在刷错包或者误修改删除系统文件后会出现无法开机或者是移动定制(联通合约机)版想刷标准版,这时就会用到线刷,首先就是安装线刷驱动。 在XP和win7上线刷是比较方便的,用那个驱动自动安装版,直接就可以安装好,完成线刷。不过现在也有好多机友换成了win8/8.1系统,再使用这个_mt65驱动

SonarQube简介及客户端集成_sonar的客户端区别-程序员宅基地

文章浏览阅读1k次。SonarQube是一个代码质量管理平台,可以扫描监测代码并给出质量评价及修改建议,通过插件机制支持25+中开发语言,可以很容易与gradle\maven\jenkins等工具进行集成,是非常流行的代码质量管控平台。通CheckStyle、findbugs等工具定位不同,SonarQube定位于平台,有完善的管理机制及强大的管理页面,并通过插件支持checkstyle及findbugs等既有的流..._sonar的客户端区别

元学习系列(六):神经图灵机详细分析_神经图灵机方法改进-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏27次。神经图灵机是LSTM、GRU的改进版本,本质上依然包含一个外部记忆结构、可对记忆进行读写操作,主要针对读写操作进行了改进,或者说提出了一种新的读写操作思路。神经图灵机之所以叫这个名字是因为它通过深度学习模型模拟了图灵机,但是我觉得如果先去介绍图灵机的概念,就会搞得很混乱,所以这里主要从神经图灵机改进了LSTM的哪些方面入手进行讲解,同时,由于模型的结构比较复杂,为了让思路更清晰,这次也会分开几..._神经图灵机方法改进

【机器学习】机器学习模型迭代方法(Python)-程序员宅基地

文章浏览阅读2.8k次。一、模型迭代方法机器学习模型在实际应用的场景,通常要根据新增的数据下进行模型的迭代,常见的模型迭代方法有以下几种:1、全量数据重新训练一个模型,直接合并历史训练数据与新增的数据,模型直接离线学习全量数据,学习得到一个全新的模型。优缺点:这也是实际最为常见的模型迭代方式,通常模型效果也是最好的,但这样模型迭代比较耗时,资源耗费比较多,实时性较差,特别是在大数据场景更为困难;2、模型融合的方法,将旧模..._模型迭代

base64图片打成Zip包上传,以及服务端解压的简单实现_base64可以装换zip吗-程序员宅基地

文章浏览阅读2.3k次。1、前言上传图片一般采用异步上传的方式,但是异步上传带来不好的地方,就如果图片有改变或者删除,图片服务器端就会造成浪费。所以有时候就会和参数同步提交。笔者喜欢base64图片一起上传,但是图片过多时就会出现数据丢失等异常。因为tomcat的post请求默认是2M的长度限制。2、解决办法有两种:① 修改tomcat的servel.xml的配置文件,设置 maxPostSize=..._base64可以装换zip吗

Opencv自然场景文本识别系统(源码&教程)_opencv自然场景实时识别文字-程序员宅基地

文章浏览阅读1k次,点赞17次,收藏22次。Opencv自然场景文本识别系统(源码&教程)_opencv自然场景实时识别文字

随便推点

ESXi 快速复制虚拟机脚本_exsi6.7快速克隆centos-程序员宅基地

文章浏览阅读1.3k次。拷贝虚拟机文件时间比较长,因为虚拟机 flat 文件很大,所以要等。脚本完成后,以复制虚拟机文件夹。将以下脚本内容写入文件。_exsi6.7快速克隆centos

好友推荐—基于关系的java和spark代码实现_本关任务:使用 spark core 知识完成 " 好友推荐 " 的程序。-程序员宅基地

文章浏览阅读2k次。本文主要实现基于二度好友的推荐。数学公式参考于:http://blog.csdn.net/qq_14950717/article/details/52197565测试数据为自己随手画的关系图把图片整理成文本信息如下:a b c d e f yb c a f gc a b dd c a e h q re f h d af e a b gg h f bh e g i di j m n ..._本关任务:使用 spark core 知识完成 " 好友推荐 " 的程序。

南京大学-高级程序设计复习总结_南京大学高级程序设计-程序员宅基地

文章浏览阅读367次。南京大学高级程序设计期末复习总结,c++面向对象编程_南京大学高级程序设计

4.朴素贝叶斯分类器实现-matlab_朴素贝叶斯 matlab训练和测试输出-程序员宅基地

文章浏览阅读3.1k次,点赞2次,收藏12次。实现朴素贝叶斯分类器,并且根据李航《统计机器学习》第四章提供的数据训练与测试,结果与书中一致分别实现了朴素贝叶斯以及带有laplace平滑的朴素贝叶斯%书中例题实现朴素贝叶斯%特征1的取值集合A1=[1;2;3];%特征2的取值集合A2=[4;5;6];%S M LAValues={A1;A2};%Y的取值集合YValue=[-1;1];%数据集和T=[ 1,4,-1;..._朴素贝叶斯 matlab训练和测试输出

Markdown 文本换行_markdowntext 换行-程序员宅基地

文章浏览阅读1.6k次。Markdown 文本换行_markdowntext 换行

错误:0xC0000022 在运行 Microsoft Windows 非核心版本的计算机上,运行”slui.exe 0x2a 0xC0000022″以显示错误文本_错误: 0xc0000022 在运行 microsoft windows 非核心版本的计算机上,运行-程序员宅基地

文章浏览阅读6.7w次,点赞2次,收藏37次。win10 2016长期服务版激活错误解决方法:打开“注册表编辑器”;(Windows + R然后输入Regedit)修改SkipRearm的值为1:(在HKEY_LOCAL_MACHINE–》SOFTWARE–》Microsoft–》Windows NT–》CurrentVersion–》SoftwareProtectionPlatform里面,将SkipRearm的值修改为1)重..._错误: 0xc0000022 在运行 microsoft windows 非核心版本的计算机上,运行“slui.ex