TDengine在数字治理系统中处理轨迹数据的应用

背景介绍:广州市雅恒信息科技有限公司专注提供优质的互联网整体解决方案,在数字治理系统开发有着成熟丰富的经验。数字治理系统是基于城乡治理的概念,对治理过程进行信息化的记录和管理,并加以大数据分析、智能硬件监测和无人机巡查辅助。城乡治理是指基层政府对行政区域内进行网络的划分,并将每个网格责任到网格员,网格员通过开展巡查活动,对发现的问题进行上报、交办、处理、跟踪的过程。

巧遇TDengine

2019年首次听说TDengine Database,真正接触是从收到一个直播的链接转发,陶建辉老师在极客时间直播,标题这样描述:35年码龄老程序员给您讲述TDengine的超高性能是如何实现的。直播现场,陶老师大汗淋漓,大家都在说没开空调,主持人解释,陶老师现在高烧,坚持给大家讲解。感性与理性。之后,深圳见面会,老板时间挺忙,赶在最后一天去的,没有见到陶老师,见到了我们的侯老师。一聊,相见恨晚。

TDengine在数字治理系统中处理轨迹数据的应用 - TDengine Database 时序数据库

场景介绍

雅恒做了接近两年的“基层数字网格管理系统”。这个管理系统能够对镇街进行大小网格的划分,以网格内建筑和核心部件为基本信息点,在网格内开展消防、禁毒、环卫、两违等巡查活动,并形成规范上传、下达工作流程规范。但我们这段时间其实也在琢磨这个系统的架构是不是已经老了,该如何优化呢?这时候正好通过TDengine了解到时序数据库(Time-Series Database)的概念,应用后虽然不能一步到位把系统做一个很大的提升,但产品不是应该这样一点点改进吗?

在我们的应用场景中,网格员在使用app和小程序进行巡查活动时,每隔30秒会检查一次移动距离,如果超过20米就会上传一个坐标到服务端。一个街道大约有100名网格员,每月有效巡查距离超过100公里,一年约1200000米;一个街道一年就会产生约6000000条坐标记录(武汉大约有一万七千名网格员)。我们有可能随时翻阅三年前某条巡查线路。按原关系数据库的结构设计,这就需要从坐标记录表里根据巡查线路id检索出一个个的坐标点,然后重组成一条巡查线路。三年的记录总量接近2000万条,此时关系型数据库的查询响应应急非常慢,必须要通过按照时间分库分表来提高性能,但随之而来的问题就是遇到跨库跨表查询时间段处理的麻烦(想想就觉得累)。

我们基于TDengine Database的特性以及使用原则,把坐标记录数据迁移到这上面来:

  1. 一个网格账号一张坐标记录表,这个网格员巡查产生的坐标记录全都按时间顺序记录到这个表上来;
  2. 巡查线路坐标点的检索按线路的巡查时间范围,时序数据库对时间的处理是天生的优势,这样也降低了跨库的耦合;
  3. 我们还借助了微服务低耦合易扩展的特性,独立出坐标读写模块,以下是完整的系统架构,供参考,欢迎批评指正。
TDengine在数字治理系统中处理轨迹数据的应用 - TDengine Database 时序数据库

用户直接交互的是应用层,包含:浏览器的Web应用、微信小程序或者Android平台的App。其中Web应用还与GIS系统进行交互获取地图瓦片和坐标载入显示等信息。支撑应用层的是业务运算后台服务,包含用户管理、权限管理、问题管理等业务运算。底层存储使用了MySQL+ TDengine的配合。业务运算后台还会与其他门户数据中心的第三方服务进行交互,实现整个数字治理系统的各项需求。

底层数据存储上的思考是这样的。业务逻辑相关的数据需要大量账号信息管理、关联查询、业务从属处理,是典型关系数据库应用场景,因此这部分数据存入MySQL;巡查坐标数据是前文提到的海量时序位置信息,涉及更多的是按时间读取数据,按照账号计算轨迹等,是典型的时序结构化数据储存分析场景,非常适合存入TDengine。这里把位置数据从原来的MySQL中摘出来后,通过一个坐标计算微服务来转存、计算巡查坐标数据,实现了原有的业务运算后台与TDengine之间的交互,且不对原来的运算后台产生大量代码改动,非常方便。

