最近,因为项目需要,将 TDengine 整合进入项目中进行使用。虽然之前也在前面的项目中多多少少用过,但抽了周末时间看了一番官网,并总结些记录及使用,物联网领域中的应用更是广泛了,如果想学习物联网领域,那么时序数据库的概念应该多少都有了解,而今天文章的主角--TDengine,更是时序数据库中的一颗巨星。
一、概述篇1.1 概述TDengine 是一款开源、云原生的时序数据库,专为物联网、工业互联网、金融、IT 运维监控等场景设计并优化。它能让大量设备、数据采集器每天产生的高达 TB 甚至 PB 级的数据得到高效实时的处理,对业务的运行状态进行实时的监测、预警,从大数据中挖掘出商业价值。
在 2022 年 8 月,TD 推出了 3.0 版本(备注:2.x版本后续不再维护)。 3.x 的版本中,性能测试方面大幅度高于 InfluxDB 和 TimescaleDB,支持 10亿个设备采集数据,100个节点,支持存储与计算分离。因此,挺适合我们在物联网领域中进行集成使用的。
官网:www.taosdata.com/
1.2 基本概念详见官网,这里的概念还是得理解清楚才能进行后续的表设计。
1.2.1 采集量(Metric)采集值是指传感器、设备或其他类型采集点采集的物理量,比如电流、电压、温度,是随着时间变化的,数据类型可以是整型、浮点型、布尔型、字符串,随时间迁移,采集数据量越来越大。 也有称采集指标,也有人称采集点位。做物联网的应该都明白这个概念。
1.2.2 标签(Tag)标签是静态的,比如设备地址,设备颜色,静态的,但是 TD 允许用户修改。
1.2.3 数据采集点(Data Collection Point)问题:修改标签后,原来存储的标签是原来值还是修改后的值?
数据采集点是指按照预设时间周期或受事件触发采集物理量的硬件或软件。一个数据采集点可以采集一个或多个采集量,但这些采集量都是同一时刻采集的,具有相同的时间戳。
1.2.4 表(table)为充分利用其数据的时序性和其他数据特点,TDengine 采取一个数据采集点一张表的策略,要求对每个数据采集点单独建表(比如有一千万个智能电表,就需创建一千万张表,比如表格中的 d1001,d1002,d1003,d1004 都需单独建表),用来存储这个数据采集点所采集的时序数据。这种设计有几大优点:
如果采用传统的方式,将多个数据采集点的数据写入一张表,由于网络延时不可控,不同数据采集点的数据到达服务器的时序是无法保证的,写入操作是要有锁保护的,而且一个数据采集点的数据是难以保证连续存储在一起的。采用一个数据采集点一张表的方式,能最大程度的保证单个数据采集点的插入和查询的性能是最优的。
1.2.5 超级表总结:即一个采集点对应一张表
一个采集点一张表,导致表的数量巨大,而且要经常做到聚合操作?
因此,引入超级表的概念。
超级表是指某一特定类型的数据采集点的集合,同一类型的数据采集点,其表结构是完全一样的,但每个表的静态属性tag 是不一样的。
1.2.6 子表通过超级表创建的表称为子表。查询可在子表和超级表上查询,如果从主表查询,TD 会对子表进行过滤,然后筛选出符合的子表再查询,这样的效率大大提升。
子表 VS 普通表
超级表 VS 子表
在 centos7上安装服务端,然后在本机电脑上安装client端。 下载安装包,上传到服务器。
1、tar 解压目录
2、进入到安装包所在目录,先解压文件后,进入子目录,执行其中的 install.sh 安装脚本 以上服务端的安装就好了。
3、安装后,使用 systemctl 命令来启动 TDengine 的服务进程。
sql复制代码systemctl start taosd
如下 systemctl 命令可以帮助你管理 TDengine 服务:
-- 启动服务进程:systemctl start taosd
-- 停止服务进程:systemctl stop taosd
-- 重启服务进程:systemctl restart taosd
-- 查看服务状态:systemctl status taosd
systemctl stop taosd 指令在执行后并不会马上停止 TDengine 服务,而是等待系统中必要的落盘工作正常完成,在数据量很大的情况下,这可能会消耗较长时间。
如果系统不支持 systemd,则进入 /usr/local/taos/bin/taosd 方式启动 TDengine 服务。
使用 taos 命令,进入命令行, 执行官网的案例 :
sql复制代码taos> CREATE TABLE t (ts TIMESTAMP, speed INT);
Create OK, 0 row(s) affected (0.014925s)
taos> INSERT INTO t VALUES ('2019-07-15 00:00:00', 10);
Insert OK, 1 row(s) affected (0.001002s)
taos> INSERT INTO t VALUES ('2019-07-15 01:00:00', 20);
Insert OK, 1 row(s) affected (0.000965s)
taos> SELECT * FROM t;
ts | speed |
========================================
2019-07-15 00:00:00.000 | 10 |
2019-07-15 01:00:00.000 | 20 |
Query OK, 2 row(s) in set (0.001406s)
建立连接,windows 可与直接连接服务端进行使用。 TD 提供两种连接方式:
区别:Rest 连接无需安装 taosc,性能下降 30%
下面介绍原生连接的方式,即我们需要在本机上装 client 软件。 默认安装路径为:C:\TDengine,其中包括以下文件(目录):
需要我们配置 taos.cfg 文件,将 firstEP 修改为 TDengine 服务器的 End Point ,例如
firstEp mytd:6030
同时,在 host 目录下,C:\Windows\system32\drivers\etc\hosts 新增 host 记录
192.168.31.102 mytd
执行 taos 命令: 发现下面bug
Failed to check Server Edition, Reason:0x80002662:Fail to get table info, error: some vnode/qnode/mnode(s) out of service
原因是,没有修改服务端的域名。修改 linux 中 /etc/hosts 下新增 mytd 192.168.31.102。
本服务演示的软件版本为 3.0.7.0
二、TD 集成2.1 开发指南TD 的使用,要做下面几件事情。
xml复制代码 <dependencies>
<dependency>
<groupId>com.taosdata.jdbc</groupId>
<artifactId>taos-jdbcdriver</artifactId>
<version>3.2.4</version>
</dependency>
</dependencies>
编写测试代码:
ini复制代码public class TDTest {
public static void main(String[] args) throws SQLException {
String jdbcUrl = "jdbc:TAOS://mytd:6030?user=root&password=taosdata";
Properties connProps = new Properties();
connProps.setProperty(TSDBDriver.PROPERTY_KEY_charset, "UTF-8");
connProps.setProperty(TSDBDriver.PROPERTY_KEY_LOCALE, "en_US.UTF-8");
connProps.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC-8");
Connection conn = DriverManager.getConnection(jdbcUrl, connProps);
System.out.println("Connected");
conn.close();
}
}
2.3 数据建模
在物联网场景中,一般有多种不同类型的采集设备,采集多种不同的物理量。
同一种采集设备类型,往往有多个设备分布在不同的地点。以智能电表为例子。
TDengine的创新:一个采集点一张表。
2.3.1 创建库前提:需要建库、建超级表、建表、才能写入数据。
建议为数据特征相同的表创建一个库,每个库可以配置不同的存储策略。
ini复制代码create database power keep 365 ; 这个库的数据保留 365 天
2.3.2 引入超级表
sql复制代码create table meters (ts timestamp, current float, voltage int) TAGS (location binary(64) , groupId int)
一个数据采集点一张表,意味着 1000万智能电表对应 1000 万张表。
一个物联网系统,往往存在海量同类型的数据采集点,如何对这么多张表进行操作就是一个巨大的挑战。
超级表引入:为方便对同类型多表的操作。
创建超级表时,需提供:表名、表结构 Schema、标签 Schema
超级表的列分为两部分:
diff复制代码create table d1001 using meters tags("Beijing",2);
- TDengine 对每个数据采集点需要独立建表
- 因为源于超级表(meters)创建而成,也称子表(d1001)
- 创建时,需要使用超级表做模板,同时指定标签的具体值
- 一个超级表,可包含若干个子表,子表数量没有限制
建议将数据采集点的全局唯一ID作为子表名(如设备序列号)
用户在写数据时,并不确定某个子表是否存在,此时,可使用自动建表语法来创建不存在的表,若该表已经存在则不会建立新表。
sql复制代码insert into d1001 using meters tags("beijing" , 2 ) values (now, 10.2, 219);
- 上述 SQL 语句将记录(now,102,219)插入进表 d1001
- 如果这张表没有创建,就会自己创建子表
思考:多列模型 VS 单列模型
TD的建议:
sql复制代码
CREATE STABLE ocloud_point (ts timestamp, pointValue float) TAGS (unit binary(64));
# 创建子表
create table ocloud_point_1001_0001 using ocloud_point tags("kwh");
# 自动建表
INSERT INTO ocloud_point_1001_0001 USING ocloud_point TAGS ("kwh") VALUES (NOW, 10.2),(NOW,1022);
INSERT INTO ocloud_point_1001_0001 USING ocloud_point TAGS ("kwh") VALUES (NOW, 10.2),(NOW,1022);
对同一张表,如果新插入记录的时间戳已经存在,则指定了新值的列会用新值覆盖旧值,而没有指定新值的列则不受影响。。
TD 的查询功能演示 : 单列 和 多列
sql复制代码taos> select * from ocloud_point;
ts | pointvalue | unit |
==================================================================================
2023-08-12 16:44:48.947 | 10.1999998 | kwh |
2023-08-12 16:45:27.280 | 1022.0000000 | kwh |
Query OK, 2 row(s) in set (0.002652s)
taos> select pointvalue from ocloud_point;
pointvalue |
=======================
10.1999998 |
1022.0000000 |
Query OK, 2 row(s) in set (0.003383s)
scss复制代码taos> select avg(pointvalue) from ocloud_point;
avg(pointvalue) |
============================
516.099999904632568 |
2.4 springboot 项目集成
1、新增依赖
3.6.0复制代码</dependency>
<!--Tdengine-->
<dependency>
<groupId>com.taosdata.jdbc</groupId>
<artifactId>taos-jdbcdriver</artifactId>
<version>3.2.4</version>
</dependency>
2、修改双数据源配置
yaml复制代码# 数据源
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
stat-view-servlet:
enabled: true
loginUsername: admin
loginPassword: 123456
allow:
web-stat-filter:
enabled: true
dynamic:
primary: master
datasource:
master:
driver-class-name: com.mysql.cj.jdbc.Driver
username: ${MYSQL_USER:root}
password: ${MYSQL_PWD:root}
url: jdbc:mysql://${MYSQL_HOST:127.0.0.1}:${MYSQL_PORT:3306}/${MYSQL_DB:ocloud_data}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT+8&allowMultiQueries=true&allowPublicKeyRetrieval=true
taosd:
driver-class-name: com.taosdata.jdbc.TSDBDriver
url: jdbc:TAOS://192.168.100.98:6030/ocloud?timezone=Asia/Beijing&charset=UTF-8
username: root
password: taosdata
3、可以编写 mapper 层
less复制代码@Mapper
@DS("taosd")
public interface TDEngineMapper extends BaseMapper<PointValueEntity> {
/**
* 批量插入
* @param list
*/
@InterceptorIgnore(tenantLine = "true")
void pointValueInsert(@Param("list") List<PointValueEntity> list);
/**
* 获取个数
* @param data
* @return
*/
Long getCountByTimestamp(PointSelectDTO data);
/**
* 分页查询
* @param data
* @return
*/
@InterceptorIgnore(tenantLine = "true")
List<PointValueEntity> getPageByTimestamp(PointPageDTO data);
}
4、xml 的案例代码
xml复制代码<!-- 批量插入-->
<insert id="pointValueInsert" parameterType="java.util.List">
insert into
<foreach collection="list" item="item" separator=" " close=";">
ocloud_point_${item.deviceId}_${item.pointId} using ocloud_point tags(#{item.deviceId},#{item.pointId}) values (#{item.ts},#{item.pointValue})
</foreach>
</insert>
<!-- 按时间范围获取个数 -->
<select id="getCountByTimestamp" parameterType="com.unisun.ocloud.data.tdEngine.dto.PointSelectDTO" resultType="java.lang.Long">
<choose>
<when test="deviceId != null and pointId != null and deviceId != '' and pointId != ''">
SELECT count(0) FROM ocloud_point_${deviceId}_${pointId} WHERE ts BETWEEN #{startTime} AND #{endTime}
</when>
<otherwise>
SELECT count(0) FROM ocloud_point WHERE ts BETWEEN #{startTime} AND #{endTime}
</otherwise>
</choose>
</select>
<!-- 按时间周期分页查询 -->
<select id="getPageByTimestamp" resultType="com.unisun.ocloud.data.tdEngine.entity.PointValueEntity">
select _wstart,FIRST(point_value) as pointValue,ts,device_id,point_id
FROM ocloud_point
<where>
<if test="pointId!=null and pointId!=''">
and point_id = #{pointId}
</if>
<if test="deviceId!=null and deviceId!=''">
and device_id = #{deviceId}
</if>
<if test="startTime!=null and endTime!=null">
and ts BETWEEN #{startTime} AND #{endTime}
</if>
</where>
interval(${inter})
FILL(NULL)
LIMIT ${pageStart},${pageSize}
</select>
小结
有关 TD 的入门使用就到这了,官方给的demo案例也不错,主要可以了解下这款数据库的基本用法,在监控数据领域、物联网采集领域、涉及到时序数据库相关场景的使用时,可以多一项考虑。且随着 TD 的日益成熟,很多高级功能也待自己去探索,比如流式计算的使用、窗口函数、发布订阅等,如若后续碰到相关场景,在考虑作文跟进,本篇的基础介绍就到此结束了,感谢阅读!
Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved