背景:
公司之前接了一个关于设备管理的项目,设备是用来控制和记录加油站的油气回收设备状态,以往的设备故障需要加油站的工作人员主动联系人工到现场,根据当前温度天气以及其他异常对设备进行参数调整或者更换油膜,现在要通过系统远程控制调整设备的各种阈值,并实时记录设备运行状态以及各方面的数据。设备在全国大概有2000台,每30秒钟每个设备会产生13条数据要存到数据库,作为历史记录和展示看板,但是普通的SQL承受不了如此庞大的插入,即使可以支持插入,后期的查询庞大的数据也会变得非常慢,另一方面数据量的大小也是一个不可避免的问题,而TDengine数据库也可以支持海量数据的快速查询。且TDengie支持配置过期数据,这样就可以再数据有效期后自动删除不再使用的数据,然后就引入了TDengine时序数据库。
项目介绍:
TDengie的性能非常强悍,而且不像redis那样是非结构化的数据,TDengine是结构化数据,且支持标准的SQL语句进行插入和查询操作,另外TDengine自带时间戳非常适合设备类的数据采集场景,读写能力非常快,达到千万级,虽然修改能力差,但是刚好设备采集的场景本就不会用到修改的操作。因此引入TDengine作为数据采集的数据库就非常恰到好处了。最后,结合项目的其他应用场景,整个项目的大概的业务逻辑如图:
超级表介绍:
为了能更好的使用TDengine,发挥它的性能,然后又重新再学写了下TDengine的知:时序数据库最大的特点是默认就用时间作为唯一ID,也就是同一时间一个普通表只允许插入一条数据,如果插入同一个时间的数据,数据库只会保留一条,这样也同时避免了问题数据,但是那么多台设备会在同一时间插入数据怎么办呢,TDengine非常nice的就是加了超级表这个概念,超级表简直是为了这种情况而生的:创建超级表的时候会生成表的格式,然后通过Tag添加这个超级表下的子表,不同的设备就可以用不同的Tag表示,每个设备对应类型的数据会插入到超级表中的某个子表中,而子表与子表中间就通过Tag区别,Tag就可以设置为设备ID,举个例子就类似java程序中,超级表就代表Class,而子表就是通过Class创建出来的实例,那么每个实例是独立的却又都依赖于超级表,每个实例之间的数据也可以独立的插入和删除而不会互相影响。这样就完美的解决了三个问题:
- 多个设备大量插入数据,且时间相同,会是同时插入;
- 如果因为程序或者设备问题插入相同时间,相同内容的数据,导致统计结果有误;
- 后期的海量数据需要快速读取问题;
这样的设计好处是,同类型的数据会放在一个超级表中,却不是放在同一个子表中,不仅增加了查询效率,在针对某个设备进行查询的时候也会变得更加快速和便捷,且可以把同类数据一起管理,降低了后续的维护成本。
Springboot中,propities配置信息如下:
spring.datasource.tdengine-server.jdbc-url=jdbc:TAOS://110.110.110.110:6030/td?timezone=GMT%2b8
spring.datasource.tdengine-server.username=root
spring.datasource.tdengine-server.password=123456
spring.datasource.tdengine-server.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.tdengine-server.minimum-idle=10
spring.datasource.tdengine-server.maximum-pool-size=150
spring.datasource.tdengine-server.auto-commit=true
spring.datasource.tdengine-server.idle-timeout=30000
spring.datasource.tdengine-server.pool-name=TDengineDruidCP
spring.datasource.tdengine-server.max-lifetime=1800000
spring.datasource.tdengine-server.connection-timeout=30000
spring.datasource.tdengine-server.connection-test-query= show tables
创建超级表和子表示例:
-- 创建超级表
CREATE STABLE super_table_name (
ts TIMESTAMP,
sensor_id INT,
value DOUBLE
) TAGS (location NCHAR(50));
--创建子表
CREATE TABLE child_table_name USING super_table_name TAGS ('location1');
--插入数据到子表
INSERT INTO child_table_name (ts, sensor_id, value) VALUES ('2024-09-11 10:30:00', 123, 45.6);
mybaits中,给超级表中子表插入数据的例子,需要传入子表的tag,
<insert id="insert" parameterType="com.ags.meper.examples.entity.tdengine.QiTing" >
insert into ${tableName} USING twice_qiting TAGS(#{userid}) values (#{jksj},#{jyjid},#{jyqid},#{czlx},#{czlxsj},#{jssj},#{id})
</insert>
超级表与子表的示意图:
ps: 安装 TDengine的时候服务端可客户端的版本尽量一致,以避免出现不兼容问题。
避免出现不同设备往一个子表中写入数据的情况