数据模型

遵循一台设备一张表的设计思路。在我们系统中是一个用户一张表,用于记录该用户的所有历史轨迹信息。所有子表都基于一个名为“super”的超级表创建,将设备ID定义成一个tag,用于对表进行区分。

快速开发

封装好API,直接用Spring Boot调用JDBC来与TDengine数据源交互即可。下面的示例中,使用Spring Boot+TDengine实时存储GPS坐标,实现过程非常的简单。

第一步 在Linux上开启TDengine服务;

第二步 创建一个的Spring Boot项目,在application.properties中配置TDengine的连接信息(ps:端口默认为0,用户名默认为root,密码默认为taosdata);

server.port=8085
server.servlet.context-path=/api

#taos
taosdata.url=jdbc:TAOS://192.168.1.241:0/db?user=root&password=taosdata
taosdata.driverClassName=com.taosdata.jdbc.TSDBDriver

第三步 在pom.xml中配置相关依赖,下载jar包;

<!-- taos Start -->
<dependency>
  <groupId>com.taosdata.jdbc</groupId>
  <artifactId>taos-jdbcdriver</artifactId>
  <version>1.0.1</version>
</dependency>
<!-- taos END -->

第四步 建立config文件,对应application.properties中的url和driverClassName;

@Component
@ConfigurationProperties(prefix = “taosdata”)
public class TaosdataConfig {
  private String url;
  private String driverClassName;
  public String getUrl() { 
    return url;
  }
  public void setUrl(String url) {
    this.url = url;
  }
  public String getDriverClassName() {
    return driverClassName;
  }
  public void setDriverClassName(String driverClassName) {
    this.driverClassName = driverClassName;
  }
}

第五步 实现相关业务逻辑(ps:与传统关系型数据库一样,采用sql语法)。创建库,建表,插入,查询等操作在Java中实现如下。

建库、建表:
String sql1 = “create database if not exists coor”;
stmt.executeUpdate(sql1);
String sql2 = “use coor”;
stmt.executeUpdate(sql2);
String sql3 = “create table if not exists super (ts timestamp, lng double, lat double) tags (id nchar(32))”;
stmt.executeUpdate(sql3);
String sql4 = “create table if not exists “ + (“u” + userid) + “using super tags(‘” + userid + “’)”;
stmt.executeUpdate(sql4);

插入数据:
String sql5 = insert into “ + (“u” + userid) + “ values(“ + now.getTime()+”,” + lng + “,” + lat + “)”;
stmt.executeUpdate(sql5);

查询:
StringBuilder sql6 = new StringBuilder(“select * from u”)
  .append(userid)
  .append(“ where ts>=’”)
  .append(stime)
  .append(“’ and ts <= ‘”)
  .append(etime)
  .append(“’”);
ResultSet resSet = stmt.executeQuery(sql6.toString());
Timestamp ts = null;
while(resSet.next()) {
  ts = resSet.getTimestamp(“ts”);
  lng = resSet.getDouble(“lng”);
  lat = resSet.getDouble(“lat”);
  //业务处理略去…
}

总结

雅恒通过将网格巡查位置数据从关系库MySQL转存入时序库TDengine后,解决了大数据量、长周期查询时的性能和易用性问题,避免了分库分表维护的麻烦。通过微服务的方式把TDengine集成进原有系统,提供时序数据存储和计算服务,整体上对原有系统冲击很小,迁移改造比较顺利。数据压缩率可能也是一个很有价值的考量目标。后续也许会继续查看TDengine自带的流计算等功能,看是否能进一步减轻业务层的计算压力,提高计算资源利用率。

作者简介

黄勇,毕业于湖南工学院,从事Java开发工作多年,目前是雅恒核心研发成员。曾主持广东省政法委基层网格化管理系统的开发工作,并深度参与过广州市番禺区教育局教育信息素养提升平台、海幢寺噪音监测系统等系统工程的开发工作。

原文首发于雅恒科技公众号,链接:https://mp.weixin.qq.com/s/qHQI9dRCc60NVdu3cJ-0kQ