使用TDengine快速搭建车联网平台

车联网属于物联网的一个分支,通过车载终端采集数据,利用无线网络传输到云服务平台进行持久化存储,最终提供基于实时/历史数据的个性化服务。

目前初创型的车辆网企业,接入的车辆通常低于10万,数据采集频率远远大于1秒。这个级别的数据规模,如果采用HBase系的技术方案,需要至少6台8核32G配置的机器,而采用TDengine Database作为数据存储引擎,一台2核8G的机器就可以完成。

技术架构

TDengine Database作为时序处理引擎,可以完全不用Kafka、HDFS/HBase/Spark、Redis等软件,大幅简化大数据平台的设计,降低研发成本和运营成本。因为需要集成的开源组件少,因而系统可以更加健壮,也更容易保证数据的一致性。

  • 基于HBase的解决方案,架构图如下
TDengine Database
图 1 基于HBase的技术架构图
  • 而基于TDengine的解决方案,架构图如下
TDengine Database
图 2 基于TDengine的技术架构图

数据模型

车载终端采集的数据字段非常多,很多企业按照国标ISO 22901建立数据模型,也有公司按照业务需要使用自定义的数据模型。但通常,采集数据都包含如下字段,本文也采用这种方法构造数据模型。

  • 采集时间(时间戳)
  • 车辆标志(字符串)
  • 经度(双精度浮点)
  • 维度(双精度浮点)
  • 海拔(浮点)
  • 方向(浮点)
  • 速度(浮点)
  • 车牌号(字符串)
  • 车辆型号(字符串)
  • 车辆vid(字符串)

不同于其他时序数据引擎,TDengine为每辆车单独创建一张数据表,数据字段为采集时间、车辆标志、经度、纬度、海拔、方向、速度等与时间序列相关的采集数据;标签字段为车牌号、车辆型号等车辆本身固定的描述信息。这里面有一个小技巧,浮点数据压缩比相对整型数据压缩比很差,经度纬度通常精确到小数点后7位,因此将经度纬度增大1E7倍转为长整型存储,将海拔、方向、速度增大1E2倍转为整型存储。

创建数据库的语句为

create database db cache 8192 ablocks 2 tblocks 1000 tables 10000;

创建超级表的SQL语句为

create table vehicle(ts timestamp, longitude bigint, latitude bigint, altitude int, direction int, velocity int) tags(card int, model binary(10));

以车辆vid作为表名(例如vid为1,车牌号为25746,类型为bmw),那么创建数据表的语句为

create table v1 using tags(25746, ‘bmw’);

数据写入

仍然以车辆v1为例,写入一条记录到表v1的SQL语句为

insert into v1 values(1562150939000,1,2,3,4,5);

测试数据的生成,可以采用批量数据写入方法,类似

insert into v1 values(1562150939000,1,1,1,1,1) (1562150969000,2,2,2,2,2) (1562150999000,3,3,3,3,3) (……)(……);

本文采用C语言编写了一个车辆模拟数据生成程序,该程序首先10万张数据表,然后每张数据表写入1个月的数据(数据间隔1分钟,计44000条数据)

#include <stdio.h> 
#include <stdlib.h>
#include <string.h> 
#include <unistd.h> 
#include "time.h"
#include "taos.h"
int main(int argc, char *argv[]) {
   taos_init();

   TAOS *taos = taos_connect("127.0.0.1", "root", "taosdata", NULL, 0);
   if (taos == NULL) {
     printf("failed to connect to server, reason:%s\n", taos_errstr(taos));
     exit(1);
   }

   if (taos_query(taos, "create database db cache 8192 ablocks 2 tblocks 1000 tables 10000") != 0) {
     printf("failed to create database, reason:%s\n", taos_errstr(taos));
     exit(1);
   }

   taos_query(taos, "use db");

   char sql[65000] = "create table vehicles(ts timestamp, longitude bigint, latitude bigint, altitude int, direction int, velocity int) tags(card int, model binary(10))";
   if (taos_query(taos, sql) != 0) {
     printf("failed to create stable, reason:%s\n", taos_errstr(taos));
     exit(1);
   }

   int begin = time(NULL);
   for (int table = 0; table < 100000; ++table) {
     sprintf(sql, "create table v%d using vehicles tags(%d, 't%d')", table, table, table);
     if (taos_query(taos, sql) != 0) {
       printf("failed to create table t%d, reason:%s\n", table, taos_errstr(taos));
       exit(1);
     }

     for (int loop = 0; loop < 44; loop++) {   
       int len = sprintf(sql, "insert into v%d values", table);   
       for (int row = 0; row < 1000; row++) {     
         len += sprintf(sql + len, "(%ld,%d,%d,%d,%d,%d)", 1561910400000L + 60000L * (row + loop * 1000L), row, row, row, row, row);   
       }   
       if (taos_query(taos, sql) != 0) {     
         printf("failed to insert table t%d, reason:%s\n", table, taos_errstr(taos));   
       } 
     }
   }
   int end = time(NULL);
     printf("insert finished, time spend %d seconds", end - begin);
   }
}

