Merge branch 'develop' into hotfix/sangshuduo/2.0doc-emq-insert
|
@ -190,7 +190,7 @@ matrix:
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- cmake .. > /dev/null
|
- cmake .. > /dev/null
|
||||||
- make > /dev/null
|
- make > /dev/null
|
||||||
|
|
||||||
- os: linux
|
- os: linux
|
||||||
arch: arm64
|
arch: arm64
|
||||||
|
|
|
@ -1,5 +1,14 @@
|
||||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
|
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
|
||||||
PROJECT(TDengine)
|
IF (CMAKE_VERSION VERSION_LESS 3.0)
|
||||||
|
PROJECT(TDengine CXX)
|
||||||
|
SET(PROJECT_VERSION_MAJOR "${LIB_MAJOR_VERSION}")
|
||||||
|
SET(PROJECT_VERSION_MINOR "${LIB_MINOR_VERSION}")
|
||||||
|
SET(PROJECT_VERSION_PATCH"${LIB_PATCH_VERSION}")
|
||||||
|
SET(PROJECT_VERSION "${LIB_VERSION_STRING}")
|
||||||
|
ELSE ()
|
||||||
|
CMAKE_POLICY(SET CMP0048 NEW)
|
||||||
|
PROJECT(TDengine VERSION "${LIB_VERSION_STRING}" LANGUAGES CXX)
|
||||||
|
ENDIF ()
|
||||||
|
|
||||||
SET(TD_ACCOUNT FALSE)
|
SET(TD_ACCOUNT FALSE)
|
||||||
SET(TD_ADMIN FALSE)
|
SET(TD_ADMIN FALSE)
|
||||||
|
|
|
@ -53,14 +53,18 @@ IF (TD_LINUX_64)
|
||||||
ADD_DEFINITIONS(-D_M_X64)
|
ADD_DEFINITIONS(-D_M_X64)
|
||||||
ADD_DEFINITIONS(-D_TD_LINUX_64)
|
ADD_DEFINITIONS(-D_TD_LINUX_64)
|
||||||
IF (NOT TD_ARM)
|
IF (NOT TD_ARM)
|
||||||
IF (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
|
#IF ((${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") OR (${CMAKE_CXX_COMPILER_ID} MATCHES "clang"))
|
||||||
SET(COMMON_FLAGS "-std=gnu99 -Wall -Werror -Wno-missing-braces -fPIC -g3 -gdwarf-2 -msse4.2 -D_FILE_OFFSET_BITS=64 -D_LARGE_FILE")
|
# SET(COMMON_FLAGS "-std=gnu99 -Wall -Werror -Wno-missing-braces -fPIC -g3 -gdwarf-2 -msse4.2 -D_FILE_OFFSET_BITS=64 -D_LARGE_FILE")
|
||||||
ELSE ()
|
#ELSE ()
|
||||||
SET(COMMON_FLAGS "-std=gnu99 -Wall -Werror -fPIC -malign-double -g3 -gdwarf-2 -malign-stringops -msse4.2 -D_FILE_OFFSET_BITS=64 -D_LARGE_FILE")
|
# SET(COMMON_FLAGS "-std=gnu99 -Wall -Werror -fPIC -malign-double -g3 -gdwarf-2 -malign-stringops -msse4.2 -D_FILE_OFFSET_BITS=64 -D_LARGE_FILE")
|
||||||
ENDIF ()
|
#ENDIF ()
|
||||||
|
SET(COMMON_FLAGS "-std=gnu99 -Wall -Werror -fPIC -g3 -gdwarf-2 -msse4.2 -D_FILE_OFFSET_BITS=64 -D_LARGE_FILE")
|
||||||
ELSE ()
|
ELSE ()
|
||||||
SET(COMMON_FLAGS "-std=gnu99 -Wall -Werror -fPIC -g -fsigned-char -fpack-struct=8 -D_FILE_OFFSET_BITS=64 -D_LARGE_FILE")
|
SET(COMMON_FLAGS "-std=gnu99 -Wall -Werror -fPIC -g -fsigned-char -fpack-struct=8 -D_FILE_OFFSET_BITS=64 -D_LARGE_FILE")
|
||||||
ENDIF ()
|
ENDIF ()
|
||||||
|
MESSAGE(STATUS "CMAKE_CXX_COMPILER_ID: " ${CMAKE_CXX_COMPILER_ID})
|
||||||
|
MESSAGE(STATUS "COMMON_FLAGS: " ${COMMON_FLAGS})
|
||||||
|
|
||||||
FIND_PATH(ICONV_INCLUDE_EXIST iconv.h /usr/include/ /usr/local/include/)
|
FIND_PATH(ICONV_INCLUDE_EXIST iconv.h /usr/include/ /usr/local/include/)
|
||||||
IF (ICONV_INCLUDE_EXIST)
|
IF (ICONV_INCLUDE_EXIST)
|
||||||
ADD_DEFINITIONS(-DUSE_LIBICONV)
|
ADD_DEFINITIONS(-DUSE_LIBICONV)
|
||||||
|
|
|
@ -52,14 +52,16 @@ ELSE ()
|
||||||
MESSAGE(STATUS "input cpuType unknown " ${CPUTYPE})
|
MESSAGE(STATUS "input cpuType unknown " ${CPUTYPE})
|
||||||
ENDIF ()
|
ENDIF ()
|
||||||
|
|
||||||
#
|
|
||||||
# Get OS information and store in variable TD_OS_INFO.
|
|
||||||
#
|
|
||||||
execute_process(COMMAND chmod 777 ${TD_COMMUNITY_DIR}/packaging/tools/get_os.sh)
|
|
||||||
execute_process(COMMAND ${TD_COMMUNITY_DIR}/packaging/tools/get_os.sh "" OUTPUT_VARIABLE TD_OS_INFO)
|
|
||||||
MESSAGE(STATUS "The current os is " ${TD_OS_INFO})
|
|
||||||
|
|
||||||
IF (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
IF (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||||
|
#
|
||||||
|
# Get OS information and store in variable TD_OS_INFO.
|
||||||
|
#
|
||||||
|
execute_process(COMMAND chmod 777 ${TD_COMMUNITY_DIR}/packaging/tools/get_os.sh)
|
||||||
|
execute_process(COMMAND ${TD_COMMUNITY_DIR}/packaging/tools/get_os.sh "" OUTPUT_VARIABLE TD_OS_INFO)
|
||||||
|
MESSAGE(STATUS "The current os is " ${TD_OS_INFO})
|
||||||
|
|
||||||
SET(TD_LINUX TRUE)
|
SET(TD_LINUX TRUE)
|
||||||
IF (${CMAKE_SIZEOF_VOID_P} MATCHES 8)
|
IF (${CMAKE_SIZEOF_VOID_P} MATCHES 8)
|
||||||
SET(TD_LINUX_64 TRUE)
|
SET(TD_LINUX_64 TRUE)
|
||||||
|
|
|
@ -917,15 +917,16 @@ TDengine 同时也提供了node.js 的连接器。用户可以通过[npm](https:
|
||||||
首先,通过[npm](https://www.npmjs.com/)安装node.js 连接器.
|
首先,通过[npm](https://www.npmjs.com/)安装node.js 连接器.
|
||||||
|
|
||||||
```cmd
|
```cmd
|
||||||
npm install td-connector
|
npm install td2.0-connector
|
||||||
```
|
```
|
||||||
我们建议用户使用npm 安装node.js连接器。如果您没有安装npm, 可以将*src/connector/nodejs/*拷贝到您的nodejs 项目目录下
|
我们建议用户使用npm 安装node.js连接器。如果您没有安装npm, 可以将*src/connector/nodejs/*拷贝到您的nodejs 项目目录下
|
||||||
|
|
||||||
我们使用[node-gyp](https://github.com/nodejs/node-gyp)和TDengine服务端进行交互。安装node.js 连接器之前,还需安装以下软件:
|
我们使用[node-gyp](https://github.com/nodejs/node-gyp)和TDengine服务端进行交互。安装node.js 连接器之前,还需安装以下软件:
|
||||||
|
|
||||||
### Unix
|
### Linux
|
||||||
|
|
||||||
- `python` (建议`v2.7` , `v3.x.x` 目前还不支持)
|
- `python` (建议`v2.7` , `v3.x.x` 目前还不支持)
|
||||||
|
- `node` 必须采用v8.x版本,之后的版本存在兼容性的问题。
|
||||||
- `make`
|
- `make`
|
||||||
- c语言编译器比如[GCC](https://gcc.gnu.org)
|
- c语言编译器比如[GCC](https://gcc.gnu.org)
|
||||||
|
|
||||||
|
@ -980,10 +981,10 @@ npm install td-connector
|
||||||
|
|
||||||
#### 连接
|
#### 连接
|
||||||
|
|
||||||
使用node.js连接器时,必须先<em>require</em> ```td-connector```,然后使用 ```taos.connect``` 函数。```taos.connect``` 函数必须提供的参数是```host```,其它参数在没有提供的情况下会使用如下的默认值。最后需要初始化```cursor``` 来和TDengine服务端通信
|
使用node.js连接器时,必须先<em>require</em> ```td2.0-connector```,然后使用 ```taos.connect``` 函数。```taos.connect``` 函数必须提供的参数是```host```,其它参数在没有提供的情况下会使用如下的默认值。最后需要初始化```cursor``` 来和TDengine服务端通信
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const taos = require('td-connector');
|
const taos = require('td2.0-connector');
|
||||||
var conn = taos.connect({host:"127.0.0.1", user:"root", password:"taosdata", config:"/etc/taos",port:0})
|
var conn = taos.connect({host:"127.0.0.1", user:"root", password:"taosdata", config:"/etc/taos",port:0})
|
||||||
var cursor = conn.cursor(); // Initializing a new cursor
|
var cursor = conn.cursor(); // Initializing a new cursor
|
||||||
```
|
```
|
||||||
|
|
|
@ -1,117 +0,0 @@
|
||||||
#TDengine文档
|
|
||||||
|
|
||||||
TDengine是一个高效的存储、查询、分析时序大数据的平台,专为物联网、车联网、工业互联网、运维监测等优化而设计。您可以像使用关系型数据库MySQL一样来使用它,但建议您在使用前仔细阅读一遍下面的文档,特别是[数据模型](data-model-and-architecture)与数据建模一节。除本文档之外,欢迎[下载产品白皮书](https://www.taosdata.com/downloads/TDengine%20White%20Paper.pdf)。
|
|
||||||
|
|
||||||
##TDengine 介绍
|
|
||||||
- TDengine 简介及特色
|
|
||||||
- TDengine 适用场景
|
|
||||||
- TDengine 性能指标介绍和验证方法
|
|
||||||
|
|
||||||
##立即开始
|
|
||||||
- 快捷安装:可通过源码、安装包或docker安装,三秒钟搞定
|
|
||||||
- 轻松启动:使用systemctl 启停TDengine
|
|
||||||
- 命令行程序TAOS:访问TDengine的简便方式
|
|
||||||
- [极速体验](https://www.taosdata.com/cn/getting-started/#TDengine-极速体验):运行示例程序,快速体验高效的数据插入、查询
|
|
||||||
|
|
||||||
##数据模型和整体架构
|
|
||||||
- 数据模型:关系型数据库模型,但要求每个采集点单独建表
|
|
||||||
- 集群与基本逻辑单元:吸取NoSQL优点,支持水平扩展,支持高可靠
|
|
||||||
- 存储模型与数据分区:标签数据与时序数据完全分离,按vnode和时间两个维度对数据切分
|
|
||||||
- 数据写入与复制流程:先写入WAL、之后写入缓存,再给应用确认,支持多副本
|
|
||||||
- 缓存与持久化:最新数据缓存在内存中,但落盘时采用列式存储、超高压缩比
|
|
||||||
- 高效查询:支持各种函数、时间轴聚合、插值、多表聚合
|
|
||||||
|
|
||||||
##数据建模
|
|
||||||
- 创建库:为具有相似数据特征的数据采集点创建一个库
|
|
||||||
- 创建超级表:为同一类型的数据采集点创建一个超级表
|
|
||||||
- 创建表:使用超级表做模板,为每一个具体的数据采集点单独建表
|
|
||||||
|
|
||||||
##高效写入数据
|
|
||||||
- SQL写入:使用SQL insert命令向一张或多张表写入单条或多条记录
|
|
||||||
- Telegraf 写入:配置Telegraf, 不用任何代码,将采集数据直接写入
|
|
||||||
- Prometheus写入:配置Prometheus, 不用任何代码,将数据直接写入
|
|
||||||
- EMQ X Broker:配置EMQ X,不用任何代码,就可将MQTT数据直接写入
|
|
||||||
|
|
||||||
##高效查询数据
|
|
||||||
- 主要查询功能:支持各种标准函数,设置过滤条件,时间段查询
|
|
||||||
- 多表聚合查询:使用超级表,设置标签过滤条件,进行高效聚合查询
|
|
||||||
- 降采样查询:按时间段分段聚合,支持插值
|
|
||||||
|
|
||||||
##高级功能
|
|
||||||
- 连续查询(Continuous Query):基于滑动窗口,定时自动的对数据流进行查询计算
|
|
||||||
- 数据订阅(Publisher/Subscriber):象典型的消息队列,应用可订阅接收到的最新数据
|
|
||||||
- [缓存 (Cache)](https://www.taosdata.com/cn/documentation/advanced-features/#缓存-(Cache)):每个设备最新的数据都会缓存在内存中,可快速获取
|
|
||||||
- [报警监测(Alarm monitoring)](https://www.taosdata.com/blog/2020/04/14/1438.html/):根据配置规则,自动监测超限行为数据,并主动推送
|
|
||||||
|
|
||||||
##连接器
|
|
||||||
- C/C++ Connector:通过libtaos客户端的库,连接TDengine服务器的主要方法
|
|
||||||
- Java Connector(JDBC):通过标准的JDBC API,给Java应用提供到TDengine的连接
|
|
||||||
- Python Connector:给Python应用提供一个连接TDengine服务器的驱动
|
|
||||||
- RESTful Connector:提供一最简单的连接TDengine服务器的方式
|
|
||||||
- Go Connector:给Go应用提供一个连接TDengine服务器的驱动
|
|
||||||
- Node.js Connector:给node应用提供一个链接TDengine服务器的驱动
|
|
||||||
|
|
||||||
##与其他工具的连接
|
|
||||||
- Grafana:获取并可视化保存在TDengine的数据
|
|
||||||
- Matlab:通过配置Matlab的JDBC数据源访问保存在TDengine的数据
|
|
||||||
- R:通过配置R的JDBC数据源访问保存在TDengine的数据
|
|
||||||
|
|
||||||
## TDengine集群的安装、管理
|
|
||||||
|
|
||||||
- 安装:与单节点的安装一样,但要设好配置文件里的参数first
|
|
||||||
- 节点管理:增加、删除、查看集群的节点
|
|
||||||
- mnode的管理:系统自动创建、无需任何人工干预
|
|
||||||
- 负载均衡:一旦节点个数或负载有变化,自动进行
|
|
||||||
- 节点离线处理:节点离线超过一定时长,将从集群中剔除
|
|
||||||
- Arbitrator:对于偶数个副本的情形,使用它可以防止split brain。
|
|
||||||
|
|
||||||
##TDengine的运营和维护
|
|
||||||
|
|
||||||
- 容量规划:根据场景,估算硬件资源
|
|
||||||
- 容错和灾备:设置正确的WAL和数据副本数
|
|
||||||
- 系统配置:端口,缓存大小,文件块大小和其他系统配置
|
|
||||||
- 用户管理:添加、删除TDengine用户,修改用户密码
|
|
||||||
- 数据导入:可按脚本文件导入,也可按数据文件导入
|
|
||||||
- 数据导出:从shell按表导出,也可用taosdump工具做各种导出
|
|
||||||
- 系统监控:检查系统现有的连接、查询、流式计算,日志和事件等
|
|
||||||
- 文件目录结构:TDengine数据文件、配置文件等所在目录 Hui Li
|
|
||||||
|
|
||||||
##TAOS SQL
|
|
||||||
- 支持的数据类型:支持时间戳、整型、浮点型、布尔型、字符型等多种数据类型
|
|
||||||
- 数据库管理:添加、删除、查看数据库
|
|
||||||
- 表管理:添加、删除、查看、修改表
|
|
||||||
- 超级表管理:添加、删除、查看、修改超级表
|
|
||||||
- 标签管理:增加、删除、修改标签
|
|
||||||
- 数据写入:支持单表单条、多条、多表多条写入,支持历史数据写入
|
|
||||||
- 数据查询:支持时间段、值过滤、排序、查询结果手动分页等
|
|
||||||
- SQL函数:支持各种聚合函数、选择函数、计算函数,如avg, min, diff等
|
|
||||||
- 时间维度聚合:将表中数据按照时间段进行切割后聚合,降维处理
|
|
||||||
|
|
||||||
##TDengine的技术设计
|
|
||||||
- 系统模块:taosd的功能和模块划分
|
|
||||||
- 技术博客:更多的技术分析和架构设计文章
|
|
||||||
|
|
||||||
## 常用工具
|
|
||||||
|
|
||||||
- [TDengine样例数据导入工具](https://www.taosdata.com/cn/documentation/blog/2020/01/18/如何快速验证性能和主要功能?tdengine样例数据导入工/)
|
|
||||||
- [TDengine性能对比测试工具](https://www.taosdata.com/cn/documentation/blog/2020/01/13/用influxdb开源的性能测试工具对比influxdb和tdengine/)
|
|
||||||
|
|
||||||
##TDengine与其他数据库的对比测试
|
|
||||||
|
|
||||||
- [用InfluxDB开源的性能测试工具对比InfluxDB和TDengine](https://www.taosdata.com/cn/documentation/blog/2020/01/13/用influxdb开源的性能测试工具对比influxdb和tdengine/)
|
|
||||||
- [TDengine与OpenTSDB对比测试](https://www.taosdata.com/cn/documentation/blog/2019/08/21/tdengine与opentsdb对比测试/)
|
|
||||||
- [TDengine与Cassandra对比测试](https://www.taosdata.com/cn/documentation/blog/2019/08/14/tdengine与cassandra对比测试/)
|
|
||||||
- [TDengine与InfluxDB对比测试](https://www.taosdata.com/cn/documentation/blog/2019/07/19/tdengine与influxdb对比测试/)
|
|
||||||
- [TDengine与InfluxDB、OpenTSDB、Cassandra、MySQL、ClickHouse等数据库的对比测试报告](https://www.taosdata.com/downloads/TDengine_Testing_Report_cn.pdf)
|
|
||||||
|
|
||||||
##物联网大数据
|
|
||||||
- [物联网、工业互联网大数据的特点](https://www.taosdata.com/blog/2019/07/09/物联网、工业互联网大数据的特点/)
|
|
||||||
- [物联网大数据平台应具备的功能和特点](https://www.taosdata.com/blog/2019/07/29/物联网大数据平台应具备的功能和特点/)
|
|
||||||
- [通用大数据架构为什么不适合处理物联网数据?](https://www.taosdata.com/blog/2019/07/09/通用互联网大数据处理架构为什么不适合处理物联/)
|
|
||||||
- [物联网、车联网、工业互联网大数据平台,为什么推荐使用TDengine?](https://www.taosdata.com/blog/2019/07/09/物联网、车联网、工业互联网大数据平台,为什么/)
|
|
||||||
|
|
||||||
##培训和FAQ
|
|
||||||
- <a href='https://www.taosdata.com/en/faq'>FAQ</a>:常见问题与答案
|
|
||||||
- <a href='https://www.taosdata.com/en/blog/?categories=4'>应用案列</a>:一些使用实例来解释如何使用TDengine
|
|
||||||
|
|
||||||
|
|
After Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 120 KiB |
After Width: | Height: | Size: 75 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 67 KiB |
After Width: | Height: | Size: 97 KiB |
After Width: | Height: | Size: 60 KiB |
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 87 KiB |
After Width: | Height: | Size: 65 KiB |
After Width: | Height: | Size: 114 KiB |
After Width: | Height: | Size: 169 KiB |
After Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 92 KiB |
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 71 KiB |
After Width: | Height: | Size: 56 KiB |
|
@ -0,0 +1,128 @@
|
||||||
|
# TDengine文档
|
||||||
|
|
||||||
|
TDengine是一个高效的存储、查询、分析时序大数据的平台,专为物联网、车联网、工业互联网、运维监测等优化而设计。您可以像使用关系型数据库MySQL一样来使用它,但建议您在使用前仔细阅读一遍下面的文档,特别是[数据模型](https://www.taosdata.com/cn/documentation20/data-model-and-architecture)与[数据建模](https://www.taosdata.com/cn/documentation20/model)一节。除本文档之外,欢迎[下载产品白皮书](https://www.taosdata.com/downloads/TDengine White Paper.pdf)。如需查阅TDengine 1.6 文档,请点击[这里](https://www.taosdata.com/cn/documentation16/)访问。
|
||||||
|
|
||||||
|
## TDengine介绍
|
||||||
|
|
||||||
|
- TDengine 简介及特色
|
||||||
|
- TDengine 适用场景
|
||||||
|
|
||||||
|
## [立即开始](https://www.taosdata.com/cn/getting-started)
|
||||||
|
|
||||||
|
- [快捷安装](https://www.taosdata.com/cn/documentation20/getting-started/#快捷安装):可通过源码、安装包或docker安装,三秒钟搞定
|
||||||
|
- [轻松启动](https://www.taosdata.com/cn/documentation20/getting-started/#轻松启动):使用systemctl 启停TDengine
|
||||||
|
- [命令行程序TAOS](https://www.taosdata.com/cn/documentation20/getting-started/#TDengine命令行程序):访问TDengine的简便方式
|
||||||
|
- [极速体验](https://www.taosdata.com/cn/documentation20/getting-started/#TDengine-极速体验):运行示例程序,快速体验高效的数据插入、查询
|
||||||
|
|
||||||
|
## [数据模型和整体架构](https://www.taosdata.com/cn/documentation20/architecture)
|
||||||
|
|
||||||
|
- [数据模型](https://www.taosdata.com/cn/documentation20/architecture/#数据模型):关系型数据库模型,但要求每个采集点单独建表
|
||||||
|
- [集群与基本逻辑单元](https://www.taosdata.com/cn/documentation20/architecture/#集群与基本逻辑单元):吸取NoSQL优点,支持水平扩展,支持高可靠
|
||||||
|
- [存储模型与数据分区、分片](https://www.taosdata.com/cn/documentation20/architecture/#存储模型与数据分区、分片):标签数据与时序数据完全分离,按vnode和时间两个维度对数据切分
|
||||||
|
- [数据写入与复制流程](https://www.taosdata.com/cn/documentation20/architecture/#数据写入与复制流程):先写入WAL、之后写入缓存,再给应用确认,支持多副本
|
||||||
|
- [缓存与持久化](https://www.taosdata.com/cn/documentation20/architecture/#缓存与持久化):最新数据缓存在内存中,但落盘时采用列式存储、超高压缩比
|
||||||
|
- [数据查询](https://www.taosdata.com/cn/documentation20/architecture/#数据查询):支持各种函数、时间轴聚合、插值、多表聚合
|
||||||
|
|
||||||
|
## [数据建模](https://www.taosdata.com/cn/documentation20/model)
|
||||||
|
|
||||||
|
- [创建库](https://www.taosdata.com/cn/documentation20/model/#创建库):为具有相似数据特征的数据采集点创建一个库
|
||||||
|
- [创建超级表](https://www.taosdata.com/cn/documentation20/model/#创建超级表):为同一类型的数据采集点创建一个超级表
|
||||||
|
- [创建表](https://www.taosdata.com/cn/documentation20/model/#创建表):使用超级表做模板,为每一个具体的数据采集点单独建表
|
||||||
|
|
||||||
|
## [高效写入数据](https://www.taosdata.com/cn/documentation20/insert)
|
||||||
|
|
||||||
|
- [SQL写入](https://www.taosdata.com/cn/documentation20/insert/#SQL写入):使用SQL insert命令向一张或多张表写入单条或多条记录
|
||||||
|
- [Telegraf写入](https://www.taosdata.com/cn/documentation20/insert/#Telegraf直接写入):配置Telegraf, 不用任何代码,将采集数据直接写入
|
||||||
|
- [Prometheus写入](https://www.taosdata.com/cn/documentation20/insert/#Prometheus直接写入):配置Prometheus, 不用任何代码,将数据直接写入
|
||||||
|
- [EMQ X Broker](https://www.taosdata.com/cn/documentation20/insert/#EMQ-X-Broker直接写入):配置EMQ X,不用任何代码,就可将MQTT数据直接写入
|
||||||
|
|
||||||
|
## [高效查询数据](https://www.taosdata.com/cn/documentation20/queries)
|
||||||
|
|
||||||
|
- [主要查询功能](https://www.taosdata.com/cn/documentation20/queries/#主要查询功能):支持各种标准函数,设置过滤条件,时间段查询
|
||||||
|
- [多表聚合查询](https://www.taosdata.com/cn/documentation20/queries/#多表聚合查询):使用超级表,设置标签过滤条件,进行高效聚合查询
|
||||||
|
- [降采样查询值](https://www.taosdata.com/cn/documentation20/queries/#降采样查询、插值):按时间段分段聚合,支持插值
|
||||||
|
|
||||||
|
## [高级功能](https://www.taosdata.com/cn/documentation20/advanced-features)
|
||||||
|
|
||||||
|
- [连续查询(Continuous Query)](https://www.taosdata.com/cn/documentation20/advanced-features/#连续查询(Continuous-Query)):基于滑动窗口,定时自动的对数据流进行查询计算
|
||||||
|
- [数据订阅(Publisher/Subscriber)](https://www.taosdata.com/cn/documentation20/advanced-features/#数据订阅(Publisher/Subscriber)):象典型的消息队列,应用可订阅接收到的最新数据
|
||||||
|
- [缓存(Cache)](https://www.taosdata.com/cn/documentation20/advanced-features/#缓存(Cache)):每个设备最新的数据都会缓存在内存中,可快速获取
|
||||||
|
- [报警监测](https://www.taosdata.com/cn/documentation20/advanced-features/#报警监测(Alert)):根据配置规则,自动监测超限行为数据,并主动推送
|
||||||
|
|
||||||
|
## [连接器](https://www.taosdata.com/cn/documentation20/connector)
|
||||||
|
|
||||||
|
- [C/C++ Connector](https://www.taosdata.com/cn/documentation20/connector/#C/C++-Connector):通过libtaos客户端的库,连接TDengine服务器的主要方法
|
||||||
|
- [Java Connector(JDBC)](https://www.taosdata.com/cn/documentation20/connector/#Java-Connector):通过标准的JDBC API,给Java应用提供到TDengine的连接
|
||||||
|
- [Python Connector](https://www.taosdata.com/cn/documentation20/connector/#Python-Connector):给Python应用提供一个连接TDengine服务器的驱动
|
||||||
|
- [RESTful Connector](https://www.taosdata.com/cn/documentation20/connector/#RESTful-Connector):提供一最简单的连接TDengine服务器的方式
|
||||||
|
- [Go Connector](https://www.taosdata.com/cn/documentation20/connector/#Go-Connector):给Go应用提供一个连接TDengine服务器的驱动
|
||||||
|
- [Node.js Connector](https://www.taosdata.com/cn/documentation20/connector/#Node.js-Connector):给node应用提供一个链接TDengine服务器的驱动
|
||||||
|
|
||||||
|
## [与其他工具的连接](https://www.taosdata.com/cn/documentation20/connections-with-other-tools)
|
||||||
|
|
||||||
|
- [Grafana](https://www.taosdata.com/cn/documentation20/connections-with-other-tools/#Grafana):获取并可视化保存在TDengine的数据
|
||||||
|
- [Matlab](https://www.taosdata.com/cn/documentation20/connections-with-other-tools/#Matlab):通过配置Matlab的JDBC数据源访问保存在TDengine的数据
|
||||||
|
- [R](https://www.taosdata.com/cn/documentation20/connections-with-other-tools/#R):通过配置R的JDBC数据源访问保存在TDengine的数据
|
||||||
|
|
||||||
|
## [TDengine集群的安装、管理](https://www.taosdata.com/cn/documentation20/cluster)
|
||||||
|
|
||||||
|
- [安装](https://www.taosdata.com/cn/documentation20/cluster/#创建第一个节点):与单节点的安装一样,但要设好配置文件里的参数first
|
||||||
|
- [节点管理](https://www.taosdata.com/cn/documentation20/cluster/#节点管理):增加、删除、查看集群的节点
|
||||||
|
- [mnode的管理](https://www.taosdata.com/cn/documentation20/cluster/#Mnode的高可用):系统自动创建、无需任何人工干预
|
||||||
|
- [负载均衡](https://www.taosdata.com/cn/documentation20/cluster/#负载均衡):一旦节点个数或负载有变化,自动进行
|
||||||
|
- [节点离线处理](https://www.taosdata.com/cn/documentation20/cluster/#节点离线处理):节点离线超过一定时长,将从集群中剔除
|
||||||
|
- [Arbitrator](https://www.taosdata.com/cn/documentation20/cluster/#Arbitrator的使用):对于偶数个副本的情形,使用它可以防止split brain
|
||||||
|
|
||||||
|
## [TDengine的运营和维护](https://www.taosdata.com/cn/documentation20/administrator)
|
||||||
|
|
||||||
|
- [容量规划](https://www.taosdata.com/cn/documentation20/administrator/#容量规划):根据场景,估算硬件资源
|
||||||
|
- [容错和灾备](https://www.taosdata.com/cn/documentation20/administrator/#容错和灾备):设置正确的WAL和数据副本数
|
||||||
|
- [系统配置](https://www.taosdata.com/cn/documentation20/administrator/#服务端配置):端口,缓存大小,文件块大小和其他系统配置
|
||||||
|
- [用户管理](https://www.taosdata.com/cn/documentation20/administrator/#用户管理):添加、删除TDengine用户,修改用户密码
|
||||||
|
- [数据导入](https://www.taosdata.com/cn/documentation20/administrator/#数据导入):可按脚本文件导入,也可按数据文件导入
|
||||||
|
- [数据导出](https://www.taosdata.com/cn/documentation20/administrator/#数据导出):从shell按表导出,也可用taosdump工具做各种导出
|
||||||
|
- [系统监控](https://www.taosdata.com/cn/documentation20/administrator/#系统监控):检查系统现有的连接、查询、流式计算,日志和事件等
|
||||||
|
- [文件目录结构](https://www.taosdata.com/cn/documentation20/administrator/#文件目录结构):TDengine数据文件、配置文件等所在目录
|
||||||
|
|
||||||
|
## [TAOS SQL](https://www.taosdata.com/cn/documentation20/taos-sql)
|
||||||
|
|
||||||
|
- [支持的数据类型](https://www.taosdata.com/cn/documentation20/taos-sql/#支持的数据类型):支持时间戳、整型、浮点型、布尔型、字符型等多种数据类型
|
||||||
|
- [数据库管理](https://www.taosdata.com/cn/documentation20/taos-sql/#数据库管理):添加、删除、查看数据库
|
||||||
|
- [表管理](https://www.taosdata.com/cn/documentation20/taos-sql/#表管理):添加、删除、查看、修改表
|
||||||
|
- [超级表管理](https://www.taosdata.com/cn/documentation20/taos-sql/#超级表STable管理):添加、删除、查看、修改超级表
|
||||||
|
- [标签管理](https://www.taosdata.com/cn/documentation20/taos-sql/#超级表-STable-中-TAG-管理):增加、删除、修改标签
|
||||||
|
- [数据写入](https://www.taosdata.com/cn/documentation20/taos-sql/#数据写入):支持单表单条、多条、多表多条写入,支持历史数据写入
|
||||||
|
- [数据查询](https://www.taosdata.com/cn/documentation20/taos-sql/#数据查询):支持时间段、值过滤、排序、查询结果手动分页等
|
||||||
|
- [SQL函数](https://www.taosdata.com/cn/documentation20/taos-sql/#SQL函数):支持各种聚合函数、选择函数、计算函数,如avg, min, diff等
|
||||||
|
- [时间维度聚合](https://www.taosdata.com/cn/documentation20/taos-sql/#时间维度聚合):将表中数据按照时间段进行切割后聚合,降维处理
|
||||||
|
|
||||||
|
## TDengine的技术设计
|
||||||
|
|
||||||
|
- 系统模块:taosd的功能和模块划分
|
||||||
|
- 数据复制:支持实时同步、异步复制,保证系统的High Availibility
|
||||||
|
- [技术博客](https://www.taosdata.com/cn/blog/?categories=3):更多的技术分析和架构设计文章
|
||||||
|
|
||||||
|
## 常用工具
|
||||||
|
|
||||||
|
- [TDengine样例导入工具](https://www.taosdata.com/blog/2020/01/18/1166.html)
|
||||||
|
- [TDengine性能对比测试工具](https://www.taosdata.com/blog/2020/01/18/1166.html)
|
||||||
|
|
||||||
|
## TDengine与其他数据库的对比测试
|
||||||
|
|
||||||
|
- [用InfluxDB开源的性能测试工具对比InfluxDB和TDengine](https://www.taosdata.com/blog/2020/01/13/1105.html)
|
||||||
|
- [TDengine与OpenTSDB对比测试](https://www.taosdata.com/blog/2019/08/21/621.html)
|
||||||
|
- [TDengine与Cassandra对比测试](https://www.taosdata.com/blog/2019/08/14/573.html)
|
||||||
|
- [TDengine与InfluxDB对比测试](https://www.taosdata.com/blog/2019/07/19/419.html)
|
||||||
|
- [TDengine与InfluxDB、OpenTSDB、Cassandra、MySQL、ClickHouse等数据库的对比测试报告](https://www.taosdata.com/downloads/TDengine_Testing_Report_cn.pdf)
|
||||||
|
|
||||||
|
##物联网大数据
|
||||||
|
|
||||||
|
- [物联网、工业互联网大数据的特点](https://www.taosdata.com/blog/2019/07/09/105.html)
|
||||||
|
- [物联网大数据平台应具备的功能和特点](https://www.taosdata.com/blog/2019/07/29/542.html)
|
||||||
|
- [通用大数据架构为什么不适合处理物联网数据?](https://www.taosdata.com/blog/2019/07/09/107.html)
|
||||||
|
- [物联网、车联网、工业互联网大数据平台,为什么推荐使用TDengine?](https://www.taosdata.com/blog/2019/07/09/109.html)
|
||||||
|
|
||||||
|
## [培训和FAQ](https://www.taosdata.com/cn/faq)
|
||||||
|
|
||||||
|
- [FAQ](https://www.taosdata.com/cn/documentation20/faq):常见问题与答案
|
||||||
|
- [应用案列](https://www.taosdata.com/cn/blog/?categories=4):一些使用实例来解释如何使用TDengine
|
|
@ -1,11 +1,11 @@
|
||||||
# TDengine 适用场景介绍(草案)
|
# TDengine 介绍
|
||||||
|
|
||||||
## TDengine 简介
|
## TDengine 简介
|
||||||
|
|
||||||
<!-- 本节内容来源于白皮书 -->
|
|
||||||
|
|
||||||
TDengine是涛思数据面对高速增长的物联网大数据市场和技术挑战推出的创新性的大数据处理产品,它不依赖任何第三方软件,也不是优化或包装了一个开源的数据库或流式计算产品,而是在吸取众多传统关系型数据库、NoSQL数据库、流式计算引擎、消息队列等软件的优点之后自主开发的产品,在时序空间大数据处理上,有着自己独到的优势。
|
TDengine是涛思数据面对高速增长的物联网大数据市场和技术挑战推出的创新性的大数据处理产品,它不依赖任何第三方软件,也不是优化或包装了一个开源的数据库或流式计算产品,而是在吸取众多传统关系型数据库、NoSQL数据库、流式计算引擎、消息队列等软件的优点之后自主开发的产品,在时序空间大数据处理上,有着自己独到的优势。
|
||||||
|
|
||||||
|
TDengine的模块之一是时序数据库。但除此之外,为减少研发的复杂度、系统维护的难度,TDengine还提供缓存、消息队列、订阅、流式计算等功能,为物联网、工业互联网大数据的处理提供全栈的技术方案,是一个高效易用的物联网大数据平台。与Hadoop等典型的大数据平台相比,它具有如下鲜明的特点:
|
||||||
|
|
||||||
* __10倍以上的性能提升__:定义了创新的数据存储结构,单核每秒就能处理至少2万次请求,插入数百万个数据点,读出一千万以上数据点,比现有通用数据库快了十倍以上。
|
* __10倍以上的性能提升__:定义了创新的数据存储结构,单核每秒就能处理至少2万次请求,插入数百万个数据点,读出一千万以上数据点,比现有通用数据库快了十倍以上。
|
||||||
* __硬件或云服务成本降至1/5__:由于超强性能,计算资源不到通用大数据方案的1/5;通过列式存储和先进的压缩算法,存储空间不到通用数据库的1/10
|
* __硬件或云服务成本降至1/5__:由于超强性能,计算资源不到通用大数据方案的1/5;通过列式存储和先进的压缩算法,存储空间不到通用数据库的1/10
|
||||||
* __全栈时序数据处理引擎__:将数据库、消息队列、缓存、流式计算等功能融合一起,应用无需再集成Kafka/Redis/HBase/Spark/HDFS等软件,大幅降低应用开发和维护的复杂度成本。
|
* __全栈时序数据处理引擎__:将数据库、消息队列、缓存、流式计算等功能融合一起,应用无需再集成Kafka/Redis/HBase/Spark/HDFS等软件,大幅降低应用开发和维护的复杂度成本。
|
||||||
|
@ -13,52 +13,48 @@ TDengine是涛思数据面对高速增长的物联网大数据市场和技术挑
|
||||||
* __与第三方工具无缝连接__:不用一行代码,即可与Telegraf, Grafana, EMQ, Prometheus, Matlab, R等集成。后续将支持OPC, Hadoop, Spark等, BI工具也将无缝连接。
|
* __与第三方工具无缝连接__:不用一行代码,即可与Telegraf, Grafana, EMQ, Prometheus, Matlab, R等集成。后续将支持OPC, Hadoop, Spark等, BI工具也将无缝连接。
|
||||||
* __零运维成本、零学习成本__:安装、集群一秒搞定,无需分库分表,实时备份。标准SQL,支持JDBC, RESTful, 支持Python/Java/C/C++/Go, 与MySQL相似,零学习成本。
|
* __零运维成本、零学习成本__:安装、集群一秒搞定,无需分库分表,实时备份。标准SQL,支持JDBC, RESTful, 支持Python/Java/C/C++/Go, 与MySQL相似,零学习成本。
|
||||||
|
|
||||||
|
采用TDengine,可将典型的物联网、车联网、工业互联网大数据平台的总拥有成本大幅降低。但需要指出的是,因充分利用了物联网时序数据的特点,它无法用来处理网络爬虫、微博、微信、电商、ERP、CRM等通用型数据。
|
||||||
|
|
||||||
<!--sdASF -->
|
|
||||||
|
|
||||||
## TDengine 总体适用场景
|
## TDengine 总体适用场景
|
||||||
|
|
||||||
作为一个IOT大数据平台,TDengine的典型适用场景是在IOT范畴,而且用户有一定的数据量。本文后续的介绍主要针对这个范畴里面的系统。范畴之外的系统,比如CRM,ERP等,不在本文讨论范围内。
|
作为一个IOT大数据平台,TDengine的典型适用场景是在IOT范畴,而且用户有一定的数据量。本文后续的介绍主要针对这个范畴里面的系统。范畴之外的系统,比如CRM,ERP等,不在本文讨论范围内。
|
||||||
|
|
||||||
|
|
||||||
## 数据源特点和需求
|
### 数据源特点和需求
|
||||||
从数据源角度,设计人员可以从已经角度分析TDengine在目标应用系统里面的适用性。
|
从数据源角度,设计人员可以从已经角度分析TDengine在目标应用系统里面的适用性。
|
||||||
|
|
||||||
|数据源特点和需求|不适用|可能适用|非常适用|简单说明|
|
|数据源特点和需求|不适用|可能适用|非常适用|简单说明|
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
|总体数据量巨大| | | ✅ |TDengine在容量方面提供出色的水平扩展功能,并且具备匹配高压缩的存储结构,达到业界最优的存储效率。|
|
|总体数据量巨大| | | √ |TDengine在容量方面提供出色的水平扩展功能,并且具备匹配高压缩的存储结构,达到业界最优的存储效率。|
|
||||||
|数据输入速度偶尔或者持续巨大| | | ✅ | TDengine的性能大大超过同类产品,可以在同样的硬件环境下持续处理大量的输入数据,并且提供很容易在用户环境里面运行的性能评估工具。|
|
|数据输入速度偶尔或者持续巨大| | | √ | TDengine的性能大大超过同类产品,可以在同样的硬件环境下持续处理大量的输入数据,并且提供很容易在用户环境里面运行的性能评估工具。|
|
||||||
|数据源数目巨大| | | ✅ |TDengine设计中包含专门针对大量数据源的优化,包括数据的写入和查询,尤其适合高效处理海量(千万或者更多量级)的数据源。|
|
|数据源数目巨大| | | √ |TDengine设计中包含专门针对大量数据源的优化,包括数据的写入和查询,尤其适合高效处理海量(千万或者更多量级)的数据源。|
|
||||||
|
|
||||||
|
### 系统架构要求
|
||||||
|
|
||||||
## 系统架构要求
|
|
||||||
|系统架构要求|不适用|可能适用|非常适用|简单说明|
|
|系统架构要求|不适用|可能适用|非常适用|简单说明|
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
|要求简单可靠的系统架构| | | ✅ |TDengine的系统架构非常简单可靠,自带消息队列,缓存,流式计算,监控等功能,无需集成额外的第三方产品。|
|
|要求简单可靠的系统架构| | | √ |TDengine的系统架构非常简单可靠,自带消息队列,缓存,流式计算,监控等功能,无需集成额外的第三方产品。|
|
||||||
|要求容错和高可靠| | | ✅ |TDengine的集群功能,自动提供容错灾备等高可靠功能|
|
|要求容错和高可靠| | | √ |TDengine的集群功能,自动提供容错灾备等高可靠功能|
|
||||||
|标准化规范| | | ✅ |TDengine使用标准的SQL语言提供主要功能,遵守标准化规范|
|
|标准化规范| | | √ |TDengine使用标准的SQL语言提供主要功能,遵守标准化规范|
|
||||||
|
|
||||||
## 系统功能需求
|
### 系统功能需求
|
||||||
|系统功能需求|不适用|可能适用|非常适用|简单说明|
|
|系统功能需求|不适用|可能适用|非常适用|简单说明|
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
|要求完整的内置数据处理算法| | ✅ | |TDengine的实现了通用的数据处理算法,但是还没有做到妥善处理各行各业的所有要求,因此特殊类型的处理还需要应用层面处理。|
|
|要求完整的内置数据处理算法| | √ | |TDengine的实现了通用的数据处理算法,但是还没有做到妥善处理各行各业的所有要求,因此特殊类型的处理还需要应用层面处理。|
|
||||||
|需要大量的交叉查询处理| | ✅ | |这种类型的处理更多应该用关系型数据系统处理,或者应该考虑TDengine和关系型数据系统配合实现系统功能|
|
|需要大量的交叉查询处理| | √ | |这种类型的处理更多应该用关系型数据系统处理,或者应该考虑TDengine和关系型数据系统配合实现系统功能|
|
||||||
|
|
||||||
|
### 系统性能需求
|
||||||
## 系统性能需求
|
|
||||||
|系统性能需求|不适用|可能适用|非常适用|简单说明|
|
|系统性能需求|不适用|可能适用|非常适用|简单说明|
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
|要求较大的总体处理能力| | | ✅ |TDengine的集群功能可以轻松地让多服务器配合达成处理能力的提升。|
|
|要求较大的总体处理能力| | | √ |TDengine的集群功能可以轻松地让多服务器配合达成处理能力的提升。|
|
||||||
|要求高速处理数据 | | | ✅ |TDengine的专门为IOT优化的存储和数据处理的设计,一般可以让系统得到超出同类产品多倍数的处理速度提升。|
|
|要求高速处理数据 | | | √ |TDengine的专门为IOT优化的存储和数据处理的设计,一般可以让系统得到超出同类产品多倍数的处理速度提升。|
|
||||||
|要求快速处理小粒度数据| | | ✅ |这方面TDengine性能可以完全对标关系型和NoSQL型数据处理系统。|
|
|要求快速处理小粒度数据| | | √ |这方面TDengine性能可以完全对标关系型和NoSQL型数据处理系统。|
|
||||||
|
|
||||||
|
### 系统维护需求
|
||||||
## 系统维护需求
|
|
||||||
|系统维护需求|不适用|可能适用|非常适用|简单说明|
|
|系统维护需求|不适用|可能适用|非常适用|简单说明|
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
|要求系统可靠运行| | | ✅ |TDengine的系统架构非常稳定可靠,日常维护也简单便捷,对维护人员的要求简洁明了,最大程度上杜绝人为错误和事故。|
|
|要求系统可靠运行| | | √ |TDengine的系统架构非常稳定可靠,日常维护也简单便捷,对维护人员的要求简洁明了,最大程度上杜绝人为错误和事故。|
|
||||||
|要求运维学习成本可控| | | ✅ |同上|
|
|要求运维学习成本可控| | | √ |同上|
|
||||||
|要求市场有大量人才储备| ✅ | | |TDengine作为新一代产品,目前人才市场里面有经验的人员还有限。但是学习成本低,我们作为厂家也提供运维的培训和辅助服务|
|
|要求市场有大量人才储备| √ | | |TDengine作为新一代产品,目前人才市场里面有经验的人员还有限。但是学习成本低,我们作为厂家也提供运维的培训和辅助服务|
|
||||||
|
|
||||||
|
## TDengine 性能指标介绍和验证方法
|
||||||
|
|
|
@ -38,7 +38,7 @@ which systemd
|
||||||
|
|
||||||
如果系统中不存在`systemd`命令,请考虑[通过源码安装](#通过源码安装)TDengine。
|
如果系统中不存在`systemd`命令,请考虑[通过源码安装](#通过源码安装)TDengine。
|
||||||
|
|
||||||
具体的安装过程,请参见[`TDengine多种安装包的安装和卸载`](https://www.taosdata.com/blog/2019/08/09/566.html)
|
具体的安装过程,请参见<a href="https://www.taosdata.com/blog/2019/08/09/566.html">TDengine多种安装包的安装和卸载</a>。
|
||||||
|
|
||||||
## 轻松启动
|
## 轻松启动
|
||||||
|
|
|
@ -4,14 +4,14 @@
|
||||||
|
|
||||||
TDengine采用关系型数据模型,需要建库、建表。因此对于一个具体的应用场景,需要考虑库的设计,超级表和普通表的设计。本节不讨论细致的语法规则,只介绍概念。
|
TDengine采用关系型数据模型,需要建库、建表。因此对于一个具体的应用场景,需要考虑库的设计,超级表和普通表的设计。本节不讨论细致的语法规则,只介绍概念。
|
||||||
|
|
||||||
##创建库
|
## 创建库
|
||||||
|
|
||||||
不同类型的数据采集点往往具有不同的数据特征,包括数据采集频率的高低,数据保留时间的长短,副本的数目,数据块的大小等等。为让各种场景下TDengine都能最大效率的工作,TDengine建议将不同数据特征的表创建在不同的库里,因为每个库可以配置不同的存储策略。创建一个库时,除SQL标准的选项外,应用还可以指定保留时长、副本数、内存块个数、时间精度、文件块里最大最小记录条数、是否压缩、一个数据文件覆盖的天数等多种参数。比如:
|
不同类型的数据采集点往往具有不同的数据特征,包括数据采集频率的高低,数据保留时间的长短,副本的数目,数据块的大小等等。为让各种场景下TDengine都能最大效率的工作,TDengine建议将不同数据特征的表创建在不同的库里,因为每个库可以配置不同的存储策略。创建一个库时,除SQL标准的选项外,应用还可以指定保留时长、副本数、内存块个数、时间精度、文件块里最大最小记录条数、是否压缩、一个数据文件覆盖的天数等多种参数。比如:
|
||||||
|
|
||||||
```cmd
|
```cmd
|
||||||
CREATE DATABASE power KEEP 365 DAYS 10 REPLICA 3 BLOCKS 4;
|
CREATE DATABASE power KEEP 365 DAYS 10 REPLICA 3 BLOCKS 4;
|
||||||
```
|
```
|
||||||
上述语句将创建一个名为power的库,这个库的数据将保留365天(超过365天将被自动删除),每10天一个数据文件,副本数为3, 内存块数为4。详细的语法及参数请见TAOS SQL。
|
上述语句将创建一个名为power的库,这个库的数据将保留365天(超过365天将被自动删除),每10天一个数据文件,副本数为3, 内存块数为4。详细的语法及参数请见<a href="https://www.taosdata.com/cn/documentation20/taos-sql/">TAOS SQL </a>
|
||||||
|
|
||||||
注意:任何一张表或超级表是属于一个库的,在创建表之前,必须先创建库。
|
注意:任何一张表或超级表是属于一个库的,在创建表之前,必须先创建库。
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ CREATE DATABASE power KEEP 365 DAYS 10 REPLICA 3 BLOCKS 4;
|
||||||
```cmd
|
```cmd
|
||||||
CREATE TABLE meters (ts timestamp, current float, voltage int, phase float) TAGS (location binary(64), groupdId int);
|
CREATE TABLE meters (ts timestamp, current float, voltage int, phase float) TAGS (location binary(64), groupdId int);
|
||||||
```
|
```
|
||||||
与创建普通表一样,创建表时,需要提供表名(示例中为meters),表结构Schema,即数据列的定义,为采集的物理量(示例中为ts, current, voltage, phase),数据类型可以为整型、浮点型、字符串等。除此之外,还需要提供标签的schema (示例中为location, groupId),标签的数据类型可以为整型、浮点型、字符串等。采集点的静态属性往往可以作为标签,比如采集点的地理位置、设备型号、设备组ID、管理员ID等等。标签的schema可以事后增加、删除、修改。具体定义以及细节请见 TAOS SQL一节。
|
与创建普通表一样,创建表时,需要提供表名(示例中为meters),表结构Schema,即数据列的定义,为采集的物理量(示例中为ts, current, voltage, phase),数据类型可以为整型、浮点型、字符串等。除此之外,还需要提供标签的schema (示例中为location, groupId),标签的数据类型可以为整型、浮点型、字符串等。采集点的静态属性往往可以作为标签,比如采集点的地理位置、设备型号、设备组ID、管理员ID等等。标签的schema可以事后增加、删除、修改。具体定义以及细节请见 <a href="https://www.taosdata.com/cn/documentation20/taos-sql/">TAOS SQL </a>一节。
|
||||||
|
|
||||||
每一种类型的数据采集点需要建立一个超级表,因此一个物联网系统,往往会有多个超级表。一个系统可以有多个DB,一个DB里可以有一到多个超级表。
|
每一种类型的数据采集点需要建立一个超级表,因此一个物联网系统,往往会有多个超级表。一个系统可以有多个DB,一个DB里可以有一到多个超级表。
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ CREATE TABLE d1001 USING meters TAGS ("Beijing.Chaoyang", 2);
|
||||||
```
|
```
|
||||||
其中d1001是表名,meters是超级表的表名,后面紧跟标签Location的具体标签值”Beijing.Chaoyang",标签groupId的具体标签值2。虽然在创建表时,需要指定标签值,但可以事后修改。详细细则请见 TAOS SQL。
|
其中d1001是表名,meters是超级表的表名,后面紧跟标签Location的具体标签值”Beijing.Chaoyang",标签groupId的具体标签值2。虽然在创建表时,需要指定标签值,但可以事后修改。详细细则请见 TAOS SQL。
|
||||||
|
|
||||||
TDengine建议将数据采集点的全局唯一ID作为表名。但对于有的场景,并没有唯一的ID,可以将多个ID组合成一个唯一的ID。不建议将具有唯一性的ID作为标签值。
|
TDengine建议将数据采集点的全局唯一ID作为表名(比如设备序列号)。但对于有的场景,并没有唯一的ID,可以将多个ID组合成一个唯一的ID。不建议将具有唯一性的ID作为标签值。
|
||||||
|
|
||||||
**自动建表**:在某些特殊场景中,用户在写数据时并不确定某个数据采集点的表是否存在,此时可在写入数据时使用自动建表语法来创建不存在的表,若该表已存在则不会建立新表。比如:
|
**自动建表**:在某些特殊场景中,用户在写数据时并不确定某个数据采集点的表是否存在,此时可在写入数据时使用自动建表语法来创建不存在的表,若该表已存在则不会建立新表。比如:
|
||||||
|
|
||||||
|
@ -40,4 +40,5 @@ INSERT INTO d1001 USING METERS TAGS ("Beijng.Chaoyang", 2) VALUES (now, 10.2, 21
|
||||||
```
|
```
|
||||||
上述SQL语句将记录(now, 10.2, 219, 0.32) 插入进表d1001。如果表d1001还未创建,则使用超级表meters做模板自动创建,同时打上标签值“Beijing.Chaoyang", 2。
|
上述SQL语句将记录(now, 10.2, 219, 0.32) 插入进表d1001。如果表d1001还未创建,则使用超级表meters做模板自动创建,同时打上标签值“Beijing.Chaoyang", 2。
|
||||||
|
|
||||||
**多列模型**:TDengine支持多列模型,只要这些物理量是同时采集的,这些量就可以作为不同列放在同一张表里。有的数据采集点有多组采集量,每一组的数据采集时间是不一样的,这时需要对同一个采集点建多张表。但还有一种极限的设计,单列模型,无论是否同时采集,每个采集的物理量单独建表。TDengine建议,只要采集时间一致,就采用多列模型,因为插入效率以及存储效率更高。
|
**多列模型**:TDengine支持多列模型,只要这些物理量是同时采集的,这些量就可以作为不同列放在同一张表里。有的数据采集点有多组采集量,每一组的数据采集时间是不一样的,这时需要对同一个采集点建多张表。但还有一种极限的设计,单列模型,无论是否同时采集,每个采集的物理量单独建表。TDengine建议,只要采集时间一致,就采用多列模型,因为插入效率以及存储效率更高。
|
||||||
|
|
|
@ -6,17 +6,16 @@
|
||||||
|
|
||||||
## 主要查询功能
|
## 主要查询功能
|
||||||
|
|
||||||
TDengine采用SQL作为查询语言,应用程序可以通过C/C++, JDBC, GO, Python连接器发送SQL查询语句,用户还可以通过TAOS Shell直接手动执行SQL即席查询,十分方便。支持如下查询功能:
|
TDengine 采用 SQL 作为查询语言。应用程序可以通过 C/C++, Java, Go, Python 连接器发送 SQL 语句,用户可以通过 TDengine 提供的命令行(Command Line Interface, CLI)工具 TAOS Shell 手动执行 SQL 即席查询(Ad-Hoc Query)。TDengine 支持如下查询功能:
|
||||||
|
|
||||||
- 查询单列、或多列查询
|
- 单列、多列数据查询
|
||||||
- 支持值过滤条件:\>, \<, =, \<> 大于,小于,等于,不等于等等
|
- 标签和数值的多种过滤条件:\>, \<, =, \<>, like 等
|
||||||
- 支持对标签的模糊匹配
|
- 聚合结果的分组(Group by)、排序(Order by)、约束输出(Limit/Offset)
|
||||||
- 支持Group by, Order by, Limit, Offset
|
- 数值列及聚合结果的四则运算
|
||||||
- 支持列之间的四则运算
|
- 时间戳对齐的连接查询(Join Query)操作
|
||||||
- 支持时间戳对齐的JOIN操作
|
- 多种聚合/计算函数: count, max, min, avg, sum, twa, stddev, leastsquares, top, bottom, first, last, percentile, apercentile, last_row, spread, diff等
|
||||||
- 支持多种函数: count, max, min, avg, sum, twa, stddev, leastsquares, top, bottom, first, last, percentile, apercentile, last_row, spread, diff
|
|
||||||
|
|
||||||
例如:在TAOS Shell中,从表d1001中查询出vlotage >215的记录,按时间降序排列,仅仅输出2条。
|
例如:在TAOS Shell中,从表d1001中查询出vlotage > 215的记录,按时间降序排列,仅仅输出2条。
|
||||||
```mysql
|
```mysql
|
||||||
taos> select * from d1001 where voltage > 215 order by ts desc limit 2;
|
taos> select * from d1001 where voltage > 215 order by ts desc limit 2;
|
||||||
ts | current | voltage | phase |
|
ts | current | voltage | phase |
|
||||||
|
@ -27,11 +26,11 @@ Query OK, 2 row(s) in set (0.001100s)
|
||||||
```
|
```
|
||||||
为满足物联网场景的需求,TDengine支持几个特殊的函数,比如twa(时间加权平均),spread (最大值与最小值的差),last_row(最后一条记录)等,更多与物联网场景相关的函数将添加进来。TDengine还支持连续查询。
|
为满足物联网场景的需求,TDengine支持几个特殊的函数,比如twa(时间加权平均),spread (最大值与最小值的差),last_row(最后一条记录)等,更多与物联网场景相关的函数将添加进来。TDengine还支持连续查询。
|
||||||
|
|
||||||
具体的查询语法请看TAOS SQL。
|
具体的查询语法请看<a href="https://www.taosdata.com/cn/documentation20/taos-sql/">TAOS SQL </a>。
|
||||||
|
|
||||||
## 多表聚合查询
|
## 多表聚合查询
|
||||||
|
|
||||||
TDengine对每个数据采集点单独建表,但应用经常需要对数据点之间进行聚合。为高效的进行聚合操作,TDengine引入超级表(STable)的概念。超级表用来代表一特定类型的数据采集点,它是表的集合,包含多张表。这集合里每张表的Schema是一样的,但每张表都带有自己的静态标签,标签可以多个,可以随时增加、删除和修改。
|
TDengine对每个数据采集点单独建表,但在实际应用中经常需要对不同的采集点数据进行聚合。为高效的进行聚合操作,TDengine引入超级表(STable)的概念。超级表用来代表一特定类型的数据采集点,它是包含多张表的表集合,集合里每张表的模式(schema)完全一致,但每张表都带有自己的静态标签,标签可以多个,可以随时增加、删除和修改。
|
||||||
|
|
||||||
应用可通过指定标签的过滤条件,对一个STable下的全部或部分表进行聚合或统计操作,这样大大简化应用的开发。其具体流程如下图所示:
|
应用可通过指定标签的过滤条件,对一个STable下的全部或部分表进行聚合或统计操作,这样大大简化应用的开发。其具体流程如下图所示:
|
||||||
|
|
||||||
|
@ -39,16 +38,16 @@ TDengine对每个数据采集点单独建表,但应用经常需要对数据点
|
||||||
|
|
||||||
<center> 多表聚合查询原理图 </center>
|
<center> 多表聚合查询原理图 </center>
|
||||||
|
|
||||||
1:应用将一个查询条件发往系统;2: taosc将超级表的名字发往Meta Node(管理节点);3:管理节点将超级表所拥有的vnode列表发回taosc;4:taosc将计算的请求连同标签过滤条件发往这些vnode对应的多个数据节点;5:每个vnode先在内存里查找出自己节点里符合标签过滤条件的表的集合,然后扫描存储的时序数据,完成相应的聚合计算,将结果返回给taosc;6:taosc将多个数据节点返回的结果做最后的聚合,将其返回给应用。
|
1:应用将一个查询条件发往系统;2: taosc将超级表的名字发往 Meta Node(管理节点);3:管理节点将超级表所拥有的 vnode 列表发回 taosc;4:taosc将计算的请求连同标签过滤条件发往这些vnode对应的多个数据节点;5:每个vnode先在内存里查找出自己节点里符合标签过滤条件的表的集合,然后扫描存储的时序数据,完成相应的聚合计算,将结果返回给taosc;6:taosc将多个数据节点返回的结果做最后的聚合,将其返回给应用。
|
||||||
|
|
||||||
由于TDengine在vnode内将标签数据与时序数据分离存储,通过先在内存里过滤标签数据,将需要扫描的数据集大幅减少,大幅提升了聚合计算速度。同时,由于数据分布在多个vnode/dnode,聚合计算操作在多个vnode里并发进行,又进一步提升了聚合的速度。
|
由于TDengine在vnode内将标签数据与时序数据分离存储,通过先在内存里过滤标签数据,将需要扫描的数据集大幅减少,大幅提升聚合计算速度。同时,由于数据分布在多个vnode/dnode,聚合计算操作在多个vnode里并发进行,又进一步提升了聚合的速度。
|
||||||
|
|
||||||
对普通表的聚合函数以及绝大部分操作都适用于超级表,语法完全一样,细节请看TAOS SQL。
|
对普通表的聚合函数以及绝大部分操作都适用于超级表,语法完全一样,细节请看 TAOS SQL。
|
||||||
|
|
||||||
比如:在TAOS Shell,查找所有智能电表采集的电压平均值,并按照location分组
|
比如:在TAOS Shell,查找所有智能电表采集的电压平均值,并按照location分组
|
||||||
|
|
||||||
```mysql
|
```mysql
|
||||||
taos> select avg(voltage) from meters group by location;
|
taos> SELECT AVG(voltage) FROM meters GROUP BY location;
|
||||||
avg(voltage) | location |
|
avg(voltage) | location |
|
||||||
=============================================================
|
=============================================================
|
||||||
222.000000000 | Beijing.Haidian |
|
222.000000000 | Beijing.Haidian |
|
||||||
|
@ -58,18 +57,18 @@ Query OK, 2 row(s) in set (0.002136s)
|
||||||
|
|
||||||
## 降采样查询、插值
|
## 降采样查询、插值
|
||||||
|
|
||||||
物联网场景里,经常需要做down sampling,需要将采集的数据按时间段进行聚合。TDengine提供了一个简便的关键词interval让操作变得极为简单。比如:将智能电表d1001采集的电流值每10秒钟求和
|
物联网场景里,经常需要通过降采样(down sampling)将采集的数据按时间段进行聚合。TDengine 提供了一个简便的关键词 interval 让按照时间窗口的查询操作变得极为简单。比如,将智能电表 d1001 采集的电流值每10秒钟求和
|
||||||
```mysql
|
```mysql
|
||||||
taos> SELECT sum(current) FROM d1001 interval(10s) ;
|
taos> SELECT sum(current) FROM d1001 INTERVAL(10s) ;
|
||||||
ts | sum(current) |
|
ts | sum(current) |
|
||||||
======================================================
|
======================================================
|
||||||
2018-10-03 14:38:00.000 | 10.300000191 |
|
2018-10-03 14:38:00.000 | 10.300000191 |
|
||||||
2018-10-03 14:38:10.000 | 24.900000572 |
|
2018-10-03 14:38:10.000 | 24.900000572 |
|
||||||
Query OK, 2 row(s) in set (0.000883s)
|
Query OK, 2 row(s) in set (0.000883s)
|
||||||
```
|
```
|
||||||
降采样操作还适用于超级表,比如:将所有智能电表采集的电流值每秒钟求和
|
降采样操作也适用于超级表,比如:将所有智能电表采集的电流值每秒钟求和
|
||||||
```mysql
|
```mysql
|
||||||
taos> SELECT sum(current) FROM meters interval(1s) ;
|
taos> SELECT SUM(current) FROM meters INTERVAL(1s) ;
|
||||||
ts | sum(current) |
|
ts | sum(current) |
|
||||||
======================================================
|
======================================================
|
||||||
2018-10-03 14:38:04.000 | 10.199999809 |
|
2018-10-03 14:38:04.000 | 10.199999809 |
|
||||||
|
@ -82,5 +81,5 @@ Query OK, 5 row(s) in set (0.001538s)
|
||||||
|
|
||||||
物联网场景里,每个数据采集点采集数据的时间是难同步的,但很多分析算法(比如FFT)需要把采集的数据严格按照时间等间隔的对齐,在很多系统里,需要应用自己写程序来处理,但使用TDengine的降采样操作就轻松解决。如果一个时间间隔里,没有采集的数据,TDengine还提供插值计算的功能。
|
物联网场景里,每个数据采集点采集数据的时间是难同步的,但很多分析算法(比如FFT)需要把采集的数据严格按照时间等间隔的对齐,在很多系统里,需要应用自己写程序来处理,但使用TDengine的降采样操作就轻松解决。如果一个时间间隔里,没有采集的数据,TDengine还提供插值计算的功能。
|
||||||
|
|
||||||
语法规则细节请见TAOS SQL。
|
语法规则细节请见<a href="https://www.taosdata.com/cn/documentation20/taos-sql/">TAOS SQL </a>。
|
||||||
|
|
|
@ -947,7 +947,7 @@ SELECT function_list FROM stb_name
|
||||||
2. 在时间维度聚合中,返回的结果中时间序列严格单调递增。
|
2. 在时间维度聚合中,返回的结果中时间序列严格单调递增。
|
||||||
3. 如果查询对象是超级表,则聚合函数会作用于该超级表下满足值过滤条件的所有表的数据。如果查询中没有使用group by语句,则返回的结果按照时间序列严格单调递增;如果查询中使用了group by语句分组,则返回结果中每个group内不按照时间序列严格单调递增。
|
3. 如果查询对象是超级表,则聚合函数会作用于该超级表下满足值过滤条件的所有表的数据。如果查询中没有使用group by语句,则返回的结果按照时间序列严格单调递增;如果查询中使用了group by语句分组,则返回结果中每个group内不按照时间序列严格单调递增。
|
||||||
|
|
||||||
**示例:**智能电表的建表语句如下:
|
**示例:** 智能电表的建表语句如下:
|
||||||
|
|
||||||
```mysql
|
```mysql
|
||||||
CREATE TABLE meters (ts timestamp, current float, voltage int, phase float) TAGS (location binary(64), groupId int);
|
CREATE TABLE meters (ts timestamp, current float, voltage int, phase float) TAGS (location binary(64), groupId int);
|
||||||
|
@ -963,7 +963,7 @@ SELECT AVG(current),MAX(current),LEASTSQUARES(current, start_val, step_val), PER
|
||||||
FILL(PREV);
|
FILL(PREV);
|
||||||
```
|
```
|
||||||
|
|
||||||
##TAOS SQL 边界限制
|
## TAOS SQL 边界限制
|
||||||
- 数据库名最大长度为33
|
- 数据库名最大长度为33
|
||||||
- 表名最大长度为193,每行数据最大长度16k个字符
|
- 表名最大长度为193,每行数据最大长度16k个字符
|
||||||
- 列名最大长度为65,最多允许1024列,最少需要2列,第一列必须是时间戳
|
- 列名最大长度为65,最多允许1024列,最少需要2列,第一列必须是时间戳
|
|
@ -1,48 +1,70 @@
|
||||||
#系统管理
|
# TDengine的运营与维护
|
||||||
|
|
||||||
## 容量规划
|
## 容量规划
|
||||||
|
|
||||||
系统的处理能力是有限的,但通过对TDengine配置参数的调整,可以在性能与容量上进行平衡。
|
使用TDengine来搭建一个物联网大数据平台,计算资源、存储资源需要根据业务场景进行规划。下面分别讨论系统运行所需要的内存、CPU以及硬盘空间。
|
||||||
|
|
||||||
**内存需求**
|
### 内存需求
|
||||||
|
|
||||||
每个Database可以创建固定数目的Vnode,默认与CPU核数相同,可通过maxVgroupsPerDb配置;每个vnode会占用固定大小的内存,与参数blocks和cache有关;每个Table会占用与Tag总大小有关的内存;此外,系统会有一些固定的内存开销。因此,每个Database需要的系统内存可通过如下公式计算:
|
每个DB可以创建固定数目的vnode,默认与CPU核数相同,可通过maxVgroupsPerDb配置;每个vnode会占用固定大小的内存(大小与数据库的配置参数blocks和cache有关);每个Table会占用与标签总长度有关的内存;此外,系统会有一些固定的内存开销。因此,每个DB需要的系统内存可通过如下公式计算:
|
||||||
|
|
||||||
```
|
```
|
||||||
Memory Size = maxVgroupsPerDb * (blocks * cache + 10Mb) + numOfTables * (tagSizePerTable + 0.5Kb)
|
Memory Size = maxVgroupsPerDb * (blocks * cache + 10Mb) + numOfTables * (tagSizePerTable + 0.5Kb)
|
||||||
```
|
```
|
||||||
|
|
||||||
**CPU需求**
|
示例:假设是4核机器,cache是缺省大小16M, blocks是缺省值6,假设有10万张表,标签总长度是256字节,则总的内存需求为:4\*(16\*6+10) + 100000*(0.25+0.5)/1000 = 499M。
|
||||||
|
|
||||||
每个Vnode可以容纳的最大数据表数,可以通过参数maxTablesPerVnode设置,但每个Vnode的数据表数在1万以下性能最佳。系统总的Vnode数目最好不要超过CPU核数的两倍。
|
实际运行的系统往往会根据数据特点的不同,将数据存放在不同的DB里。因此做规划时,也需要考虑。
|
||||||
|
|
||||||
**存储需求**
|
如果内存充裕,可以加大Blocks的配置,这样更多数据将保存在内存里,提高查询速度。
|
||||||
|
|
||||||
在绝大多数场景下,TDengine的压缩比不会低于5倍。压缩前的原始数据大小可通过如下方式计算:
|
### CPU需求
|
||||||
|
|
||||||
|
CPU的需求取决于如下两方面:
|
||||||
|
|
||||||
|
* __数据插入__ TDengine单核每秒能至少处理一万个插入请求。每个插入请求可以带多条记录,一次插入一条记录与插入10条记录,消耗的计算资源差别很小。因此每次插入,条数越大,插入效率越高。如果一个插入请求带200条以上记录,单核就能达到每秒插入100万条记录的速度。但对前端数据采集的要求越高,因为需要缓存记录,然后一批插入。
|
||||||
|
* __查询需求__ TDengine提供高效的查询,但是每个场景的查询差异很大,查询频次变化也很大,难以给出客观数字。需要用户针对自己的场景,写一些查询语句,才能确定。
|
||||||
|
|
||||||
|
因此仅对数据插入而言,CPU是可以估算出来的,但查询所耗的计算资源无法估算。在实际运营过程中,不建议CPU使用率超过50%,超过后,需要增加新的节点,以获得更多计算资源。
|
||||||
|
|
||||||
|
### 存储需求
|
||||||
|
|
||||||
|
TDengine相对于通用数据库,有超高的压缩比,在绝大多数场景下,TDengine的压缩比不会低于5倍,有的场合,压缩比可达到10倍以上,取决于实际场景的数据特征。压缩前的原始数据大小可通过如下方式计算:
|
||||||
|
|
||||||
```
|
```
|
||||||
Raw DataSize = numOfTables * rowSizePerTable * rowsPerTable
|
Raw DataSize = numOfTables * rowSizePerTable * rowsPerTable
|
||||||
```
|
```
|
||||||
|
|
||||||
用户可以通过参数keep,设置数据在磁盘中的最大保存时长。
|
示例:1000万台智能电表,每台电表每15分钟采集一次数据,每次采集的数据128字节,那么一年的原始数据量是:10000000\*128\*24\*60/15*365 = 44851T。TDengine大概需要消耗44851/5=8970T, 8.9P空间。
|
||||||
|
|
||||||
|
用户可以通过参数keep,设置数据在磁盘中的最大保存时长。为进一步减少存储成本,TDengine还提供多级存储,最冷的数据可以存放在最廉价的存储介质上,应用的访问不用做任何调整,只是读取速度降低了。
|
||||||
|
|
||||||
|
为提高速度,可以配置多快硬盘,这样可以并发写入或读取数据。
|
||||||
|
|
||||||
|
### 物理机或虚拟机台数
|
||||||
|
|
||||||
|
根据上面的内存、CPU、存储的预估,就可以知道整个系统需要多少核、多少内存、多少存储空间。如果数据副本数不为1,总需求量需要再乘以副本数。
|
||||||
|
|
||||||
|
因为TDengine具有很好的水平扩展能力,根据总量,再根据单个物理机或虚拟机的资源,就可以轻松决定需要购置多少台物理机或虚拟机了。
|
||||||
|
|
||||||
## 容错和灾备
|
## 容错和灾备
|
||||||
|
|
||||||
**容错**
|
### 容错
|
||||||
|
|
||||||
TDengine支持**WAL**(Write Ahead Log)机制,实现数据的容错能力,保证数据的高可用。
|
TDengine支持**WAL**(Write Ahead Log)机制,实现数据的容错能力,保证数据的高可用。
|
||||||
|
|
||||||
TDengine接收到应用的请求数据包时,先将请求的原始数据包写入数据库日志文件,等数据成功写入数据库数据文件后,再删除相应的WAL。这样保证了TDengine能够在断电等因素导致的服务重启时从数据库日志文件中恢复数据,避免数据的丢失。
|
TDengine接收到应用的请求数据包时,先将请求的原始数据包写入数据库日志文件,等数据成功写入数据库数据文件后,再删除相应的WAL。这样保证了TDengine能够在断电等因素导致的服务重启时从数据库日志文件中恢复数据,避免数据的丢失。
|
||||||
|
|
||||||
涉及的系统配置参数有两个.
|
涉及的系统配置参数有两个:
|
||||||
|
|
||||||
walLevel:WAL级别,1:写wal, 但不执行fsync; 2:写wal, 而且执行fsync。
|
- walLevel:WAL级别,0:不写wal; 1:写wal, 但不执行fsync; 2:写wal, 而且执行fsync。
|
||||||
|
- fsync:当walLevel设置为2时,执行fsync的周期。设置为0,表示每次写入,立即执行fsync。
|
||||||
|
|
||||||
fsync:当walLevel设置为2时,执行fsync的周期。设置为0,表示每次写入,立即执行fsync。
|
如果要100%的保证数据不丢失,需要将walLevel设置为2,fsync设置为0。这时写入速度将会下降。但如果应用侧启动的写数据的线程数达到一定的数量(超过50),那么写入数据的性能也会很不错,只会比fsync设置为3000毫秒下降30%左右。
|
||||||
|
|
||||||
**灾备**
|
### 灾备
|
||||||
|
|
||||||
TDengine的集群通过多个副本的机制,来提供系统的高可靠性,实现灾备能力。
|
TDengine的集群通过多个副本的机制,来提供系统的高可用性,实现灾备能力。
|
||||||
|
|
||||||
TDengine集群是由mnode负责管理的,为保证mnode的高可靠,可以配置多个mnode副本,副本数由系统配置参数numOfMnodes决定,为了支持高可靠,需要设置大于1。为保证元数据的强一致性,mnode副本之间通过同步方式进行数据复制,保证了元数据的强一致性。
|
TDengine集群是由mnode负责管理的,为保证mnode的高可靠,可以配置多个mnode副本,副本数由系统配置参数numOfMnodes决定,为了支持高可靠,需要设置大于1。为保证元数据的强一致性,mnode副本之间通过同步方式进行数据复制,保证了元数据的强一致性。
|
||||||
|
|
||||||
|
@ -50,33 +72,7 @@ TDengine集群中的时序数据的副本数是与数据库关联的,一个集
|
||||||
|
|
||||||
TDengine集群的节点数必须大于等于副本数,否则创建表时将报错。
|
TDengine集群的节点数必须大于等于副本数,否则创建表时将报错。
|
||||||
|
|
||||||
当TDengine集群中的节点部署在不同的物理机上(比如不同的机架、或不同的IDC),并设置多个副本数时,就实现了异地容灾,从而提供系统的高可靠性,无需再使用其他软件或工具。
|
当TDengine集群中的节点部署在不同的物理机上,并设置多个副本数时,就实现了系统的高可靠性,无需再使用其他软件或工具。TDengine企业版还可以将副本部署在不同机房,从而实现异地容灾。
|
||||||
|
|
||||||
## 文件目录结构
|
|
||||||
|
|
||||||
安装TDengine后,默认会在操作系统中生成下列目录或文件:
|
|
||||||
|
|
||||||
| 目录/文件 | 说明 |
|
|
||||||
| ---------------------- | :------------------------------------------------|
|
|
||||||
| /usr/local/taos/bin | TDengine可执行文件目录。其中的执行文件都会软链接到/usr/bin目录下。 |
|
|
||||||
| /usr/local/taos/connector | TDengine各种连接器目录。 |
|
|
||||||
| /usr/local/taos/driver | TDengine动态链接库目录。会软链接到/usr/lib目录下。 |
|
|
||||||
| /usr/local/taos/examples | TDengine各种语言应用示例目录。 |
|
|
||||||
| /usr/local/taos/include | TDengine对外提供的C语言接口的头文件。 |
|
|
||||||
| /etc/taos/taos.cfg | TDengine默认[配置文件] |
|
|
||||||
| /var/lib/taos | TDengine默认数据文件目录,可通过[配置文件]修改位置. |
|
|
||||||
| /var/log/taos | TDengine默认日志文件目录,可通过[配置文件]修改位置 |
|
|
||||||
|
|
||||||
**可执行文件**
|
|
||||||
|
|
||||||
TDengine的所有可执行文件默认存放在 _/usr/local/taos/bin_ 目录下。其中包括:
|
|
||||||
|
|
||||||
- _taosd_:TDengine服务端可执行文件
|
|
||||||
- _taos_: TDengine Shell可执行文件
|
|
||||||
- _taosdump_:数据导入导出工具
|
|
||||||
- remove.sh:卸载TDengine的脚本, 请谨慎执行,链接到/usr/bin目录下的rmtaos命令。会删除TDengine的安装目录/usr/local/taos,但会保留/etc/taos、/var/lib/taos、/var/log/taos。
|
|
||||||
|
|
||||||
您可以通过修改系统配置文件taos.cfg来配置不同的数据目录和日志目录。
|
|
||||||
|
|
||||||
## 服务端配置
|
## 服务端配置
|
||||||
|
|
||||||
|
@ -84,8 +80,8 @@ TDengine系统后台服务由taosd提供,可以在配置文件taos.cfg里修
|
||||||
|
|
||||||
下面仅仅列出一些重要的配置参数,更多的参数请看配置文件里的说明。各个参数的详细介绍及作用请看前述章节。**注意:配置修改后,需要重启*taosd*服务才能生效。**
|
下面仅仅列出一些重要的配置参数,更多的参数请看配置文件里的说明。各个参数的详细介绍及作用请看前述章节。**注意:配置修改后,需要重启*taosd*服务才能生效。**
|
||||||
|
|
||||||
- first: taosd启动时,主动连接的集群中第一个dnode的end point, 缺省值为 localhost:6030。
|
- firstEp: taosd启动时,主动连接的集群中第一个dnode的end point, 缺省值为 localhost:6030。
|
||||||
- second: taosd启动时,如果first连接不上,尝试连接集群中第二个dnode的end point, 缺省值为空。
|
- secondEp: taosd启动时,如果first连接不上,尝试连接集群中第二个dnode的end point, 缺省值为空。
|
||||||
- fqdn:数据节点的FQDN。如果为空,将自动获取操作系统配置的第一个, 缺省值为空。
|
- fqdn:数据节点的FQDN。如果为空,将自动获取操作系统配置的第一个, 缺省值为空。
|
||||||
- serverPort:taosd启动后,对外服务的端口号,默认值为6030。
|
- serverPort:taosd启动后,对外服务的端口号,默认值为6030。
|
||||||
- httpPort: RESTful服务使用的端口号,所有的HTTP请求(TCP)都需要向该接口发起查询/写入请求。
|
- httpPort: RESTful服务使用的端口号,所有的HTTP请求(TCP)都需要向该接口发起查询/写入请求。
|
||||||
|
@ -129,7 +125,7 @@ TDengine集群中加入一个新的dnode时,涉及集群相关的一些参数
|
||||||
- statusInterval: dnode向mnode报告状态时长。单位为秒,默认值:1。
|
- statusInterval: dnode向mnode报告状态时长。单位为秒,默认值:1。
|
||||||
- maxTablesPerVnode: 每个vnode中能够创建的最大表个数。默认值:1000000。
|
- maxTablesPerVnode: 每个vnode中能够创建的最大表个数。默认值:1000000。
|
||||||
- maxVgroupsPerDb: 每个数据库中能够使用的最大vnode个数。
|
- maxVgroupsPerDb: 每个数据库中能够使用的最大vnode个数。
|
||||||
- arbitrator: 系统中裁决器的end point。
|
- arbitrator: 系统中裁决器的end point,缺省为空
|
||||||
- timezone:时区。从系统中动态获取当前的时区设置。
|
- timezone:时区。从系统中动态获取当前的时区设置。
|
||||||
- locale:系统区位信息及编码格式。系统中动态获取,如果自动获取失败,需要用户在配置文件设置或通过API设置。
|
- locale:系统区位信息及编码格式。系统中动态获取,如果自动获取失败,需要用户在配置文件设置或通过API设置。
|
||||||
- charset:字符集编码。系统中动态获取,如果自动获取失败,需要用户在配置文件设置或通过API设置。
|
- charset:字符集编码。系统中动态获取,如果自动获取失败,需要用户在配置文件设置或通过API设置。
|
||||||
|
@ -140,8 +136,8 @@ TDengine系统的前台交互客户端应用程序为taos,它与taosd共享同
|
||||||
|
|
||||||
客户端配置参数列表及解释
|
客户端配置参数列表及解释
|
||||||
|
|
||||||
- first: taos启动时,主动连接的集群中第一个taosd实例的end point, 缺省值为 localhost:6030。
|
- firstEp: taos启动时,主动连接的集群中第一个taosd实例的end point, 缺省值为 localhost:6030。
|
||||||
- second: taos启动时,如果first连接不上,尝试连接集群中第二个taosd实例的end point, 缺省值为空。
|
- secondEp: taos启动时,如果first连接不上,尝试连接集群中第二个taosd实例的end point, 缺省值为空。
|
||||||
- charset:字符集编码。系统中动态获取,如果自动获取失败,需要用户在配置文件设置或通过API设置。
|
- charset:字符集编码。系统中动态获取,如果自动获取失败,需要用户在配置文件设置或通过API设置。
|
||||||
- locale:系统区位信息及编码格式。系统中动态获取,如果自动获取失败,需要用户在配置文件设置或通过API设置。
|
- locale:系统区位信息及编码格式。系统中动态获取,如果自动获取失败,需要用户在配置文件设置或通过API设置。
|
||||||
|
|
||||||
|
@ -230,9 +226,7 @@ Query OK, 9 row(s) affected (0.004763s)
|
||||||
|
|
||||||
**taosdump工具导入**
|
**taosdump工具导入**
|
||||||
|
|
||||||
TDengine提供了方便的数据库导入导出工具taosdump。用户可以将taosdump从一个系统导出的数据,导入到其他系统中。具体使用方法,请参见博客:
|
TDengine提供了方便的数据库导入导出工具taosdump。用户可以将taosdump从一个系统导出的数据,导入到其他系统中。具体使用方法,请参见博客:<a href='https://www.taosdata.com/blog/2020/03/09/1334.html'>TDengine DUMP工具使用指南</a>
|
||||||
|
|
||||||
[TDengine DUMP工具使用指南]: https://www.taosdata.com/blog/2020/03/09/1334.html
|
|
||||||
|
|
||||||
## 数据导出
|
## 数据导出
|
||||||
|
|
||||||
|
@ -250,9 +244,7 @@ select * from <tb_name> >> data.csv
|
||||||
|
|
||||||
**用taosdump导出数据**
|
**用taosdump导出数据**
|
||||||
|
|
||||||
TDengine提供了方便的数据库导出工具taosdump。用户可以根据需要选择导出所有数据库、一个数据库或者数据库中的一张表,所有数据或一时间段的数据,甚至仅仅表的定义。具体使用方法,请参见博客:
|
TDengine提供了方便的数据库导出工具taosdump。用户可以根据需要选择导出所有数据库、一个数据库或者数据库中的一张表,所有数据或一时间段的数据,甚至仅仅表的定义。具体使用方法,请参见博客:<a href='https://www.taosdata.com/blog/2020/03/09/1334.html'>TDengine DUMP工具使用指南</a>
|
||||||
|
|
||||||
[TDengine DUMP工具使用指南]: https://www.taosdata.com/blog/2020/03/09/1334.html
|
|
||||||
|
|
||||||
## 系统连接、任务查询管理
|
## 系统连接、任务查询管理
|
||||||
|
|
||||||
|
@ -298,4 +290,33 @@ KILL STREAM <stream-id>
|
||||||
|
|
||||||
TDengine启动后,会自动创建一个监测数据库SYS,并自动将服务器的CPU、内存、硬盘空间、带宽、请求数、磁盘读写速度、慢查询等信息定时写入该数据库。TDengine还将重要的系统操作(比如登录、创建、删除数据库等)日志以及各种错误报警信息记录下来存放在SYS库里。系统管理员可以从CLI直接查看这个数据库,也可以在WEB通过图形化界面查看这些监测信息。
|
TDengine启动后,会自动创建一个监测数据库SYS,并自动将服务器的CPU、内存、硬盘空间、带宽、请求数、磁盘读写速度、慢查询等信息定时写入该数据库。TDengine还将重要的系统操作(比如登录、创建、删除数据库等)日志以及各种错误报警信息记录下来存放在SYS库里。系统管理员可以从CLI直接查看这个数据库,也可以在WEB通过图形化界面查看这些监测信息。
|
||||||
|
|
||||||
这些监测信息的采集缺省是打开的,但可以修改配置文件里的选项enableMonitor将其关闭或打开。
|
这些监测信息的采集缺省是打开的,但可以修改配置文件里的选项enableMonitor将其关闭或打开。
|
||||||
|
|
||||||
|
## 文件目录结构
|
||||||
|
|
||||||
|
安装TDengine后,默认会在操作系统中生成下列目录或文件:
|
||||||
|
|
||||||
|
| 目录/文件 | 说明 |
|
||||||
|
| ------------------------- | :----------------------------------------------------------- |
|
||||||
|
| /usr/local/taos/bin | TDengine可执行文件目录。其中的执行文件都会软链接到/usr/bin目录下。 |
|
||||||
|
| /usr/local/taos/connector | TDengine各种连接器目录。 |
|
||||||
|
| /usr/local/taos/driver | TDengine动态链接库目录。会软链接到/usr/lib目录下。 |
|
||||||
|
| /usr/local/taos/examples | TDengine各种语言应用示例目录。 |
|
||||||
|
| /usr/local/taos/include | TDengine对外提供的C语言接口的头文件。 |
|
||||||
|
| /etc/taos/taos.cfg | TDengine默认[配置文件] |
|
||||||
|
| /var/lib/taos | TDengine默认数据文件目录,可通过[配置文件]修改位置. |
|
||||||
|
| /var/log/taos | TDengine默认日志文件目录,可通过[配置文件]修改位置 |
|
||||||
|
|
||||||
|
**可执行文件**
|
||||||
|
|
||||||
|
TDengine的所有可执行文件默认存放在 _/usr/local/taos/bin_ 目录下。其中包括:
|
||||||
|
|
||||||
|
- _taosd_:TDengine服务端可执行文件
|
||||||
|
- _taos_: TDengine Shell可执行文件
|
||||||
|
- _taosdump_:数据导入导出工具
|
||||||
|
- remove.sh:卸载TDengine的脚本, 请谨慎执行,链接到/usr/bin目录下的rmtaos命令。会删除TDengine的安装目录/usr/local/taos,但会保留/etc/taos、/var/lib/taos、/var/log/taos。
|
||||||
|
|
||||||
|
您可以通过修改系统配置文件taos.cfg来配置不同的数据目录和日志目录。
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -119,9 +119,9 @@ taos_consume
|
||||||
taos_unsubscribe
|
taos_unsubscribe
|
||||||
```
|
```
|
||||||
|
|
||||||
这些API的文档请见 [C/C++ 数据订阅接口](TODO: update link),
|
这些API的文档请见 [C/C++ 数据订阅接口](connector/#C/C++-Connector),
|
||||||
下面仍以智能电表场景为例介绍一下它们的具体用法(超级表和子表结构请参考上一节“连续查询”),
|
下面仍以智能电表场景为例介绍一下它们的具体用法(超级表和子表结构请参考上一节“连续查询”),
|
||||||
完整的示例代码可以在[这里](TODO: update link)找到。
|
完整的示例代码可以在 [这里](https://github.com/taosdata/TDengine/blob/master/tests/examples/c/subscribe.c) 找到。
|
||||||
|
|
||||||
如果我们希望当某个电表的电流超过一定限制(比如10A)后能得到通知并进行一些处理, 有两种方法:
|
如果我们希望当某个电表的电流超过一定限制(比如10A)后能得到通知并进行一些处理, 有两种方法:
|
||||||
一是分别对每张子表进行查询,每次查询后记录最后一条数据的时间戳,后续只查询这个时间戳之后的数据:
|
一是分别对每张子表进行查询,每次查询后记录最后一条数据的时间戳,后续只查询这个时间戳之后的数据:
|
||||||
|
@ -296,7 +296,8 @@ $ taos
|
||||||
您可以继续插入一些数据观察示例程序的输出。
|
您可以继续插入一些数据观察示例程序的输出。
|
||||||
|
|
||||||
|
|
||||||
## 缓存 (Cache)
|
## 缓存(Cache)
|
||||||
|
|
||||||
TDengine采用时间驱动缓存管理策略(First-In-First-Out,FIFO),又称为写驱动的缓存管理机制。这种策略有别于读驱动的数据缓存模式(Least-Recent-Use,LRU),直接将最近写入的数据保存在系统的缓存中。当缓存达到临界值的时候,将最早的数据批量写入磁盘。一般意义上来说,对于物联网数据的使用,用户最为关心最近产生的数据,即当前状态。TDengine充分利用了这一特性,将最近到达的(当前状态)数据保存在缓存中。
|
TDengine采用时间驱动缓存管理策略(First-In-First-Out,FIFO),又称为写驱动的缓存管理机制。这种策略有别于读驱动的数据缓存模式(Least-Recent-Use,LRU),直接将最近写入的数据保存在系统的缓存中。当缓存达到临界值的时候,将最早的数据批量写入磁盘。一般意义上来说,对于物联网数据的使用,用户最为关心最近产生的数据,即当前状态。TDengine充分利用了这一特性,将最近到达的(当前状态)数据保存在缓存中。
|
||||||
|
|
||||||
TDengine通过查询函数向用户提供毫秒级的数据获取能力。直接将最近到达的数据保存在缓存中,可以更加快速地响应用户针对最近一条或一批数据的查询分析,整体上提供更快的数据库查询响应能力。从这个意义上来说,可通过设置合适的配置参数将TDengine作为数据缓存来使用,而不需要再部署额外的缓存系统,可有效地简化系统架构,降低运维的成本。需要注意的是,TDengine重启以后系统的缓存将被清空,之前缓存的数据均会被批量写入磁盘,缓存的数据将不会像专门的Key-value缓存系统再将之前缓存的数据重新加载到缓存中。
|
TDengine通过查询函数向用户提供毫秒级的数据获取能力。直接将最近到达的数据保存在缓存中,可以更加快速地响应用户针对最近一条或一批数据的查询分析,整体上提供更快的数据库查询响应能力。从这个意义上来说,可通过设置合适的配置参数将TDengine作为数据缓存来使用,而不需要再部署额外的缓存系统,可有效地简化系统架构,降低运维的成本。需要注意的是,TDengine重启以后系统的缓存将被清空,之前缓存的数据均会被批量写入磁盘,缓存的数据将不会像专门的Key-value缓存系统再将之前缓存的数据重新加载到缓存中。
|
||||||
|
@ -308,7 +309,14 @@ TDengine将内存池按块划分进行管理,数据在内存块里按照列式
|
||||||
你可以通过函数last_row快速获取一张表或一张超级表的最后一条记录,这样很便于在大屏显示各设备的实时状态或采集值。例如:
|
你可以通过函数last_row快速获取一张表或一张超级表的最后一条记录,这样很便于在大屏显示各设备的实时状态或采集值。例如:
|
||||||
|
|
||||||
```mysql
|
```mysql
|
||||||
select last_row(degree) from thermometer where location='beijing';
|
select last_row(voltage) from meters where location='Beijing.Chaoyang';
|
||||||
```
|
```
|
||||||
|
|
||||||
该SQL语句将获取所有位于北京的传感器最后记录的温度值。
|
该SQL语句将获取所有位于北京朝阳区的电表最后记录的电压值。
|
||||||
|
|
||||||
|
|
||||||
|
## 报警监测(Alert)
|
||||||
|
|
||||||
|
在 TDengine 的应用场景中,报警监测是一个常见需求,从概念上说,它要求程序从最近一段时间的数据中筛选出符合一定条件的数据,并基于这些数据根据定义好的公式计算出一个结果,当这个结果符合某个条件且持续一定时间后,以某种形式通知用户。
|
||||||
|
|
||||||
|
为了满足用户对报警监测的需求,TDengine 以独立模块的形式提供了这一功能,有关它的安装使用方法,请参考博客 [使用 TDengine 进行报警监测](https://www.taosdata.com/blog/2020/04/14/1438.html) 。
|
|
@ -4,46 +4,48 @@
|
||||||
### 物联网典型场景
|
### 物联网典型场景
|
||||||
在典型的物联网、车联网、运维监测场景中,往往有多种不同类型的数据采集设备,采集一个到多个不同的物理量。而同一种采集设备类型,往往又有多个具体的采集设备分布在不同的地点。大数据处理系统就是要将各种采集的数据汇总,然后进行计算和分析。对于同一类设备,其采集的数据都是很规则的。以智能电表为例,假设每个智能电表采集电流、电压、相位三个量,其采集的数据类似如下的表格:
|
在典型的物联网、车联网、运维监测场景中,往往有多种不同类型的数据采集设备,采集一个到多个不同的物理量。而同一种采集设备类型,往往又有多个具体的采集设备分布在不同的地点。大数据处理系统就是要将各种采集的数据汇总,然后进行计算和分析。对于同一类设备,其采集的数据都是很规则的。以智能电表为例,假设每个智能电表采集电流、电压、相位三个量,其采集的数据类似如下的表格:
|
||||||
|
|
||||||
| Device ID | Time Stamp | current | voltage | phase | | location | groupId |
|
| Device ID | Time Stamp | current | voltage | phase | location | groupId |
|
||||||
| :-------: | :-----------: | :-----: | :-----: | :---: | :--- | :--------------: | :-----: |
|
| :-------: | :-----------: | :-----: | :-----: | :---: | :--------------: | :-----: |
|
||||||
| D1001 | 1538548685000 | 10.3 | 219 | 0.31 | | Beijing.Chaoyang | 2 |
|
| d1001 | 1538548685000 | 10.3 | 219 | 0.31 | Beijing.Chaoyang | 2 |
|
||||||
| D1002 | 1538548684000 | 10.2 | 220 | 0.23 | | Beijing.Chaoyang | 3 |
|
| d1002 | 1538548684000 | 10.2 | 220 | 0.23 | Beijing.Chaoyang | 3 |
|
||||||
| D1003 | 1538548686500 | 11.5 | 221 | 0.35 | | Beijing.Haidian | 3 |
|
| d1003 | 1538548686500 | 11.5 | 221 | 0.35 | Beijing.Haidian | 3 |
|
||||||
| D1004 | 1538548685500 | 13.4 | 223 | 0.29 | | Beijing.Haidian | 2 |
|
| d1004 | 1538548685500 | 13.4 | 223 | 0.29 | Beijing.Haidian | 2 |
|
||||||
| D1001 | 1538548695000 | 12.6 | 218 | 0.33 | | Beijing.Chaoyang | 2 |
|
| d1001 | 1538548695000 | 12.6 | 218 | 0.33 | Beijing.Chaoyang | 2 |
|
||||||
| D1004 | 1538548696600 | 11.8 | 221 | 0.28 | | Beijing.Haidian | 2 |
|
| d1004 | 1538548696600 | 11.8 | 221 | 0.28 | Beijing.Haidian | 2 |
|
||||||
| D1002 | 1538548696650 | 10.3 | 218 | 0.25 | | Beijing.Chaoyang | 3 |
|
| d1002 | 1538548696650 | 10.3 | 218 | 0.25 | Beijing.Chaoyang | 3 |
|
||||||
| D1001 | 1538548696800 | 12.3 | 221 | 0.31 | | Beijing.Chaoyang | 2 |
|
| d1001 | 1538548696800 | 12.3 | 221 | 0.31 | Beijing.Chaoyang | 2 |
|
||||||
|
|
||||||
<center> 表1:智能电表数据示例</center>
|
<center> 表1:智能电表数据示例</center>
|
||||||
|
|
||||||
每一条记录都有设备ID,时间戳,采集的物理量(如上图中的电流、电压、相位),还有与每个设备相关的静态标签(如上述表一中的位置Location和分组groupId)。每个设备是受外界的触发,或按照设定的周期采集数据。采集的数据点是时序的,是一个数据流。
|
每一条记录都有设备ID,时间戳,采集的物理量(如上图中的电流、电压、相位),还有与每个设备相关的静态标签(如上述表一中的位置Location和分组groupId)。每个设备是受外界的触发,或按照设定的周期采集数据。采集的数据点是时序的,是一个数据流。
|
||||||
|
|
||||||
### 数据特征
|
### 数据特征
|
||||||
除时序特征外,仔细研究发现,物联网、车联网、运维监测类数据还具有很多其他明显的特征。
|
除时序特征外,仔细研究发现,物联网、车联网、运维监测类数据还具有很多其他明显的特征。
|
||||||
|
|
||||||
1. 数据是结构化的;
|
1. 数据高度结构化;
|
||||||
2. 数据极少有更新或删除操作;
|
2. 数据极少有更新或删除操作;
|
||||||
3. 无需传统数据库的事务处理;
|
3. 无需传统数据库的事务处理;
|
||||||
4. 相对互联网应用,写多读少;
|
4. 相对互联网应用,写多读少;
|
||||||
5. 流量平稳,根据设备数量和采集频次,可以预测出来;
|
5. 流量平稳,根据设备数量和采集频次,可以预测出来;
|
||||||
6. 用户关注的是一段时间的趋势,而不是某一特点时间点的值;
|
6. 用户关注的是一段时间的趋势,而不是某一特点时间点的值;
|
||||||
7. 数据是有保留期限的;
|
7. 数据有保留期限;
|
||||||
8. 数据的查询分析一定是基于时间段和地理区域的;
|
8. 数据的查询分析一定是基于时间段和地理区域;
|
||||||
9. 除存储查询外,还往往需要各种统计和实时计算操作;
|
9. 除存储查询外,还需要各种统计和实时计算操作;
|
||||||
10. 数据量巨大,一天采集的数据就可以超过100亿条。
|
10. 数据量巨大,一天采集的数据就可以超过100亿条。
|
||||||
|
|
||||||
充分利用上述特征,TDengine采取了一特殊的优化的存储和计算设计来处理时序数据,能将系统处理能力显著提高。
|
充分利用上述特征,TDengine 采取了特殊的优化的存储和计算设计来处理时序数据,能将系统处理能力显著提高。
|
||||||
|
|
||||||
### 关系型数据库模型
|
### 关系型数据库模型
|
||||||
因为采集的数据一般是结构化数据,而且为降低学习门槛,TDengine采用传统的关系型数据库模型管理数据。因此用户需要先创建库,然后创建表,之后才能插入或查询数据。TDengine采用的是结构化存储,而不是NoSQL的key-value存储。
|
因为采集的数据一般是结构化数据,而且为降低学习门槛,TDengine采用传统的关系型数据库模型管理数据。因此用户需要先创建库,然后创建表,之后才能插入或查询数据。TDengine采用的是结构化存储,而不是NoSQL的key-value存储。
|
||||||
|
|
||||||
### 一个数据采集点一张表
|
### 一个数据采集点一张表
|
||||||
为充分利用其数据的时序性和其他数据特点,TDengine要求**对每个数据采集点单独建表**(比如有一千万个智能电表,就需创建一千万张表,上述表格中的D1001, D1002, D1003, D1004都需单独建表),用来存储这个采集点所采集的时序数据。这种设计有几大优点:
|
为充分利用其数据的时序性和其他数据特点,TDengine要求**对每个数据采集点单独建表**(比如有一千万个智能电表,就需创建一千万张表,上述表格中的d1001, d1002, d1003, d1004都需单独建表),用来存储这个采集点所采集的时序数据。这种设计有几大优点:
|
||||||
|
|
||||||
1. 能保证一个采集点的数据在存储介质上是一块一块连续的。如果读取一个时间段的数据,它能大幅减少随机读取操作,成数量级的提升读取和查询速度。
|
1. 能保证一个采集点的数据在存储介质上是一块一块连续的。如果读取一个时间段的数据,它能大幅减少随机读取操作,成数量级的提升读取和查询速度。
|
||||||
2. 由于不同采集设备产生数据的过程完全独立,每个设备的数据源是唯一的,一张表也就只有一个写入者,这样就可采用无锁方式来写,写入速度就能大幅提升。
|
2. 由于不同采集设备产生数据的过程完全独立,每个设备的数据源是唯一的,一张表也就只有一个写入者,这样就可采用无锁方式来写,写入速度就能大幅提升。
|
||||||
3. 对于一个数据采集点而言,其产生的数据是时序的,因此写的操作可用追加的方式实现,进一步大幅提高数据写入速度。
|
3. 对于一个数据采集点而言,其产生的数据是时序的,因此写的操作可用追加的方式实现,进一步大幅提高数据写入速度。
|
||||||
|
|
||||||
如果采用传统的方式,将多个设备的数据写入一张表,由于网络延时不可控,不同设备的数据到达服务器的时序是无法保证的,写入操作是要有锁保护的,而且一个设备的数据是难以保证连续存储在一起的。**采用一个采集点一张表的方式,能最大程度的保证单个数据采集点的插入和查询的性能是最优的。**
|
如果采用传统的方式,将多个设备的数据写入一张表,由于网络延时不可控,不同设备的数据到达服务器的时序是无法保证的,写入操作是要有锁保护的,而且一个设备的数据是难以保证连续存储在一起的。**采用一个数据采集点一张表的方式,能最大程度的保证单个数据采集点的插入和查询的性能是最优的。**
|
||||||
|
|
||||||
TDengine 建议用数据采集点的名字(如上表中的D1001)来做表名。每个数据采集点可能同时采集多个物理量(如上表中的curent, voltage, phase),每个物理量对应一张表中的一列,数据类型可以是整型、浮点型、字符串等。除此之外,表的第一列必须是时间戳,即数据类型为 timestamp。对采集的数据,TDengine将自动按照时间戳建立索引,但对采集的物理量不建任何索引。数据是用列式存储方式保存。
|
TDengine 建议用数据采集点的名字(如上表中的D1001)来做表名。每个数据采集点可能同时采集多个物理量(如上表中的curent, voltage, phase),每个物理量对应一张表中的一列,数据类型可以是整型、浮点型、字符串等。除此之外,表的第一列必须是时间戳,即数据类型为 timestamp。对采集的数据,TDengine将自动按照时间戳建立索引,但对采集的物理量不建任何索引。数据是用列式存储方式保存。
|
||||||
|
|
||||||
|
@ -56,13 +58,12 @@ TDengine 建议用数据采集点的名字(如上表中的D1001)来做表名。
|
||||||
|
|
||||||
当对多个具有相同数据类型的数据采集点进行聚合操作时,TDengine将先把满足标签过滤条件的表从超级表的中查找出来,然后再扫描这些表的时序数据,进行聚合操作,这样能将需要扫描的数据集大幅减少,从而大幅提高聚合计算的性能。
|
当对多个具有相同数据类型的数据采集点进行聚合操作时,TDengine将先把满足标签过滤条件的表从超级表的中查找出来,然后再扫描这些表的时序数据,进行聚合操作,这样能将需要扫描的数据集大幅减少,从而大幅提高聚合计算的性能。
|
||||||
|
|
||||||
##集群与基本逻辑单元
|
## 集群与基本逻辑单元
|
||||||
TDengine 的设计是基于单个硬件、软件系统不可靠,基于任何单台计算机都无法提供足够计算能力和存储能力处理海量数据的假设进行设计的。因此 TDengine 从研发的第一天起,就按照分布式高可靠架构进行设计,是支持水平扩展的,这样任何单台或多台服务器发生硬件故障或软件错误都不影响系统的可用性和可靠性。同时,通过节点虚拟化并辅以自动化负载均衡技术,TDengine 能最高效率地利用异构集群中的计算和存储资源降低硬件投资。
|
TDengine 的设计是基于单个硬件、软件系统不可靠,基于任何单台计算机都无法提供足够计算能力和存储能力处理海量数据的假设进行设计的。因此 TDengine 从研发的第一天起,就按照分布式高可靠架构进行设计,是支持水平扩展的,这样任何单台或多台服务器发生硬件故障或软件错误都不影响系统的可用性和可靠性。同时,通过节点虚拟化并辅以自动化负载均衡技术,TDengine 能最高效率地利用异构集群中的计算和存储资源降低硬件投资。
|
||||||
|
|
||||||
###主要逻辑单元
|
### 主要逻辑单元
|
||||||
TDengine 分布式架构的逻辑结构图如下:
|
TDengine 分布式架构的逻辑结构图如下:
|
||||||
<center> <img src="../assets/structure.png"> </center>
|
<center> <img src="../assets/structure.png"> </center>
|
||||||
|
|
||||||
<center> 图 1 TDengine架构示意图 </center>
|
<center> 图 1 TDengine架构示意图 </center>
|
||||||
一个完整的 TDengine 系统是运行在一到多个物理节点上的,逻辑上,它包含数据节点(dnode)、TDengine客户端(taosc)以及应用(app)。系统中存在一到多个数据节点,这些数据节点组成一个集群(cluster)。应用通过taosc的API与TDengine集群进行互动。下面对每个逻辑单元进行简要介绍。
|
一个完整的 TDengine 系统是运行在一到多个物理节点上的,逻辑上,它包含数据节点(dnode)、TDengine客户端(taosc)以及应用(app)。系统中存在一到多个数据节点,这些数据节点组成一个集群(cluster)。应用通过taosc的API与TDengine集群进行互动。下面对每个逻辑单元进行简要介绍。
|
||||||
|
|
||||||
|
@ -70,11 +71,11 @@ TDengine 分布式架构的逻辑结构图如下:
|
||||||
|
|
||||||
**数据节点(dnode):** dnode 是 TDengine 服务器侧执行代码 taosd 在物理节点上的一个运行实例,一个工作的系统必须有至少一个数据节点。dnode包含零到多个逻辑的虚拟节点(VNODE),零或者至多一个逻辑的管理节点(mnode). dnode在系统中的唯一标识由实例的End Point(EP)决定。EP是dnode所在物理节点的FQDN(Fully Qualified Domain Name)和系统所配置的网络端口号(Port)的组合。通过配置不同的端口,一个物理节点(一台物理机、虚拟机或容器)可以运行多个实例,或有多个数据节点。
|
**数据节点(dnode):** dnode 是 TDengine 服务器侧执行代码 taosd 在物理节点上的一个运行实例,一个工作的系统必须有至少一个数据节点。dnode包含零到多个逻辑的虚拟节点(VNODE),零或者至多一个逻辑的管理节点(mnode). dnode在系统中的唯一标识由实例的End Point(EP)决定。EP是dnode所在物理节点的FQDN(Fully Qualified Domain Name)和系统所配置的网络端口号(Port)的组合。通过配置不同的端口,一个物理节点(一台物理机、虚拟机或容器)可以运行多个实例,或有多个数据节点。
|
||||||
|
|
||||||
**虚拟节点(vnode)**: 为更好的支持数据分片、负载均衡,防止数据过热或倾斜,数据节点被虚拟化成多个虚拟节点(vnode,图中V)。每个 vnode 都是一个相对独立的工作单元,是时序数据存储的基本单元,具有独立的运行线程、内存空间与持久化存储的路径。一个 vnode 包含一定数量的表(数据采集点)。当创建一张新表时,系统会检查是否需要创建新的 vnode。一个数据节点上能创建的 vnode 的数量取决于该数据节点所在物理节点的硬件资源。一个 vnode 只属于一个DB,但一个DB可以有多个 vnode。一个 vnode 除存储的时序数据外,也保存有所包含的表的SCHEMA、标签值等。一个虚拟节点由所属的数据节点的EP,以及所属的Vgroup ID在系统内唯一标识,是由管理节点创建并管理的。
|
**虚拟节点(vnode)**: 为更好的支持数据分片、负载均衡,防止数据过热或倾斜,数据节点被虚拟化成多个虚拟节点(vnode,图中V2, V3, V4等)。每个 vnode 都是一个相对独立的工作单元,是时序数据存储的基本单元,具有独立的运行线程、内存空间与持久化存储的路径。一个 vnode 包含一定数量的表(数据采集点)。当创建一张新表时,系统会检查是否需要创建新的 vnode。一个数据节点上能创建的 vnode 的数量取决于该数据节点所在物理节点的硬件资源。一个 vnode 只属于一个DB,但一个DB可以有多个 vnode。一个 vnode 除存储的时序数据外,也保存有所包含的表的SCHEMA、标签值等。一个虚拟节点由所属的数据节点的EP,以及所属的Vgroup ID在系统内唯一标识,是由管理节点创建并管理的。
|
||||||
|
|
||||||
**管理节点(mnode):** 一个虚拟的逻辑单元,负责所有数据节点运行状态的监控和维护,以及节点之间的负载均衡(图中M)。同时,管理节点也负责元数据(包括用户、数据库、表、静态标签等)的存储和管理,因此也称为 Meta Node。TDengine 集群中可配置多个(最多不超过5个) mnode,它们自动构建成为一个管理节点集群(图中M0, M1, M2)。mnode 间采用 master/slave 的机制进行管理,而且采取强一致方式进行数据同步, 任何数据更新操作只能在 Master 上进行。mnode 集群的创建由系统自动完成,无需人工干预。每个dnode上至多有一个mnode,由所属的数据节点的EP来唯一标识。每个dnode通过内部消息交互自动获取整个集群中所有 mnode 所在的 dnode 的EP。
|
**管理节点(mnode):** 一个虚拟的逻辑单元,负责所有数据节点运行状态的监控和维护,以及节点之间的负载均衡(图中M)。同时,管理节点也负责元数据(包括用户、数据库、表、静态标签等)的存储和管理,因此也称为 Meta Node。TDengine 集群中可配置多个(最多不超过5个) mnode,它们自动构建成为一个虚拟管理节点组(图中M0, M1, M2)。mnode 间采用 master/slave 的机制进行管理,而且采取强一致方式进行数据同步, 任何数据更新操作只能在 Master 上进行。mnode 集群的创建由系统自动完成,无需人工干预。每个dnode上至多有一个mnode,由所属的数据节点的EP来唯一标识。每个dnode通过内部消息交互自动获取整个集群中所有 mnode 所在的 dnode 的EP。
|
||||||
|
|
||||||
**虚拟节点组(VGroup):** 不同数据节点上的 vnode 可以组成一个虚拟节点组(vnode group)来保证系统的高可靠。虚拟节点组内采取master/slave的方式进行管理。写操作只能在 master vnode 上进行,系统采用异步复制的方式将数据同步到 slave vnode,这样确保了一份数据在多个物理节点上有拷贝。一个 vgroup 里虚拟节点个数就是数据的副本数。如果一个DB的副本数为N,系统必须有至少N个数据节点。副本数在创建DB时通过参数 replica 可以指定,缺省为1。使用 TDengine 的多副本特性,可以不再需要昂贵的磁盘阵列等存储设备,获得同样的数据高可靠性。虚拟节点组由管理节点创建、管理,并且由管理节点分配一系统唯一的ID,vnode group ID。如果两个虚拟节点的vnode group ID相同,说明他们属于同一个组,数据互为备份。虚拟节点组里虚拟节点的个数是可以动态改变的,容许只有一个,也就是没有数据复制。Vnode group ID是永远不变的,即使一个虚拟节点组被删除,它的ID也不会被收回重复利用。
|
**虚拟节点组(VGroup):** 不同数据节点上的 vnode 可以组成一个虚拟节点组(vnode group)来保证系统的高可靠。虚拟节点组内采取master/slave的方式进行管理。写操作只能在 master vnode 上进行,系统采用异步复制的方式将数据同步到 slave vnode,这样确保了一份数据在多个物理节点上有拷贝。一个 vgroup 里虚拟节点个数就是数据的副本数。如果一个DB的副本数为N,系统必须有至少N个数据节点。副本数在创建DB时通过参数 replica 可以指定,缺省为1。使用 TDengine 的多副本特性,可以不再需要昂贵的磁盘阵列等存储设备,获得同样的数据高可靠性。虚拟节点组由管理节点创建、管理,并且由管理节点分配一系统唯一的ID,vnode group ID。如果两个虚拟节点的vnode group ID相同,说明他们属于同一个组,数据互为备份。虚拟节点组里虚拟节点的个数是可以动态改变的,容许只有一个,也就是没有数据复制。Vnode group ID是永远不变的,即使一个虚拟节点组被删除,它的ID也不会被收回重复利用。
|
||||||
|
|
||||||
**TAOSC:** taosc是TDengine给应用提供的驱动程序(driver),负责处理应用与集群的接口交互,内嵌于JDBC、ODBC driver中,或者C、Python、Go语言连接库里。应用都是通过taosc,而不是直接连接集群中的数据节点与整个集群进行交互的。这个模块负责获取并缓存元数据;将插入、查询等请求转发到正确的数据节点;在把结果返回给应用时,还需要负责最后一级的聚合、排序、过滤等操作。对于JDBC, ODBC, C/C++接口而言,这个模块是在应用所处的物理节点上运行,但消耗的资源很小。同时,为支持全分布式的RESTful接口,taosc在TDengine集群的每个dnode上都有一运行实例。
|
**TAOSC:** taosc是TDengine给应用提供的驱动程序(driver),负责处理应用与集群的接口交互,内嵌于JDBC、ODBC driver中,或者C、Python、Go语言连接库里。应用都是通过taosc,而不是直接连接集群中的数据节点与整个集群进行交互的。这个模块负责获取并缓存元数据;将插入、查询等请求转发到正确的数据节点;在把结果返回给应用时,还需要负责最后一级的聚合、排序、过滤等操作。对于JDBC, ODBC, C/C++接口而言,这个模块是在应用所处的物理节点上运行,但消耗的资源很小。同时,为支持全分布式的RESTful接口,taosc在TDengine集群的每个dnode上都有一运行实例。
|
||||||
|
|
||||||
|
@ -95,12 +96,10 @@ TDengine 分布式架构的逻辑结构图如下:
|
||||||
|
|
||||||
**重定向**:无论是dnode还是taosc,最先都是要发起与mnode的链接,但mnode是系统自动创建并维护的,因此对于用户来说,并不知道哪个dnode在运行mnode。TDengine只要求向系统中任何一个工作的dnode发起链接即可。因为任何一个正在运行的dnode,都维护有目前运行的mnode EP List。当收到一个来自新启动的dnode或taosc的链接请求,如果自己不是mnode,则将mnode EP List回复给对方,taosc或新启动的dnode收到这个list, 就重新尝试建立链接。当mnode EP List发生改变,通过节点之间的消息交互,各个数据节点就很快获取最新列表,并通知taosc。
|
**重定向**:无论是dnode还是taosc,最先都是要发起与mnode的链接,但mnode是系统自动创建并维护的,因此对于用户来说,并不知道哪个dnode在运行mnode。TDengine只要求向系统中任何一个工作的dnode发起链接即可。因为任何一个正在运行的dnode,都维护有目前运行的mnode EP List。当收到一个来自新启动的dnode或taosc的链接请求,如果自己不是mnode,则将mnode EP List回复给对方,taosc或新启动的dnode收到这个list, 就重新尝试建立链接。当mnode EP List发生改变,通过节点之间的消息交互,各个数据节点就很快获取最新列表,并通知taosc。
|
||||||
|
|
||||||
###一典型的操作流程
|
### 一典型的操作流程
|
||||||
为解释vnode, mnode, taosc和应用之间的关系以及各自扮演的角色,下面对写入数据这个典型操作的流程进行剖析。
|
为解释vnode, mnode, taosc和应用之间的关系以及各自扮演的角色,下面对写入数据这个典型操作的流程进行剖析。
|
||||||
<center> <img src="../assets/message.png"> </center>
|
<center> <img src="../assets/message.png"> </center>
|
||||||
|
|
||||||
<center> 图 2 TDengine典型的操作流程 </center>
|
<center> 图 2 TDengine典型的操作流程 </center>
|
||||||
|
|
||||||
1. 应用通过JDBC、ODBC或其他API接口发起插入数据的请求。
|
1. 应用通过JDBC、ODBC或其他API接口发起插入数据的请求。
|
||||||
2. taosc会检查缓存,看是有保存有该表的meta data。如果有,直接到第4步。如果没有,taosc将向mnode发出get meta-data请求。
|
2. taosc会检查缓存,看是有保存有该表的meta data。如果有,直接到第4步。如果没有,taosc将向mnode发出get meta-data请求。
|
||||||
3. mnode将该表的meta-data返回给taosc。Meta-data包含有该表的schema, 而且还有该表所属的vgroup信息(vnode ID以及所在的dnode的End Point,如果副本数为N,就有N组End Point)。如果taosc迟迟得不到mnode回应,而且存在多个mnode, taosc将向下一个mnode发出请求。
|
3. mnode将该表的meta-data返回给taosc。Meta-data包含有该表的schema, 而且还有该表所属的vgroup信息(vnode ID以及所在的dnode的End Point,如果副本数为N,就有N组End Point)。如果taosc迟迟得不到mnode回应,而且存在多个mnode, taosc将向下一个mnode发出请求。
|
||||||
|
@ -118,7 +117,7 @@ TDengine 分布式架构的逻辑结构图如下:
|
||||||
|
|
||||||
## 存储模型与数据分区、分片
|
## 存储模型与数据分区、分片
|
||||||
|
|
||||||
###存储模型
|
### 存储模型
|
||||||
TDengine存储的数据包括采集的时序数据以及库、表相关的元数据、标签数据等,这些数据具体分为三部分:
|
TDengine存储的数据包括采集的时序数据以及库、表相关的元数据、标签数据等,这些数据具体分为三部分:
|
||||||
|
|
||||||
- 时序数据:存放于vnode里,由data、head和last三个文件组成,数据量大,查询量取决于应用场景。容许乱序写入,但暂时不支持删除和更新操作。通过采用一个采集点一张表的模型,一个时间段的数据是连续存储,对单张表的写入是简单的追加操作,一次读,可以读到多条记录,这样保证对单个采集点的插入和查询操作,性能达到最优。
|
- 时序数据:存放于vnode里,由data、head和last三个文件组成,数据量大,查询量取决于应用场景。容许乱序写入,但暂时不支持删除和更新操作。通过采用一个采集点一张表的模型,一个时间段的数据是连续存储,对单张表的写入是简单的追加操作,一次读,可以读到多条记录,这样保证对单个采集点的插入和查询操作,性能达到最优。
|
||||||
|
@ -130,7 +129,7 @@ TDengine存储的数据包括采集的时序数据以及库、表相关的元数
|
||||||
- 能够极大地降低标签数据存储的冗余度:一般的NoSQL数据库或时序数据库,采用的K-V存储,其中的Key包含时间戳、设备ID、各种标签。每条记录都带有这些重复的内容,浪费存储空间。而且如果应用要在历史数据上增加、修改或删除标签,需要遍历数据,重写一遍,操作成本极其昂贵。
|
- 能够极大地降低标签数据存储的冗余度:一般的NoSQL数据库或时序数据库,采用的K-V存储,其中的Key包含时间戳、设备ID、各种标签。每条记录都带有这些重复的内容,浪费存储空间。而且如果应用要在历史数据上增加、修改或删除标签,需要遍历数据,重写一遍,操作成本极其昂贵。
|
||||||
- 能够实现极为高效的多表之间的聚合查询:做多表之间聚合查询时,先把符合标签过滤条件的表查找出来,然后再查找这些表相应的数据块,这样大幅减少要扫描的数据集,从而大幅提高查询效率。而且标签数据采用全内存的结构进行管理和维护,千万级别规模的标签数据查询可以在毫秒级别返回。
|
- 能够实现极为高效的多表之间的聚合查询:做多表之间聚合查询时,先把符合标签过滤条件的表查找出来,然后再查找这些表相应的数据块,这样大幅减少要扫描的数据集,从而大幅提高查询效率。而且标签数据采用全内存的结构进行管理和维护,千万级别规模的标签数据查询可以在毫秒级别返回。
|
||||||
|
|
||||||
###数据分片
|
### 数据分片
|
||||||
对于海量的数据管理,为实现水平扩展,一般都需要采取分片(Sharding)分区(Partitioning)策略。TDengine是通过vnode来实现数据分片的,通过一个时间段一个数据文件来实现时序数据分区的。
|
对于海量的数据管理,为实现水平扩展,一般都需要采取分片(Sharding)分区(Partitioning)策略。TDengine是通过vnode来实现数据分片的,通过一个时间段一个数据文件来实现时序数据分区的。
|
||||||
|
|
||||||
vnode(虚拟数据节点)负责为采集的时序数据提供写入、查询和计算功能。为便于负载均衡、数据恢复、支持异构环境,TDengine将一个数据节点根据其计算和存储资源切分为多个vnode。这些vnode的管理是TDengine自动完成的,对应用完全透明。
|
vnode(虚拟数据节点)负责为采集的时序数据提供写入、查询和计算功能。为便于负载均衡、数据恢复、支持异构环境,TDengine将一个数据节点根据其计算和存储资源切分为多个vnode。这些vnode的管理是TDengine自动完成的,对应用完全透明。
|
||||||
|
@ -141,12 +140,12 @@ vnode(虚拟数据节点)负责为采集的时序数据提供写入、查询和
|
||||||
|
|
||||||
每张表的meda data(包含schema, 标签等)也存放于vnode里,而不是集中存放于mnode,实际上这是对Meta数据的分片,这样便于高效并行的进行标签过滤操作。
|
每张表的meda data(包含schema, 标签等)也存放于vnode里,而不是集中存放于mnode,实际上这是对Meta数据的分片,这样便于高效并行的进行标签过滤操作。
|
||||||
|
|
||||||
###数据分区
|
### 数据分区
|
||||||
TDengine除vnode分片之外,还按照时间段进行分区。每个数据文件只包含一个时间段的时序数据,时间段的长度由DB的配置参数days决定。这种按时间段分区的方法还便于高效实现数据的保留策略,只要数据文件超过规定的天数(系统配置参数keep),将被自动删除。而且不同的时间段可以存放于不同的路径和存储介质,以便于大数据的冷热管理,实现多级存储。
|
TDengine除vnode分片之外,还对时序数据按照时间段进行分区。每个数据文件只包含一个时间段的时序数据,时间段的长度由DB的配置参数days决定。这种按时间段分区的方法还便于高效实现数据的保留策略,只要数据文件超过规定的天数(系统配置参数keep),将被自动删除。而且不同的时间段可以存放于不同的路径和存储介质,以便于大数据的冷热管理,实现多级存储。
|
||||||
|
|
||||||
总的来说,**TDengine是通过vnode以及时间两个维度,对大数据进行切分**,便于并行高效的管理,实现水平扩展。
|
总的来说,**TDengine是通过vnode以及时间两个维度,对大数据进行切分**,便于并行高效的管理,实现水平扩展。
|
||||||
|
|
||||||
###负载均衡
|
### 负载均衡
|
||||||
每个dnode都定时向 mnode(虚拟管理节点)报告其状态(包括硬盘空间、内存大小、CPU、网络、虚拟节点个数等),因此mnode了解整个集群的状态。基于整体状态,当mnode发现某个dnode负载过重,它会将dnode上的一个或多个vnode挪到其他dnode。在挪动过程中,对外服务继续进行,数据插入、查询和计算操作都不受影响。
|
每个dnode都定时向 mnode(虚拟管理节点)报告其状态(包括硬盘空间、内存大小、CPU、网络、虚拟节点个数等),因此mnode了解整个集群的状态。基于整体状态,当mnode发现某个dnode负载过重,它会将dnode上的一个或多个vnode挪到其他dnode。在挪动过程中,对外服务继续进行,数据插入、查询和计算操作都不受影响。
|
||||||
|
|
||||||
如果mnode一段时间没有收到dnode的状态报告,mnode会认为这个dnode已经离线。如果离线时间超过一定时长(时长由配置参数offlineThreshold决定),该dnode将被mnode强制剔除出集群。该dnode上的vnodes如果副本数大于一,系统将自动在其他dnode上创建新的副本,以保证数据的副本数。如果该dnode上还有mnode, 而且mnode的副本数大于一,系统也将自动在其他dnode上创建新的mnode, 以保证mnode的副本数。
|
如果mnode一段时间没有收到dnode的状态报告,mnode会认为这个dnode已经离线。如果离线时间超过一定时长(时长由配置参数offlineThreshold决定),该dnode将被mnode强制剔除出集群。该dnode上的vnodes如果副本数大于一,系统将自动在其他dnode上创建新的副本,以保证数据的副本数。如果该dnode上还有mnode, 而且mnode的副本数大于一,系统也将自动在其他dnode上创建新的mnode, 以保证mnode的副本数。
|
||||||
|
@ -155,14 +154,13 @@ TDengine除vnode分片之外,还按照时间段进行分区。每个数据文
|
||||||
|
|
||||||
负载均衡过程无需任何人工干预,应用也无需重启,将自动连接新的节点,完全透明。
|
负载均衡过程无需任何人工干预,应用也无需重启,将自动连接新的节点,完全透明。
|
||||||
|
|
||||||
##数据写入与复制流程
|
## 数据写入与复制流程
|
||||||
如果一个数据库有N个副本,那一个虚拟节点组就有N个虚拟节点,但是只有一个是Master,其他都是slave。当应用将新的记录写入系统时,只有Master vnode能接受写的请求。如果slave vnode收到写的请求,系统将通知taosc需要重新定向。
|
如果一个数据库有N个副本,那一个虚拟节点组就有N个虚拟节点,但是只有一个是Master,其他都是slave。当应用将新的记录写入系统时,只有Master vnode能接受写的请求。如果slave vnode收到写的请求,系统将通知taosc需要重新定向。
|
||||||
###Master vnode写入流程
|
### Master vnode写入流程
|
||||||
Master Vnode遵循下面的写入流程:
|
Master Vnode遵循下面的写入流程:
|
||||||
<center> <img src="../assets/write_master.png"> </center>
|
<center> <img src="../assets/write_master.png"> </center>
|
||||||
|
|
||||||
<center> 图 3 TDengine Master写入流程 </center>
|
<center> 图 3 TDengine Master写入流程 </center>
|
||||||
|
|
||||||
1. Master vnode收到应用的数据插入请求,验证OK,进入下一步;
|
1. Master vnode收到应用的数据插入请求,验证OK,进入下一步;
|
||||||
2. 如果系统配置参数walLevel打开(设置为2),vnode将把该请求的原始数据包写入数据库日志文件WAL,以保证TDengine能够在断电等因素导致的服务重启时从数据库日志文件中恢复数据,避免数据的丢失;
|
2. 如果系统配置参数walLevel打开(设置为2),vnode将把该请求的原始数据包写入数据库日志文件WAL,以保证TDengine能够在断电等因素导致的服务重启时从数据库日志文件中恢复数据,避免数据的丢失;
|
||||||
3. 如果有多个副本,vnode将把数据包转发给同一虚拟节点组内slave vnodes, 该转发包带有数据的版本号(version)
|
3. 如果有多个副本,vnode将把数据包转发给同一虚拟节点组内slave vnodes, 该转发包带有数据的版本号(version)
|
||||||
|
@ -175,7 +173,6 @@ Master Vnode遵循下面的写入流程:
|
||||||
<center> <img src="../assets/write_slave.png"> </center>
|
<center> <img src="../assets/write_slave.png"> </center>
|
||||||
|
|
||||||
<center> 图 4 TDengine Slave写入流程 </center>
|
<center> 图 4 TDengine Slave写入流程 </center>
|
||||||
|
|
||||||
1. Slave vnode收到Master vnode转发了的数据插入请求。
|
1. Slave vnode收到Master vnode转发了的数据插入请求。
|
||||||
2. 如果系统配置参数walLevl设置为2,vnode将把该请求的原始数据包写入日志(WAL);
|
2. 如果系统配置参数walLevl设置为2,vnode将把该请求的原始数据包写入日志(WAL);
|
||||||
3. 写入内存,更新内存中的skip list。
|
3. 写入内存,更新内存中的skip list。
|
||||||
|
@ -207,24 +204,24 @@ Vnode会保持一个数据版本号(Version),对内存数据进行持久化存
|
||||||
3. 在线的虚拟节点数过半,而且有虚拟节点是slave的话,该虚拟节点自动成为master
|
3. 在线的虚拟节点数过半,而且有虚拟节点是slave的话,该虚拟节点自动成为master
|
||||||
4. 对于2和3,如果多个虚拟节点满足成为master的要求,那么虚拟节点组的节点列表里,最前面的选为master
|
4. 对于2和3,如果多个虚拟节点满足成为master的要求,那么虚拟节点组的节点列表里,最前面的选为master
|
||||||
|
|
||||||
更多的关于数据复制的流程,请见[TDengine 2.0 数据复制模块设计](https://jira.taosdata.com:18090/pages/viewpage.action?pageId=6266055)。
|
更多的关于数据复制的流程,请见<a href="https://www.taosdata.com/cn/documentation20/replica/">TDengine 2.0数据复制模块设计</a>。
|
||||||
|
|
||||||
###同步复制
|
### 同步复制
|
||||||
对于数据一致性要求更高的场景,异步数据复制无法满足要求,因为有极小的概率丢失数据,因此TDengine提供同步复制的机制供用户选择。在创建数据库时,除指定副本数replica之外,用户还需要指定新的参数quorum。如果quorum大于一,它表示每次Master转发给副本时,需要等待quorum-1个回复确认,才能通知应用,数据在slave已经写入成功。如果在一定的时间内,得不到quorum-1个回复确认,master vnode将返回错误给应用。
|
对于数据一致性要求更高的场景,异步数据复制无法满足要求,因为有极小的概率丢失数据,因此TDengine提供同步复制的机制供用户选择。在创建数据库时,除指定副本数replica之外,用户还需要指定新的参数quorum。如果quorum大于一,它表示每次Master转发给副本时,需要等待quorum-1个回复确认,才能通知应用,数据在slave已经写入成功。如果在一定的时间内,得不到quorum-1个回复确认,master vnode将返回错误给应用。
|
||||||
|
|
||||||
采用同步复制,系统的性能会有所下降,而且latency会增加。因为元数据要强一致,Mnode之间的数据同步就是采用的同步复制。
|
采用同步复制,系统的性能会有所下降,而且latency会增加。因为元数据要强一致,mnode之间的数据同步缺省就是采用的同步复制。
|
||||||
|
|
||||||
注:vnode之间的同步复制仅仅企业版支持
|
注:vnode之间的同步复制仅仅企业版支持
|
||||||
|
|
||||||
##缓存与持久化
|
## 缓存与持久化
|
||||||
###缓存
|
### 缓存
|
||||||
TDengine采用时间驱动缓存管理策略(First-In-First-Out,FIFO),又称为写驱动的缓存管理机制。这种策略有别于读驱动的数据缓存模式(Least-Recent-Used,LRU),直接将最近写入的数据保存在系统的缓存中。当缓存达到临界值的时候,将最早的数据批量写入磁盘。一般意义上来说,对于物联网数据的使用,用户最为关心的是刚产生的数据,即当前状态。TDengine充分利用这一特性,将最近到达的(当前状态)数据保存在缓存中。
|
TDengine采用时间驱动缓存管理策略(First-In-First-Out,FIFO),又称为写驱动的缓存管理机制。这种策略有别于读驱动的数据缓存模式(Least-Recent-Used,LRU),直接将最近写入的数据保存在系统的缓存中。当缓存达到临界值的时候,将最早的数据批量写入磁盘。一般意义上来说,对于物联网数据的使用,用户最为关心的是刚产生的数据,即当前状态。TDengine充分利用这一特性,将最近到达的(当前状态)数据保存在缓存中。
|
||||||
|
|
||||||
TDengine通过查询函数向用户提供毫秒级的数据获取能力。直接将最近到达的数据保存在缓存中,可以更加快速地响应用户针对最近一条或一批数据的查询分析,整体上提供更快的数据库查询响应能力。从这个意义上来说,**可通过设置合适的配置参数将TDengine作为数据缓存来使用,而不需要再部署Redis或其他额外的缓存系统**,可有效地简化系统架构,降低运维的成本。需要注意的是,TDengine重启以后系统的缓存将被清空,之前缓存的数据均会被批量写入磁盘,缓存的数据将不会像专门的Key-value缓存系统再将之前缓存的数据重新加载到缓存中。
|
TDengine通过查询函数向用户提供毫秒级的数据获取能力。直接将最近到达的数据保存在缓存中,可以更加快速地响应用户针对最近一条或一批数据的查询分析,整体上提供更快的数据库查询响应能力。从这个意义上来说,**可通过设置合适的配置参数将TDengine作为数据缓存来使用,而不需要再部署Redis或其他额外的缓存系统**,可有效地简化系统架构,降低运维的成本。需要注意的是,TDengine重启以后系统的缓存将被清空,之前缓存的数据均会被批量写入磁盘,缓存的数据将不会像专门的Key-value缓存系统再将之前缓存的数据重新加载到缓存中。
|
||||||
|
|
||||||
每个vnode有自己独立的内存,而且由多个固定大小的内存块组成,不同vnode之间完全隔离。数据写入时,类似于日志的写法,数据被顺序追加写入内存,但每个vnode维护有自己的skip list,便于迅速查找。当一半以上的内存块写满时,启动落盘操作,而且后续写的操作在新的内存块进行。这样,一个vnode里有一半内存块是保留有最近的数据的,以达到缓存、快速查找的目的。一个vnode的内存块的个数由配置参数blocks决定,内存块的大小由配置参数cache决定。
|
每个vnode有自己独立的内存,而且由多个固定大小的内存块组成,不同vnode之间完全隔离。数据写入时,类似于日志的写法,数据被顺序追加写入内存,但每个vnode维护有自己的skip list,便于迅速查找。当一半以上的内存块写满时,启动落盘操作,而且后续写的操作在新的内存块进行。这样,一个vnode里有一半内存块是保留有最近的数据的,以达到缓存、快速查找的目的。一个vnode的内存块的个数由配置参数blocks决定,内存块的大小由配置参数cache决定。
|
||||||
|
|
||||||
###持久化存储
|
### 持久化存储
|
||||||
TDengine采用数据驱动的方式让缓存中的数据写入硬盘进行持久化存储。当vnode中缓存的数据达到一定规模时,为了不阻塞后续数据的写入,TDengine也会拉起落盘线程将缓存的数据写入持久化存储。TDengine在数据落盘时会打开新的数据库日志文件,在落盘成功后则会删除老的数据库日志文件,避免日志文件无限制的增长。
|
TDengine采用数据驱动的方式让缓存中的数据写入硬盘进行持久化存储。当vnode中缓存的数据达到一定规模时,为了不阻塞后续数据的写入,TDengine也会拉起落盘线程将缓存的数据写入持久化存储。TDengine在数据落盘时会打开新的数据库日志文件,在落盘成功后则会删除老的数据库日志文件,避免日志文件无限制的增长。
|
||||||
|
|
||||||
为充分利用时序数据特点,TDengine将一个vnode保存在持久化存储的数据切分成多个文件,每个文件只保存固定天数的数据,这个天数由系统配置参数days决定。切分成多个文件后,给定查询的起止日期,无需任何索引,就可以立即定位需要打开哪些数据文件,大大加快读取速度。
|
为充分利用时序数据特点,TDengine将一个vnode保存在持久化存储的数据切分成多个文件,每个文件只保存固定天数的数据,这个天数由系统配置参数days决定。切分成多个文件后,给定查询的起止日期,无需任何索引,就可以立即定位需要打开哪些数据文件,大大加快读取速度。
|
||||||
|
@ -239,7 +236,7 @@ TDengine采用数据驱动的方式让缓存中的数据写入硬盘进行持久
|
||||||
|
|
||||||
数据写入磁盘时,根据系统配置参数comp决定是否压缩数据。TDengine提供了三种压缩选项:无压缩、一阶段压缩和两阶段压缩,分别对应comp值为0、1和2的情况。一阶段压缩根据数据的类型进行了相应的压缩,压缩算法包括delta-delta编码、simple 8B方法、zig-zag编码、LZ4等算法。二阶段压缩在一阶段压缩的基础上又用通用压缩算法进行了压缩,压缩率更高。
|
数据写入磁盘时,根据系统配置参数comp决定是否压缩数据。TDengine提供了三种压缩选项:无压缩、一阶段压缩和两阶段压缩,分别对应comp值为0、1和2的情况。一阶段压缩根据数据的类型进行了相应的压缩,压缩算法包括delta-delta编码、simple 8B方法、zig-zag编码、LZ4等算法。二阶段压缩在一阶段压缩的基础上又用通用压缩算法进行了压缩,压缩率更高。
|
||||||
|
|
||||||
###多级存储
|
### 多级存储
|
||||||
在默认配置下,TDengine会将所有数据保存在/var/lib/taos目录下,而且每个vnode的数据文件保存在该目录下的不同目录。为扩大存储空间,尽量减少文件读取的瓶颈,提高数据吞吐率 TDengine可通过配置系统参数dataDir让多个挂载的硬盘被系统同时使用。除此之外,TDengine也提供了数据分级存储的功能,即根据数据文件的新老程度存储在不同的存储介质上。比如最新的数据存储在SSD上,超过一周的数据存储在本地硬盘上,超过4周的数据存储在网络存储设备上,这样来降低存储成本,而又保证高效的访问数据。数据在不同存储介质上的移动是由系统自动完成的,对应用是完全透明的。数据的分级存储也是通过系统参数dataDir来配置。
|
在默认配置下,TDengine会将所有数据保存在/var/lib/taos目录下,而且每个vnode的数据文件保存在该目录下的不同目录。为扩大存储空间,尽量减少文件读取的瓶颈,提高数据吞吐率 TDengine可通过配置系统参数dataDir让多个挂载的硬盘被系统同时使用。除此之外,TDengine也提供了数据分级存储的功能,即根据数据文件的新老程度存储在不同的存储介质上。比如最新的数据存储在SSD上,超过一周的数据存储在本地硬盘上,超过4周的数据存储在网络存储设备上,这样来降低存储成本,而又保证高效的访问数据。数据在不同存储介质上的移动是由系统自动完成的,对应用是完全透明的。数据的分级存储也是通过系统参数dataDir来配置。
|
||||||
|
|
||||||
dataDir的配置格式如下:
|
dataDir的配置格式如下:
|
||||||
|
@ -263,13 +260,40 @@ dataDir /mnt/disk6/taos 2
|
||||||
|
|
||||||
注:多级存储功能仅企业版支持
|
注:多级存储功能仅企业版支持
|
||||||
|
|
||||||
##数据查询
|
## 数据查询
|
||||||
TDengine提供了多种多样针对表和超级表的查询处理功能,除了常规的聚合查询之外,还提供针对时序数据的窗口查询、统计聚合等功能。TDengine的查询处理需要客户端、vnode, mnode节点协同完成。
|
TDengine提供了多种多样针对表和超级表的查询处理功能,除了常规的聚合查询之外,还提供针对时序数据的窗口查询、统计聚合等功能。TDengine的查询处理需要客户端、vnode, mnode节点协同完成。
|
||||||
|
|
||||||
5.1 单表查询
|
### 单表查询
|
||||||
|
SQL语句的解析和校验工作在客户端完成。解析SQL语句并生成抽象语法树(Abstract Syntax Tree, AST),然后对其进行校验和检查。以及向管理节点(mnode)请求查询中指定表的元数据信息(table metadata)。
|
||||||
|
|
||||||
5.4 时间轴聚合、插值
|
根据元数据信息中的End Point信息,将查询请求序列化后发送到该表所在的数据节点(dnode)。dnode接收到查询请求后,识别出该查询请求指向的虚拟节点(vnode),将消息转发到vnode的查询执行队列。vnode的查询执行线程建立基础的查询执行环境,并立即返回该查询请求,同时开始执行该查询。
|
||||||
|
|
||||||
5.2 多表聚合查询
|
客户端在获取查询结果的时候,dnode的查询执行队列中的工作线程会等待vnode执行线程执行完成,才能将查询结果返回到请求的客户端。
|
||||||
|
|
||||||
5.3 预计算
|
### 按时间轴聚合、降采样、插值
|
||||||
|
|
||||||
|
时序数据有别于普通数据的显著特征是每条记录均具有时间戳,因此针对具有时间戳数据在时间轴上进行聚合是不同于普通数据库的重要功能。从这点上来看,与流计算引擎的窗口查询有相似的地方。
|
||||||
|
|
||||||
|
在TDengine中引入关键词interval来进行时间轴上固定长度时间窗口的切分,并按照时间窗口对数据进行聚合,对窗口范围内的数据按需进行聚合。例如:
|
||||||
|
```mysql
|
||||||
|
select count(*) from d1001 interval(1h)
|
||||||
|
```
|
||||||
|
|
||||||
|
针对d1001设备采集的数据,按照1小时的时间窗口返回每小时存储的记录数量。
|
||||||
|
|
||||||
|
在需要连续获得查询结果的应用场景下,如果给定的时间区间存在数据缺失,会导致该区间数据结果也丢失。TDengine提供策略针对时间轴聚合计算的结果进行插值,通过使用关键词Fill就能够对时间轴聚合结果进行插值。例如:
|
||||||
|
```mysql
|
||||||
|
select count(*) from d1001 interval(1h) fill(prev)
|
||||||
|
```
|
||||||
|
|
||||||
|
针对d1001设备采集数据统计每小时记录数,如果某一个小时不存在数据,这返回之前一个小时的统计数据。TDengine提供前向插值(prev)、线性插值(linear)、NULL值填充(NULL)、特定值填充(value)。
|
||||||
|
|
||||||
|
### 多表聚合查询
|
||||||
|
多表聚合查询与单表查询的整体流程相同,但是存在如下的差异:
|
||||||
|
|
||||||
|
- 由于多表可能分布在不同的节点(dnode),因此多表的聚合查询需要首先获得表所在的全部数据节点的信息,并且同时向相关的dnode发出查询请求。
|
||||||
|
- 每个vnode的计算获得的中间结果(partial results)需要进行第二阶段的聚合才能形成最终结果,第二阶段的聚合过程在客户端完成。
|
||||||
|
- 由于表标签信息存储在vnode中,因此针对标签信息的查询也需要vnode完成。客户端将标签的过滤表达式封装在查询请求结构体中发送给vnode,由vnode的查询执行线程从中抽取出标签查询条件,然后执行查询。标签查询与过滤是在针对表的查询之前完成。标签查询完成以后,将符合条件的表纳入到接下来的查询处理流程中。
|
||||||
|
|
||||||
|
### 预计算
|
||||||
|
为有效提升查询处理的性能,针对物联网数据的不可更改的特点,在数据块头部记录该数据块中存储数据的统计信息:包括最大值、最小值、和。我们称之为预计算单元。如果查询处理涉及整个数据块的全部数据,直接使用预计算结果,完全不需要读取数据块的内容。由于预计算数据量远小于磁盘上存储的数据块数据的大小,对于磁盘IO为瓶颈的查询处理,使用预计算结果可以极大地减小读取IO压力,加速查询处理的流程。预计算机制与Postgre SQL的索引BRIN(block range index)有异曲同工之妙。
|
|
@ -0,0 +1,144 @@
|
||||||
|
#TDengine 集群安装、管理
|
||||||
|
|
||||||
|
多个taosd的运行实例可以组成一个集群,以保证TDengine的高可靠运行,并提供水平扩展能力。要了解TDengine 2.0的集群管理,需要对集群的基本概念有所了解,请看TDengine 2.0整体架构一章。
|
||||||
|
|
||||||
|
集群的每个节点是由End Point来唯一标识的,End Point是由FQDN(Fully Qualified Domain Name)外加Port组成,比如 h1.taosdata.com:6030。一般FQDN就是服务器的hostname,可通过Linux命令“hostname"获取。端口是这个节点对外服务的端口号,缺省是6030,但可以通过taos.cfg里配置参数serverPort进行修改。
|
||||||
|
|
||||||
|
TDengine的集群管理极其简单,除添加和删除节点需要人工干预之外,其他全部是自动完成,最大程度的降低了运维的工作量。本章对集群管理的操作做详细的描述。
|
||||||
|
|
||||||
|
##安装、创建第一个节点
|
||||||
|
|
||||||
|
集群是由一个一个dnode组成的,是从一个dnode的创建开始的。创建第一个节点很简单,就按照["立即开始“](https://www.taosdata.com/cn/getting-started/)一章的方法进行安装、启动即可。
|
||||||
|
|
||||||
|
启动后,请执行taos, 启动taos shell,从shell里执行命令"show dnodes;",如下所示:
|
||||||
|
```
|
||||||
|
Welcome to the TDengine shell from Linux, Client Version:2.0.0.0
|
||||||
|
Copyright (c) 2017 by TAOS Data, Inc. All rights reserved.
|
||||||
|
|
||||||
|
taos> show dnodes;
|
||||||
|
id | end_point | vnodes | cores | status | role | create_time |
|
||||||
|
=====================================================================================
|
||||||
|
1 | h1.taos.com:6030 | 0 | 2 | ready | any | 2020-07-31 03:49:29.202 |
|
||||||
|
Query OK, 1 row(s) in set (0.006385s)
|
||||||
|
|
||||||
|
taos>
|
||||||
|
```
|
||||||
|
上述命令里,可以看到这个刚启动的这个节点的End Point是:h1.taos.com:6030
|
||||||
|
|
||||||
|
## 安装、创建后续节点
|
||||||
|
|
||||||
|
将新的节点添加到现有集群,具体有以下几步:
|
||||||
|
|
||||||
|
1. 按照["立即开始“](https://www.taosdata.com/cn/getting-started/)一章的方法进行安装,**但不要启动taosd**
|
||||||
|
|
||||||
|
2. 如果是使用涛思数据的官方安装包进行安装,在安装结束时,会询问集群的End Port, 输入第一个节点的End Point即可。如果是源码安装,请编辑配置文件taos.cfg(缺省是在/etc/taos/目录),增加一行:
|
||||||
|
|
||||||
|
```
|
||||||
|
firstEp h1.taos.com:6030
|
||||||
|
```
|
||||||
|
|
||||||
|
请注意将示例的“h1.taos.com:6030" 替换为你自己第一个节点的End Point
|
||||||
|
|
||||||
|
3. 按照["立即开始“](https://www.taosdata.com/cn/getting-started/)一章的方法启动taosd
|
||||||
|
|
||||||
|
4. 在Linux shell里执行命令"hostname"找出本机的FQDN, 假设为h2.taos.com。如果无法找到,可以查看taosd日志文件taosdlog.0里前面几行日志(一般在/var/log/taos目录),fqdn以及port都会打印出来。
|
||||||
|
|
||||||
|
5. 在第一个节点,使用CLI程序taos, 登录进TDengine系统, 使用命令:
|
||||||
|
|
||||||
|
```
|
||||||
|
CREATE DNODE "h2.taos.com:6030";
|
||||||
|
```
|
||||||
|
|
||||||
|
将新节点的End Point添加进集群的EP列表。**"fqdn:port"需要用双引号引起来**,否则出错。请注意将示例的“h2.taos.com:6030" 替换为你自己第一个节点的End Point
|
||||||
|
|
||||||
|
6. 使用命令
|
||||||
|
|
||||||
|
```
|
||||||
|
SHOW DNODES;
|
||||||
|
```
|
||||||
|
|
||||||
|
查看新节点是否被成功加入。
|
||||||
|
|
||||||
|
按照上述步骤可以源源不断的将新的节点加入到集群。
|
||||||
|
|
||||||
|
**提示:**
|
||||||
|
|
||||||
|
- firstEp, secondEp这两个参数仅仅在该节点第一次加入集群时有作用,加入集群后,该节点会保存最新的mnode的End Point列表,不再依赖这两个参数。
|
||||||
|
- 两个没有配置firstEp, secondEp参数的dnode启动后,会独立运行起来。这个时候,无法将其中一个节点加入到另外一个节点,形成集群。**无法将两个独立的集群合并成为新的集群**。
|
||||||
|
|
||||||
|
##节点管理
|
||||||
|
|
||||||
|
###添加节点
|
||||||
|
执行CLI程序taos, 使用root账号登录进系统, 执行:
|
||||||
|
```
|
||||||
|
CREATE DNODE "fqdn:port";
|
||||||
|
```
|
||||||
|
将新节点的End Point添加进集群的EP列表。**"fqdn:port"需要用双引号引起来**,否则出错。一个节点对外服务的fqdn和port可以通过配置文件taos.cfg进行配置,缺省是自动获取。
|
||||||
|
|
||||||
|
###删除节点
|
||||||
|
执行CLI程序taos, 使用root账号登录进TDengine系统,执行:
|
||||||
|
```
|
||||||
|
DROP DNODE "fqdn:port";
|
||||||
|
```
|
||||||
|
其中fqdn是被删除的节点的FQDN,port是其对外服务器的端口号
|
||||||
|
|
||||||
|
###查看节点
|
||||||
|
执行CLI程序taos,使用root账号登录进TDengine系统,执行:
|
||||||
|
```
|
||||||
|
SHOW DNODES;
|
||||||
|
```
|
||||||
|
它将列出集群中所有的dnode,每个dnode的fqdn:port, 状态(ready, offline等),vnode数目,还未使用的vnode数目等信息。在添加或删除一个节点后,可以使用该命令查看。
|
||||||
|
|
||||||
|
###查看虚拟节点组
|
||||||
|
|
||||||
|
为充分利用多核技术,并提供scalability,数据需要分片处理。因此TDengine会将一个DB的数据切分成多份,存放在多个vnode里。这些vnode可能分布在多个dnode里,这样就实现了水平扩展。一个vnode仅仅属于一个DB,但一个DB可以有多个vnode。vnode的是mnode根据当前系统资源的情况,自动进行分配的,无需任何人工干预。
|
||||||
|
|
||||||
|
执行CLI程序taos,使用root账号登录进TDengine系统,执行:
|
||||||
|
```
|
||||||
|
SHOW VGROUPS;
|
||||||
|
```
|
||||||
|
##高可用性
|
||||||
|
TDengine通过多副本的机制来提供系统的高可用性。副本数是与DB关联的,一个集群里可以有多个DB,根据运营的需求,每个DB可以配置不同的副本数。创建数据库时,通过参数replica 指定副本数(缺省为1)。如果副本数为1,系统的可靠性无法保证,只要数据所在的节点宕机,就将无法提供服务。集群的节点数必须大于等于副本数,否则创建表时将返回错误“more dnodes are needed"。比如下面的命令将创建副本数为3的数据库demo:
|
||||||
|
```
|
||||||
|
CREATE DATABASE demo replica 3;
|
||||||
|
```
|
||||||
|
一个DB里的数据会被切片分到多个vnode group,vnode group里的vnode数目就是DB的副本数,同一个vnode group里各vnode的数据是完全一致的。为保证高可用性,vnode group里的vnode一定要分布在不同的dnode里(实际部署时,需要在不同的物理机上),只要一个vgroup里超过半数的vnode处于工作状态,这个vgroup就能正常的对外服务。
|
||||||
|
|
||||||
|
一个dnode里可能有多个DB的数据,因此一个dnode离线时,可能会影响到多个DB。如果一个vnode group里的一半或一半以上的vnode不工作,那么该vnode group就无法对外服务,无法插入或读取数据,这样会影响到它所属的DB的一部分表的d读写操作。
|
||||||
|
|
||||||
|
因为vnode的引入,无法简单的给出结论:“集群中过半dnode工作,集群就应该工作”。但是对于简单的情形,很好下结论。比如副本数为3,只有三个dnode,那如果仅有一个节点不工作,整个集群还是可以正常工作的,但如果有两个节点不工作,那整个集群就无法正常工作了。
|
||||||
|
|
||||||
|
##Mnode的高可用
|
||||||
|
TDengine集群是由mnode (taosd的一个模块,逻辑节点) 负责管理的,为保证mnode的高可用,可以配置多个mnode副本,副本数由系统配置参数numOfMnodes决定,有效范围为1-3。为保证元数据的强一致性,mnode副本之间是通过同步的方式进行数据复制的。
|
||||||
|
|
||||||
|
一个集群有多个dnode, 但一个dnode至多运行一个mnode实例。多个dnode情况下,哪个dnode可以作为mnode呢?这是完全由系统根据整个系统资源情况,自动指定的。用户可通过CLI程序taos,在TDengine的console里,执行如下命令:
|
||||||
|
```
|
||||||
|
SHOW MNODES;
|
||||||
|
```
|
||||||
|
来查看mnode列表,该列表将列出mnode所处的dnode的End Point和角色(master, slave, unsynced 或offline)。
|
||||||
|
当集群中第一个节点启动时,该节点一定会运行一个mnode实例,否则该dnode无法正常工作,因为一个系统是必须有至少一个mnode的。如果numOfMnodes配置为2,启动第二个dnode时,该dnode也将运行一个mnode实例。
|
||||||
|
|
||||||
|
为保证mnode服务的高可用性,numOfMnodes必须设置为2或更大。因为mnode保存的元数据必须是强一致的,如果numOfMnodes大于2,复制参数quorum自动设为2,也就是说,至少要保证有两个副本写入数据成功,才通知客户端应用写入成功。
|
||||||
|
|
||||||
|
##负载均衡
|
||||||
|
|
||||||
|
有三种情况,将触发负载均衡,而且都无需人工干预。
|
||||||
|
|
||||||
|
- 当一个新节点添加进集群时,系统将自动触发负载均衡,一些节点上的数据将被自动转移到新节点上,无需任何人工干预。
|
||||||
|
- 当一个节点从集群中移除时,系统将自动把该节点上的数据转移到其他节点,无需任何人工干预。
|
||||||
|
- 如果一个节点过热(数据量过大),系统将自动进行负载均衡,将该节点的一些vnode自动挪到其他节点。
|
||||||
|
|
||||||
|
当上述三种情况发生时,系统将启动一各个节点的负载计算,从而决定如何挪动。
|
||||||
|
|
||||||
|
##节点离线处理
|
||||||
|
如果一个节点离线,TDengine集群将自动检测到。有如下两种情况:
|
||||||
|
- 改节点离线超过一定时间(taos.cfg里配置参数offlineThreshold控制时长),系统将自动把该节点删除,产生系统报警信息,触发负载均衡流程。如果该被删除的节点重现上线时,它将无法加入集群,需要系统管理员重新将其添加进集群才会开始工作。
|
||||||
|
- 离线后,在offlineThreshold的时长内重新上线,系统将自动启动数据恢复流程,等数据完全恢复后,该节点将开始正常工作。
|
||||||
|
|
||||||
|
**注意:**如果一个虚拟节点组(包括mnode组)里每个节点都处于离线或unsynced状态,必须等该虚拟节点组里的所有节点都上线、都能交换状态信息后,才能选出Master,该虚拟节点组才能对外提供服务。比如整个集群有3个节点,副本数为3,如果3个节点都宕机,然后2个节点重启,是无法工作的,只有等3个节点都重启成功,才能对外服务。
|
||||||
|
|
||||||
|
##Arbitrator的使用
|
||||||
|
|
||||||
|
如果副本数为偶数,当一个vnode group里一半或超过一半的vnode不工作时,是无法从中选出master的。同理,一半或超过一半的mnode不工作时,是无法选出mnode的master的,因为存在“split brain”问题。为解决这个问题,TDengine引入了arbitrator的概念。Arbitrator模拟一个vnode或mnode在工作,但只简单的负责网络连接,不处理任何数据插入或访问。只要包含arbitrator在内,超过半数的vnode或mnode工作,那么该vnode group或mnode组就可以正常的提供数据插入或查询服务。比如对于副本数为2的情形,如果一个节点A离线,但另外一个节点B正常,而且能连接到arbitrator, 那么节点B就能正常工作。
|
||||||
|
|
||||||
|
TDengine安装包里带有一个执行程序tarbitrator, 找任何一台Linux服务器运行它即可。该程序对系统资源几乎没有要求,只需要保证有网络连接即可。该应用的命令行参数`-p`可以指定其对外服务的端口号,缺省是6030。配置每个taosd实例时,可以在配置文件taos.cfg里将参数arbitrator设置为arbitrator的End Point。如果该参数配置了,当副本数为偶数数,系统将自动连接配置的arbitrator。
|
|
@ -1,45 +1,80 @@
|
||||||
#集群管理
|
#集群安装、管理
|
||||||
|
|
||||||
多个taosd的运行实例可以组成一个集群,以保证TDengine的高可靠运行,并提供水平扩展能力。要了解TDengine 2.0的集群管理,需要对集群的基本概念有所了解,请看TDengine 2.0整体架构一章。
|
多个taosd的运行实例可以组成一个集群,以保证TDengine的高可靠运行,并提供水平扩展能力。要了解TDengine 2.0的集群管理,需要对集群的基本概念有所了解,请看TDengine 2.0整体架构一章。
|
||||||
|
|
||||||
|
集群的每个节点是由End Point来唯一标识的,End Point是由FQDN(Fully Qualified Domain Name)外加Port组成,比如 h1.taosdata.com:6030。一般FQDN就是服务器的hostname,可通过Linux命令“hostname"获取。端口是这个节点对外服务的端口号,缺省是6030,但可以通过taos.cfg里配置参数serverPort进行修改。
|
||||||
|
|
||||||
TDengine的集群管理极其简单,除添加和删除节点需要人工干预之外,其他全部是自动完成,最大程度的降低了运维的工作量。本章对集群管理的操作做详细的描述。
|
TDengine的集群管理极其简单,除添加和删除节点需要人工干预之外,其他全部是自动完成,最大程度的降低了运维的工作量。本章对集群管理的操作做详细的描述。
|
||||||
|
|
||||||
##创建第一个节点
|
##安装、创建第一个节点
|
||||||
集群是由一个一个dnode组成的,是从一个dnode的创建开始的。创建第一个节点很简单,确保系统配置文件taos.cfg里的参数first与second没有进行设置后,简单的运行taosd即可。配置文件taos.cfg里参数first与second缺省都是设置为空,因此无需特殊操作。如果为了统一配置,可以将参数first配置为第一个dnode的End Point。
|
|
||||||
|
|
||||||
这个节点创建后,它是集群中的第一个节点。如果该节点收到一个新节点加入集群的请求,它将检查这个新节点的End Point是否在集群的EP列表中,如果在,就容许其加入。如果不在列表中,就拒绝其加入。
|
集群是由一个一个dnode组成的,是从一个dnode的创建开始的。创建第一个节点很简单,就按照["立即开始“](https://www.taosdata.com/cn/getting-started/)一章的方法进行安装、启动即可。
|
||||||
|
|
||||||
##节点管理
|
启动后,请执行taos, 启动taos shell,从shell里执行命令"show dnodes;",如下所示:
|
||||||
###添加节点
|
|
||||||
具体有以下几个步骤:
|
|
||||||
1. 修改新节点配置文件taos.cfg, 将其中参数first与second设置为现有集群中运行节点的End Point, 然后启动taosd。second可以不用设置,目的是为集群规模较大时,first无法访问时,该节点将尝试访问second节点。
|
|
||||||
2. 使用CLI程序taos, 登录进系统, 使用命令:
|
|
||||||
```
|
```
|
||||||
CREATE DNODE "fqdn:port";
|
Welcome to the TDengine shell from Linux, Client Version:2.0.0.0
|
||||||
```
|
Copyright (c) 2017 by TAOS Data, Inc. All rights reserved.
|
||||||
将新节点的End Point添加进集群的EP列表。**"fqdn:port"需要用双引号引起来**,否则出错。一个节点对外服务的fqdn和port可以通过配置文件taos.cfg进行配置。
|
|
||||||
3. 使用命令
|
|
||||||
```
|
|
||||||
SHOW DNODES
|
|
||||||
```
|
|
||||||
查看新节点是否被成功加入。`
|
|
||||||
|
|
||||||
**示例:**假设有两个节点,其EP分别为 host1:6030, host2:6050. 可以按照以下步骤创建集群
|
taos> show dnodes;
|
||||||
|
id | end_point | vnodes | cores | status | role | create_time |
|
||||||
|
=====================================================================================
|
||||||
|
1 | h1.taos.com:6030 | 0 | 2 | ready | any | 2020-07-31 03:49:29.202 |
|
||||||
|
Query OK, 1 row(s) in set (0.006385s)
|
||||||
|
|
||||||
1. 在host1, 启动taosd;
|
taos>
|
||||||
2. 修改host2:6050的配置文件taos.cfg, 将参数first设置为host1:6030,然后启动host2:6050这个taosd实例;
|
|
||||||
3. 执行“taos -h host1" 链接到host1, 在TDengine console里执行命令:
|
|
||||||
```
|
```
|
||||||
CREATE DNODE "host2:6050";
|
上述命令里,可以看到这个刚启动的这个节点的End Point是:h1.taos.com:6030
|
||||||
|
|
||||||
|
## 安装、创建后续节点
|
||||||
|
|
||||||
|
将新的节点添加到现有集群,具体有以下几步:
|
||||||
|
|
||||||
|
1. 按照["立即开始“](https://www.taosdata.com/cn/getting-started/)一章的方法进行安装,但不要启动taosd
|
||||||
|
|
||||||
|
2. 如果是使用涛思数据的官方安装包进行安装,在安装结束时,会询问集群的End Port, 输入第一个节点的End Point即可。如果是源码安装,请编辑配置文件taos.cfg(缺省是在/etc/taos/目录),增加一行:
|
||||||
|
|
||||||
```
|
```
|
||||||
4. 在TDengine console里执行`"show dnodes"`查看节点是否加入成功.
|
firstEp h1.taos.com:6030
|
||||||
5. 按照上述第2到第5步,逐步把其他节点添加到集群中。
|
```
|
||||||
|
|
||||||
|
请注意将示例的“h1.taos.com:6030" 替换为你自己第一个节点的End Point
|
||||||
|
|
||||||
|
3. 按照["立即开始“](https://www.taosdata.com/cn/getting-started/)一章的方法启动taosd
|
||||||
|
|
||||||
|
4. 在Linux shell里执行命令"hostname"找出本机的FQDN, 假设为h2.taos.com。如果无法找到,可以查看taosd日志文件taosdlog.0里前面几行日志(一般在/var/log/taos目录),fqdn以及port都会打印出来。
|
||||||
|
|
||||||
|
5. 在第一个节点,使用CLI程序taos, 登录进TDengine系统, 使用命令:
|
||||||
|
|
||||||
|
```
|
||||||
|
CREATE DNODE "h2.taos.com:6030";
|
||||||
|
```
|
||||||
|
|
||||||
|
将新节点的End Point添加进集群的EP列表。**"fqdn:port"需要用双引号引起来**,否则出错。请注意将示例的“h2.taos.com:6030" 替换为你自己第一个节点的End Point
|
||||||
|
|
||||||
|
6. 使用命令
|
||||||
|
|
||||||
|
```
|
||||||
|
SHOW DNODES;
|
||||||
|
```
|
||||||
|
|
||||||
|
查看新节点是否被成功加入。
|
||||||
|
|
||||||
|
按照上述步骤可以源源不断的将新的节点加入到集群。
|
||||||
|
|
||||||
**提示:**
|
**提示:**
|
||||||
|
|
||||||
- first, second这两个参数仅仅在该节点第一次加入集群时有作用,加入集群后,该节点会保存最新的mnode的End Point列表,不再依赖这两个参数。
|
- firstEp, secondEp这两个参数仅仅在该节点第一次加入集群时有作用,加入集群后,该节点会保存最新的mnode的End Point列表,不再依赖这两个参数。
|
||||||
- 两个没有配置first, second参数的dnode启动后,会独立运行起来。这个时候,无法将其中一个节点加入到另外一个节点,形成集群。**无法将两个独立的集群合并成为新的集群**。
|
- 两个没有配置first, second参数的dnode启动后,会独立运行起来。这个时候,无法将其中一个节点加入到另外一个节点,形成集群。**无法将两个独立的集群合并成为新的集群**。
|
||||||
|
|
||||||
|
##节点管理
|
||||||
|
|
||||||
|
###添加节点
|
||||||
|
执行CLI程序taos, 使用root账号登录进系统, 执行:
|
||||||
|
```
|
||||||
|
CREATE DNODE "fqdn:port";
|
||||||
|
```
|
||||||
|
将新节点的End Point添加进集群的EP列表。**"fqdn:port"需要用双引号引起来**,否则出错。一个节点对外服务的fqdn和port可以通过配置文件taos.cfg进行配置,缺省是自动获取。
|
||||||
|
|
||||||
###删除节点
|
###删除节点
|
||||||
执行CLI程序taos, 使用root账号登录进TDengine系统,执行:
|
执行CLI程序taos, 使用root账号登录进TDengine系统,执行:
|
||||||
```
|
```
|
|
@ -45,11 +45,11 @@ C/C++的API类似于MySQL的C API。应用程序使用时,需要包含TDengine
|
||||||
|
|
||||||
创建数据库连接,初始化连接上下文。其中需要用户提供的参数包含:
|
创建数据库连接,初始化连接上下文。其中需要用户提供的参数包含:
|
||||||
|
|
||||||
* ip:TDengine管理主节点的IP地址
|
- ip:TDengine管理主节点的IP地址
|
||||||
* user:用户名
|
- user:用户名
|
||||||
* pass:密码
|
- pass:密码
|
||||||
* db:数据库名字,如果用户没有提供,也可以正常连接,用户可以通过该连接创建新的数据库,如果用户提供了数据库名字,则说明该数据库用户已经创建好,缺省使用该数据库
|
- db:数据库名字,如果用户没有提供,也可以正常连接,用户可以通过该连接创建新的数据库,如果用户提供了数据库名字,则说明该数据库用户已经创建好,缺省使用该数据库
|
||||||
* port:端口号
|
- port:端口号
|
||||||
|
|
||||||
返回值为空表示失败。应用程序需要保存返回的参数,以便后续API调用。
|
返回值为空表示失败。应用程序需要保存返回的参数,以便后续API调用。
|
||||||
|
|
|
@ -5,19 +5,19 @@
|
||||||
2.0版本在之前版本的基础上,进行了完全的重构,配置文件和数据文件是不兼容的。在升级之前务必进行如下操作:
|
2.0版本在之前版本的基础上,进行了完全的重构,配置文件和数据文件是不兼容的。在升级之前务必进行如下操作:
|
||||||
|
|
||||||
1. 删除配置文件,执行 <code> sudo rm -rf /etc/taos/taos.cfg </code>
|
1. 删除配置文件,执行 <code> sudo rm -rf /etc/taos/taos.cfg </code>
|
||||||
|
|
||||||
2. 删除日志文件,执行 <code> sudo rm -rf /var/log/taos/ </code>
|
2. 删除日志文件,执行 <code> sudo rm -rf /var/log/taos/ </code>
|
||||||
|
|
||||||
3. 确保数据已经不再需要的前提下,删除数据文件,执行 <code> sudo rm -rf /var/lib/taos/ </code>
|
3. 确保数据已经不再需要的前提下,删除数据文件,执行 <code> sudo rm -rf /var/lib/taos/ </code>
|
||||||
|
|
||||||
4. 安装最新稳定版本的TDengine
|
4. 安装最新稳定版本的TDengine
|
||||||
5. 如果数据需要迁移数据或者数据文件损坏,请联系涛思数据官方技术支持团队,进行协助解决
|
5. 如果数据需要迁移数据或者数据文件损坏,请联系涛思数据官方技术支持团队,进行协助解决
|
||||||
|
|
||||||
#### 2. <a href='blog/2019/12/03/jdbcdriver找不到动态链接库/'>Windows平台下JDBCDriver找不到动态链接库</a>
|
#### 2. Windows平台下JDBCDriver找不到动态链接库,怎么办?
|
||||||
|
请看为此问题撰写的<a href='blog/2019/12/03/jdbcdriver找不到动态链接库/'>技术博客 </a>
|
||||||
|
|
||||||
#### 3. <a href='blog/2019/12/03/创建数据表时提示more-dnodes-are-needed/'>创建数据表时提示more dnodes are needed</a>
|
#### 3. 创建数据表时提示more dnodes are needed
|
||||||
|
请看为此问题撰写的<a href='blog/2019/12/03/创建数据表时提示more-dnodes-are-needed/'>技术博客</a>
|
||||||
|
|
||||||
#### 4. <a href='blog/2019/12/06/tdengine-crash时生成core文件的方法/'>TDengine crash时生成core文件的方法</a>
|
#### 4. 如何让TDengine crash时生成core文件?
|
||||||
|
请看为此问题撰写的<a href='blog/2019/12/06/tdengine-crash时生成core文件的方法/'>技术博客</a>
|
||||||
|
|
||||||
#### 5. 遇到错误"failed to connect to server", 我怎么办?
|
#### 5. 遇到错误"failed to connect to server", 我怎么办?
|
||||||
|
|
||||||
|
@ -68,7 +68,8 @@ windows下插入nchar类的数据中如果有中文,请先确认系统的地
|
||||||
|
|
||||||
Connection = DriverManager.getConnection(url, properties);
|
Connection = DriverManager.getConnection(url, properties);
|
||||||
|
|
||||||
#### 12. <a href='blog/2020/01/06/tdengine-go-windows驱动的编译/'>TDengine GO windows驱动的编译</a>
|
#### 12.TDengine GO windows驱动的如何编译?
|
||||||
|
请看为此问题撰写的<a href='blog/2020/01/06/tdengine-go-windows驱动的编译/'>技术博客
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,14 +18,17 @@ TDengine也支持一次向多个表写入数据,比如下面这条命令就向
|
||||||
INSERT INTO d1001 VALUES (1538548685000, 10.3, 219, 0.31) (1538548695000, 12.6, 218, 0.33) d1002 VALUES (1538548696800, 12.3, 221, 0.31);
|
INSERT INTO d1001 VALUES (1538548685000, 10.3, 219, 0.31) (1538548695000, 12.6, 218, 0.33) d1002 VALUES (1538548696800, 12.3, 221, 0.31);
|
||||||
```
|
```
|
||||||
|
|
||||||
**Tips:** 要提高写入效率,需要批量写入。一批写入的记录条数越多,插入效率就越高。但一条记录不能超过16K,一条SQL语句总长度不能超过64K(可通过参数maxSQLLength配置)。
|
详细的SQL INSERT语法规则请见<a href="https://www.taosdata.com/cn/documentation20/taos-sql/">TAOS SQL </a>
|
||||||
|
|
||||||
详细的SQL INSERT语法规则请见TAOS SQL
|
**Tips:**
|
||||||
|
|
||||||
##Prometheus直接写入
|
- 要提高写入效率,需要批量写入。一批写入的记录条数越多,插入效率就越高。但一条记录不能超过16K,一条SQL语句总长度不能超过64K(可通过参数maxSQLLength配置)。
|
||||||
|
- TDengine支持多线程同时写入,要进一步提高写入速度,一个客户端需要打开20个以上的线程同时写。但线程数达到一定数量后,无法再提高,甚至还会下降,因为线程切频繁切换,带来额外开销。
|
||||||
|
|
||||||
|
## Prometheus直接写入
|
||||||
[Prometheus](https://www.prometheus.io/)作为Cloud Native Computing Fundation毕业的项目,在性能监控以及K8S性能监控领域有着非常广泛的应用。TDengine提供一个小工具[Bailongma](https://github.com/taosdata/Bailongma),只需在Prometheus做简单配置,无需任何代码,就可将Prometheus采集的数据直接写入TDengine,并按规则在TDengine自动创建库和相关表项。博文[用Docker容器快速搭建一个Devops监控Demo](https://www.taosdata.com/blog/2020/02/03/1189.html)即是采用bailongma将Prometheus和Telegraf的数据写入TDengine中的示例,可以参考。
|
[Prometheus](https://www.prometheus.io/)作为Cloud Native Computing Fundation毕业的项目,在性能监控以及K8S性能监控领域有着非常广泛的应用。TDengine提供一个小工具[Bailongma](https://github.com/taosdata/Bailongma),只需在Prometheus做简单配置,无需任何代码,就可将Prometheus采集的数据直接写入TDengine,并按规则在TDengine自动创建库和相关表项。博文[用Docker容器快速搭建一个Devops监控Demo](https://www.taosdata.com/blog/2020/02/03/1189.html)即是采用bailongma将Prometheus和Telegraf的数据写入TDengine中的示例,可以参考。
|
||||||
|
|
||||||
###从源代码编译blm_prometheus
|
### 从源代码编译blm_prometheus
|
||||||
用户需要从github下载[Bailongma](https://github.com/taosdata/Bailongma)的源码,使用Golang语言编译器编译生成可执行文件。在开始编译前,需要准备好以下条件:
|
用户需要从github下载[Bailongma](https://github.com/taosdata/Bailongma)的源码,使用Golang语言编译器编译生成可执行文件。在开始编译前,需要准备好以下条件:
|
||||||
- Linux操作系统的服务器
|
- Linux操作系统的服务器
|
||||||
- 安装好Golang, 1.10版本以上
|
- 安装好Golang, 1.10版本以上
|
||||||
|
@ -39,7 +42,7 @@ go build
|
||||||
|
|
||||||
一切正常的情况下,就会在对应的目录下生成一个blm_prometheus的可执行程序。
|
一切正常的情况下,就会在对应的目录下生成一个blm_prometheus的可执行程序。
|
||||||
|
|
||||||
###安装Prometheus
|
### 安装Prometheus
|
||||||
通过Prometheus的官网下载安装。[下载地址](https://prometheus.io/download/)
|
通过Prometheus的官网下载安装。[下载地址](https://prometheus.io/download/)
|
||||||
|
|
||||||
### 配置Prometheus
|
### 配置Prometheus
|
||||||
|
@ -70,7 +73,7 @@ blm_prometheus会将收到的prometheus的数据拼装成TDengine的写入请求
|
||||||
blm_prometheus对prometheus提供服务的端口号。
|
blm_prometheus对prometheus提供服务的端口号。
|
||||||
```
|
```
|
||||||
|
|
||||||
###启动示例
|
### 启动示例
|
||||||
|
|
||||||
通过以下命令启动一个blm_prometheus的API服务
|
通过以下命令启动一个blm_prometheus的API服务
|
||||||
```
|
```
|
||||||
|
@ -82,7 +85,7 @@ remote_write:
|
||||||
- url: "http://10.1.2.3:8088/receive"
|
- url: "http://10.1.2.3:8088/receive"
|
||||||
```
|
```
|
||||||
|
|
||||||
###查询prometheus写入数据
|
### 查询prometheus写入数据
|
||||||
prometheus产生的数据格式如下:
|
prometheus产生的数据格式如下:
|
||||||
```
|
```
|
||||||
Timestamp: 1576466279341,
|
Timestamp: 1576466279341,
|
||||||
|
@ -104,10 +107,10 @@ use prometheus;
|
||||||
select * from apiserver_request_latencies_bucket;
|
select * from apiserver_request_latencies_bucket;
|
||||||
```
|
```
|
||||||
|
|
||||||
##Telegraf直接写入
|
## Telegraf直接写入
|
||||||
[Telegraf](https://www.influxdata.com/time-series-platform/telegraf/)是一流行的IT运维数据采集开源工具,TDengine提供一个小工具[Bailongma](https://github.com/taosdata/Bailongma),只需在Telegraf做简单配置,无需任何代码,就可将Telegraf采集的数据直接写入TDengine,并按规则在TDengine自动创建库和相关表项。博文[用Docker容器快速搭建一个Devops监控Demo](https://www.taosdata.com/blog/2020/02/03/1189.html)即是采用bailongma将Prometheus和Telegraf的数据写入TDengine中的示例,可以参考。
|
[Telegraf](https://www.influxdata.com/time-series-platform/telegraf/)是一流行的IT运维数据采集开源工具,TDengine提供一个小工具[Bailongma](https://github.com/taosdata/Bailongma),只需在Telegraf做简单配置,无需任何代码,就可将Telegraf采集的数据直接写入TDengine,并按规则在TDengine自动创建库和相关表项。博文[用Docker容器快速搭建一个Devops监控Demo](https://www.taosdata.com/blog/2020/02/03/1189.html)即是采用bailongma将Prometheus和Telegraf的数据写入TDengine中的示例,可以参考。
|
||||||
|
|
||||||
###从源代码编译blm_telegraf
|
### 从源代码编译blm_telegraf
|
||||||
用户需要从github下载[Bailongma](https://github.com/taosdata/Bailongma)的源码,使用Golang语言编译器编译生成可执行文件。在开始编译前,需要准备好以下条件:
|
用户需要从github下载[Bailongma](https://github.com/taosdata/Bailongma)的源码,使用Golang语言编译器编译生成可执行文件。在开始编译前,需要准备好以下条件:
|
||||||
|
|
||||||
- Linux操作系统的服务器
|
- Linux操作系统的服务器
|
||||||
|
@ -123,7 +126,7 @@ go build
|
||||||
|
|
||||||
一切正常的情况下,就会在对应的目录下生成一个blm_telegraf的可执行程序。
|
一切正常的情况下,就会在对应的目录下生成一个blm_telegraf的可执行程序。
|
||||||
|
|
||||||
###安装Telegraf
|
### 安装Telegraf
|
||||||
目前TDengine支持Telegraf 1.7.4以上的版本。用户可以根据当前的操作系统,到Telegraf官网下载安装包,并执行安装。下载地址如下:https://portal.influxdata.com/downloads
|
目前TDengine支持Telegraf 1.7.4以上的版本。用户可以根据当前的操作系统,到Telegraf官网下载安装包,并执行安装。下载地址如下:https://portal.influxdata.com/downloads
|
||||||
|
|
||||||
### 配置Telegraf
|
### 配置Telegraf
|
||||||
|
@ -142,7 +145,7 @@ go build
|
||||||
|
|
||||||
关于如何使用Telegraf采集数据以及更多有关使用Telegraf的信息,请参考Telegraf官方的[文档](https://docs.influxdata.com/telegraf/v1.11/)。
|
关于如何使用Telegraf采集数据以及更多有关使用Telegraf的信息,请参考Telegraf官方的[文档](https://docs.influxdata.com/telegraf/v1.11/)。
|
||||||
|
|
||||||
###启动blm_telegraf程序
|
### 启动blm_telegraf程序
|
||||||
blm_telegraf程序有以下选项,在启动blm_telegraf程序时可以通过设定这些选项来设定blm_telegraf的配置。
|
blm_telegraf程序有以下选项,在启动blm_telegraf程序时可以通过设定这些选项来设定blm_telegraf的配置。
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
@ -165,7 +168,7 @@ blm_telegraf会将收到的telegraf的数据拼装成TDengine的写入请求,
|
||||||
blm_telegraf对telegraf提供服务的端口号。
|
blm_telegraf对telegraf提供服务的端口号。
|
||||||
```
|
```
|
||||||
|
|
||||||
###启动示例
|
### 启动示例
|
||||||
通过以下命令启动一个blm_telegraf的API服务
|
通过以下命令启动一个blm_telegraf的API服务
|
||||||
```
|
```
|
||||||
./blm_telegraf -host 127.0.0.1 -port 8089
|
./blm_telegraf -host 127.0.0.1 -port 8089
|
||||||
|
@ -177,7 +180,7 @@ blm_telegraf对telegraf提供服务的端口号。
|
||||||
url = "http://10.1.2.3:8089/telegraf"
|
url = "http://10.1.2.3:8089/telegraf"
|
||||||
```
|
```
|
||||||
|
|
||||||
###查询telegraf写入数据
|
### 查询telegraf写入数据
|
||||||
telegraf产生的数据格式如下:
|
telegraf产生的数据格式如下:
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
|
@ -210,7 +213,7 @@ use telegraf;
|
||||||
select * from cpu;
|
select * from cpu;
|
||||||
```
|
```
|
||||||
|
|
||||||
## EMQ X MQTT Broker直接写入
|
## EMQ X Broker直接写入
|
||||||
|
|
||||||
MQTT是一流行的物联网数据传输协议,[EMQ](https://github.com/emqx/emqx)是一开源的MQTT Broker软件,无需任何代码,只需要在EMQ Dashboard里使用“规则”做简单配置,即可将MQTT的数据直接写入TDengine。EMQ X 支持通过 发送到 Web 服务 的方式保存数据到 TDEngine,也在企业版上提供原生的 TDEngine 驱动实现直接保存。详细使用方法请参考 [EMQ 官方文档](https://docs.emqx.io/broker/latest/cn/rule/rule-example.html#%E4%BF%9D%E5%AD%98%E6%95%B0%E6%8D%AE%E5%88%B0-tdengine)。
|
MQTT是一流行的物联网数据传输协议,[EMQ](https://github.com/emqx/emqx)是一开源的MQTT Broker软件,无需任何代码,只需要在EMQ Dashboard里使用“规则”做简单配置,即可将MQTT的数据直接写入TDengine。EMQ X 支持通过 发送到 Web 服务 的方式保存数据到 TDEngine,也在企业版上提供原生的 TDEngine 驱动实现直接保存。详细使用方法请参考 [EMQ 官方文档](https://docs.emqx.io/broker/latest/cn/rule/rule-example.html#%E4%BF%9D%E5%AD%98%E6%95%B0%E6%8D%AE%E5%88%B0-tdengine)。
|
||||||
|
|
|
@ -0,0 +1,253 @@
|
||||||
|
# TDengine 2.0 数据复制模块设计
|
||||||
|
|
||||||
|
## 数据复制概述
|
||||||
|
|
||||||
|
数据复制(Replication)是指同一份数据在多个物理地点保存。它的目的是防止数据丢失,提高系统的高可用性(High Availability),而且通过应用访问多个副本,提升数据查询性能。
|
||||||
|
|
||||||
|
在高可靠的大数据系统里,数据复制是必不可少的一大功能。数据复制又分为实时复制与非实时复制。实时复制是指任何数据的更新(包括数据的增加、删除、修改)操作,会被实时的复制到所有副本,这样任何一台机器宕机或网络出现故障,整个系统还能提供最新的数据,保证系统的正常工作。而非实时复制,是指传统的数据备份操作,按照固定的时间周期,将一份数据全量或增量复制到其他地方。如果主节点宕机,副本是很大可能没有最新数据,因此在有些场景是无法满足要求的。
|
||||||
|
|
||||||
|
TDengine面向的是物联网场景,需要支持数据的实时复制,来最大程度保证系统的可靠性。实时复制有两种方式,一种是异步复制,一种是同步复制。异步复制(Asynchronous Replication)是指数据由Master转发给Slave后,Master并不需要等待Slave回复确认,这种方式效率高,但有极小的概率会丢失数据。同步复制是指Master将数据转发给Slave后,需要等待Slave的回复确认,才会通知应用写入成功,这种方式效率偏低,但能保证数据绝不丢失。
|
||||||
|
|
||||||
|
数据复制是与数据存储(写入、读取)密切相关的,但两者又是相对独立,可以完全脱耦的。在TDengine系统中,有两种不同类型的数据,一种是时序数据,由TSDB模块负责;一种是元数据(Meta Data), 由MNODE负责。这两种性质不同的数据都需要同步功能。数据复制模块通过不同的实例启动配置参数,为这两种类型数据都提供同步功能。
|
||||||
|
|
||||||
|
在阅读本文之前,请先阅读《<a href="../architecture/ ">TDengine 2.0 整体架构</a >》,了解TDengine的集群设计和基本概念
|
||||||
|
|
||||||
|
特别注明:本文中提到数据更新操作包括数据的增加、删除与修改。
|
||||||
|
|
||||||
|
## 基本概念和定义
|
||||||
|
|
||||||
|
TDengine里存在vnode, mnode, vnode用来存储时序数据,mnode用来存储元数据。但从同步数据复制的模块来看,两者没有本质的区别,因此本文里的虚拟节点不仅包括vnode, 也包括mnode, vgoup也指mnode group, 除非特别注明。
|
||||||
|
|
||||||
|
**版本(version)**:
|
||||||
|
|
||||||
|
一个虚拟节点组里多个虚拟节点互为备份,来保证数据的有效与可靠,是依靠虚拟节点组的数据版本号来维持的。TDengine2.0设计里,对于版本的定义如下:客户端发起增加、删除、修改的流程,无论是一条记录还是多条,只要是在一个请求里,这个数据更新请求被TDengine的一个虚拟节点收到后,经过合法性检查后,可以被写入系统时,就会被分配一个版本号。这个版本号在一个虚拟节点里从1开始,是单调连续递增的。无论这条记录是采集的时序数据还是meta data, 一样处理。当Master转发一个写入请求到slave时,必须带上版本号。一个虚拟节点将一数据更新请求写入WAL时,需要带上版本号。
|
||||||
|
|
||||||
|
不同虚拟节点组的数据版本号是完全独立的,互不相干的。版本号本质上是数据更新记录的transaction ID,但用来标识数据集的版本。
|
||||||
|
|
||||||
|
**角色(role):**
|
||||||
|
|
||||||
|
一个虚拟节点可以是master, slave, unsynced或offline状态。
|
||||||
|
|
||||||
|
- master: 具有最新的数据,容许客户端往里写入数据,一个虚拟节点组,至多一个master.
|
||||||
|
- slave:与master是同步的,但不容许客户端往里写入数据,根据配置,可以容许客户端对其进行查询。
|
||||||
|
- unsynced: 节点处于非同步状态,比如虚拟节点刚启动、或与其他虚拟节点的连接出现故障等。处于该状态时,该虚拟节点既不能提供写入,也不能提供查询服务
|
||||||
|
- offline: 由于宕机或网络原因,无法访问到某虚拟节点时,其他虚拟节点将该虚拟节点标为离线。但请注意,该虚拟节点本身的状态可能是unsynced或其他,但不会是离线。
|
||||||
|
|
||||||
|
**Quorum:**
|
||||||
|
|
||||||
|
指数据写入成功所需要的确认数。对于异步复制,quorum设为1,具有master角色的虚拟节点自己确认即可。对于同步复制,需要至少大于等于2。原则上,Quorum >=1 并且 Quorum <= replication(副本数)。这个参数在启动一个同步模块实例时需要提供。
|
||||||
|
|
||||||
|
**WAL:**
|
||||||
|
|
||||||
|
TDengine的WAL(Write Ahead Log)与cassandra的commit log, mySQL的bin log, Postgres的WAL没本质区别。没有写入数据库文件,还保存在内存的数据都会先存在WAL。当数据已经成功写入数据库数据文件,相应的WAL会被删除。但需要特别指明的是,在TDengine系统里,有几点:
|
||||||
|
|
||||||
|
- 每个虚拟节点有自己独立的wal
|
||||||
|
- WAL里包含而且仅仅包含来自客户端的数据更新操作,每个更新操作都会被打上一个版本号
|
||||||
|
|
||||||
|
**复制实例:**
|
||||||
|
|
||||||
|
复制模块只是一可执行的代码,复制实例是指正在运行的复制模块的一个实例,一个节点里,可以存在多个实例。原则上,一个节点有多少虚拟节点,就可以启动多少实例。对于副本数为1的场景,应用可以决定是否需要启动同步实例。应用启动一个同步模块的实例时,需要提供的就是虚拟节点组的配置信息,包括:
|
||||||
|
|
||||||
|
- 虚拟节点个数,即replication number
|
||||||
|
- 各虚拟节点所在节点的信息,包括node的end point
|
||||||
|
- quorum, 需要的数据写入成功的确认数
|
||||||
|
- 虚拟节点的初始版本号
|
||||||
|
|
||||||
|
## 数据复制模块的基本工作原理
|
||||||
|
|
||||||
|
TDengine采取的是Master-Slave模式进行同步,与流行的RAFT一致性算法比较一致。总结下来,有几点:
|
||||||
|
|
||||||
|
1. 一个vgroup里有一到多个虚拟节点,每个虚拟节点都有自己的角色
|
||||||
|
2. 客户端只能向角色是master的虚拟节点发起数据更新操作,因为master具有最新版本的数据,如果向非Master发起数据更新操作,会直接收到错误
|
||||||
|
3. 客户端可以向master, 也可以向角色是Slave的虚拟节点发起查询操作,但不能对unsynced的虚拟节点发起任何操作
|
||||||
|
4. 如果master不存在,这个vgroup是不能对外提供数据更新和查询服务的
|
||||||
|
5. master收到客户端的数据更新操作时,会将其转发给slave节点
|
||||||
|
6. 一个虚拟节点的版本号比master低的时候,会发起数据恢复流程,成功后,才会成为slave
|
||||||
|
|
||||||
|
数据实时复制有三个主要流程:选主、数据转发、数据恢复。后续做详细讨论。
|
||||||
|
|
||||||
|
## 虚拟节点之间的网络链接
|
||||||
|
|
||||||
|
虚拟节点之间通过TCP进行链接,节点之间的状态交换、数据包的转发都是通过这个TCP链接(peerFd)进行。为避免竞争,两个虚拟节点之间的TCP链接,总是由IP地址(UINT32)小的节点作为TCP客户端发起。一旦TCP链接被中断,虚拟节点能通过TCP socket自动检测到,将对方标为offline。如果监测到任何错误(比如数据恢复流程),虚拟节点将主动重置该链接。
|
||||||
|
|
||||||
|
一旦作为客户端的节点链接不成或中断,它将周期性的每隔一秒钟去试图去链接一次。因为TCP本身有心跳机制,虚拟节点之间不再另行提供心跳。
|
||||||
|
|
||||||
|
如果一个unsynced节点要发起数据恢复流程,它与Master将建立起专有的TCP链接(syncFd)。数据恢复完成后,该链接会被关闭。而且为限制资源的使用,系统只容许一定数量(配置参数tsMaxSyncNum)的数据恢复的socket存在。如果超过这个数字,系统会将新的数据恢复请求延后处理。
|
||||||
|
|
||||||
|
任意一个节点,无论有多少虚拟节点,都会启动而且只会启动一个TCP server, 来接受来自其他虚拟节点的上述两类TCP的链接请求。当TCP socket建立起来,客户端侧发送的消息体里会带有vgId(全局唯一的vgroup ID), TCP 服务器侧会检查该vgId是否已经在该节点启动运行。如果已经启动运行,就接受其请求。如果不存在,就直接将链接请求关闭。在TDengine代码里,mnode group的vgId设置为1。
|
||||||
|
|
||||||
|
## 选主流程
|
||||||
|
|
||||||
|
当同一组的两个虚拟节点之间(vnode A, vnode B)建立连接后,他们互换status消息。status消息里包含本地存储的同一虚拟节点组内所有虚拟节点的role和version。
|
||||||
|
|
||||||
|
如果一个虚拟节点(vnode A)检测到与同一虚拟节点组内另外一虚拟节点(vnode B)的链接中断,vnode A将立即把vnode B的role设置为offline。无论是接收到另外一虚拟节点发来的status消息,还是检测与另外一虚拟节点的链接中断,该虚拟节点都将进入状态处理流程。状态处理流程的规则如下:
|
||||||
|
|
||||||
|
1. 如果检测到在线的节点数没有超过一半,则将自己的状态设置为unsynced.
|
||||||
|
2. 如果在线的虚拟节点数超过一半,会检查master节点是否存在,如果存在,则会决定是否将自己状态改为slave或启动数据恢复流程
|
||||||
|
3. 如果master不存在,则会检查自己保存的各虚拟节点的状态信息与从另一节点接收到的是否一致,如果一致,说明节点组里状态已经稳定一致,则会触发选举流程。如果不一致,说明状态还没趋于一致,即使master不存在,也不进行选主。由于要求状态信息一致才进行选举,每个虚拟节点根据同样的信息,会选出同一个虚拟节点做master,无需投票表决。
|
||||||
|
4. 自己的状态是根据规则自己决定并修改的,并不需要其他节点同意,包括成为master。一个节点无权修改其他节点的状态。
|
||||||
|
5. 如果一个虚拟节点检测到自己或其他虚拟节点的role发生改变,该节点会广播它自己保存的各个虚拟节点的状态信息(role和version).
|
||||||
|
|
||||||
|
具体的流程图如下:
|
||||||
|
|
||||||
|
<center> <img src="../assets/replica-master.png"> </center>
|
||||||
|
|
||||||
|
选择Master的具体规则如下:
|
||||||
|
|
||||||
|
1. 如果只有一个副本,该副本永远就是master
|
||||||
|
2. 所有副本都在线时,版本最高的被选为master
|
||||||
|
3. 在线的虚拟节点数过半,而且有虚拟节点是slave的话,该虚拟节点自动成为master
|
||||||
|
4. 对于2和3,如果多个虚拟节点满足成为master的要求,那么虚拟节点组的节点列表里,最前面的选为master
|
||||||
|
|
||||||
|
按照上面的规则,如果所有虚拟节点都是unsynced(比如全部重启),只有所有虚拟节点上线,才能选出master,该虚拟节点组才能开始对外提供服务。当一个虚拟节点的role发生改变时,sync模块回通过回调函数notifyRole通知应用。
|
||||||
|
|
||||||
|
## 数据转发流程
|
||||||
|
|
||||||
|
如果vnode A是master, vnode B是slave, vnode A能接受客户端的写请求,而vnode B不能。当vnode A收到写的请求后,遵循下面的流程:
|
||||||
|
|
||||||
|
<center> <img src="../assets/replica-forward.png"> </center>
|
||||||
|
|
||||||
|
1. 应用对写请求做基本的合法性检查,通过,则给改请求包打上一个版本号(version, 单调递增)
|
||||||
|
2. 应用将打上版本号的写请求封装一个WAL Head, 写入WAL(Write Ahead Log)
|
||||||
|
3. 应用调用API syncForwardToPeer,如多vnode B是slave状态,sync模块将包含WAL Head的数据包通过Forward消息发送给vnode B,否则就不转发。
|
||||||
|
4. vnode B收到Forward消息后,调用回调函数writeToCache, 交给应用处理
|
||||||
|
5. vnode B应用在写入成功后,都需要调用syncAckForward通知sync模块已经写入成功。
|
||||||
|
6. 如果quorum大于1,vnode B需要等待应用的回复确认,收到确认后,vnode B发送Forward Response消息给node A。
|
||||||
|
7. 如果quorum大于1,vnode A需要等待vnode B或其他副本对Forward消息的确认。
|
||||||
|
8. 如果quorum大于1,vnode A收到quorum-1条确认消息后,调用回调函数confirmForward,通知应用写入成功。
|
||||||
|
9. 如果quorum为1,上述6,7,8步不会发生。
|
||||||
|
10. 如果要等待slave的确认,master会启动2秒的定时器(可配置),如果超时,则认为失败。
|
||||||
|
|
||||||
|
对于回复确认,sync模块提供的是异步回调函数,因此APP在调用syncForwardToPeer之后,无需等待,可以处理下一个操作。在Master与Slave的TCP链接管道里,可能有多个Forward消息,这些消息是严格按照应用提供的顺序排好的。对于Forward Response也是一样,TCP管道里存在多个,但都是排序好的。这个顺序,SYNC模块并没有做特别的事情,是由APP单线程顺序写来保证的(TDengine里每个vnode的写数据,都是单线程)。
|
||||||
|
|
||||||
|
## 数据恢复流程
|
||||||
|
|
||||||
|
如果一虚拟节点(vnode B) 处于unsynced状态,master存在(vnode A),而且其版本号比master的低,它将立即启动数据恢复流程。在理解恢复流程时,需要澄清几个关于文件的概念和处理规则。
|
||||||
|
|
||||||
|
1. 每个文件(无论是archived data的file还是wal)都有一个index, 这需要应用来维护(vnode里,该index就是fileId*3 + 0/1/2, 对应data, head与last三个文件)。如果index为0,表示系统里最老的数据文件。对于mnode里的文件,数量是固定的,对应于acct, user, db, table等文件。
|
||||||
|
2. 任何一个数据文件(file)有名字、大小,还有一个magic number。只有文件名、大小与magic number一致时,两个文件才判断是一样的,无需同步。Magic number可以是checksum, 也可以是简单的文件大小。怎么计算magic,换句话说,如何检测数据文件是否有效,完全由应用决定。
|
||||||
|
3. 文件名的处理有点复杂,因为每台服务器的路径可能不一致。比如node A的TDengine的数据文件存放在 /etc/taos目录下,而node B的数据存放在 /home/jhtao目录下。因此同步模块需要应用在启动一个同步实例时提供一个path,这样两台服务器的绝对路径可以不一样,但仍然可以做对比,做同步。
|
||||||
|
4. 当sync模块调用回调函数getFileInfo获得数据文件信息时,有如下的规则
|
||||||
|
1. index 为0,表示获取最老的文件,同时修改index返回给sync模块。如果index不为0,表示获取指定位置的文件。
|
||||||
|
2. 如果name为空,表示sync想获取位于index位置的文件信息,包括magic, size。Master节点会这么调用
|
||||||
|
3. 如果name不为空,表示sync想获取指定文件名和index的信息,slave节点会这么调用
|
||||||
|
4. 如果某个index的文件不存在,magic返回0,表示文件已经是最后一个。因此整个系统里,文件的index必须是连续的一段整数。
|
||||||
|
5. 当sync模块调用回调函数getWalInfo获得wal信息时,有如下规则
|
||||||
|
1. index为0,表示获得最老的WAL文件, 返回时,index更新为具体的数字
|
||||||
|
2. 如果返回0,表示这是最新的一个WAL文件,如果返回值是1,表示后面还有更新的WAL文件
|
||||||
|
3. 返回的文件名为空,那表示没有WAL文件
|
||||||
|
6. 无论是getFileInfo, 还是getWalInfo, 只要获取出错(不是文件不存在),返回-1即可,系统会报错,停止同步
|
||||||
|
|
||||||
|
整个数据恢复流程分为两大步骤,第一步,先恢复archived data(file), 然后恢复wal。具体流程如下:
|
||||||
|
|
||||||
|
<center> <img src="../assets/replica-restore.png"> </center>
|
||||||
|
|
||||||
|
1. 通过已经建立的TCP链接,发送sync req给master节点
|
||||||
|
2. master收到sync req后,以client的身份,向vnode B主动建立一新的专用于同步的TCP链接(syncFd)
|
||||||
|
3. 新的TCP链接建立成功后,master将开始retrieve流程,对应的,vnode B将同步启动restore流程
|
||||||
|
4. Retrieve/Restore流程里,先处理所有archived data (vnode里的data, head, last文件),后处理WAL data。
|
||||||
|
5. 对于archived data,master将通过回调函数getFileInfo获取数据文件的基本信息,包括文件名、magic以及文件大小。
|
||||||
|
6. master 将获得的文件名、magic以及文件大小发给vnode B
|
||||||
|
7. vnode B将回调函数getFile获得magic和文件大小,如果两者一致,就认为无需同步,如果两者不一致 ,就认为需要同步。vnode B将结果通过消息FileAck发回master
|
||||||
|
8. 如果文件需要同步,master就调用sendfile把整个文件发往vnode B
|
||||||
|
9. 如果文件不需要同步,master(vnode A)就重复5,6,7,8,直到所有文件被处理完
|
||||||
|
|
||||||
|
对于WAL同步,流程如下:
|
||||||
|
|
||||||
|
1. master节点调用回调函数getWalInfo,获取WAL的文件名。
|
||||||
|
2. 如果getWalInfo返回值大于0,表示该文件还不是最后一个WAL,因此master调用sendfile一下把该文件发送给vnode B
|
||||||
|
3. 如果getWalInfo返回时为0,表示该文件是最后一个WAL,因为文件可能还处于写的状态中,sync模块要根据WAL Head的定义逐条读出记录,然后发往vnode B。
|
||||||
|
4. vnode A读取TCP链接传来的数据,按照WAL Head,逐条读取,如果版本号比现有的大,调用回调函数writeToCache,交给应用处理。如果小,直接扔掉。
|
||||||
|
5. 上述流程循环,直到所有WAL文件都被处理完。处理完后,master就会将新来的数据包通过Forward消息转发给slave。
|
||||||
|
|
||||||
|
从同步文件启动起,sync模块会通过inotify监控所有处理过的file以及wal。一旦发现被处理过的文件有更新变化,同步流程将中止,会重新启动。因为有可能落盘操作正在进行(比如历史数据导入,内存数据落盘),把已经处理过的文件进行了修改,需要重新同步才行。
|
||||||
|
|
||||||
|
对于最后一个WAL (LastWal)的处理逻辑有点复杂,因为这个文件往往是打开写的状态,有很多场景需要考虑,比如:
|
||||||
|
|
||||||
|
- LastWal文件size在增长,需要重新读;
|
||||||
|
- LastWal文件虽然已经打开写,但内容为空;
|
||||||
|
- LastWal文件已经被关闭,应用生成了新的Last WAL文件;
|
||||||
|
- LastWal文件没有被关闭,但数据落盘的原因,没有读到完整的一条记录;
|
||||||
|
- LastWal文件没有被关闭,但数据落盘的原因,还有部分记录暂时读取不到;
|
||||||
|
|
||||||
|
sync模块通过inotify监控LastWal文件的更新和关闭操作。而且在确认已经尽可能读完LastWal的数据后,会将对方同步状态设置为SYNC_CACHE。该状态下,master节点会将新的记录转发给vnode B,而此时vnode B并没有完成同步,需要把这些转发包先存在recv buffer里,等WAL处理完后,vnode A再把recv buffer里的数据包通过回调writeToCache交给应用处理。
|
||||||
|
|
||||||
|
等vnode B把这些buffered forwards处理完,同步流程才算结束,vnode B正式变为slave。
|
||||||
|
|
||||||
|
## Master分布均匀性问题
|
||||||
|
|
||||||
|
因为Master负责写、转发,消耗的资源会更多,因此Master在整个集群里分布均匀比较理想。
|
||||||
|
|
||||||
|
但在TDengine的设计里,如果多个虚拟节点都符合master条件,TDengine选在列表中最前面的做Master, 这样是否导致在集群里,Master数量的分布不均匀问题呢?这取决于应用的设计。
|
||||||
|
|
||||||
|
给一个具体例子,系统里仅仅有三个节点,IP地址分别为IP1, IP2, IP3. 在各个节点上,TDengine创建了多个虚拟节点组,每个虚拟节点组都有三个副本。如果三个副本的顺序在所有虚拟节点组里都是IP1, IP2, IP3, 那毫无疑问,master将集中在IP1这个节点,这是我们不想看到的。
|
||||||
|
|
||||||
|
但是,如果在创建虚拟节点组时,增加随机性,这个问题就不存在了。比如在vgroup 1, 顺序是IP1, IP2, IP3, 在vgroup 2里,顺序是IP2, IP3, IP1, 在vgroup 3里,顺序是IP3, IP1, IP2。最后master的分布会是均匀的。
|
||||||
|
|
||||||
|
因此在创建一个虚拟节点组时,应用需要保证节点的顺序是round robin或完全随机。
|
||||||
|
|
||||||
|
## 少数虚拟节点写入成功的问题
|
||||||
|
|
||||||
|
在某种情况下,写入成功的确认数大于0,但小于配置的Quorum, 虽然有虚拟节点数据更新成功,master仍然会认为数据更新失败,并通知客户端写入失败。
|
||||||
|
|
||||||
|
这个时候,系统存在数据不一致的问题,因为有的虚拟节点已经写入成功,而有的写入失败。一个处理方式是,Master重置(reset)与其他虚拟节点的连接,该虚拟节点组将自动进入选举流程。按照规则,已经成功写入数据的虚拟节点将成为新的master,组内的其他虚拟节点将从master那里恢复数据。
|
||||||
|
|
||||||
|
因为写入失败,客户端会重新写入数据。但对于TDengine而言,是OK的。因为时序数据都是有时间戳的,时间戳相同的数据更新操作,第一次会执行,但第二次会自动扔掉。对于Meta Data(增加、删除库、表等等)的操作,也是OK的。一张表、库已经被创建或删除,再创建或删除,不会被执行的。
|
||||||
|
|
||||||
|
在TDengine的设计里,虚拟节点与虚拟节点之间,是一个TCP链接,是一个pipeline,数据块一个接一个按顺序在这个pipeline里等待处理。一旦某个数据块的处理失败,这个链接会被重置,后续的数据块的处理都会失败。因此不会存在Pipeline里一个数据块更新失败,但下一个数据块成功的可能。
|
||||||
|
|
||||||
|
## Split Brain的问题
|
||||||
|
|
||||||
|
选举流程中,有个强制要求,那就是一定有超过半数的虚拟节点在线。但是如果replication正好是偶数,这个时候,完全可能存在splt brain问题。
|
||||||
|
|
||||||
|
为解决这个问题,TDengine提供Arbitrator的解决方法。Arbitrator是一个节点,它的任务就是接受任何虚拟节点的链接请求,并保持它。
|
||||||
|
|
||||||
|
在启动复制模块实例时,在配置参数中,应用可以提供Arbitrator的IP地址。如果是奇数个副本,复制模块不会与这个arbitrator去建立链接,但如果是偶数个副本,就会主动去建立链接。
|
||||||
|
|
||||||
|
Arbitrator的程序tarbitrator.c在复制模块的同一目录, 编译整个系统时,会在bin目录生成。命令行参数“-?”查看可以配置的参数,比如绑定的IP地址,监听的端口号。
|
||||||
|
|
||||||
|
## 与RAFT相比的异同
|
||||||
|
|
||||||
|
数据一致性协议流行的有两种,Paxos与Raft. 本设计的实现与Raft有很多类同之处,下面做一些比较
|
||||||
|
|
||||||
|
相同之处:
|
||||||
|
|
||||||
|
- 三大流程一致:Raft里有Leader election, replication, safety,完全对应TDengine的选举、数据转发、数据恢复三个流程
|
||||||
|
- 节点状态定义一致:Raft里每个节点有Leader, Follower, Candidate三个状态,TDengine里是Master, Slave, Unsynced, Offline。多了一个offlince, 但本质上是一样的,因为offline是外界看一个节点的状态,但该节点本身是处于master, slave 或unsynced的
|
||||||
|
- 数据转发流程完全一样,Master(leader)需要等待回复确认。
|
||||||
|
- 数据恢复流程几乎一样,Raft没有涉及历史数据同步问题,只考虑了WAL数据同步
|
||||||
|
|
||||||
|
不同之处:
|
||||||
|
|
||||||
|
- 选举流程不一样:Raft里任何一个节点是candidate时,主动向其他节点发出vote request, 如果超过半数回答Yes, 这个candidate就成为Leader,开始一个新的term. 而TDengine的实现里,节点上线、离线或角色改变都会触发状态消息在节点组类传播,等节点组里状态稳定一致之后才触发选举流程,因为状态稳定一致,基于同样的状态信息,每个节点做出的决定会是一致的,一旦某个节点符合成为master的条件,无需其他节点认可,它会自动将自己设为master。TDengine里,任何一个节点检测到其他节点或自己的角色发生改变,就会给节点组内其他节点进行广播的。Raft里不存在这样的机制,因此需要投票来解决。
|
||||||
|
- 对WAL的一条记录,Raft用term + index来做唯一标识。但TDengine只用version(类似index),在TDengine实现里,仅仅用version是完全可行的, 因为TDengine的选举机制,没有term的概念。
|
||||||
|
|
||||||
|
如果整个虚拟节点组全部宕机,重启,但不是所有虚拟节点都上线,这个时候TDengine是不会选出master的,因为未上线的节点有可能有最高version的数据。而RAFT协议,只要超过半数上线,就会选出Leader。
|
||||||
|
|
||||||
|
## Meta Data的数据复制
|
||||||
|
|
||||||
|
TDengine里存在时序数据,也存在Meta Data。Meta Data对数据的可靠性要求更高,那么TDengine设计能否满足要求呢?下面做个仔细分析
|
||||||
|
|
||||||
|
TDengine里Meta Data包括以下:
|
||||||
|
|
||||||
|
- account 信息
|
||||||
|
- 一个account下面,可以有多个user, 多个DB
|
||||||
|
- 一个DB下面有多个vgroup
|
||||||
|
- 一个DB下面有多个stable
|
||||||
|
- 一个vgroup下面有多个table
|
||||||
|
- 整个系统有多个mnode, dnode
|
||||||
|
- 一个dnode可以有多个vnode
|
||||||
|
|
||||||
|
上述的account, user, DB, vgroup, table, stable, mnode, dnode都有自己的属性,这些属性是TDengine自己定义的,不会开放给用户进行修改。这些Meta Data的查询都比较简单,都可以采用key-value模型进行存储。这些Meta Data还具有几个特点:
|
||||||
|
|
||||||
|
1. 上述的Meta Data之间有一定的层级关系,比如必须先创建DB,才能创建table, stable。只有先创建dnode,才可能创建vnode, 才可能创建vgroup。因此他们创建的顺序是绝对不能错的。
|
||||||
|
2. 在客户端应用的数据更新操作得到TDengine服务器侧确认后,所执行的数据更新操作绝对不能丢失。否则会造成客户端应用与服务器的数据不一致。
|
||||||
|
3. 上述的Meta Data是容许重复操作的。比如插入新记录后,再插入一次,删除一次后,再删除一次,更新一次后,再更新一次,不会对系统产生任何影响,不会改变系统任何状态。
|
||||||
|
|
||||||
|
对于特点1,本设计里,数据的写入是单线程的,按照到达的先后顺序,给每个数据更新操作打上版本号,版本号大的记录一定是晚于版本号小的写入系统,数据写入顺序是100%保证的,绝对不会让版本号大的记录先写入。复制过程中,数据块的转发也是严格按照顺序进行的,因此TDengine的数据复制设计是能保证Meta Data的创建顺序的。
|
||||||
|
|
||||||
|
对于特点2,只要Quorum数设置等于replica,那么一定能保证回复确认过的数据更新操作不会在服务器侧丢失。即使某节点永不起来,只要超过一半的节点还是online, 查询服务不会受到任何影响。这时,如果某个节点离线超过一定时长,系统可以自动补充新的节点,以保证在线的节点数在绝大部分时间是100%的。
|
||||||
|
|
||||||
|
对于特点3,完全可能发生,服务器确实持久化存储了某一数据更新操作,但客户端应用出了问题,认为操作不成功,它会重新发起操作。但对于Meta Data而言,没有关系,客户端可以再次发起同样的操作,不会有任何影响。
|
||||||
|
|
||||||
|
总结来看,只要quorum设置大于一,本数据复制的设计是能满足Meta Data的需求的。目前,还没有发现漏洞。
|
|
@ -0,0 +1,107 @@
|
||||||
|
# TDengine 2.0 执行代码taosd的设计
|
||||||
|
|
||||||
|
逻辑上,TDengine系统包含dnode, taosc和App,dnode是服务器侧执行代码taosd的一个运行实例,因此taosd是TDengine的核心,本文对taosd的设计做一简单的介绍,模块内的实现细节请见其他文档。
|
||||||
|
|
||||||
|
## 系统模块图
|
||||||
|
taosd包含rpc, dnode, vnode, tsdb, query, cq, sync, wal, mnode, http, monitor等模块,具体如下图:
|
||||||
|
|
||||||
|
<center> <img src="../assets/modules.png"> </center>
|
||||||
|
|
||||||
|
taosd的启动入口是dnode模块,dnode然后启动其他模块,包括可选配置的http, monitor模块。taosc或dnode之间交互的消息都是通过rpc模块进行,dnode模块根据接收到的消息类型,将消息分发到vnode或mnode的消息队列,或由dnode模块自己消费。dnode的工作线程(worker)消费消息队列里的消息,交给mnode或vnode进行处理。下面对各个模块做简要说明。
|
||||||
|
|
||||||
|
## RPC模块
|
||||||
|
|
||||||
|
该模块负责taosd与taosc, 以及其他数据节点之间的通讯。TDengine没有采取标准的HTTP或gRPC等第三方工具,而是实现了自己的通讯模块RPC。
|
||||||
|
|
||||||
|
考虑到物联网场景下,数据写入的包一般不大,因此除支持TCP链接之外,RPC还支持UDP链接。当数据包小于15K时,RPC将采用UDP方式进行链接,否则将采用TCP链接。对于查询类的消息,RPC不管包的大小,总是采取TCP链接。对于UDP链接,RPC实现了自己的超时、重传、顺序检查等机制,以保证数据可靠传输。
|
||||||
|
|
||||||
|
RPC模块还提供数据压缩功能,如果数据包的字节数超过系统配置参数compressMsgSize, RPC在传输中将自动压缩数据,以节省带宽。
|
||||||
|
|
||||||
|
为保证数据的安全和数据的integrity, RPC模块采用MD5做数字签名,对数据的真实性和完整性进行认证。
|
||||||
|
|
||||||
|
## DNODE模块
|
||||||
|
|
||||||
|
该模块是整个taosd的入口,它具体负责如下任务:
|
||||||
|
|
||||||
|
- 系统的初始化,包括
|
||||||
|
- 从文件taos.cfg读取系统配置参数,从文件dnodeCfg.json读取数据节点的配置参数;
|
||||||
|
- 启动RPC模块,并建立起与taosc通讯的server链接,与其他数据节点通讯的server链接;
|
||||||
|
- 启动并初始化dnode的内部管理, 该模块将扫描该数据节点已有的vnode,并打开它们;
|
||||||
|
- 初始化可配置的模块,如mnode, http, monitor等。
|
||||||
|
- 数据节点的管理,包括
|
||||||
|
- 定时的向mnode发送status消息,报告自己的状态;
|
||||||
|
- 根据mnode的指示,创建、改变、删除vnode;
|
||||||
|
- 根据mnode的指示,修改自己的配置参数;
|
||||||
|
- 消息的分发、消费,包括
|
||||||
|
- 为每一个vnode和mnode的创建并维护一个读队列、一个写队列;
|
||||||
|
- 将从taosc或其他数据节点来的消息,根据消息类型,将其直接分发到不同的消息队列,或由自己的管理模块直接消费;
|
||||||
|
- 维护一个读的线程池,消费读队列的消息,交给vnode或mnode处理。为支持高并发,一个读线程(Worker)可以消费多个队列的消息,一个读队列可以由多个worker消费;
|
||||||
|
- 维护一个写的线程池,消费写队列的消息,交给vnode或mnode处理。为保证写操作的序列化,一个写队列只能由一个写线程负责,但一个写线程可以负责多个写队列。
|
||||||
|
|
||||||
|
taosd的消息消费由dnode通过读写线程池进行控制,是系统的中枢。该模块内的结构体图如下:
|
||||||
|
|
||||||
|
<center> <img src="../assets/dnode.png"> </center>
|
||||||
|
|
||||||
|
## VNODE模块
|
||||||
|
|
||||||
|
vnode是一独立的数据存储查询逻辑单元,但因为一个vnode只能容许一个DB,因此vnode内部没有account, DB, user等概念。为实现更好的模块化、封装以及未来的扩展,它有很多子模块,包括负责存储的TSDB,负责查询的Query, 负责数据复制的sync,负责数据库日志的的wal, 负责连续查询的cq(continuous query), 负责事件触发的流计算的event等模块,这些子模块只与vnode模块发生关系,与其他模块没有任何调用关系。模块图如下:
|
||||||
|
|
||||||
|
<center> <img src="../assets/vnode.png"> </center>
|
||||||
|
vnode模块向下,与dnodeVRead,dnodeVWrite发生互动,向上,与子模块发生互动。它主要的功能有:
|
||||||
|
|
||||||
|
- 协调各个子模块的互动。各个子模块之间都不直接调用,都需要通过vnode模块进行;
|
||||||
|
- 对于来自taosc或mnode的写操作,vnode模块将其分解为写日志(wal), 转发(sync), 本地存储(tsdb)子模块的操作;
|
||||||
|
- 对于查询操作,分发到query模块进行。
|
||||||
|
|
||||||
|
一个数据节点里有多个vnode, 因此vnode模块是有多个运行实例的。每个运行实例是完全独立的。
|
||||||
|
|
||||||
|
vnode与其子模块是通过API直接调用,而不是通过消息队列传递。而且各个子模块只与vnode模块有交互,不与dnode, rpc等模块发生任何直接关联。
|
||||||
|
|
||||||
|
## MNODE模块
|
||||||
|
|
||||||
|
mnode是整个系统的大脑,负责整个系统的资源调度,负责meta data的管理与存储。
|
||||||
|
|
||||||
|
一个运行的系统里,只有一个mnode,但它有多个副本(由系统配置参数numOfMpeers控制)。这些副本分布在不同的dnode里,目的是保证系统的高可靠运行。副本之间的数据复制是采用同步而非异步的方式,以确保数据的一致性,确保数据不会丢失。这些副本会自动选举一个Master,其他副本是slave。所有数据更新类的操作,都只能在master上进行,而查询类的可以在slave节点上进行。代码实现上,同步模块与vnode共享,但mnode被分配一个特殊的vgroup ID: 1,而且quorum大于1。整个集群系统是由多个dnode组成的,运行的mnode的副本数不可能超过dnode的个数,但不会超过配置的副本数。如果某个mnode副本宕机一段时间,只要超过半数的mnode副本仍在运行,运行的mnode会自动根据整个系统的资源情况,在其他dnode里再启动一个mnode, 以保证运行的副本数。
|
||||||
|
|
||||||
|
各个dnode通过信息交换,保存有mnode各个副本的End Point列表,并向其中的master节点定时(间隔由系统配置参数statusInterval控制)发送status消息,消息体里包含该dnode的CPU、内存、剩余存储空间、vnode个数,以及各个vnode的状态(存储空间、原始数据大小、记录条数、角色等)。这样mnode就了解整个系统的资源情况,如果用户创建新的表,就可以决定需要在哪个dnode创建;如果增加或删除dnode, 或者监测到某dnode数据过热、或离线太长,就可以决定需要挪动那些vnode,以实现负载均衡。
|
||||||
|
|
||||||
|
mnode里还负责account, user, DB, stable, table, vgroup, dnode的创建、删除与更新。mnode不仅把这些entity的meta data保存在内存,还做持久化存储。但为节省内存,各个表的标签值不保存在mnode(保存在vnode),而且子表不维护自己的schema, 而是与stable共享。为减小mnode的查询压力,taosc会缓存table、stable的schema。对于查询类的操作,各个slave mnode也可以提供,以减轻master压力。
|
||||||
|
|
||||||
|
## TSDB模块
|
||||||
|
TSDB模块是VNODE中的负责快速高并发地存储和读取属于该VNODE的表的元数据及采集的时序数据的引擎。除此之外,TSDB还提供了表结构的修改、表标签值的修改等功能。TSDB提供API供VNODE和Query等模块调用。TSDB中存储了两类数据,1:元数据信息;2:时序数据
|
||||||
|
|
||||||
|
### 元数据信息
|
||||||
|
TSDB中存储的元数据包含属于其所在的VNODE中表的类型,schema的定义等。对于超级表和超级表下的子表而言,又包含了tag的schema定义以及子表的tag值等。对于元数据信息而言,TSDB就相当于一个全内存的KV型数据库,属于该VNODE的表对象全部在内存中,方便快速查询表的信息。除此之外,TSDB还对其中的子表,按照tag的第一列取值做了全内存的索引,大大加快了对于标签的过滤查询。TSDB中的元数据的最新状态在落盘时,会以追加(append-only)的形式,写入到meta文件中。meta文件只进行追加操作,即便是元数据的删除,也会以一条记录的形式写入到文件末尾。TSDB也提供了对于元数据的修改操作,如表schema的修改,tag schema的修改以及tag值的修改等。
|
||||||
|
|
||||||
|
### 时序数据
|
||||||
|
每个TSDB在创建时,都会事先分配一定量的内存缓冲区,且内存缓冲区的大小可配可修改。表采集的时序数据,在写入TSDB时,首先以追加的方式写入到分配的内存缓冲区中,同时建立基于时间戳的内存索引,方便快速查询。当内存缓冲区的数据积累到一定的程度时(达到内存缓冲区总大小的1/3),则会触发落盘操作,将缓冲区中的数据持久化到硬盘文件上。时序数据在内存缓冲区中是以行(row)的形式存储的。
|
||||||
|
|
||||||
|
而时序数据在写入到TSDB的数据文件时,是以列(column)的形式存储的。TSDB中的数据文件包含多个数据文件组,每个数据文件组中又包含.head、.data和.last三个文件,如(v2f1801.head、v2f1801.data、v2f1801.last)数据文件组。TSDB中的数据文件组是按照时间跨度进行分片的,默认是10天一个文件组,且可通过配置文件及建库选项进行配置。分片的数据文件组又按照编号递增排列,方便快速定位某一时间段的时序数据,高效定位数据文件组。时序数据在TSDB的数据文件中是以块的形式进行列式存储的,每个块中只包含一张表的数据,且数据在一个块中是按照时间顺序递增排列的。在一个数据文件组中,.head文件负责存储数据块的索引及统计信息,如每个块的位置,压缩算法,时间戳范围等。存储在.head文件中一张表的索引信息是按照数据块中存储的数据的时间递增排列的,方便进行折半查找等工作。.head和.last文件是存储真实数据块的文件,若数据块中的数据累计到一定程度,则会写入.data文件中,否则,会写入.last文件中,等待下次落盘时合并数据写入.data文件中,从而大大减少文件中块的个数,避免数据的过度碎片化。
|
||||||
|
|
||||||
|
## Query模块
|
||||||
|
该模块负责整体系统的查询处理。客户端调用该该模块进行SQL语法解析,并将查询或写入请求发送到vnode,同时负责针对超级表的查询进行二阶段的聚合操作。在Vnode端,该模块调用TSDB模块读取系统中存储的数据进行查询处理。Query模块还定义了系统能够支持的全部查询函数,查询函数的实现机制与查询框架无耦合,可以在不修改查询流程的情况下动态增加查询函数。详细的设计请参见《TDengine 2.0查询模块设计》。
|
||||||
|
|
||||||
|
## SYNC模块
|
||||||
|
该模块实现数据的多副本复制,包括vnode与mnode的数据复制,支持异步和同步两种复制方式,以满足meta data与时序数据不同复制的需求。因为它为mnode与vnode共享,系统为mnode副本预留了一个特殊的vgroup ID:1。因此vnode group的ID是从2开始的。
|
||||||
|
|
||||||
|
每个vnode/mnode模块实例会有一对应的sync模块实例,他们是一一对应的。详细设计请见<a href="https://www.taosdata.com/cn/documentation20/replica/">TDengine 2.0 数据复制模块设计</a>
|
||||||
|
|
||||||
|
## WAL模块
|
||||||
|
该模块负责将新插入的数据写入write ahead log(WAL), 为vnode, mnode共享。以保证服务器crash或其他故障,能从WAL中恢复数据。
|
||||||
|
|
||||||
|
每个vnode/mnode模块实例会有一对应的wal模块实例,是完全一一对应的。WAL的落盘操作由两个参数walLevel, fsync控制。看具体场景,如果要100%保证数据不会丢失,需要将walLevel配置为2,fsync设置为0,每条数据插入请求,都会实时落盘后,才会给应用确认
|
||||||
|
|
||||||
|
## HTTP模块
|
||||||
|
该模块负责处理系统对外的RESTful接口,可以通过配置,由dnode启动或停止。
|
||||||
|
|
||||||
|
该模块将接收到的RESTful请求,做了各种合法性检查后,将其变成标准的SQL语句,通过taosc的异步接口,将请求发往整个系统中的任一dnode。收到处理后的结果后,再翻译成HTTP协议,返回给应用。
|
||||||
|
|
||||||
|
如果HTTP模块启动,就意味着启动了一个taosc的实例。任一一个dnode都可以启动该模块,以实现对RESTful请求的分布式处理。
|
||||||
|
|
||||||
|
## Monitor模块
|
||||||
|
该模块负责检测一个dnode的运行状态,可以通过配置,由dnode启动或停止。原则上,每个dnode都应该启动一个monitor实例。
|
||||||
|
|
||||||
|
Monitor采集TDengine里的关键操作,比如创建、删除、更新账号、表、库等,而且周期性的收集CPU、内存、网络等资源的使用情况(采集周期由系统配置参数monitorInterval控制)。获得这些数据后,monitor模块将采集的数据写入系统的日志库(DB名字由系统配置参数monitorDbName控制)。
|
||||||
|
|
||||||
|
Monitor模块使用taosc来将采集的数据写入系统,因此每个monitor实例,都有一个taosc运行实例。
|
||||||
|
|
|
@ -31,7 +31,7 @@ install_main_dir="/usr/local/taos"
|
||||||
bin_dir="/usr/local/taos/bin"
|
bin_dir="/usr/local/taos/bin"
|
||||||
|
|
||||||
# v1.5 jar dir
|
# v1.5 jar dir
|
||||||
v15_java_app_dir="/usr/local/lib/taos"
|
#v15_java_app_dir="/usr/local/lib/taos"
|
||||||
|
|
||||||
service_config_dir="/etc/systemd/system"
|
service_config_dir="/etc/systemd/system"
|
||||||
nginx_port=6060
|
nginx_port=6060
|
||||||
|
@ -79,19 +79,19 @@ osinfo=$(cat /etc/os-release | grep "NAME" | cut -d '"' -f2)
|
||||||
#echo "osinfo: ${osinfo}"
|
#echo "osinfo: ${osinfo}"
|
||||||
os_type=0
|
os_type=0
|
||||||
if echo $osinfo | grep -qwi "ubuntu" ; then
|
if echo $osinfo | grep -qwi "ubuntu" ; then
|
||||||
echo "This is ubuntu system"
|
# echo "This is ubuntu system"
|
||||||
os_type=1
|
os_type=1
|
||||||
elif echo $osinfo | grep -qwi "debian" ; then
|
elif echo $osinfo | grep -qwi "debian" ; then
|
||||||
echo "This is debian system"
|
# echo "This is debian system"
|
||||||
os_type=1
|
os_type=1
|
||||||
elif echo $osinfo | grep -qwi "Kylin" ; then
|
elif echo $osinfo | grep -qwi "Kylin" ; then
|
||||||
echo "This is Kylin system"
|
# echo "This is Kylin system"
|
||||||
os_type=1
|
os_type=1
|
||||||
elif echo $osinfo | grep -qwi "centos" ; then
|
elif echo $osinfo | grep -qwi "centos" ; then
|
||||||
echo "This is centos system"
|
# echo "This is centos system"
|
||||||
os_type=2
|
os_type=2
|
||||||
elif echo $osinfo | grep -qwi "fedora" ; then
|
elif echo $osinfo | grep -qwi "fedora" ; then
|
||||||
echo "This is fedora system"
|
# echo "This is fedora system"
|
||||||
os_type=2
|
os_type=2
|
||||||
else
|
else
|
||||||
echo "${osinfo}: This is an officially unverified linux system, If there are any problems with the installation and operation, "
|
echo "${osinfo}: This is an officially unverified linux system, If there are any problems with the installation and operation, "
|
||||||
|
@ -135,7 +135,7 @@ do
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "verType=${verType} interactiveFqdn=${interactiveFqdn}"
|
#echo "verType=${verType} interactiveFqdn=${interactiveFqdn}"
|
||||||
|
|
||||||
function kill_taosd() {
|
function kill_taosd() {
|
||||||
pid=$(ps -ef | grep "taosd" | grep -v "grep" | awk '{print $2}')
|
pid=$(ps -ef | grep "taosd" | grep -v "grep" | awk '{print $2}')
|
||||||
|
@ -189,19 +189,19 @@ function install_bin() {
|
||||||
function install_lib() {
|
function install_lib() {
|
||||||
# Remove links
|
# Remove links
|
||||||
${csudo} rm -f ${lib_link_dir}/libtaos.* || :
|
${csudo} rm -f ${lib_link_dir}/libtaos.* || :
|
||||||
${csudo} rm -rf ${v15_java_app_dir} || :
|
#${csudo} rm -rf ${v15_java_app_dir} || :
|
||||||
|
|
||||||
${csudo} cp -rf ${script_dir}/driver/* ${install_main_dir}/driver && ${csudo} chmod 777 ${install_main_dir}/driver/*
|
${csudo} cp -rf ${script_dir}/driver/* ${install_main_dir}/driver && ${csudo} chmod 777 ${install_main_dir}/driver/*
|
||||||
|
|
||||||
${csudo} ln -s ${install_main_dir}/driver/libtaos.* ${lib_link_dir}/libtaos.so.1
|
${csudo} ln -s ${install_main_dir}/driver/libtaos.* ${lib_link_dir}/libtaos.so.1
|
||||||
${csudo} ln -s ${lib_link_dir}/libtaos.so.1 ${lib_link_dir}/libtaos.so
|
${csudo} ln -s ${lib_link_dir}/libtaos.so.1 ${lib_link_dir}/libtaos.so
|
||||||
|
|
||||||
if [ "$verMode" == "cluster" ]; then
|
#if [ "$verMode" == "cluster" ]; then
|
||||||
# Compatible with version 1.5
|
# # Compatible with version 1.5
|
||||||
${csudo} mkdir -p ${v15_java_app_dir}
|
# ${csudo} mkdir -p ${v15_java_app_dir}
|
||||||
${csudo} ln -s ${install_main_dir}/connector/taos-jdbcdriver-1.0.2-dist.jar ${v15_java_app_dir}/JDBCDriver-1.0.2-dist.jar
|
# ${csudo} ln -s ${install_main_dir}/connector/taos-jdbcdriver-1.0.2-dist.jar ${v15_java_app_dir}/JDBCDriver-1.0.2-dist.jar
|
||||||
${csudo} chmod 777 ${v15_java_app_dir} || :
|
# ${csudo} chmod 777 ${v15_java_app_dir} || :
|
||||||
fi
|
#fi
|
||||||
}
|
}
|
||||||
|
|
||||||
function install_header() {
|
function install_header() {
|
||||||
|
@ -223,42 +223,41 @@ function install_config() {
|
||||||
${csudo} cp -f ${script_dir}/cfg/taos.cfg ${install_main_dir}/cfg/taos.cfg.org
|
${csudo} cp -f ${script_dir}/cfg/taos.cfg ${install_main_dir}/cfg/taos.cfg.org
|
||||||
${csudo} ln -s ${cfg_install_dir}/taos.cfg ${install_main_dir}/cfg
|
${csudo} ln -s ${cfg_install_dir}/taos.cfg ${install_main_dir}/cfg
|
||||||
|
|
||||||
if [ "$verMode" == "cluster" ]; then
|
[ ! -z $1 ] && return 0 || : # only install client
|
||||||
[ ! -z $1 ] && return 0 || : # only install client
|
|
||||||
|
|
||||||
if ((${update_flag}==1)); then
|
if ((${update_flag}==1)); then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$interactiveFqdn" == "no" ]; then
|
if [ "$interactiveFqdn" == "no" ]; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#FQDN_FORMAT="(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
|
#FQDN_FORMAT="(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
|
||||||
#FQDN_FORMAT="(:[1-6][0-9][0-9][0-9][0-9]$)"
|
#FQDN_FORMAT="(:[1-6][0-9][0-9][0-9][0-9]$)"
|
||||||
#PORT_FORMAT="(/[1-6][0-9][0-9][0-9][0-9]?/)"
|
#PORT_FORMAT="(/[1-6][0-9][0-9][0-9][0-9]?/)"
|
||||||
#FQDN_PATTERN=":[0-9]{1,5}$"
|
#FQDN_PATTERN=":[0-9]{1,5}$"
|
||||||
|
|
||||||
# first full-qualified domain name (FQDN) for TDengine cluster system
|
# first full-qualified domain name (FQDN) for TDengine cluster system
|
||||||
echo
|
echo
|
||||||
echo -e -n "${GREEN}Enter FQDN:port (like h1.taosdata.com:6030) of an existing TDengine cluster node to join OR leave it blank to build one${NC} :"
|
echo -e -n "${GREEN}Enter FQDN:port (like h1.taosdata.com:6030) of an existing TDengine cluster node to join${NC}"
|
||||||
read firstEp
|
echo
|
||||||
while true; do
|
echo -e -n "${GREEN}OR leave it blank to build one${NC}:"
|
||||||
if [ ! -z "$firstEp" ]; then
|
read firstEp
|
||||||
# check the format of the firstEp
|
while true; do
|
||||||
#if [[ $firstEp == $FQDN_PATTERN ]]; then
|
if [ ! -z "$firstEp" ]; then
|
||||||
# Write the first FQDN to configuration file
|
# check the format of the firstEp
|
||||||
${csudo} sed -i -r "s/#*\s*(firstEp\s*).*/\1$firstEp/" ${cfg_install_dir}/taos.cfg
|
#if [[ $firstEp == $FQDN_PATTERN ]]; then
|
||||||
break
|
# Write the first FQDN to configuration file
|
||||||
#else
|
${csudo} sed -i -r "s/#*\s*(firstEp\s*).*/\1$firstEp/" ${cfg_install_dir}/taos.cfg
|
||||||
# read -p "Please enter the correct FQDN:port: " firstEp
|
|
||||||
#fi
|
|
||||||
else
|
|
||||||
break
|
break
|
||||||
fi
|
#else
|
||||||
done
|
# read -p "Please enter the correct FQDN:port: " firstEp
|
||||||
|
#fi
|
||||||
fi
|
else
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -563,12 +562,11 @@ function update_TDengine() {
|
||||||
if [ -z $1 ]; then
|
if [ -z $1 ]; then
|
||||||
install_bin
|
install_bin
|
||||||
install_service
|
install_service
|
||||||
install_config
|
install_config
|
||||||
|
|
||||||
|
openresty_work=false
|
||||||
if [ "$verMode" == "cluster" ]; then
|
if [ "$verMode" == "cluster" ]; then
|
||||||
# Check if openresty is installed
|
# Check if openresty is installed
|
||||||
openresty_work=false
|
|
||||||
|
|
||||||
# Check if nginx is installed successfully
|
# Check if nginx is installed successfully
|
||||||
if type curl &> /dev/null; then
|
if type curl &> /dev/null; then
|
||||||
if curl -sSf http://127.0.0.1:${nginx_port} &> /dev/null; then
|
if curl -sSf http://127.0.0.1:${nginx_port} &> /dev/null; then
|
||||||
|
@ -580,8 +578,8 @@ function update_TDengine() {
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo
|
#echo
|
||||||
echo -e "\033[44;32;1mTDengine is updated successfully!${NC}"
|
#echo -e "\033[44;32;1mTDengine is updated successfully!${NC}"
|
||||||
echo
|
echo
|
||||||
echo -e "${GREEN_DARK}To configure TDengine ${NC}: edit /etc/taos/taos.cfg"
|
echo -e "${GREEN_DARK}To configure TDengine ${NC}: edit /etc/taos/taos.cfg"
|
||||||
if ((${service_mod}==0)); then
|
if ((${service_mod}==0)); then
|
||||||
|
@ -592,15 +590,12 @@ function update_TDengine() {
|
||||||
echo -e "${GREEN_DARK}To start TDengine ${NC}: ./taosd${NC}"
|
echo -e "${GREEN_DARK}To start TDengine ${NC}: ./taosd${NC}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$verMode" == "cluster" ]; then
|
if [ ${openresty_work} = 'true' ]; then
|
||||||
if [ ${openresty_work} = 'true' ]; then
|
echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell OR from ${GREEN_UNDERLINE}http://127.0.0.1:${nginx_port}${NC}"
|
||||||
echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell OR from ${GREEN_UNDERLINE}http://127.0.0.1:${nginx_port}${NC}"
|
|
||||||
else
|
|
||||||
echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell${NC}"
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell${NC}"
|
echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell${NC}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo -e "\033[44;32;1mTDengine is updated successfully!${NC}"
|
echo -e "\033[44;32;1mTDengine is updated successfully!${NC}"
|
||||||
else
|
else
|
||||||
|
@ -643,8 +638,8 @@ function install_TDengine() {
|
||||||
install_bin
|
install_bin
|
||||||
install_service
|
install_service
|
||||||
|
|
||||||
if [ "$verMode" == "cluster" ]; then
|
openresty_work=false
|
||||||
openresty_work=false
|
if [ "$verMode" == "cluster" ]; then
|
||||||
# Check if nginx is installed successfully
|
# Check if nginx is installed successfully
|
||||||
if type curl &> /dev/null; then
|
if type curl &> /dev/null; then
|
||||||
if curl -sSf http://127.0.0.1:${nginx_port} &> /dev/null; then
|
if curl -sSf http://127.0.0.1:${nginx_port} &> /dev/null; then
|
||||||
|
@ -669,21 +664,20 @@ function install_TDengine() {
|
||||||
echo -e "${GREEN_DARK}To start TDengine ${NC}: ${csudo} service taosd start${NC}"
|
echo -e "${GREEN_DARK}To start TDengine ${NC}: ${csudo} service taosd start${NC}"
|
||||||
else
|
else
|
||||||
echo -e "${GREEN_DARK}To start TDengine ${NC}: taosd${NC}"
|
echo -e "${GREEN_DARK}To start TDengine ${NC}: taosd${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${openresty_work} = 'true' ]; then
|
||||||
|
echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell OR from ${GREEN_UNDERLINE}http://127.0.0.1:${nginx_port}${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell${NC}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$verMode" == "cluster" ]; then
|
if [ ! -z "$firstEp" ]; then
|
||||||
if [ ${openresty_work} = 'true' ]; then
|
echo
|
||||||
echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell OR from ${GREEN_UNDERLINE}http://127.0.0.1:${nginx_port}${NC}"
|
echo -e "${GREEN_DARK}Please run${NC}: taos -h $firstEp${GREEN_DARK} to login into cluster, then${NC}"
|
||||||
else
|
echo -e "${GREEN_DARK}execute ${NC}: create dnode 'newDnodeFQDN:port'; ${GREEN_DARK}to add this new node${NC}"
|
||||||
echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell${NC}"
|
echo
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell${NC}"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo
|
|
||||||
echo -e "${GREEN_DARK}Please run${NC}: taos -h $firstEp ${GREEN_DARK} to login into cluster, then execute ${NC}: create dnode 'newDnodeFQDN:port'; ${GREEN_DARK}in TAOS shell to add this new node into the clsuter${NC}"
|
|
||||||
echo
|
|
||||||
echo -e "\033[44;32;1mTDengine is installed successfully!${NC}"
|
echo -e "\033[44;32;1mTDengine is installed successfully!${NC}"
|
||||||
echo
|
echo
|
||||||
else # Only install client
|
else # Only install client
|
||||||
|
|
|
@ -106,6 +106,31 @@ function install_config() {
|
||||||
|
|
||||||
${csudo} mv ${cfg_dir}/taos.cfg ${cfg_dir}/taos.cfg.org
|
${csudo} mv ${cfg_dir}/taos.cfg ${cfg_dir}/taos.cfg.org
|
||||||
${csudo} ln -s ${cfg_install_dir}/taos.cfg ${cfg_dir}
|
${csudo} ln -s ${cfg_install_dir}/taos.cfg ${cfg_dir}
|
||||||
|
#FQDN_FORMAT="(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
|
||||||
|
#FQDN_FORMAT="(:[1-6][0-9][0-9][0-9][0-9]$)"
|
||||||
|
#PORT_FORMAT="(/[1-6][0-9][0-9][0-9][0-9]?/)"
|
||||||
|
#FQDN_PATTERN=":[0-9]{1,5}$"
|
||||||
|
|
||||||
|
# first full-qualified domain name (FQDN) for TDengine cluster system
|
||||||
|
echo
|
||||||
|
echo -e -n "${GREEN}Enter FQDN:port (like h1.taosdata.com:6030) of an existing TDengine cluster node to join${NC}"
|
||||||
|
echo
|
||||||
|
echo -e -n "${GREEN}OR leave it blank to build one${NC}:"
|
||||||
|
read firstEp
|
||||||
|
while true; do
|
||||||
|
if [ ! -z "$firstEp" ]; then
|
||||||
|
# check the format of the firstEp
|
||||||
|
#if [[ $firstEp == $FQDN_PATTERN ]]; then
|
||||||
|
# Write the first FQDN to configuration file
|
||||||
|
${csudo} sed -i -r "s/#*\s*(firstEp\s*).*/\1$firstEp/" ${cfg_install_dir}/taos.cfg
|
||||||
|
break
|
||||||
|
#else
|
||||||
|
# read -p "Please enter the correct FQDN:port: " firstEp
|
||||||
|
#fi
|
||||||
|
else
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
function clean_service_on_sysvinit() {
|
function clean_service_on_sysvinit() {
|
||||||
|
@ -227,8 +252,8 @@ function install_TDengine() {
|
||||||
install_config
|
install_config
|
||||||
|
|
||||||
# Ask if to start the service
|
# Ask if to start the service
|
||||||
echo
|
#echo
|
||||||
echo -e "\033[44;32;1mTDengine is installed successfully!${NC}"
|
#echo -e "\033[44;32;1mTDengine is installed successfully!${NC}"
|
||||||
echo
|
echo
|
||||||
echo -e "${GREEN_DARK}To configure TDengine ${NC}: edit /etc/taos/taos.cfg"
|
echo -e "${GREEN_DARK}To configure TDengine ${NC}: edit /etc/taos/taos.cfg"
|
||||||
if ((${service_mod}==0)); then
|
if ((${service_mod}==0)); then
|
||||||
|
@ -241,7 +266,13 @@ function install_TDengine() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell${NC}"
|
echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell${NC}"
|
||||||
|
|
||||||
|
if [ ! -z "$firstEp" ]; then
|
||||||
|
echo
|
||||||
|
echo -e "${GREEN_DARK}Please run${NC}: taos -h $firstEp${GREEN_DARK} to login into cluster, then${NC}"
|
||||||
|
echo -e "${GREEN_DARK}execute ${NC}: create dnode 'newDnodeFQDN:port'; ${GREEN_DARK}to add this new node${NC}"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
echo
|
echo
|
||||||
echo -e "\033[44;32;1mTDengine is installed successfully!${NC}"
|
echo -e "\033[44;32;1mTDengine is installed successfully!${NC}"
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,10 +14,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define _DEFAULT_SOURCE
|
#define _DEFAULT_SOURCE
|
||||||
|
#include "os.h"
|
||||||
#include "tutil.h"
|
#include "tutil.h"
|
||||||
#include "tbalance.h"
|
#include "tbalance.h"
|
||||||
#include "tsync.h"
|
#include "tsync.h"
|
||||||
#include "ttime.h"
|
|
||||||
#include "ttimer.h"
|
#include "ttimer.h"
|
||||||
#include "tglobal.h"
|
#include "tglobal.h"
|
||||||
#include "tdataformat.h"
|
#include "tdataformat.h"
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
#include "tlog.h"
|
#include "tlog.h"
|
||||||
#include "tscUtil.h"
|
#include "tscUtil.h"
|
||||||
#include "tsclient.h"
|
#include "tsclient.h"
|
||||||
#include "ttime.h"
|
|
||||||
|
|
||||||
#include "com_taosdata_jdbc_TSDBJNIConnector.h"
|
#include "com_taosdata_jdbc_TSDBJNIConnector.h"
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
#include "tscSubquery.h"
|
#include "tscSubquery.h"
|
||||||
#include "tscompression.h"
|
#include "tscompression.h"
|
||||||
#include "tsqlfunction.h"
|
#include "tsqlfunction.h"
|
||||||
#include "ttime.h"
|
|
||||||
#include "tutil.h"
|
#include "tutil.h"
|
||||||
|
|
||||||
#define GET_INPUT_CHAR(x) (((char *)((x)->aInputElemBuf)) + ((x)->startOffset) * ((x)->inputBytes))
|
#define GET_INPUT_CHAR(x) (((char *)((x)->aInputElemBuf)) + ((x)->startOffset) * ((x)->inputBytes))
|
||||||
|
@ -2108,7 +2107,7 @@ static void copyTopBotRes(SQLFunctionCtx *pCtx, int32_t type) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tfree(pData);
|
taosTFree(pData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -326,7 +326,7 @@ static void tscProcessServerVer(SSqlObj *pSql) {
|
||||||
STR_WITH_SIZE_TO_VARSTR(vx, v, t);
|
STR_WITH_SIZE_TO_VARSTR(vx, v, t);
|
||||||
tscSetLocalQueryResult(pSql, vx, pExpr->aliasName, pExpr->resType, pExpr->resBytes);
|
tscSetLocalQueryResult(pSql, vx, pExpr->aliasName, pExpr->resType, pExpr->resBytes);
|
||||||
|
|
||||||
tfree(vx);
|
taosTFree(vx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tscProcessClientVer(SSqlObj *pSql) {
|
static void tscProcessClientVer(SSqlObj *pSql) {
|
||||||
|
@ -342,7 +342,7 @@ static void tscProcessClientVer(SSqlObj *pSql) {
|
||||||
STR_WITH_SIZE_TO_VARSTR(v, version, t);
|
STR_WITH_SIZE_TO_VARSTR(v, version, t);
|
||||||
tscSetLocalQueryResult(pSql, v, pExpr->aliasName, pExpr->resType, pExpr->resBytes);
|
tscSetLocalQueryResult(pSql, v, pExpr->aliasName, pExpr->resType, pExpr->resBytes);
|
||||||
|
|
||||||
tfree(v);
|
taosTFree(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tscProcessServStatus(SSqlObj *pSql) {
|
static void tscProcessServStatus(SSqlObj *pSql) {
|
||||||
|
|
|
@ -230,7 +230,7 @@ void tscCreateLocalReducer(tExtMemBuffer **pMemBuffer, int32_t numOfBuffer, tOrd
|
||||||
if (ds == NULL) {
|
if (ds == NULL) {
|
||||||
tscError("%p failed to create merge structure", pSql);
|
tscError("%p failed to create merge structure", pSql);
|
||||||
pRes->code = TSDB_CODE_TSC_OUT_OF_MEMORY;
|
pRes->code = TSDB_CODE_TSC_OUT_OF_MEMORY;
|
||||||
tfree(pReducer);
|
taosTFree(pReducer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,7 +257,7 @@ void tscCreateLocalReducer(tExtMemBuffer **pMemBuffer, int32_t numOfBuffer, tOrd
|
||||||
|
|
||||||
if (ds->filePage.num == 0) { // no data in this flush, the index does not increase
|
if (ds->filePage.num == 0) { // no data in this flush, the index does not increase
|
||||||
tscDebug("%p flush data is empty, ignore %d flush record", pSql, idx);
|
tscDebug("%p flush data is empty, ignore %d flush record", pSql, idx);
|
||||||
tfree(ds);
|
taosTFree(ds);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +267,7 @@ void tscCreateLocalReducer(tExtMemBuffer **pMemBuffer, int32_t numOfBuffer, tOrd
|
||||||
|
|
||||||
// no data actually, no need to merge result.
|
// no data actually, no need to merge result.
|
||||||
if (idx == 0) {
|
if (idx == 0) {
|
||||||
tfree(pReducer);
|
taosTFree(pReducer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,7 +275,7 @@ void tscCreateLocalReducer(tExtMemBuffer **pMemBuffer, int32_t numOfBuffer, tOrd
|
||||||
|
|
||||||
SCompareParam *param = malloc(sizeof(SCompareParam));
|
SCompareParam *param = malloc(sizeof(SCompareParam));
|
||||||
if (param == NULL) {
|
if (param == NULL) {
|
||||||
tfree(pReducer);
|
taosTFree(pReducer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
param->pLocalData = pReducer->pLocalDataSrc;
|
param->pLocalData = pReducer->pLocalDataSrc;
|
||||||
|
@ -288,8 +288,8 @@ void tscCreateLocalReducer(tExtMemBuffer **pMemBuffer, int32_t numOfBuffer, tOrd
|
||||||
|
|
||||||
pRes->code = tLoserTreeCreate(&pReducer->pLoserTree, pReducer->numOfBuffer, param, treeComparator);
|
pRes->code = tLoserTreeCreate(&pReducer->pLoserTree, pReducer->numOfBuffer, param, treeComparator);
|
||||||
if (pReducer->pLoserTree == NULL || pRes->code != 0) {
|
if (pReducer->pLoserTree == NULL || pRes->code != 0) {
|
||||||
tfree(param);
|
taosTFree(param);
|
||||||
tfree(pReducer);
|
taosTFree(pReducer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,14 +332,14 @@ void tscCreateLocalReducer(tExtMemBuffer **pMemBuffer, int32_t numOfBuffer, tOrd
|
||||||
|
|
||||||
if (pReducer->pTempBuffer == NULL || pReducer->discardData == NULL || pReducer->pResultBuf == NULL ||
|
if (pReducer->pTempBuffer == NULL || pReducer->discardData == NULL || pReducer->pResultBuf == NULL ||
|
||||||
/*pReducer->pBufForInterpo == NULL || */pReducer->pFinalRes == NULL || pReducer->prevRowOfInput == NULL) {
|
/*pReducer->pBufForInterpo == NULL || */pReducer->pFinalRes == NULL || pReducer->prevRowOfInput == NULL) {
|
||||||
tfree(pReducer->pTempBuffer);
|
taosTFree(pReducer->pTempBuffer);
|
||||||
tfree(pReducer->discardData);
|
taosTFree(pReducer->discardData);
|
||||||
tfree(pReducer->pResultBuf);
|
taosTFree(pReducer->pResultBuf);
|
||||||
tfree(pReducer->pFinalRes);
|
taosTFree(pReducer->pFinalRes);
|
||||||
tfree(pReducer->prevRowOfInput);
|
taosTFree(pReducer->prevRowOfInput);
|
||||||
tfree(pReducer->pLoserTree);
|
taosTFree(pReducer->pLoserTree);
|
||||||
tfree(param);
|
taosTFree(param);
|
||||||
tfree(pReducer);
|
taosTFree(pReducer);
|
||||||
pRes->code = TSDB_CODE_TSC_OUT_OF_MEMORY;
|
pRes->code = TSDB_CODE_TSC_OUT_OF_MEMORY;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -516,38 +516,38 @@ void tscDestroyLocalReducer(SSqlObj *pSql) {
|
||||||
|
|
||||||
tVariantDestroy(&pCtx->tag);
|
tVariantDestroy(&pCtx->tag);
|
||||||
if (pCtx->tagInfo.pTagCtxList != NULL) {
|
if (pCtx->tagInfo.pTagCtxList != NULL) {
|
||||||
tfree(pCtx->tagInfo.pTagCtxList);
|
taosTFree(pCtx->tagInfo.pTagCtxList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tfree(pLocalReducer->pCtx);
|
taosTFree(pLocalReducer->pCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
tfree(pLocalReducer->prevRowOfInput);
|
taosTFree(pLocalReducer->prevRowOfInput);
|
||||||
|
|
||||||
tfree(pLocalReducer->pTempBuffer);
|
taosTFree(pLocalReducer->pTempBuffer);
|
||||||
tfree(pLocalReducer->pResultBuf);
|
taosTFree(pLocalReducer->pResultBuf);
|
||||||
|
|
||||||
if (pLocalReducer->pResInfo != NULL) {
|
if (pLocalReducer->pResInfo != NULL) {
|
||||||
for (int32_t i = 0; i < pQueryInfo->fieldsInfo.numOfOutput; ++i) {
|
for (int32_t i = 0; i < pQueryInfo->fieldsInfo.numOfOutput; ++i) {
|
||||||
tfree(pLocalReducer->pResInfo[i].interResultBuf);
|
taosTFree(pLocalReducer->pResInfo[i].interResultBuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
tfree(pLocalReducer->pResInfo);
|
taosTFree(pLocalReducer->pResInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pLocalReducer->pLoserTree) {
|
if (pLocalReducer->pLoserTree) {
|
||||||
tfree(pLocalReducer->pLoserTree->param);
|
taosTFree(pLocalReducer->pLoserTree->param);
|
||||||
tfree(pLocalReducer->pLoserTree);
|
taosTFree(pLocalReducer->pLoserTree);
|
||||||
}
|
}
|
||||||
|
|
||||||
tfree(pLocalReducer->pFinalRes);
|
taosTFree(pLocalReducer->pFinalRes);
|
||||||
tfree(pLocalReducer->discardData);
|
taosTFree(pLocalReducer->discardData);
|
||||||
|
|
||||||
tscLocalReducerEnvDestroy(pLocalReducer->pExtMemBuffer, pLocalReducer->pDesc, pLocalReducer->resColModel,
|
tscLocalReducerEnvDestroy(pLocalReducer->pExtMemBuffer, pLocalReducer->pDesc, pLocalReducer->resColModel,
|
||||||
pLocalReducer->numOfVnode);
|
pLocalReducer->numOfVnode);
|
||||||
for (int32_t i = 0; i < pLocalReducer->numOfBuffer; ++i) {
|
for (int32_t i = 0; i < pLocalReducer->numOfBuffer; ++i) {
|
||||||
tfree(pLocalReducer->pLocalDataSrc[i]);
|
taosTFree(pLocalReducer->pLocalDataSrc[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pLocalReducer->numOfBuffer = 0;
|
pLocalReducer->numOfBuffer = 0;
|
||||||
|
@ -593,7 +593,7 @@ static int32_t createOrderDescriptor(tOrderDescriptor **pOrderDesc, SSqlCmd *pCm
|
||||||
}
|
}
|
||||||
|
|
||||||
*pOrderDesc = tOrderDesCreate(orderIdx, numOfGroupByCols, pModel, pQueryInfo->order.order);
|
*pOrderDesc = tOrderDesCreate(orderIdx, numOfGroupByCols, pModel, pQueryInfo->order.order);
|
||||||
tfree(orderIdx);
|
taosTFree(orderIdx);
|
||||||
|
|
||||||
if (*pOrderDesc == NULL) {
|
if (*pOrderDesc == NULL) {
|
||||||
return TSDB_CODE_TSC_OUT_OF_MEMORY;
|
return TSDB_CODE_TSC_OUT_OF_MEMORY;
|
||||||
|
@ -705,7 +705,7 @@ int32_t tscLocalReducerEnvCreate(SSqlObj *pSql, tExtMemBuffer ***pMemBuffer, tOr
|
||||||
|
|
||||||
if (createOrderDescriptor(pOrderDesc, pCmd, pModel) != TSDB_CODE_SUCCESS) {
|
if (createOrderDescriptor(pOrderDesc, pCmd, pModel) != TSDB_CODE_SUCCESS) {
|
||||||
pRes->code = TSDB_CODE_TSC_OUT_OF_MEMORY;
|
pRes->code = TSDB_CODE_TSC_OUT_OF_MEMORY;
|
||||||
tfree(pSchema);
|
taosTFree(pSchema);
|
||||||
return pRes->code;
|
return pRes->code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -746,7 +746,7 @@ int32_t tscLocalReducerEnvCreate(SSqlObj *pSql, tExtMemBuffer ***pMemBuffer, tOr
|
||||||
}
|
}
|
||||||
|
|
||||||
*pFinalModel = createColumnModel(pSchema, size, capacity);
|
*pFinalModel = createColumnModel(pSchema, size, capacity);
|
||||||
tfree(pSchema);
|
taosTFree(pSchema);
|
||||||
|
|
||||||
return TSDB_CODE_SUCCESS;
|
return TSDB_CODE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -765,7 +765,7 @@ void tscLocalReducerEnvDestroy(tExtMemBuffer **pMemBuffer, tOrderDescriptor *pDe
|
||||||
pMemBuffer[i] = destoryExtMemBuffer(pMemBuffer[i]);
|
pMemBuffer[i] = destoryExtMemBuffer(pMemBuffer[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
tfree(pMemBuffer);
|
taosTFree(pMemBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1036,10 +1036,10 @@ static void doFillResult(SSqlObj *pSql, SLocalReducer *pLocalReducer, bool doneO
|
||||||
|
|
||||||
pFinalDataPage->num = 0;
|
pFinalDataPage->num = 0;
|
||||||
for (int32_t i = 0; i < pQueryInfo->fieldsInfo.numOfOutput; ++i) {
|
for (int32_t i = 0; i < pQueryInfo->fieldsInfo.numOfOutput; ++i) {
|
||||||
tfree(pResPages[i]);
|
taosTFree(pResPages[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
tfree(pResPages);
|
taosTFree(pResPages);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void savePreviousRow(SLocalReducer *pLocalReducer, tFilePage *tmpBuffer) {
|
static void savePreviousRow(SLocalReducer *pLocalReducer, tFilePage *tmpBuffer) {
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
#include "tscLog.h"
|
#include "tscLog.h"
|
||||||
#include "tscSubquery.h"
|
#include "tscSubquery.h"
|
||||||
#include "tstoken.h"
|
#include "tstoken.h"
|
||||||
#include "ttime.h"
|
|
||||||
|
|
||||||
#include "tdataformat.h"
|
#include "tdataformat.h"
|
||||||
|
|
||||||
|
@ -1388,7 +1387,7 @@ static void parseFileSendDataBlock(void *param, TAOS_RES *tres, int code) {
|
||||||
assert(taos_errno(pSql) == code);
|
assert(taos_errno(pSql) == code);
|
||||||
|
|
||||||
taos_free_result(pSql);
|
taos_free_result(pSql);
|
||||||
tfree(pSupporter);
|
taosTFree(pSupporter);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
pParentSql->res.code = code;
|
pParentSql->res.code = code;
|
||||||
|
@ -1453,7 +1452,7 @@ static void parseFileSendDataBlock(void *param, TAOS_RES *tres, int code) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tfree(tokenBuf);
|
taosTFree(tokenBuf);
|
||||||
free(line);
|
free(line);
|
||||||
|
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
|
@ -1466,7 +1465,7 @@ static void parseFileSendDataBlock(void *param, TAOS_RES *tres, int code) {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
taos_free_result(pSql);
|
taos_free_result(pSql);
|
||||||
tfree(pSupporter);
|
taosTFree(pSupporter);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
pParentSql->fp = pParentSql->fetchFp;
|
pParentSql->fp = pParentSql->fetchFp;
|
||||||
|
@ -1496,7 +1495,7 @@ void tscProcessMultiVnodesImportFromFile(SSqlObj *pSql) {
|
||||||
pSql->res.code = TAOS_SYSTEM_ERROR(errno);
|
pSql->res.code = TAOS_SYSTEM_ERROR(errno);
|
||||||
tscError("%p failed to open file %s to load data from file, code:%s", pSql, pCmd->payload, tstrerror(pSql->res.code));
|
tscError("%p failed to open file %s to load data from file, code:%s", pSql, pCmd->payload, tstrerror(pSql->res.code));
|
||||||
|
|
||||||
tfree(pSupporter)
|
taosTFree(pSupporter)
|
||||||
tscQueueAsyncRes(pSql);
|
tscQueueAsyncRes(pSql);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -613,7 +613,7 @@ int taos_stmt_execute(TAOS_STMT* stmt) {
|
||||||
if (sql == NULL) {
|
if (sql == NULL) {
|
||||||
ret = TSDB_CODE_TSC_OUT_OF_MEMORY;
|
ret = TSDB_CODE_TSC_OUT_OF_MEMORY;
|
||||||
} else {
|
} else {
|
||||||
tfree(pStmt->pSql->sqlstr);
|
taosTFree(pStmt->pSql->sqlstr);
|
||||||
pStmt->pSql->sqlstr = sql;
|
pStmt->pSql->sqlstr = sql;
|
||||||
SSqlObj* pSql = taos_query((TAOS*)pStmt->taos, pStmt->pSql->sqlstr);
|
SSqlObj* pSql = taos_query((TAOS*)pStmt->taos, pStmt->pSql->sqlstr);
|
||||||
ret = taos_errno(pSql);
|
ret = taos_errno(pSql);
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
#include "tscLog.h"
|
#include "tscLog.h"
|
||||||
#include "tsclient.h"
|
#include "tsclient.h"
|
||||||
#include "ttime.h"
|
|
||||||
#include "ttimer.h"
|
#include "ttimer.h"
|
||||||
#include "tutil.h"
|
#include "tutil.h"
|
||||||
#include "taosmsg.h"
|
#include "taosmsg.h"
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
#include "tsclient.h"
|
#include "tsclient.h"
|
||||||
#include "tstoken.h"
|
#include "tstoken.h"
|
||||||
#include "tstrbuild.h"
|
#include "tstrbuild.h"
|
||||||
#include "ttime.h"
|
|
||||||
#include "ttokendef.h"
|
#include "ttokendef.h"
|
||||||
|
|
||||||
#define DEFAULT_PRIMARY_TIMESTAMP_COL_NAME "_c0"
|
#define DEFAULT_PRIMARY_TIMESTAMP_COL_NAME "_c0"
|
||||||
|
@ -737,7 +736,7 @@ int32_t tscSetTableFullName(STableMetaInfo* pTableMetaInfo, SSQLToken* pzTableNa
|
||||||
assert(pTableMetaInfo->pTableMeta == NULL);
|
assert(pTableMetaInfo->pTableMeta == NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
tfree(oldName);
|
taosTFree(oldName);
|
||||||
return TSDB_CODE_SUCCESS;
|
return TSDB_CODE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3648,7 +3647,7 @@ static int32_t setTableCondForSTableQuery(SSqlCmd* pCmd, SQueryInfo* pQueryInfo,
|
||||||
int32_t ret = setObjFullName(idBuf, account, &dbToken, &t, &xlen);
|
int32_t ret = setObjFullName(idBuf, account, &dbToken, &t, &xlen);
|
||||||
if (ret != TSDB_CODE_SUCCESS) {
|
if (ret != TSDB_CODE_SUCCESS) {
|
||||||
taosStringBuilderDestroy(&sb1);
|
taosStringBuilderDestroy(&sb1);
|
||||||
tfree(segments);
|
taosTFree(segments);
|
||||||
|
|
||||||
invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg);
|
invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -3661,7 +3660,7 @@ static int32_t setTableCondForSTableQuery(SSqlCmd* pCmd, SQueryInfo* pQueryInfo,
|
||||||
pQueryInfo->tagCond.tbnameCond.cond = strdup(str);
|
pQueryInfo->tagCond.tbnameCond.cond = strdup(str);
|
||||||
|
|
||||||
taosStringBuilderDestroy(&sb1);
|
taosStringBuilderDestroy(&sb1);
|
||||||
tfree(segments);
|
taosTFree(segments);
|
||||||
return TSDB_CODE_SUCCESS;
|
return TSDB_CODE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
#include "tscUtil.h"
|
#include "tscUtil.h"
|
||||||
#include "tschemautil.h"
|
#include "tschemautil.h"
|
||||||
#include "tsclient.h"
|
#include "tsclient.h"
|
||||||
#include "ttime.h"
|
|
||||||
#include "ttimer.h"
|
#include "ttimer.h"
|
||||||
#include "tutil.h"
|
#include "tutil.h"
|
||||||
#include "tlockfree.h"
|
#include "tlockfree.h"
|
||||||
|
@ -196,7 +195,7 @@ void tscProcessActivityTimer(void *handle, void *tmrId) {
|
||||||
|
|
||||||
pSql->cmd.command = TSDB_SQL_HB;
|
pSql->cmd.command = TSDB_SQL_HB;
|
||||||
if (TSDB_CODE_SUCCESS != tscAllocPayload(&(pSql->cmd), TSDB_DEFAULT_PAYLOAD_SIZE)) {
|
if (TSDB_CODE_SUCCESS != tscAllocPayload(&(pSql->cmd), TSDB_DEFAULT_PAYLOAD_SIZE)) {
|
||||||
tfree(pSql);
|
taosTFree(pSql);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1538,7 +1537,7 @@ int tscBuildTableMetaMsg(SSqlObj *pSql, SSqlInfo *pInfo) {
|
||||||
pCmd->payloadLen = pMsg - (char*)pInfoMsg;
|
pCmd->payloadLen = pMsg - (char*)pInfoMsg;
|
||||||
pCmd->msgType = TSDB_MSG_TYPE_CM_TABLE_META;
|
pCmd->msgType = TSDB_MSG_TYPE_CM_TABLE_META;
|
||||||
|
|
||||||
tfree(tmpData);
|
taosTFree(tmpData);
|
||||||
|
|
||||||
assert(msgLen + minMsgSize() <= pCmd->allocSize);
|
assert(msgLen + minMsgSize() <= pCmd->allocSize);
|
||||||
return TSDB_CODE_SUCCESS;
|
return TSDB_CODE_SUCCESS;
|
||||||
|
@ -1572,7 +1571,7 @@ int tscBuildMultiMeterMetaMsg(SSqlObj *pSql, SSqlInfo *pInfo) {
|
||||||
memcpy(pInfoMsg->tableIds, tmpData, pCmd->payloadLen);
|
memcpy(pInfoMsg->tableIds, tmpData, pCmd->payloadLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
tfree(tmpData);
|
taosTFree(tmpData);
|
||||||
|
|
||||||
pCmd->payloadLen += sizeof(SMgmtHead) + sizeof(SCMMultiTableInfoMsg);
|
pCmd->payloadLen += sizeof(SMgmtHead) + sizeof(SCMMultiTableInfoMsg);
|
||||||
pCmd->msgType = TSDB_MSG_TYPE_CM_TABLES_META;
|
pCmd->msgType = TSDB_MSG_TYPE_CM_TABLES_META;
|
||||||
|
@ -1962,7 +1961,7 @@ int tscProcessShowRsp(SSqlObj *pSql) {
|
||||||
pCmd->numOfCols = pQueryInfo->fieldsInfo.numOfOutput;
|
pCmd->numOfCols = pQueryInfo->fieldsInfo.numOfOutput;
|
||||||
tscFieldInfoUpdateOffset(pQueryInfo);
|
tscFieldInfoUpdateOffset(pQueryInfo);
|
||||||
|
|
||||||
tfree(pTableMeta);
|
taosTFree(pTableMeta);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -474,7 +474,7 @@ int taos_fetch_block(TAOS_RES *res, TAOS_ROW *rows) {
|
||||||
pRes->rspType = 0;
|
pRes->rspType = 0;
|
||||||
|
|
||||||
pSql->numOfSubs = 0;
|
pSql->numOfSubs = 0;
|
||||||
tfree(pSql->pSubs);
|
taosTFree(pSql->pSubs);
|
||||||
|
|
||||||
assert(pSql->fp == NULL);
|
assert(pSql->fp == NULL);
|
||||||
|
|
||||||
|
@ -753,7 +753,7 @@ int taos_validate_sql(TAOS *taos, const char *sql) {
|
||||||
if (sqlLen > tsMaxSQLStringLen) {
|
if (sqlLen > tsMaxSQLStringLen) {
|
||||||
tscError("%p sql too long", pSql);
|
tscError("%p sql too long", pSql);
|
||||||
pRes->code = TSDB_CODE_TSC_INVALID_SQL;
|
pRes->code = TSDB_CODE_TSC_INVALID_SQL;
|
||||||
tfree(pSql);
|
taosTFree(pSql);
|
||||||
return pRes->code;
|
return pRes->code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -762,7 +762,7 @@ int taos_validate_sql(TAOS *taos, const char *sql) {
|
||||||
pRes->code = TSDB_CODE_TSC_OUT_OF_MEMORY;
|
pRes->code = TSDB_CODE_TSC_OUT_OF_MEMORY;
|
||||||
tscError("%p failed to malloc sql string buffer", pSql);
|
tscError("%p failed to malloc sql string buffer", pSql);
|
||||||
tscDebug("%p Valid SQL result:%d, %s pObj:%p", pSql, pRes->code, taos_errstr(taos), pObj);
|
tscDebug("%p Valid SQL result:%d, %s pObj:%p", pSql, pRes->code, taos_errstr(taos), pObj);
|
||||||
tfree(pSql);
|
taosTFree(pSql);
|
||||||
return pRes->code;
|
return pRes->code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -896,7 +896,7 @@ int taos_load_table_info(TAOS *taos, const char *tableNameList) {
|
||||||
if (tblListLen > MAX_TABLE_NAME_LENGTH) {
|
if (tblListLen > MAX_TABLE_NAME_LENGTH) {
|
||||||
tscError("%p tableNameList too long, length:%d, maximum allowed:%d", pSql, tblListLen, MAX_TABLE_NAME_LENGTH);
|
tscError("%p tableNameList too long, length:%d, maximum allowed:%d", pSql, tblListLen, MAX_TABLE_NAME_LENGTH);
|
||||||
pRes->code = TSDB_CODE_TSC_INVALID_SQL;
|
pRes->code = TSDB_CODE_TSC_INVALID_SQL;
|
||||||
tfree(pSql);
|
taosTFree(pSql);
|
||||||
return pRes->code;
|
return pRes->code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -904,7 +904,7 @@ int taos_load_table_info(TAOS *taos, const char *tableNameList) {
|
||||||
if (str == NULL) {
|
if (str == NULL) {
|
||||||
pRes->code = TSDB_CODE_TSC_OUT_OF_MEMORY;
|
pRes->code = TSDB_CODE_TSC_OUT_OF_MEMORY;
|
||||||
tscError("%p failed to malloc sql string buffer", pSql);
|
tscError("%p failed to malloc sql string buffer", pSql);
|
||||||
tfree(pSql);
|
taosTFree(pSql);
|
||||||
return pRes->code;
|
return pRes->code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#include "tsched.h"
|
#include "tsched.h"
|
||||||
#include "tcache.h"
|
#include "tcache.h"
|
||||||
#include "tsclient.h"
|
#include "tsclient.h"
|
||||||
#include "ttime.h"
|
|
||||||
#include "ttimer.h"
|
#include "ttimer.h"
|
||||||
#include "tutil.h"
|
#include "tutil.h"
|
||||||
|
|
||||||
|
@ -156,7 +155,7 @@ static void tscProcessStreamQueryCallback(void *param, TAOS_RES *tres, int numOf
|
||||||
|
|
||||||
STableMetaInfo* pTableMetaInfo = tscGetTableMetaInfoFromCmd(&pStream->pSql->cmd, 0, 0);
|
STableMetaInfo* pTableMetaInfo = tscGetTableMetaInfoFromCmd(&pStream->pSql->cmd, 0, 0);
|
||||||
taosCacheRelease(tscCacheHandle, (void**)&(pTableMetaInfo->pTableMeta), true);
|
taosCacheRelease(tscCacheHandle, (void**)&(pTableMetaInfo->pTableMeta), true);
|
||||||
tfree(pTableMetaInfo->vgroupList);
|
taosTFree(pTableMetaInfo->vgroupList);
|
||||||
|
|
||||||
tscSetRetryTimer(pStream, pStream->pSql, retryDelay);
|
tscSetRetryTimer(pStream, pStream->pSql, retryDelay);
|
||||||
return;
|
return;
|
||||||
|
@ -260,9 +259,9 @@ static void tscProcessStreamRetrieveResult(void *param, TAOS_RES *res, int numOf
|
||||||
|
|
||||||
taosCacheRelease(tscCacheHandle, (void**)&(pTableMetaInfo->pTableMeta), false);
|
taosCacheRelease(tscCacheHandle, (void**)&(pTableMetaInfo->pTableMeta), false);
|
||||||
tscFreeSqlResult(pSql);
|
tscFreeSqlResult(pSql);
|
||||||
tfree(pSql->pSubs);
|
taosTFree(pSql->pSubs);
|
||||||
pSql->numOfSubs = 0;
|
pSql->numOfSubs = 0;
|
||||||
tfree(pTableMetaInfo->vgroupList);
|
taosTFree(pTableMetaInfo->vgroupList);
|
||||||
tscSetNextLaunchTimer(pStream, pSql);
|
tscSetNextLaunchTimer(pStream, pSql);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -592,6 +591,6 @@ void taos_close_stream(TAOS_STREAM *handle) {
|
||||||
tscFreeSqlObj(pSql);
|
tscFreeSqlObj(pSql);
|
||||||
pStream->pSql = NULL;
|
pStream->pSql = NULL;
|
||||||
|
|
||||||
tfree(pStream);
|
taosTFree(pStream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
#include "trpc.h"
|
#include "trpc.h"
|
||||||
#include "tsclient.h"
|
#include "tsclient.h"
|
||||||
#include "tsocket.h"
|
#include "tsocket.h"
|
||||||
#include "ttime.h"
|
|
||||||
#include "ttimer.h"
|
#include "ttimer.h"
|
||||||
#include "tutil.h"
|
#include "tutil.h"
|
||||||
#include "tscLog.h"
|
#include "tscLog.h"
|
||||||
|
|
|
@ -183,7 +183,7 @@ SJoinSupporter* tscCreateJoinSupporter(SSqlObj* pSql, SSubqueryState* pState, in
|
||||||
pSupporter->uid = pTableMetaInfo->pTableMeta->id.uid;
|
pSupporter->uid = pTableMetaInfo->pTableMeta->id.uid;
|
||||||
assert (pSupporter->uid != 0);
|
assert (pSupporter->uid != 0);
|
||||||
|
|
||||||
getTmpfilePath("join-", pSupporter->path);
|
taosGetTmpfilePath("join-", pSupporter->path);
|
||||||
pSupporter->f = fopen(pSupporter->path, "w");
|
pSupporter->f = fopen(pSupporter->path, "w");
|
||||||
|
|
||||||
// todo handle error
|
// todo handle error
|
||||||
|
@ -215,7 +215,7 @@ static void tscDestroyJoinSupporter(SJoinSupporter* pSupporter) {
|
||||||
pSupporter->f = NULL;
|
pSupporter->f = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
tfree(pSupporter->pIdTagList);
|
taosTFree(pSupporter->pIdTagList);
|
||||||
tscTagCondRelease(&pSupporter->tagCond);
|
tscTagCondRelease(&pSupporter->tagCond);
|
||||||
free(pSupporter);
|
free(pSupporter);
|
||||||
}
|
}
|
||||||
|
@ -407,7 +407,7 @@ void freeJoinSubqueryObj(SSqlObj* pSql) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tfree(pState);
|
taosTFree(pState);
|
||||||
pSql->numOfSubs = 0;
|
pSql->numOfSubs = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -773,7 +773,7 @@ static void tsCompRetrieveCallback(void* param, TAOS_RES* tres, int32_t numOfRow
|
||||||
|
|
||||||
// continue to retrieve ts-comp data from vnode
|
// continue to retrieve ts-comp data from vnode
|
||||||
if (!pRes->completed) {
|
if (!pRes->completed) {
|
||||||
getTmpfilePath("ts-join", pSupporter->path);
|
taosGetTmpfilePath("ts-join", pSupporter->path);
|
||||||
pSupporter->f = fopen(pSupporter->path, "w");
|
pSupporter->f = fopen(pSupporter->path, "w");
|
||||||
pRes->row = pRes->numOfRows;
|
pRes->row = pRes->numOfRows;
|
||||||
|
|
||||||
|
@ -797,7 +797,7 @@ static void tsCompRetrieveCallback(void* param, TAOS_RES* tres, int32_t numOfRow
|
||||||
tscResetForNextRetrieve(&pSql->res);
|
tscResetForNextRetrieve(&pSql->res);
|
||||||
|
|
||||||
assert(pSupporter->f == NULL);
|
assert(pSupporter->f == NULL);
|
||||||
getTmpfilePath("ts-join", pSupporter->path);
|
taosGetTmpfilePath("ts-join", pSupporter->path);
|
||||||
|
|
||||||
// TODO check for failure
|
// TODO check for failure
|
||||||
pSupporter->f = fopen(pSupporter->path, "w");
|
pSupporter->f = fopen(pSupporter->path, "w");
|
||||||
|
@ -1317,12 +1317,12 @@ static void doCleanupSubqueries(SSqlObj *pSql, int32_t numOfSubs, SSubqueryState
|
||||||
|
|
||||||
SRetrieveSupport* pSupport = pSub->param;
|
SRetrieveSupport* pSupport = pSub->param;
|
||||||
|
|
||||||
tfree(pSupport->localBuffer);
|
taosTFree(pSupport->localBuffer);
|
||||||
|
|
||||||
pthread_mutex_unlock(&pSupport->queryMutex);
|
pthread_mutex_unlock(&pSupport->queryMutex);
|
||||||
pthread_mutex_destroy(&pSupport->queryMutex);
|
pthread_mutex_destroy(&pSupport->queryMutex);
|
||||||
|
|
||||||
tfree(pSupport);
|
taosTFree(pSupport);
|
||||||
|
|
||||||
tscFreeSqlObj(pSub);
|
tscFreeSqlObj(pSub);
|
||||||
}
|
}
|
||||||
|
@ -1358,7 +1358,7 @@ int32_t tscHandleMasterSTableQuery(SSqlObj *pSql) {
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
pRes->code = TSDB_CODE_TSC_OUT_OF_MEMORY;
|
pRes->code = TSDB_CODE_TSC_OUT_OF_MEMORY;
|
||||||
tscQueueAsyncRes(pSql);
|
tscQueueAsyncRes(pSql);
|
||||||
tfree(pMemoryBuf);
|
taosTFree(pMemoryBuf);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1386,7 +1386,7 @@ int32_t tscHandleMasterSTableQuery(SSqlObj *pSql) {
|
||||||
trs->localBuffer = (tFilePage *)calloc(1, nBufferSize + sizeof(tFilePage));
|
trs->localBuffer = (tFilePage *)calloc(1, nBufferSize + sizeof(tFilePage));
|
||||||
if (trs->localBuffer == NULL) {
|
if (trs->localBuffer == NULL) {
|
||||||
tscError("%p failed to malloc buffer for local buffer, orderOfSub:%d, reason:%s", pSql, i, strerror(errno));
|
tscError("%p failed to malloc buffer for local buffer, orderOfSub:%d, reason:%s", pSql, i, strerror(errno));
|
||||||
tfree(trs);
|
taosTFree(trs);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1404,8 +1404,8 @@ int32_t tscHandleMasterSTableQuery(SSqlObj *pSql) {
|
||||||
SSqlObj *pNew = tscCreateSqlObjForSubquery(pSql, trs, NULL);
|
SSqlObj *pNew = tscCreateSqlObjForSubquery(pSql, trs, NULL);
|
||||||
if (pNew == NULL) {
|
if (pNew == NULL) {
|
||||||
tscError("%p failed to malloc buffer for subObj, orderOfSub:%d, reason:%s", pSql, i, strerror(errno));
|
tscError("%p failed to malloc buffer for subObj, orderOfSub:%d, reason:%s", pSql, i, strerror(errno));
|
||||||
tfree(trs->localBuffer);
|
taosTFree(trs->localBuffer);
|
||||||
tfree(trs);
|
taosTFree(trs);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1450,12 +1450,12 @@ static void tscFreeSubSqlObj(SRetrieveSupport *trsupport, SSqlObj *pSql) {
|
||||||
|
|
||||||
taos_free_result(pSql);
|
taos_free_result(pSql);
|
||||||
|
|
||||||
tfree(trsupport->localBuffer);
|
taosTFree(trsupport->localBuffer);
|
||||||
|
|
||||||
pthread_mutex_unlock(&trsupport->queryMutex);
|
pthread_mutex_unlock(&trsupport->queryMutex);
|
||||||
pthread_mutex_destroy(&trsupport->queryMutex);
|
pthread_mutex_destroy(&trsupport->queryMutex);
|
||||||
|
|
||||||
tfree(trsupport);
|
taosTFree(trsupport);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tscRetrieveFromDnodeCallBack(void *param, TAOS_RES *tres, int numOfRows);
|
static void tscRetrieveFromDnodeCallBack(void *param, TAOS_RES *tres, int numOfRows);
|
||||||
|
@ -1572,7 +1572,7 @@ void tscHandleSubqueryError(SRetrieveSupport *trsupport, SSqlObj *pSql, int numO
|
||||||
tscLocalReducerEnvDestroy(trsupport->pExtMemBuffer, trsupport->pOrderDescriptor, trsupport->pFinalColModel,
|
tscLocalReducerEnvDestroy(trsupport->pExtMemBuffer, trsupport->pOrderDescriptor, trsupport->pFinalColModel,
|
||||||
pState->numOfTotal);
|
pState->numOfTotal);
|
||||||
|
|
||||||
tfree(trsupport->pState);
|
taosTFree(trsupport->pState);
|
||||||
tscFreeSubSqlObj(trsupport, pSql);
|
tscFreeSubSqlObj(trsupport, pSql);
|
||||||
|
|
||||||
// in case of second stage join subquery, invoke its callback function instead of regular QueueAsyncRes
|
// in case of second stage join subquery, invoke its callback function instead of regular QueueAsyncRes
|
||||||
|
@ -1651,7 +1651,7 @@ static void tscAllDataRetrievedFromDnode(SRetrieveSupport *trsupport, SSqlObj* p
|
||||||
pParentSql->res.row = 0;
|
pParentSql->res.row = 0;
|
||||||
|
|
||||||
// only free once
|
// only free once
|
||||||
tfree(trsupport->pState);
|
taosTFree(trsupport->pState);
|
||||||
tscFreeSubSqlObj(trsupport, pSql);
|
tscFreeSubSqlObj(trsupport, pSql);
|
||||||
|
|
||||||
// set the command flag must be after the semaphore been correctly set.
|
// set the command flag must be after the semaphore been correctly set.
|
||||||
|
@ -1855,7 +1855,7 @@ static void multiVnodeInsertFinalize(void* param, TAOS_RES* tres, int numOfRows)
|
||||||
}
|
}
|
||||||
|
|
||||||
taos_free_result(tres);
|
taos_free_result(tres);
|
||||||
tfree(pSupporter);
|
taosTFree(pSupporter);
|
||||||
|
|
||||||
if (atomic_sub_fetch_32(&pState->numOfRemain, 1) > 0) {
|
if (atomic_sub_fetch_32(&pState->numOfRemain, 1) > 0) {
|
||||||
return;
|
return;
|
||||||
|
@ -1864,7 +1864,7 @@ static void multiVnodeInsertFinalize(void* param, TAOS_RES* tres, int numOfRows)
|
||||||
tscDebug("%p Async insertion completed, total inserted:%" PRId64, pParentObj, pParentObj->res.numOfRows);
|
tscDebug("%p Async insertion completed, total inserted:%" PRId64, pParentObj, pParentObj->res.numOfRows);
|
||||||
|
|
||||||
// release data block data
|
// release data block data
|
||||||
tfree(pState);
|
taosTFree(pState);
|
||||||
|
|
||||||
// restore user defined fp
|
// restore user defined fp
|
||||||
pParentObj->fp = pParentObj->fetchFp;
|
pParentObj->fp = pParentObj->fetchFp;
|
||||||
|
@ -1968,11 +1968,11 @@ int32_t tscHandleMultivnodeInsert(SSqlObj *pSql) {
|
||||||
|
|
||||||
_error:
|
_error:
|
||||||
for(int32_t j = 0; j < numOfSub; ++j) {
|
for(int32_t j = 0; j < numOfSub; ++j) {
|
||||||
tfree(pSql->pSubs[j]->param);
|
taosTFree(pSql->pSubs[j]->param);
|
||||||
taos_free_result(pSql->pSubs[j]);
|
taos_free_result(pSql->pSubs[j]);
|
||||||
}
|
}
|
||||||
|
|
||||||
tfree(pState);
|
taosTFree(pState);
|
||||||
return TSDB_CODE_TSC_OUT_OF_MEMORY;
|
return TSDB_CODE_TSC_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2143,7 +2143,7 @@ void **doSetResultRowData(SSqlObj *pSql, bool finalResult) {
|
||||||
assert(pRes->row >= 0 && pRes->row <= pRes->numOfRows);
|
assert(pRes->row >= 0 && pRes->row <= pRes->numOfRows);
|
||||||
|
|
||||||
if (pRes->row >= pRes->numOfRows) { // all the results has returned to invoker
|
if (pRes->row >= pRes->numOfRows) { // all the results has returned to invoker
|
||||||
tfree(pRes->tsrow);
|
taosTFree(pRes->tsrow);
|
||||||
return pRes->tsrow;
|
return pRes->tsrow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
#include "tcache.h"
|
#include "tcache.h"
|
||||||
#include "trpc.h"
|
#include "trpc.h"
|
||||||
#include "tsystem.h"
|
#include "tsystem.h"
|
||||||
#include "ttime.h"
|
|
||||||
#include "ttimer.h"
|
#include "ttimer.h"
|
||||||
#include "tutil.h"
|
#include "tutil.h"
|
||||||
#include "tsched.h"
|
#include "tsched.h"
|
||||||
|
|
|
@ -245,7 +245,7 @@ void tscClearInterpInfo(SQueryInfo* pQueryInfo) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pQueryInfo->fillType = TSDB_FILL_NONE;
|
pQueryInfo->fillType = TSDB_FILL_NONE;
|
||||||
tfree(pQueryInfo->fillVal);
|
taosTFree(pQueryInfo->fillVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t tscCreateResPointerInfo(SSqlRes* pRes, SQueryInfo* pQueryInfo) {
|
int32_t tscCreateResPointerInfo(SSqlRes* pRes, SQueryInfo* pQueryInfo) {
|
||||||
|
@ -259,9 +259,9 @@ int32_t tscCreateResPointerInfo(SSqlRes* pRes, SQueryInfo* pQueryInfo) {
|
||||||
|
|
||||||
// not enough memory
|
// not enough memory
|
||||||
if (pRes->tsrow == NULL || (pRes->buffer == NULL && pRes->numOfCols > 0)) {
|
if (pRes->tsrow == NULL || (pRes->buffer == NULL && pRes->numOfCols > 0)) {
|
||||||
tfree(pRes->tsrow);
|
taosTFree(pRes->tsrow);
|
||||||
tfree(pRes->buffer);
|
taosTFree(pRes->buffer);
|
||||||
tfree(pRes->length);
|
taosTFree(pRes->length);
|
||||||
|
|
||||||
pRes->code = TSDB_CODE_TSC_OUT_OF_MEMORY;
|
pRes->code = TSDB_CODE_TSC_OUT_OF_MEMORY;
|
||||||
return pRes->code;
|
return pRes->code;
|
||||||
|
@ -274,23 +274,23 @@ int32_t tscCreateResPointerInfo(SSqlRes* pRes, SQueryInfo* pQueryInfo) {
|
||||||
void tscDestroyResPointerInfo(SSqlRes* pRes) {
|
void tscDestroyResPointerInfo(SSqlRes* pRes) {
|
||||||
if (pRes->buffer != NULL) { // free all buffers containing the multibyte string
|
if (pRes->buffer != NULL) { // free all buffers containing the multibyte string
|
||||||
for (int i = 0; i < pRes->numOfCols; i++) {
|
for (int i = 0; i < pRes->numOfCols; i++) {
|
||||||
tfree(pRes->buffer[i]);
|
taosTFree(pRes->buffer[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pRes->numOfCols = 0;
|
pRes->numOfCols = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
tfree(pRes->pRsp);
|
taosTFree(pRes->pRsp);
|
||||||
tfree(pRes->tsrow);
|
taosTFree(pRes->tsrow);
|
||||||
tfree(pRes->length);
|
taosTFree(pRes->length);
|
||||||
|
|
||||||
tfree(pRes->pGroupRec);
|
taosTFree(pRes->pGroupRec);
|
||||||
tfree(pRes->pColumnIndex);
|
taosTFree(pRes->pColumnIndex);
|
||||||
tfree(pRes->buffer);
|
taosTFree(pRes->buffer);
|
||||||
|
|
||||||
if (pRes->pArithSup != NULL) {
|
if (pRes->pArithSup != NULL) {
|
||||||
tfree(pRes->pArithSup->data);
|
taosTFree(pRes->pArithSup->data);
|
||||||
tfree(pRes->pArithSup);
|
taosTFree(pRes->pArithSup);
|
||||||
}
|
}
|
||||||
|
|
||||||
pRes->data = NULL; // pRes->data points to the buffer of pRsp, no need to free
|
pRes->data = NULL; // pRes->data points to the buffer of pRsp, no need to free
|
||||||
|
@ -307,11 +307,11 @@ static void tscFreeQueryInfo(SSqlCmd* pCmd) {
|
||||||
|
|
||||||
freeQueryInfoImpl(pQueryInfo);
|
freeQueryInfoImpl(pQueryInfo);
|
||||||
clearAllTableMetaInfo(pQueryInfo, (const char*)addr, false);
|
clearAllTableMetaInfo(pQueryInfo, (const char*)addr, false);
|
||||||
tfree(pQueryInfo);
|
taosTFree(pQueryInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
pCmd->numOfClause = 0;
|
pCmd->numOfClause = 0;
|
||||||
tfree(pCmd->pQueryInfo);
|
taosTFree(pCmd->pQueryInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tscResetSqlCmdObj(SSqlCmd* pCmd) {
|
void tscResetSqlCmdObj(SSqlCmd* pCmd) {
|
||||||
|
@ -357,13 +357,13 @@ void tscPartiallyFreeSqlObj(SSqlObj* pSql) {
|
||||||
// pSql->sqlstr will be used by tscBuildQueryStreamDesc
|
// pSql->sqlstr will be used by tscBuildQueryStreamDesc
|
||||||
if (pObj->signature == pObj) {
|
if (pObj->signature == pObj) {
|
||||||
//pthread_mutex_lock(&pObj->mutex);
|
//pthread_mutex_lock(&pObj->mutex);
|
||||||
tfree(pSql->sqlstr);
|
taosTFree(pSql->sqlstr);
|
||||||
//pthread_mutex_unlock(&pObj->mutex);
|
//pthread_mutex_unlock(&pObj->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
tscFreeSqlResult(pSql);
|
tscFreeSqlResult(pSql);
|
||||||
|
|
||||||
tfree(pSql->pSubs);
|
taosTFree(pSql->pSubs);
|
||||||
pSql->numOfSubs = 0;
|
pSql->numOfSubs = 0;
|
||||||
|
|
||||||
tscResetSqlCmdObj(pCmd);
|
tscResetSqlCmdObj(pCmd);
|
||||||
|
@ -383,10 +383,10 @@ void tscFreeSqlObj(SSqlObj* pSql) {
|
||||||
SSqlCmd* pCmd = &pSql->cmd;
|
SSqlCmd* pCmd = &pSql->cmd;
|
||||||
|
|
||||||
memset(pCmd->payload, 0, (size_t)pCmd->allocSize);
|
memset(pCmd->payload, 0, (size_t)pCmd->allocSize);
|
||||||
tfree(pCmd->payload);
|
taosTFree(pCmd->payload);
|
||||||
pCmd->allocSize = 0;
|
pCmd->allocSize = 0;
|
||||||
|
|
||||||
tfree(pSql->sqlstr);
|
taosTFree(pSql->sqlstr);
|
||||||
sem_destroy(&pSql->rspSem);
|
sem_destroy(&pSql->rspSem);
|
||||||
free(pSql);
|
free(pSql);
|
||||||
}
|
}
|
||||||
|
@ -396,12 +396,12 @@ void tscDestroyDataBlock(STableDataBlocks* pDataBlock) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tfree(pDataBlock->pData);
|
taosTFree(pDataBlock->pData);
|
||||||
tfree(pDataBlock->params);
|
taosTFree(pDataBlock->params);
|
||||||
|
|
||||||
// free the refcount for metermeta
|
// free the refcount for metermeta
|
||||||
taosCacheRelease(tscCacheHandle, (void**)&(pDataBlock->pTableMeta), false);
|
taosCacheRelease(tscCacheHandle, (void**)&(pDataBlock->pTableMeta), false);
|
||||||
tfree(pDataBlock);
|
taosTFree(pDataBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
SParamInfo* tscAddParamToDataBlock(STableDataBlocks* pDataBlock, char type, uint8_t timePrec, short bytes,
|
SParamInfo* tscAddParamToDataBlock(STableDataBlocks* pDataBlock, char type, uint8_t timePrec, short bytes,
|
||||||
|
@ -679,7 +679,7 @@ int32_t tscMergeTableDataBlocks(SSqlObj* pSql, SArray* pTableDataBlockList) {
|
||||||
|
|
||||||
taosHashCleanup(pVnodeDataBlockHashList);
|
taosHashCleanup(pVnodeDataBlockHashList);
|
||||||
tscDestroyBlockArrayList(pVnodeDataBlockList);
|
tscDestroyBlockArrayList(pVnodeDataBlockList);
|
||||||
tfree(dataBuf->pData);
|
taosTFree(dataBuf->pData);
|
||||||
|
|
||||||
return TSDB_CODE_TSC_OUT_OF_MEMORY;
|
return TSDB_CODE_TSC_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
|
@ -735,7 +735,7 @@ void tscCloseTscObj(STscObj* pObj) {
|
||||||
}
|
}
|
||||||
|
|
||||||
tscDebug("%p DB connection is closed, dnodeConn:%p", pObj, pObj->pDnodeConn);
|
tscDebug("%p DB connection is closed, dnodeConn:%p", pObj, pObj->pDnodeConn);
|
||||||
tfree(pObj);
|
taosTFree(pObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tscIsInsertData(char* sqlstr) {
|
bool tscIsInsertData(char* sqlstr) {
|
||||||
|
@ -916,7 +916,7 @@ void tscFieldInfoClear(SFieldInfo* pFieldInfo) {
|
||||||
|
|
||||||
if (pInfo->pArithExprInfo != NULL) {
|
if (pInfo->pArithExprInfo != NULL) {
|
||||||
tExprTreeDestroy(&pInfo->pArithExprInfo->pExpr, NULL);
|
tExprTreeDestroy(&pInfo->pArithExprInfo->pExpr, NULL);
|
||||||
tfree(pInfo->pArithExprInfo);
|
taosTFree(pInfo->pArithExprInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1029,7 +1029,7 @@ void* sqlExprDestroy(SSqlExpr* pExpr) {
|
||||||
tVariantDestroy(&pExpr->param[i]);
|
tVariantDestroy(&pExpr->param[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
tfree(pExpr);
|
taosTFree(pExpr);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -1116,11 +1116,11 @@ SColumn* tscColumnListInsert(SArray* pColumnList, SColumnIndex* pColIndex) {
|
||||||
static void destroyFilterInfo(SColumnFilterInfo* pFilterInfo, int32_t numOfFilters) {
|
static void destroyFilterInfo(SColumnFilterInfo* pFilterInfo, int32_t numOfFilters) {
|
||||||
for(int32_t i = 0; i < numOfFilters; ++i) {
|
for(int32_t i = 0; i < numOfFilters; ++i) {
|
||||||
if (pFilterInfo[i].filterstr) {
|
if (pFilterInfo[i].filterstr) {
|
||||||
tfree(pFilterInfo[i].pz);
|
taosTFree(pFilterInfo[i].pz);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tfree(pFilterInfo);
|
taosTFree(pFilterInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
SColumn* tscColumnClone(const SColumn* src) {
|
SColumn* tscColumnClone(const SColumn* src) {
|
||||||
|
@ -1351,7 +1351,7 @@ void tscTagCondRelease(STagCond* pTagCond) {
|
||||||
size_t s = taosArrayGetSize(pTagCond->pCond);
|
size_t s = taosArrayGetSize(pTagCond->pCond);
|
||||||
for (int32_t i = 0; i < s; ++i) {
|
for (int32_t i = 0; i < s; ++i) {
|
||||||
SCond* p = taosArrayGet(pTagCond->pCond, i);
|
SCond* p = taosArrayGet(pTagCond->pCond, i);
|
||||||
tfree(p->cond);
|
taosTFree(p->cond);
|
||||||
}
|
}
|
||||||
|
|
||||||
taosArrayDestroy(pTagCond->pCond);
|
taosArrayDestroy(pTagCond->pCond);
|
||||||
|
@ -1546,7 +1546,7 @@ static void freeQueryInfoImpl(SQueryInfo* pQueryInfo) {
|
||||||
|
|
||||||
pQueryInfo->tsBuf = tsBufDestroy(pQueryInfo->tsBuf);
|
pQueryInfo->tsBuf = tsBufDestroy(pQueryInfo->tsBuf);
|
||||||
|
|
||||||
tfree(pQueryInfo->fillVal);
|
taosTFree(pQueryInfo->fillVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tscClearSubqueryInfo(SSqlCmd* pCmd) {
|
void tscClearSubqueryInfo(SSqlCmd* pCmd) {
|
||||||
|
@ -1566,7 +1566,7 @@ void clearAllTableMetaInfo(SQueryInfo* pQueryInfo, const char* address, bool rem
|
||||||
free(pTableMetaInfo);
|
free(pTableMetaInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
tfree(pQueryInfo->pTableMetaInfo);
|
taosTFree(pQueryInfo->pTableMetaInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
STableMetaInfo* tscAddTableMetaInfo(SQueryInfo* pQueryInfo, const char* name, STableMeta* pTableMeta,
|
STableMetaInfo* tscAddTableMetaInfo(SQueryInfo* pQueryInfo, const char* name, STableMeta* pTableMeta,
|
||||||
|
@ -1613,7 +1613,7 @@ void tscClearTableMetaInfo(STableMetaInfo* pTableMetaInfo, bool removeFromCache)
|
||||||
}
|
}
|
||||||
|
|
||||||
taosCacheRelease(tscCacheHandle, (void**)&(pTableMetaInfo->pTableMeta), removeFromCache);
|
taosCacheRelease(tscCacheHandle, (void**)&(pTableMetaInfo->pTableMeta), removeFromCache);
|
||||||
tfree(pTableMetaInfo->vgroupList);
|
taosTFree(pTableMetaInfo->vgroupList);
|
||||||
|
|
||||||
tscColumnListDestroy(pTableMetaInfo->tagColList);
|
tscColumnListDestroy(pTableMetaInfo->tagColList);
|
||||||
pTableMetaInfo->tagColList = NULL;
|
pTableMetaInfo->tagColList = NULL;
|
||||||
|
@ -2084,7 +2084,7 @@ void tscTryQueryNextClause(SSqlObj* pSql, __async_cb_func_t fp) {
|
||||||
|
|
||||||
pRes->numOfTotal = num;
|
pRes->numOfTotal = num;
|
||||||
|
|
||||||
tfree(pSql->pSubs);
|
taosTFree(pSql->pSubs);
|
||||||
pSql->numOfSubs = 0;
|
pSql->numOfSubs = 0;
|
||||||
pSql->fp = fp;
|
pSql->fp = fp;
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
|
#include "os.h"
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "taos.h"
|
#include "taos.h"
|
||||||
#include "tstoken.h"
|
#include "tstoken.h"
|
||||||
#include "ttime.h"
|
|
||||||
#include "tutil.h"
|
#include "tutil.h"
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
|
|
|
@ -80,7 +80,7 @@ typedef struct {
|
||||||
#define schemaFLen(s) ((s)->flen)
|
#define schemaFLen(s) ((s)->flen)
|
||||||
#define schemaVLen(s) ((s)->vlen)
|
#define schemaVLen(s) ((s)->vlen)
|
||||||
#define schemaColAt(s, i) ((s)->columns + i)
|
#define schemaColAt(s, i) ((s)->columns + i)
|
||||||
#define tdFreeSchema(s) tfree((s))
|
#define tdFreeSchema(s) taosTFree((s))
|
||||||
|
|
||||||
STSchema *tdDupSchema(STSchema *pSchema);
|
STSchema *tdDupSchema(STSchema *pSchema);
|
||||||
int tdEncodeSchema(void **buf, STSchema *pSchema);
|
int tdEncodeSchema(void **buf, STSchema *pSchema);
|
||||||
|
@ -284,7 +284,7 @@ typedef struct {
|
||||||
#define kvRowCpy(dst, r) memcpy((dst), (r), kvRowLen(r))
|
#define kvRowCpy(dst, r) memcpy((dst), (r), kvRowLen(r))
|
||||||
#define kvRowColVal(r, colIdx) POINTER_SHIFT(kvRowValues(r), (colIdx)->offset)
|
#define kvRowColVal(r, colIdx) POINTER_SHIFT(kvRowValues(r), (colIdx)->offset)
|
||||||
#define kvRowColIdxAt(r, i) (kvRowColIdx(r) + (i))
|
#define kvRowColIdxAt(r, i) (kvRowColIdx(r) + (i))
|
||||||
#define kvRowFree(r) tfree(r)
|
#define kvRowFree(r) taosTFree(r)
|
||||||
#define kvRowEnd(r) POINTER_SHIFT(r, kvRowLen(r))
|
#define kvRowEnd(r) POINTER_SHIFT(r, kvRowLen(r))
|
||||||
|
|
||||||
SKVRow tdKVRowDup(SKVRow row);
|
SKVRow tdKVRowDup(SKVRow row);
|
||||||
|
|
|
@ -93,7 +93,7 @@ int tdInitTSchemaBuilder(STSchemaBuilder *pBuilder, int32_t version) {
|
||||||
|
|
||||||
void tdDestroyTSchemaBuilder(STSchemaBuilder *pBuilder) {
|
void tdDestroyTSchemaBuilder(STSchemaBuilder *pBuilder) {
|
||||||
if (pBuilder) {
|
if (pBuilder) {
|
||||||
tfree(pBuilder->columns);
|
taosTFree(pBuilder->columns);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,8 +361,8 @@ int tdInitDataCols(SDataCols *pCols, STSchema *pSchema) {
|
||||||
|
|
||||||
void tdFreeDataCols(SDataCols *pCols) {
|
void tdFreeDataCols(SDataCols *pCols) {
|
||||||
if (pCols) {
|
if (pCols) {
|
||||||
tfree(pCols->buf);
|
taosTFree(pCols->buf);
|
||||||
tfree(pCols->cols);
|
taosTFree(pCols->cols);
|
||||||
free(pCols);
|
free(pCols);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -685,8 +685,8 @@ int tdInitKVRowBuilder(SKVRowBuilder *pBuilder) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void tdDestroyKVRowBuilder(SKVRowBuilder *pBuilder) {
|
void tdDestroyKVRowBuilder(SKVRowBuilder *pBuilder) {
|
||||||
tfree(pBuilder->pColIdx);
|
taosTFree(pBuilder->pColIdx);
|
||||||
tfree(pBuilder->buf);
|
taosTFree(pBuilder->buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tdResetKVRowBuilder(SKVRowBuilder *pBuilder) {
|
void tdResetKVRowBuilder(SKVRowBuilder *pBuilder) {
|
||||||
|
|
|
@ -111,7 +111,7 @@ int32_t tsFsyncPeriod = TSDB_DEFAULT_FSYNC_PERIOD;
|
||||||
int32_t tsReplications = TSDB_DEFAULT_DB_REPLICA_OPTION;
|
int32_t tsReplications = TSDB_DEFAULT_DB_REPLICA_OPTION;
|
||||||
int32_t tsQuorum = TSDB_DEFAULT_DB_QUORUM_OPTION;
|
int32_t tsQuorum = TSDB_DEFAULT_DB_QUORUM_OPTION;
|
||||||
int32_t tsMaxVgroupsPerDb = 0;
|
int32_t tsMaxVgroupsPerDb = 0;
|
||||||
int32_t tsMinTablePerVnode = 100;
|
int32_t tsMinTablePerVnode = TSDB_TABLES_STEP;
|
||||||
int32_t tsMaxTablePerVnode = TSDB_DEFAULT_TABLES;
|
int32_t tsMaxTablePerVnode = TSDB_DEFAULT_TABLES;
|
||||||
int32_t tsTableIncStepPerVnode = TSDB_TABLES_STEP;
|
int32_t tsTableIncStepPerVnode = TSDB_TABLES_STEP;
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,7 @@ void tVariantDestroy(tVariant *pVar) {
|
||||||
if (pVar == NULL) return;
|
if (pVar == NULL) return;
|
||||||
|
|
||||||
if (pVar->nType == TSDB_DATA_TYPE_BINARY || pVar->nType == TSDB_DATA_TYPE_NCHAR) {
|
if (pVar->nType == TSDB_DATA_TYPE_BINARY || pVar->nType == TSDB_DATA_TYPE_NCHAR) {
|
||||||
tfree(pVar->pz);
|
taosTFree(pVar->pz);
|
||||||
pVar->nLen = 0;
|
pVar->nLen = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,12 +52,7 @@
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-lang3</artifactId>
|
<artifactId>commons-lang3</artifactId>
|
||||||
<version>${commons-lang3.version}</version>
|
<version>${commons-lang3.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.jacoco</groupId>
|
|
||||||
<artifactId>jacoco-maven-plugin</artifactId>
|
|
||||||
<version>0.8.3</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
|
@ -105,32 +100,7 @@
|
||||||
<configuration>
|
<configuration>
|
||||||
<testFailureIgnore>true</testFailureIgnore>
|
<testFailureIgnore>true</testFailureIgnore>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
|
||||||
<groupId>org.jacoco</groupId>
|
|
||||||
<artifactId>jacoco-maven-plugin</artifactId>
|
|
||||||
<version>0.8.3</version>
|
|
||||||
<configuration>
|
|
||||||
<includes>
|
|
||||||
<include>com/**/*</include>
|
|
||||||
</includes>
|
|
||||||
</configuration>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>pre-test</id>
|
|
||||||
<goals>
|
|
||||||
<goal>prepare-agent</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
<execution>
|
|
||||||
<id>post-test</id>
|
|
||||||
<phase>test</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>report</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -10,6 +10,7 @@ const Struct = require('ref-struct');
|
||||||
const FieldTypes = require('./constants');
|
const FieldTypes = require('./constants');
|
||||||
const errors = require ('./error');
|
const errors = require ('./error');
|
||||||
const TaosObjects = require('./taosobjects');
|
const TaosObjects = require('./taosobjects');
|
||||||
|
const { NULL_POINTER } = require('ref');
|
||||||
|
|
||||||
module.exports = CTaosInterface;
|
module.exports = CTaosInterface;
|
||||||
|
|
||||||
|
@ -177,9 +178,10 @@ var char_arr = ArrayType(ref.types.char);
|
||||||
var TaosField = Struct({
|
var TaosField = Struct({
|
||||||
'name': char_arr,
|
'name': char_arr,
|
||||||
});
|
});
|
||||||
TaosField.fields.name.type.size = 64;
|
TaosField.fields.name.type.size = 65;
|
||||||
TaosField.defineProperty('bytes', ref.types.short);
|
|
||||||
TaosField.defineProperty('type', ref.types.char);
|
TaosField.defineProperty('type', ref.types.char);
|
||||||
|
TaosField.defineProperty('bytes', ref.types.short);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -202,10 +204,10 @@ function CTaosInterface (config = null, pass = false) {
|
||||||
'taos_connect': [ ref.types.void_ptr, [ ref.types.char_ptr, ref.types.char_ptr, ref.types.char_ptr, ref.types.char_ptr, ref.types.int ] ],
|
'taos_connect': [ ref.types.void_ptr, [ ref.types.char_ptr, ref.types.char_ptr, ref.types.char_ptr, ref.types.char_ptr, ref.types.int ] ],
|
||||||
//void taos_close(TAOS *taos)
|
//void taos_close(TAOS *taos)
|
||||||
'taos_close': [ ref.types.void, [ ref.types.void_ptr ] ],
|
'taos_close': [ ref.types.void, [ ref.types.void_ptr ] ],
|
||||||
//TAOS_RES *taos_use_result(TAOS *taos);
|
//int *taos_fetch_lengths(TAOS_RES *taos);
|
||||||
'taos_use_result': [ ref.types.void_ptr, [ ref.types.void_ptr ] ],
|
'taos_fetch_lengths': [ ref.types.void_ptr, [ ref.types.void_ptr ] ],
|
||||||
//int taos_query(TAOS *taos, char *sqlstr)
|
//int taos_query(TAOS *taos, char *sqlstr)
|
||||||
'taos_query': [ ref.types.int, [ ref.types.void_ptr, ref.types.char_ptr ] ],
|
'taos_query': [ ref.types.void_ptr, [ ref.types.void_ptr, ref.types.char_ptr ] ],
|
||||||
//int taos_affected_rows(TAOS *taos)
|
//int taos_affected_rows(TAOS *taos)
|
||||||
'taos_affected_rows': [ ref.types.int, [ ref.types.void_ptr] ],
|
'taos_affected_rows': [ ref.types.int, [ ref.types.void_ptr] ],
|
||||||
//int taos_fetch_block(TAOS_RES *res, TAOS_ROW *rows)
|
//int taos_fetch_block(TAOS_RES *res, TAOS_ROW *rows)
|
||||||
|
@ -329,22 +331,22 @@ CTaosInterface.prototype.query = function query(connection, sql) {
|
||||||
CTaosInterface.prototype.affectedRows = function affectedRows(connection) {
|
CTaosInterface.prototype.affectedRows = function affectedRows(connection) {
|
||||||
return this.libtaos.taos_affected_rows(connection);
|
return this.libtaos.taos_affected_rows(connection);
|
||||||
}
|
}
|
||||||
CTaosInterface.prototype.useResult = function useResult(connection) {
|
CTaosInterface.prototype.useResult = function useResult(result) {
|
||||||
let result = this.libtaos.taos_use_result(connection);
|
|
||||||
let fields = [];
|
let fields = [];
|
||||||
let pfields = this.fetchFields(result);
|
let pfields = this.fetchFields(result);
|
||||||
if (ref.isNull(pfields) == false) {
|
if (ref.isNull(pfields) == false) {
|
||||||
pfields = ref.reinterpret(pfields, this.fieldsCount(connection) * 68, 0);
|
pfields = ref.reinterpret(pfields, this.fieldsCount(result) * 68, 0);
|
||||||
for (let i = 0; i < pfields.length; i += 68) {
|
for (let i = 0; i < pfields.length; i += 68) {
|
||||||
//0 - 63 = name //64 - 65 = bytes, 66 - 67 = type
|
//0 - 63 = name //64 - 65 = bytes, 66 - 67 = type
|
||||||
fields.push( {
|
fields.push( {
|
||||||
name: ref.readCString(ref.reinterpret(pfields,64,i)),
|
name: ref.readCString(ref.reinterpret(pfields,65,i)),
|
||||||
bytes: pfields[i + 64],
|
type: pfields[i + 65],
|
||||||
type: pfields[i + 66]
|
bytes: pfields[i + 66]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {result:result, fields:fields}
|
return fields;
|
||||||
}
|
}
|
||||||
CTaosInterface.prototype.fetchBlock = function fetchBlock(result, fields) {
|
CTaosInterface.prototype.fetchBlock = function fetchBlock(result, fields) {
|
||||||
let pblock = ref.ref(ref.ref(ref.NULL)); // equal to our raw data
|
let pblock = ref.ref(ref.ref(ref.NULL)); // equal to our raw data
|
||||||
|
@ -352,19 +354,33 @@ CTaosInterface.prototype.fetchBlock = function fetchBlock(result, fields) {
|
||||||
if (num_of_rows == 0) {
|
if (num_of_rows == 0) {
|
||||||
return {block:null, num_of_rows:0};
|
return {block:null, num_of_rows:0};
|
||||||
}
|
}
|
||||||
let isMicro = (this.libtaos.taos_result_precision(result) == FieldTypes.C_TIMESTAMP_MICRO)
|
var fieldL = this.libtaos.taos_fetch_lengths(result);
|
||||||
|
|
||||||
|
let isMicro = (this.libtaos.taos_result_precision(result) == FieldTypes.C_TIMESTAMP_MICRO);
|
||||||
|
|
||||||
|
var fieldlens = [];
|
||||||
|
|
||||||
|
if (ref.isNull(fieldL) == false) {
|
||||||
|
|
||||||
|
for (let i = 0; i < fields.length; i ++) {
|
||||||
|
let plen = ref.reinterpret(fieldL, 4, i*4);
|
||||||
|
let len = plen.readInt32LE(0);
|
||||||
|
fieldlens.push(len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let blocks = new Array(fields.length);
|
let blocks = new Array(fields.length);
|
||||||
blocks.fill(null);
|
blocks.fill(null);
|
||||||
num_of_rows = Math.abs(num_of_rows);
|
num_of_rows = Math.abs(num_of_rows);
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
pblock = pblock.deref();
|
pblock = pblock.deref();
|
||||||
for (let i = 0; i < fields.length; i++) {
|
for (let i = 0; i < fields.length; i++) {
|
||||||
|
pdata = ref.reinterpret(pblock,8,i*8);
|
||||||
|
pdata = ref.ref(pdata.readPointer());
|
||||||
if (!convertFunctions[fields[i]['type']] ) {
|
if (!convertFunctions[fields[i]['type']] ) {
|
||||||
throw new errors.DatabaseError("Invalid data type returned from database");
|
throw new errors.DatabaseError("Invalid data type returned from database");
|
||||||
}
|
}
|
||||||
blocks[i] = convertFunctions[fields[i]['type']](pblock, num_of_rows, fields[i]['bytes'], offset, isMicro);
|
blocks[i] = convertFunctions[fields[i]['type']](pdata, 1, fieldlens[i], offset, isMicro);
|
||||||
offset += fields[i]['bytes'] * num_of_rows;
|
|
||||||
}
|
}
|
||||||
return {blocks: blocks, num_of_rows:Math.abs(num_of_rows)}
|
return {blocks: blocks, num_of_rows:Math.abs(num_of_rows)}
|
||||||
}
|
}
|
||||||
|
@ -381,17 +397,17 @@ CTaosInterface.prototype.numFields = function numFields(result) {
|
||||||
return this.libtaos.taos_num_fields(result);
|
return this.libtaos.taos_num_fields(result);
|
||||||
}
|
}
|
||||||
// Fetch fields count by connection, the latest query
|
// Fetch fields count by connection, the latest query
|
||||||
CTaosInterface.prototype.fieldsCount = function fieldsCount(connection) {
|
CTaosInterface.prototype.fieldsCount = function fieldsCount(result) {
|
||||||
return this.libtaos.taos_field_count(connection);
|
return this.libtaos.taos_field_count(result);
|
||||||
}
|
}
|
||||||
CTaosInterface.prototype.fetchFields = function fetchFields(result) {
|
CTaosInterface.prototype.fetchFields = function fetchFields(result) {
|
||||||
return this.libtaos.taos_fetch_fields(result);
|
return this.libtaos.taos_fetch_fields(result);
|
||||||
}
|
}
|
||||||
CTaosInterface.prototype.errno = function errno(connection) {
|
CTaosInterface.prototype.errno = function errno(result) {
|
||||||
return this.libtaos.taos_errno(connection);
|
return this.libtaos.taos_errno(result);
|
||||||
}
|
}
|
||||||
CTaosInterface.prototype.errStr = function errStr(connection) {
|
CTaosInterface.prototype.errStr = function errStr(result) {
|
||||||
return ref.readCString(this.libtaos.taos_errstr(connection));
|
return ref.readCString(this.libtaos.taos_errstr(result));
|
||||||
}
|
}
|
||||||
// Async
|
// Async
|
||||||
CTaosInterface.prototype.query_a = function query_a(connection, sql, callback, param = ref.ref(ref.NULL)) {
|
CTaosInterface.prototype.query_a = function query_a(connection, sql, callback, param = ref.ref(ref.NULL)) {
|
||||||
|
@ -412,18 +428,32 @@ CTaosInterface.prototype.fetch_rows_a = function fetch_rows_a(result, callback,
|
||||||
// Data preparation to pass to cursor. Could be bottleneck in query execution callback times.
|
// Data preparation to pass to cursor. Could be bottleneck in query execution callback times.
|
||||||
let row = cti.libtaos.taos_fetch_row(result2);
|
let row = cti.libtaos.taos_fetch_row(result2);
|
||||||
let fields = cti.fetchFields_a(result2);
|
let fields = cti.fetchFields_a(result2);
|
||||||
|
|
||||||
let isMicro = (cti.libtaos.taos_result_precision(result2) == FieldTypes.C_TIMESTAMP_MICRO);
|
let isMicro = (cti.libtaos.taos_result_precision(result2) == FieldTypes.C_TIMESTAMP_MICRO);
|
||||||
let blocks = new Array(fields.length);
|
let blocks = new Array(fields.length);
|
||||||
blocks.fill(null);
|
blocks.fill(null);
|
||||||
numOfRows2 = Math.abs(numOfRows2);
|
numOfRows2 = Math.abs(numOfRows2);
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
|
var fieldL = cti.libtaos.taos_fetch_lengths(result);
|
||||||
|
var fieldlens = [];
|
||||||
|
if (ref.isNull(fieldL) == false) {
|
||||||
|
|
||||||
|
for (let i = 0; i < fields.length; i ++) {
|
||||||
|
let plen = ref.reinterpret(fieldL, 8, i*8);
|
||||||
|
let len = ref.get(plen,0,ref.types.int32);
|
||||||
|
fieldlens.push(len);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (numOfRows2 > 0){
|
if (numOfRows2 > 0){
|
||||||
for (let i = 0; i < fields.length; i++) {
|
for (let i = 0; i < fields.length; i++) {
|
||||||
if (!convertFunctions[fields[i]['type']] ) {
|
if (!convertFunctions[fields[i]['type']] ) {
|
||||||
throw new errors.DatabaseError("Invalid data type returned from database");
|
throw new errors.DatabaseError("Invalid data type returned from database");
|
||||||
}
|
}
|
||||||
blocks[i] = convertFunctions[fields[i]['type']](row, numOfRows2, fields[i]['bytes'], offset, isMicro);
|
let prow = ref.reinterpret(row,8,i*8);
|
||||||
offset += fields[i]['bytes'] * numOfRows2;
|
prow = prow.readPointer();
|
||||||
|
prow = ref.ref(prow);
|
||||||
|
blocks[i] = convertFunctions[fields[i]['type']](prow, 1, fieldlens[i], offset, isMicro);
|
||||||
|
//offset += fields[i]['bytes'] * numOfRows2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
callback(param2, result2, numOfRows2, blocks);
|
callback(param2, result2, numOfRows2, blocks);
|
||||||
|
@ -440,11 +470,11 @@ CTaosInterface.prototype.fetchFields_a = function fetchFields_a (result) {
|
||||||
if (ref.isNull(pfields) == false) {
|
if (ref.isNull(pfields) == false) {
|
||||||
pfields = ref.reinterpret(pfields, 68 * pfieldscount , 0);
|
pfields = ref.reinterpret(pfields, 68 * pfieldscount , 0);
|
||||||
for (let i = 0; i < pfields.length; i += 68) {
|
for (let i = 0; i < pfields.length; i += 68) {
|
||||||
//0 - 63 = name //64 - 65 = bytes, 66 - 67 = type
|
//0 - 64 = name //65 = type, 66 - 67 = bytes
|
||||||
fields.push( {
|
fields.push( {
|
||||||
name: ref.readCString(ref.reinterpret(pfields,64,i)),
|
name: ref.readCString(ref.reinterpret(pfields,65,i)),
|
||||||
bytes: pfields[i + 64],
|
type: pfields[i + 65],
|
||||||
type: pfields[i + 66]
|
bytes: pfields[i + 66]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,9 +74,11 @@ TDengineConnection.prototype.rollback = function rollback() {
|
||||||
* Clear the results from connector
|
* Clear the results from connector
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
TDengineConnection.prototype._clearResultSet = function _clearResultSet() {
|
/*
|
||||||
|
TDengineConnection.prototype._clearResultSet = function _clearResultSet() {
|
||||||
var result = this._chandle.useResult(this._conn).result;
|
var result = this._chandle.useResult(this._conn).result;
|
||||||
if (result) {
|
if (result) {
|
||||||
this._chandle.freeResult(result)
|
this._chandle.freeResult(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
|
@ -98,7 +98,7 @@ TDengineCursor.prototype.execute = function execute(operation, options, callback
|
||||||
if (this._connection == null) {
|
if (this._connection == null) {
|
||||||
throw new errors.ProgrammingError('Cursor is not connected');
|
throw new errors.ProgrammingError('Cursor is not connected');
|
||||||
}
|
}
|
||||||
this._connection._clearResultSet();
|
|
||||||
this._reset_result();
|
this._reset_result();
|
||||||
|
|
||||||
let stmt = operation;
|
let stmt = operation;
|
||||||
|
@ -111,18 +111,18 @@ TDengineCursor.prototype.execute = function execute(operation, options, callback
|
||||||
});
|
});
|
||||||
obs.observe({ entryTypes: ['measure'] });
|
obs.observe({ entryTypes: ['measure'] });
|
||||||
performance.mark('A');
|
performance.mark('A');
|
||||||
res = this._chandle.query(this._connection._conn, stmt);
|
this._result = this._chandle.query(this._connection._conn, stmt);
|
||||||
performance.mark('B');
|
performance.mark('B');
|
||||||
performance.measure('query', 'A', 'B');
|
performance.measure('query', 'A', 'B');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res = this._chandle.query(this._connection._conn, stmt);
|
this._result = this._chandle.query(this._connection._conn, stmt);
|
||||||
}
|
}
|
||||||
|
res = this._chandle.errno(this._result);
|
||||||
if (res == 0) {
|
if (res == 0) {
|
||||||
let fieldCount = this._chandle.fieldsCount(this._connection._conn);
|
let fieldCount = this._chandle.fieldsCount(this._result);
|
||||||
if (fieldCount == 0) {
|
if (fieldCount == 0) {
|
||||||
let affectedRowCount = this._chandle.affectedRows(this._connection._conn);
|
let affectedRowCount = this._chandle.affectedRows(this._result);
|
||||||
let response = this._createAffectedResponse(affectedRowCount, time)
|
let response = this._createAffectedResponse(affectedRowCount, time)
|
||||||
if (options['quiet'] != true) {
|
if (options['quiet'] != true) {
|
||||||
console.log(response);
|
console.log(response);
|
||||||
|
@ -131,16 +131,15 @@ TDengineCursor.prototype.execute = function execute(operation, options, callback
|
||||||
return affectedRowCount; //return num of affected rows, common with insert, use statements
|
return affectedRowCount; //return num of affected rows, common with insert, use statements
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let resAndField = this._chandle.useResult(this._connection._conn, fieldCount)
|
this._fields = this._chandle.useResult(this._result);
|
||||||
this._result = resAndField.result;
|
this.fields = this._fields;
|
||||||
this._fields = resAndField.fields;
|
|
||||||
this.fields = resAndField.fields;
|
|
||||||
wrapCB(callback);
|
wrapCB(callback);
|
||||||
|
|
||||||
return this._result; //return a pointer to the result
|
return this._result; //return a pointer to the result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new errors.ProgrammingError(this._chandle.errStr(this._connection._conn))
|
throw new errors.ProgrammingError(this._chandle.errStr(this._result))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -198,18 +197,18 @@ TDengineCursor.prototype.fetchall = function fetchall(options, callback) {
|
||||||
while(true) {
|
while(true) {
|
||||||
|
|
||||||
let blockAndRows = this._chandle.fetchBlock(this._result, this._fields);
|
let blockAndRows = this._chandle.fetchBlock(this._result, this._fields);
|
||||||
|
|
||||||
let block = blockAndRows.blocks;
|
let block = blockAndRows.blocks;
|
||||||
let num_of_rows = blockAndRows.num_of_rows;
|
let num_of_rows = blockAndRows.num_of_rows;
|
||||||
|
|
||||||
if (num_of_rows == 0) {
|
if (num_of_rows == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this._rowcount += num_of_rows;
|
this._rowcount += num_of_rows;
|
||||||
|
let numoffields = this._fields.length;
|
||||||
for (let i = 0; i < num_of_rows; i++) {
|
for (let i = 0; i < num_of_rows; i++) {
|
||||||
data.push([]);
|
data.push([]);
|
||||||
let rowBlock = new Array(this._fields.length);
|
|
||||||
for (let j = 0; j < this._fields.length; j++) {
|
let rowBlock = new Array(numoffields);
|
||||||
|
for (let j = 0; j < numoffields; j++) {
|
||||||
rowBlock[j] = block[j][i];
|
rowBlock[j] = block[j][i];
|
||||||
}
|
}
|
||||||
data[data.length-1] = (rowBlock);
|
data[data.length-1] = (rowBlock);
|
||||||
|
@ -221,7 +220,7 @@ TDengineCursor.prototype.fetchall = function fetchall(options, callback) {
|
||||||
let response = this._createSetResponse(this._rowcount, time)
|
let response = this._createSetResponse(this._rowcount, time)
|
||||||
console.log(response);
|
console.log(response);
|
||||||
|
|
||||||
this._connection._clearResultSet();
|
// this._connection._clearResultSet();
|
||||||
let fields = this.fields;
|
let fields = this.fields;
|
||||||
this._reset_result();
|
this._reset_result();
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
@ -266,13 +265,15 @@ TDengineCursor.prototype.execute_a = function execute_a (operation, options, cal
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resCode >= 0) {
|
if (resCode >= 0) {
|
||||||
let fieldCount = cr._chandle.numFields(res2);
|
// let fieldCount = cr._chandle.numFields(res2);
|
||||||
if (fieldCount == 0) {
|
// if (fieldCount == 0) {
|
||||||
cr._chandle.freeResult(res2);
|
// //cr._chandle.freeResult(res2);
|
||||||
}
|
// return res2;
|
||||||
else {
|
// }
|
||||||
return res2;
|
// else {
|
||||||
}
|
// return res2;
|
||||||
|
// }
|
||||||
|
return res2;
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -381,6 +382,9 @@ TDengineCursor.prototype.stopQuery = function stopQuery(result) {
|
||||||
}
|
}
|
||||||
TDengineCursor.prototype._reset_result = function _reset_result() {
|
TDengineCursor.prototype._reset_result = function _reset_result() {
|
||||||
this._rowcount = -1;
|
this._rowcount = -1;
|
||||||
|
if (this._result != null) {
|
||||||
|
this._chandle.freeResult(this._result);
|
||||||
|
}
|
||||||
this._result = null;
|
this._result = null;
|
||||||
this._fields = null;
|
this._fields = null;
|
||||||
this.data = [];
|
this.data = [];
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "td-connector",
|
"name": "td2.0-connector",
|
||||||
"version": "1.6.1",
|
"version": "2.0.1",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "td-connector",
|
"name": "td2.0-connector",
|
||||||
"version": "2.0.0",
|
"version": "2.0.1",
|
||||||
"description": "A Node.js connector for TDengine.",
|
"description": "A Node.js connector for TDengine.",
|
||||||
"main": "tdengine.js",
|
"main": "tdengine.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -1,23 +1,24 @@
|
||||||
# TDengine Node.js connector
|
# TDengine Node.js connector
|
||||||
[](https://github.com/taosdata/TDengine/tree/master/src/connector/nodejs) [](https://github.com/taosdata/TDengine/#what-is-tdengine)
|
[](https://github.com/taosdata/TDengine/tree/master/src/connector/nodejs) [](https://github.com/taosdata/TDengine/#what-is-tdengine)
|
||||||
|
|
||||||
This is the Node.js library that lets you connect to [TDengine](https://www.github.com/taosdata/tdengine). It is built so that you can use as much of it as you want or as little of it as you want through providing an extensive API. If you want the raw data in the form of an array of arrays for the row data retrieved from a table, you can do that. If you want to wrap that data with objects that allow you easily manipulate and display data such as using a prettifier function, you can do that!
|
This is the Node.js library that lets you connect to [TDengine](https://www.github.com/taosdata/tdengine) 2.0 version. It is built so that you can use as much of it as you want or as little of it as you want through providing an extensive API. If you want the raw data in the form of an array of arrays for the row data retrieved from a table, you can do that. If you want to wrap that data with objects that allow you easily manipulate and display data such as using a prettifier function, you can do that!
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
To get started, just type in the following to install the connector through [npm](https://www.npmjs.com/)
|
To get started, just type in the following to install the connector through [npm](https://www.npmjs.com/)
|
||||||
|
|
||||||
```cmd
|
```cmd
|
||||||
npm install td-connector
|
npm install td2.0-connector
|
||||||
```
|
```
|
||||||
|
|
||||||
To interact with TDengine, we make use of the [node-gyp](https://github.com/nodejs/node-gyp) library. To install, you will need to install the following depending on platform (the following instructions are quoted from node-gyp)
|
To interact with TDengine, we make use of the [node-gyp](https://github.com/nodejs/node-gyp) library. To install, you will need to install the following depending on platform (the following instructions are quoted from node-gyp)
|
||||||
|
|
||||||
### On Unix
|
### On Linux
|
||||||
|
|
||||||
- `python` (`v2.7` recommended, `v3.x.x` is **not** supported)
|
- `python` (`v2.7` recommended, `v3.x.x` is **not** supported)
|
||||||
- `make`
|
- `make`
|
||||||
- A proper C/C++ compiler toolchain, like [GCC](https://gcc.gnu.org)
|
- A proper C/C++ compiler toolchain, like [GCC](https://gcc.gnu.org)
|
||||||
|
- `node` (between `v10.x` and `v11.x`, other version has some dependency compatibility problems)
|
||||||
|
|
||||||
### On macOS
|
### On macOS
|
||||||
|
|
||||||
|
@ -71,12 +72,12 @@ The following is a short summary of the basic usage of the connector, the full
|
||||||
|
|
||||||
### Connection
|
### Connection
|
||||||
|
|
||||||
To use the connector, first require the library ```td-connector```. Running the function ```taos.connect``` with the connection options passed in as an object will return a TDengine connection object. The required connection option is ```host```, other options if not set, will be the default values as shown below.
|
To use the connector, first require the library ```td2.0-connector```. Running the function ```taos.connect``` with the connection options passed in as an object will return a TDengine connection object. The required connection option is ```host```, other options if not set, will be the default values as shown below.
|
||||||
|
|
||||||
A cursor also needs to be initialized in order to interact with TDengine from Node.js.
|
A cursor also needs to be initialized in order to interact with TDengine from Node.js.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const taos = require('td-connector');
|
const taos = require('td2.0-connector');
|
||||||
var conn = taos.connect({host:"127.0.0.1", user:"root", password:"taosdata", config:"/etc/taos",port:0})
|
var conn = taos.connect({host:"127.0.0.1", user:"root", password:"taosdata", config:"/etc/taos",port:0})
|
||||||
var cursor = conn.cursor(); // Initializing a new cursor
|
var cursor = conn.cursor(); // Initializing a new cursor
|
||||||
```
|
```
|
||||||
|
|
|
@ -19,7 +19,7 @@ function randomBool() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize
|
// Initialize
|
||||||
|
//c1.execute('drop database td_connector_test;');
|
||||||
c1.execute('create database if not exists td_connector_test;');
|
c1.execute('create database if not exists td_connector_test;');
|
||||||
c1.execute('use td_connector_test;')
|
c1.execute('use td_connector_test;')
|
||||||
c1.execute('create table if not exists all_types (ts timestamp, _int int, _bigint bigint, _float float, _double double, _binary binary(40), _smallint smallint, _tinyint tinyint, _bool bool, _nchar nchar(40));');
|
c1.execute('create table if not exists all_types (ts timestamp, _int int, _bigint bigint, _float float, _double double, _binary binary(40), _smallint smallint, _tinyint tinyint, _bool bool, _nchar nchar(40));');
|
||||||
|
@ -46,12 +46,14 @@ for (let i = 0; i < 10000; i++) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select
|
// Select
|
||||||
c1.execute('select * from td_connector_test.all_types limit 10 offset 1000;');
|
console.log('select * from td_connector_test.all_types limit 3 offset 100;');
|
||||||
|
c1.execute('select * from td_connector_test.all_types limit 2 offset 100;');
|
||||||
var d = c1.fetchall();
|
var d = c1.fetchall();
|
||||||
console.log(c1.fields);
|
console.log(c1.fields);
|
||||||
console.log(d);
|
console.log(d);
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
|
console.log('select count(*), avg(_int), sum(_float), max(_bigint), min(_double) from td_connector_test.all_types;')
|
||||||
c1.execute('select count(*), avg(_int), sum(_float), max(_bigint), min(_double) from td_connector_test.all_types;');
|
c1.execute('select count(*), avg(_int), sum(_float), max(_bigint), min(_double) from td_connector_test.all_types;');
|
||||||
var d = c1.fetchall();
|
var d = c1.fetchall();
|
||||||
console.log(c1.fields);
|
console.log(c1.fields);
|
||||||
|
@ -62,9 +64,11 @@ console.log(d);
|
||||||
c1.query('select count(*), stddev(_double), min(_tinyint) from all_types where _tinyint > 50 and _int < 0;', true).then(function(result){
|
c1.query('select count(*), stddev(_double), min(_tinyint) from all_types where _tinyint > 50 and _int < 0;', true).then(function(result){
|
||||||
result.pretty();
|
result.pretty();
|
||||||
})
|
})
|
||||||
|
|
||||||
c1.query('select _tinyint, _bool from all_types where _tinyint > 50 and _int < 0 limit 50;', true).then(function(result){
|
c1.query('select _tinyint, _bool from all_types where _tinyint > 50 and _int < 0 limit 50;', true).then(function(result){
|
||||||
result.pretty();
|
result.pretty();
|
||||||
})
|
})
|
||||||
|
|
||||||
c1.query('select stddev(_double), stddev(_bigint), stddev(_float) from all_types;', true).then(function(result){
|
c1.query('select stddev(_double), stddev(_bigint), stddev(_float) from all_types;', true).then(function(result){
|
||||||
result.pretty();
|
result.pretty();
|
||||||
})
|
})
|
||||||
|
@ -83,54 +87,58 @@ q.execute().then(function(r) {
|
||||||
|
|
||||||
// Raw Async Testing (Callbacks, not promises)
|
// Raw Async Testing (Callbacks, not promises)
|
||||||
function cb2(param, result, rowCount, rd) {
|
function cb2(param, result, rowCount, rd) {
|
||||||
|
console.log('CB2 Callbacked!');
|
||||||
console.log("RES *", result);
|
console.log("RES *", result);
|
||||||
console.log("Async fetched", rowCount, "rows");
|
console.log("Async fetched", rowCount, " rows");
|
||||||
console.log("Passed Param: ", param);
|
console.log("Passed Param: ", param);
|
||||||
console.log("Fields", rd.fields);
|
console.log("Fields ", rd.fields);
|
||||||
console.log("Data", rd.data);
|
console.log("Data ", rd.data);
|
||||||
|
|
||||||
}
|
}
|
||||||
function cb1(param,result,code) {
|
function cb1(param,result,code) {
|
||||||
console.log('Callbacked!');
|
console.log('CB1 Callbacked!');
|
||||||
console.log("RES *", result);
|
console.log("RES * ", result);
|
||||||
console.log("Status: ", code);
|
console.log("Status: ", code);
|
||||||
console.log("Passed Param", param);
|
console.log("Passed Param ", param);
|
||||||
c1.fetchall_a(result, cb2, param)
|
c1.fetchall_a(result, cb2, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
c1.execute_a("describe td_connector_test.all_types;", cb1, {myparam:3.141});
|
c1.execute_a("describe td_connector_test.all_types;", cb1, {myparam:3.141});
|
||||||
|
|
||||||
function cb4(param, result, rowCount, rd) {
|
function cb4(param, result, rowCount, rd) {
|
||||||
|
console.log('CB4 Callbacked!');
|
||||||
console.log("RES *", result);
|
console.log("RES *", result);
|
||||||
console.log("Async fetched", rowCount, "rows");
|
console.log("Async fetched", rowCount, "rows");
|
||||||
console.log("Passed Param: ", param);
|
console.log("Passed Param: ", param);
|
||||||
console.log("Fields", rd.fields);
|
console.log("Fields", rd.fields);
|
||||||
console.log("Data", rd.data);
|
console.log("Data", rd.data);
|
||||||
|
|
||||||
}
|
}
|
||||||
// Without directly calling fetchall_a
|
// Without directly calling fetchall_a
|
||||||
var thisRes;
|
var thisRes;
|
||||||
function cb3(param,result,code) {
|
function cb3(param,result,code) {
|
||||||
console.log('Callbacked!');
|
console.log('CB3 Callbacked!');
|
||||||
console.log("RES *", result);
|
console.log("RES *", result);
|
||||||
console.log("Status: ", code);
|
console.log("Status:", code);
|
||||||
console.log("Passed Param", param);
|
console.log("Passed Param", param);
|
||||||
thisRes = result;
|
thisRes = result;
|
||||||
}
|
}
|
||||||
//Test calling execute and fetchall seperately and not through callbacks
|
//Test calling execute and fetchall seperately and not through callbacks
|
||||||
var param = c1.execute_a("describe td_connector_test.all_types;", cb3, {e:2.718});
|
var param = c1.execute_a("describe td_connector_test.all_types;", cb3, {e:2.718});
|
||||||
console.log("Passed Param outside of callback: ", param);
|
console.log("Passed Param outside of callback: ", param);
|
||||||
|
console.log(param);
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
c1.fetchall_a(thisRes, cb4, param);
|
c1.fetchall_a(thisRes, cb4, param);
|
||||||
},100);
|
},100);
|
||||||
|
|
||||||
// Async through promises
|
// Async through promises
|
||||||
var aq = c1.query('select count(*) from td_connector_test.all_types;')
|
var aq = c1.query('select count(*) from td_connector_test.all_types;',false);
|
||||||
aq.execute_a().then(function(data) {
|
aq.execute_a().then(function(data) {
|
||||||
data.pretty();
|
data.pretty();
|
||||||
})
|
});
|
||||||
c1.query('describe td_connector_test.stabletest;').execute_a().then(r=> r.pretty());
|
c1.query('describe td_connector_test.stabletest;').execute_a().then(r=> r.pretty());
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
c1.query('drop database td_connector_test;');
|
c1.query('drop database td_connector_test;');
|
||||||
|
},200);
|
||||||
|
setTimeout(function(){
|
||||||
|
conn.close();
|
||||||
},2000);
|
},2000);
|
||||||
conn.close();
|
|
||||||
|
|
|
@ -114,7 +114,7 @@ void cqClose(void *handle) {
|
||||||
SCqObj *pTemp = pObj;
|
SCqObj *pTemp = pObj;
|
||||||
pObj = pObj->next;
|
pObj = pObj->next;
|
||||||
tdFreeSchema(pTemp->pSchema);
|
tdFreeSchema(pTemp->pSchema);
|
||||||
tfree(pTemp->sqlStr);
|
taosTFree(pTemp->sqlStr);
|
||||||
free(pTemp);
|
free(pTemp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,8 @@ IF ((TD_LINUX_64) OR (TD_LINUX_32 AND TD_ARM))
|
||||||
AUX_SOURCE_DIRECTORY(src SRC)
|
AUX_SOURCE_DIRECTORY(src SRC)
|
||||||
|
|
||||||
ADD_EXECUTABLE(taosd ${SRC})
|
ADD_EXECUTABLE(taosd ${SRC})
|
||||||
TARGET_LINK_LIBRARIES(taosd mnode taos_static monitor http mqtt tsdb twal vnode cJson lz4 balance sync)
|
# TARGET_LINK_LIBRARIES(taosd mnode taos_static monitor http mqtt tsdb twal vnode cJson lz4 balance sync)
|
||||||
|
TARGET_LINK_LIBRARIES(taosd mnode taos monitor http mqtt tsdb twal vnode cJson lz4 balance sync)
|
||||||
|
|
||||||
IF (TD_ACCOUNT)
|
IF (TD_ACCOUNT)
|
||||||
TARGET_LINK_LIBRARIES(taosd account)
|
TARGET_LINK_LIBRARIES(taosd account)
|
||||||
|
|
|
@ -75,7 +75,7 @@ void dnodeCleanupMnodePeer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
taosCloseQset(tsMPeerQset);
|
taosCloseQset(tsMPeerQset);
|
||||||
tfree(tsMPeerPool.peerWorker);
|
taosTFree(tsMPeerPool.peerWorker);
|
||||||
dInfo("dnode mpeer is closed");
|
dInfo("dnode mpeer is closed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ void dnodeCleanupMnodeWrite() {
|
||||||
}
|
}
|
||||||
|
|
||||||
taosCloseQset(tsMWriteQset);
|
taosCloseQset(tsMWriteQset);
|
||||||
tfree(tsMWritePool.writeWorker);
|
taosTFree(tsMWritePool.writeWorker);
|
||||||
dInfo("dnode mwrite is closed");
|
dInfo("dnode mwrite is closed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,13 +18,11 @@
|
||||||
#include "cJSON.h"
|
#include "cJSON.h"
|
||||||
#include "taoserror.h"
|
#include "taoserror.h"
|
||||||
#include "taosmsg.h"
|
#include "taosmsg.h"
|
||||||
#include "ttime.h"
|
|
||||||
#include "ttimer.h"
|
#include "ttimer.h"
|
||||||
#include "tsdb.h"
|
#include "tsdb.h"
|
||||||
#include "twal.h"
|
#include "twal.h"
|
||||||
#include "tqueue.h"
|
#include "tqueue.h"
|
||||||
#include "tsync.h"
|
#include "tsync.h"
|
||||||
#include "ttime.h"
|
|
||||||
#include "ttimer.h"
|
#include "ttimer.h"
|
||||||
#include "tbalance.h"
|
#include "tbalance.h"
|
||||||
#include "tglobal.h"
|
#include "tglobal.h"
|
||||||
|
|
|
@ -22,6 +22,7 @@ extern "C" {
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include "osDef.h"
|
||||||
#include "taos.h"
|
#include "taos.h"
|
||||||
|
|
||||||
#define TSDB__packed
|
#define TSDB__packed
|
||||||
|
@ -161,7 +162,7 @@ extern tDataTypeDescriptor tDataTypeDesc[11];
|
||||||
|
|
||||||
bool isValidDataType(int32_t type);
|
bool isValidDataType(int32_t type);
|
||||||
//bool isNull(const char *val, int32_t type);
|
//bool isNull(const char *val, int32_t type);
|
||||||
static inline __attribute__((always_inline)) bool isNull(const char *val, int32_t type) {
|
static FORCE_INLINE bool isNull(const char *val, int32_t type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case TSDB_DATA_TYPE_BOOL:
|
case TSDB_DATA_TYPE_BOOL:
|
||||||
return *(uint8_t *)val == TSDB_DATA_BOOL_NULL;
|
return *(uint8_t *)val == TSDB_DATA_BOOL_NULL;
|
||||||
|
|
|
@ -15,11 +15,11 @@ IF ((TD_LINUX_64) OR (TD_LINUX_32 AND TD_ARM))
|
||||||
LIST(REMOVE_ITEM SRC ./src/shellDarwin.c)
|
LIST(REMOVE_ITEM SRC ./src/shellDarwin.c)
|
||||||
ADD_EXECUTABLE(shell ${SRC})
|
ADD_EXECUTABLE(shell ${SRC})
|
||||||
|
|
||||||
IF (TD_PAGMODE_LITE)
|
# IF (TD_PAGMODE_LITE)
|
||||||
TARGET_LINK_LIBRARIES(shell taos)
|
TARGET_LINK_LIBRARIES(shell taos)
|
||||||
ELSE ()
|
# ELSE ()
|
||||||
TARGET_LINK_LIBRARIES(shell taos_static)
|
# TARGET_LINK_LIBRARIES(shell taos_static)
|
||||||
ENDIF ()
|
# ENDIF ()
|
||||||
|
|
||||||
SET_TARGET_PROPERTIES(shell PROPERTIES OUTPUT_NAME taos)
|
SET_TARGET_PROPERTIES(shell PROPERTIES OUTPUT_NAME taos)
|
||||||
ELSEIF (TD_WINDOWS_64)
|
ELSEIF (TD_WINDOWS_64)
|
||||||
|
|
|
@ -229,8 +229,8 @@ void shellReadCommand(TAOS *con, char *command) {
|
||||||
printf("\n");
|
printf("\n");
|
||||||
if (isReadyGo(&cmd)) {
|
if (isReadyGo(&cmd)) {
|
||||||
sprintf(command, "%s%s", cmd.buffer, cmd.command);
|
sprintf(command, "%s%s", cmd.buffer, cmd.command);
|
||||||
tfree(cmd.buffer);
|
taosTFree(cmd.buffer);
|
||||||
tfree(cmd.command);
|
taosTFree(cmd.command);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
updateBuffer(&cmd);
|
updateBuffer(&cmd);
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
#include "shell.h"
|
#include "shell.h"
|
||||||
#include "shellCommand.h"
|
#include "shellCommand.h"
|
||||||
#include "ttime.h"
|
|
||||||
#include "tutil.h"
|
#include "tutil.h"
|
||||||
#include "taosdef.h"
|
#include "taosdef.h"
|
||||||
#include "taoserror.h"
|
#include "taoserror.h"
|
||||||
|
@ -176,7 +175,7 @@ int32_t shellRunCommand(TAOS* con, char* command) {
|
||||||
history.hist[(history.hend + MAX_HISTORY_SIZE - 1) % MAX_HISTORY_SIZE] == NULL ||
|
history.hist[(history.hend + MAX_HISTORY_SIZE - 1) % MAX_HISTORY_SIZE] == NULL ||
|
||||||
strcmp(command, history.hist[(history.hend + MAX_HISTORY_SIZE - 1) % MAX_HISTORY_SIZE]) != 0) {
|
strcmp(command, history.hist[(history.hend + MAX_HISTORY_SIZE - 1) % MAX_HISTORY_SIZE]) != 0) {
|
||||||
if (history.hist[history.hend] != NULL) {
|
if (history.hist[history.hend] != NULL) {
|
||||||
tfree(history.hist[history.hend]);
|
taosTFree(history.hist[history.hend]);
|
||||||
}
|
}
|
||||||
history.hist[history.hend] = strdup(command);
|
history.hist[history.hend] = strdup(command);
|
||||||
|
|
||||||
|
@ -770,7 +769,7 @@ void write_history() {
|
||||||
for (int i = history.hstart; i != history.hend;) {
|
for (int i = history.hstart; i != history.hend;) {
|
||||||
if (history.hist[i] != NULL) {
|
if (history.hist[i] != NULL) {
|
||||||
fprintf(f, "%s\n", history.hist[i]);
|
fprintf(f, "%s\n", history.hist[i]);
|
||||||
tfree(history.hist[i]);
|
taosTFree(history.hist[i]);
|
||||||
}
|
}
|
||||||
i = (i + 1) % MAX_HISTORY_SIZE;
|
i = (i + 1) % MAX_HISTORY_SIZE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#include "shell.h"
|
#include "shell.h"
|
||||||
#include "shellCommand.h"
|
#include "shellCommand.h"
|
||||||
#include "tglobal.h"
|
#include "tglobal.h"
|
||||||
#include "ttime.h"
|
|
||||||
#include "tutil.h"
|
#include "tutil.h"
|
||||||
|
|
||||||
static char **shellSQLFiles = NULL;
|
static char **shellSQLFiles = NULL;
|
||||||
|
|
|
@ -201,8 +201,8 @@ void shellReadCommand(TAOS *con, char *command) {
|
||||||
printf("\n");
|
printf("\n");
|
||||||
if (isReadyGo(&cmd)) {
|
if (isReadyGo(&cmd)) {
|
||||||
sprintf(command, "%s%s", cmd.buffer, cmd.command);
|
sprintf(command, "%s%s", cmd.buffer, cmd.command);
|
||||||
tfree(cmd.buffer);
|
taosTFree(cmd.buffer);
|
||||||
tfree(cmd.command);
|
taosTFree(cmd.command);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
updateBuffer(&cmd);
|
updateBuffer(&cmd);
|
||||||
|
@ -320,7 +320,7 @@ void *shellLoopQuery(void *arg) {
|
||||||
reset_terminal_mode();
|
reset_terminal_mode();
|
||||||
} while (shellRunCommand(con, command) == 0);
|
} while (shellRunCommand(con, command) == 0);
|
||||||
|
|
||||||
tfree(command);
|
taosTFree(command);
|
||||||
exitShell();
|
exitShell();
|
||||||
|
|
||||||
pthread_cleanup_pop(1);
|
pthread_cleanup_pop(1);
|
||||||
|
|