将改C文件命名为test.c,在相同目录下创建makefile文件

ROOT = ./
TARGET = exe
LFLAGS = -Wl,-rpath,/usr/lib/ -ltaos -lpthread -lm -lrt 
CFLAGS = -O3 -g -Wall -Wno-deprecated -fPIC -Wno-unused-result -Wconversion -Wno-char-subscripts -D_REENTRANT -Wno-format -D_REENTRANT -DLINUX -msse4.2 -Wno-unused-function -D_M_X64 -std=gnu99 -I/usr/local/include/taos/

all: $(TARGET)

exe:
   gcc $(CFLAGS) ./test.c -o $(ROOT)/test $(LFLAGS)

clean:
   rm $(ROOT)test 

编译之后,将测试程序和数据库在同一台2核8G的台式机上运行,写入时间共计为3946秒,相当于4400000000条/3946秒=111.5万条/秒,折算成点数为111.5*5=557万点/秒。

insert finished, time spend 3946 seconds

该程序是单线程运行的,如将其修改成多线程,速度还会有更大提升,但是仅就目前的性能来看,对于车辆网的场景也已经足够。

数据查询

TDengine在数据查询方面做了很多针对时序数据的优化。基于上面生成的测试数据集进行查询,这是一些常见SQL语句的运行结果,性能还是有点吓人的。

  • 查询总数
TDengine Database
图 3 查询总数

  • 单辆车的明细数据
查询类型查询时间
1车当前值查询2.3ms
1车1小时明细查询2.1ms
1车1日明细查询6.3ms
1车10日明细查询15.4ms
1车31日明细查询31.6ms
TDengine Database
图 4 单辆车的明细数据查询

  • 单辆车的聚合查询
查询类型查询时间
1车1小时聚合查询1.9ms
1车1日聚合查询1.7ms
1车10日聚合查询2.3ms
1车31日聚合查询2.2ms
TDengine Database
图 5 单辆车的聚合查询

  • 多辆车的单日聚合查询
查询类型查询时间
1车单日聚合查询3.2ms
10车单日聚合查询5.1ms
100车单日聚合查询10.4ms
1000车单日聚合查询51.4ms
10000车单日聚合查询455.9ms
100000车单日聚合查询2074.8ms
TDengine Database

  • 多辆车单月聚合查询
查询类型查询时间
1车单月聚合查询3.1ms
10车单月聚合查询4.1ms
100车单月聚合查询7.7ms
1000车单月聚合查询33.7ms
10000车单月聚合查询289.5ms
100000车单月聚合查询1197.ms
TDengine Database
图 7 多辆车单月聚合查询

  • 多辆车单月曲线查询
查询类型查询时间
1车单月曲线查询6.9ms
10车单月曲线查询13.2ms
100车单月曲线查询75.6ms
1000车单月曲线查询710.9ms
10000车单月曲线查询7137.6ms
100000车单月曲线查询32130.8ms
TDengine Database
图 8 多辆车单月曲线查询

  • 资源消耗

TDengine Database
图 9 Top截图

数据库服务进程只消耗了约2.7GB的内存,CPU占用可以忽略不计。

TDengine Database
图 10 内存占用

结果分析

TDengine Database提供的时序数据解决方案,单机情况下的平均写入速度在百万条/秒级别,单辆车的所有查询均能做到实时,多辆车的查询速度也非常快,是车联网乃至物联网的必备利器。