Merge branch 'develop' into hotfix/sangshuduo/2.0doc-emq-insert

This commit is contained in:
Shuduo Sang 2020-08-01 16:16:53 +08:00
commit aee95ae154
252 changed files with 4383 additions and 3892 deletions

View File

@ -190,7 +190,7 @@ matrix:
script:
- cmake .. > /dev/null
- make > /dev/null
- make > /dev/null
- os: linux
arch: arm64

View File

@ -1,5 +1,14 @@
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_ADMIN FALSE)

View File

@ -53,14 +53,18 @@ IF (TD_LINUX_64)
ADD_DEFINITIONS(-D_M_X64)
ADD_DEFINITIONS(-D_TD_LINUX_64)
IF (NOT TD_ARM)
IF (${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")
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")
ENDIF ()
#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")
#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")
#ENDIF ()
SET(COMMON_FLAGS "-std=gnu99 -Wall -Werror -fPIC -g3 -gdwarf-2 -msse4.2 -D_FILE_OFFSET_BITS=64 -D_LARGE_FILE")
ELSE ()
SET(COMMON_FLAGS "-std=gnu99 -Wall -Werror -fPIC -g -fsigned-char -fpack-struct=8 -D_FILE_OFFSET_BITS=64 -D_LARGE_FILE")
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/)
IF (ICONV_INCLUDE_EXIST)
ADD_DEFINITIONS(-DUSE_LIBICONV)

View File

@ -52,14 +52,16 @@ ELSE ()
MESSAGE(STATUS "input cpuType unknown " ${CPUTYPE})
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")
#
# 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)
IF (${CMAKE_SIZEOF_VOID_P} MATCHES 8)
SET(TD_LINUX_64 TRUE)

View File

@ -917,15 +917,16 @@ TDengine 同时也提供了node.js 的连接器。用户可以通过[npm](https:
首先,通过[npm](https://www.npmjs.com/)安装node.js 连接器.
```cmd
npm install td-connector
npm install td2.0-connector
```
我们建议用户使用npm 安装node.js连接器。如果您没有安装npm, 可以将*src/connector/nodejs/*拷贝到您的nodejs 项目目录下
我们使用[node-gyp](https://github.com/nodejs/node-gyp)和TDengine服务端进行交互。安装node.js 连接器之前,还需安装以下软件:
### Unix
### Linux
- `python` (建议`v2.7` , `v3.x.x` 目前还不支持)
- `node` 必须采用v8.x版本之后的版本存在兼容性的问题。
- `make`
- 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
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 cursor = conn.cursor(); // Initializing a new cursor
```

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View File

@ -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

View File

@ -1,11 +1,11 @@
# TDengine 适用场景介绍(草案)
# TDengine 介绍
## TDengine 简介
<!-- 本节内容来源于白皮书 -->
TDengine是涛思数据面对高速增长的物联网大数据市场和技术挑战推出的创新性的大数据处理产品它不依赖任何第三方软件也不是优化或包装了一个开源的数据库或流式计算产品而是在吸取众多传统关系型数据库、NoSQL数据库、流式计算引擎、消息队列等软件的优点之后自主开发的产品在时序空间大数据处理上有着自己独到的优势。
TDengine的模块之一是时序数据库。但除此之外为减少研发的复杂度、系统维护的难度TDengine还提供缓存、消息队列、订阅、流式计算等功能为物联网、工业互联网大数据的处理提供全栈的技术方案是一个高效易用的物联网大数据平台。与Hadoop等典型的大数据平台相比它具有如下鲜明的特点
* __10倍以上的性能提升__定义了创新的数据存储结构单核每秒就能处理至少2万次请求插入数百万个数据点读出一千万以上数据点比现有通用数据库快了十倍以上。
* __硬件或云服务成本降至1/5__由于超强性能计算资源不到通用大数据方案的1/5通过列式存储和先进的压缩算法存储空间不到通用数据库的1/10
* __全栈时序数据处理引擎__将数据库、消息队列、缓存、流式计算等功能融合一起应用无需再集成Kafka/Redis/HBase/Spark/HDFS等软件大幅降低应用开发和维护的复杂度成本。
@ -13,52 +13,48 @@ TDengine是涛思数据面对高速增长的物联网大数据市场和技术挑
* __与第三方工具无缝连接__不用一行代码即可与Telegraf, Grafana, EMQ, Prometheus, Matlab, R等集成。后续将支持OPC, Hadoop, Spark等, BI工具也将无缝连接。
* __零运维成本、零学习成本__安装、集群一秒搞定无需分库分表实时备份。标准SQL支持JDBC, RESTful, 支持Python/Java/C/C++/Go, 与MySQL相似零学习成本。
<!--sdASF -->
采用TDengine可将典型的物联网、车联网、工业互联网大数据平台的总拥有成本大幅降低。但需要指出的是因充分利用了物联网时序数据的特点它无法用来处理网络爬虫、微博、微信、电商、ERP、CRM等通用型数据。
## TDengine 总体适用场景
作为一个IOT大数据平台TDengine的典型适用场景是在IOT范畴而且用户有一定的数据量。本文后续的介绍主要针对这个范畴里面的系统。范畴之外的系统比如CRMERP等不在本文讨论范围内。
## 数据源特点和需求
### 数据源特点和需求
从数据源角度设计人员可以从已经角度分析TDengine在目标应用系统里面的适用性。
|数据源特点和需求|不适用|可能适用|非常适用|简单说明|
|---|---|---|---|---|
|总体数据量巨大| | | |TDengine在容量方面提供出色的水平扩展功能并且具备匹配高压缩的存储结构达到业界最优的存储效率。|
|数据输入速度偶尔或者持续巨大| | | | TDengine的性能大大超过同类产品可以在同样的硬件环境下持续处理大量的输入数据并且提供很容易在用户环境里面运行的性能评估工具。|
|数据源数目巨大| | | |TDengine设计中包含专门针对大量数据源的优化包括数据的写入和查询尤其适合高效处理海量千万或者更多量级的数据源。|
|总体数据量巨大| | | |TDengine在容量方面提供出色的水平扩展功能并且具备匹配高压缩的存储结构达到业界最优的存储效率。|
|数据输入速度偶尔或者持续巨大| | | | TDengine的性能大大超过同类产品可以在同样的硬件环境下持续处理大量的输入数据并且提供很容易在用户环境里面运行的性能评估工具。|
|数据源数目巨大| | | |TDengine设计中包含专门针对大量数据源的优化包括数据的写入和查询尤其适合高效处理海量千万或者更多量级的数据源。|
## 系统架构要求
### 系统架构要求
|系统架构要求|不适用|可能适用|非常适用|简单说明|
|---|---|---|---|---|
|要求简单可靠的系统架构| | | |TDengine的系统架构非常简单可靠自带消息队列缓存流式计算监控等功能无需集成额外的第三方产品。|
|要求容错和高可靠| | | |TDengine的集群功能自动提供容错灾备等高可靠功能|
|标准化规范| | | |TDengine使用标准的SQL语言提供主要功能遵守标准化规范|
|要求简单可靠的系统架构| | | |TDengine的系统架构非常简单可靠自带消息队列缓存流式计算监控等功能无需集成额外的第三方产品。|
|要求容错和高可靠| | | |TDengine的集群功能自动提供容错灾备等高可靠功能|
|标准化规范| | | |TDengine使用标准的SQL语言提供主要功能遵守标准化规范|
## 系统功能需求
### 系统功能需求
|系统功能需求|不适用|可能适用|非常适用|简单说明|
|---|---|---|---|---|
|要求完整的内置数据处理算法| | | |TDengine的实现了通用的数据处理算法但是还没有做到妥善处理各行各业的所有要求因此特殊类型的处理还需要应用层面处理。|
|需要大量的交叉查询处理| | | |这种类型的处理更多应该用关系型数据系统处理或者应该考虑TDengine和关系型数据系统配合实现系统功能|
|要求完整的内置数据处理算法| | | |TDengine的实现了通用的数据处理算法但是还没有做到妥善处理各行各业的所有要求因此特殊类型的处理还需要应用层面处理。|
|需要大量的交叉查询处理| | | |这种类型的处理更多应该用关系型数据系统处理或者应该考虑TDengine和关系型数据系统配合实现系统功能|
## 系统性能需求
### 系统性能需求
|系统性能需求|不适用|可能适用|非常适用|简单说明|
|---|---|---|---|---|
|要求较大的总体处理能力| | | |TDengine的集群功能可以轻松地让多服务器配合达成处理能力的提升。|
|要求高速处理数据 | | | |TDengine的专门为IOT优化的存储和数据处理的设计一般可以让系统得到超出同类产品多倍数的处理速度提升。|
|要求快速处理小粒度数据| | | |这方面TDengine性能可以完全对标关系型和NoSQL型数据处理系统。|
|要求较大的总体处理能力| | | |TDengine的集群功能可以轻松地让多服务器配合达成处理能力的提升。|
|要求高速处理数据 | | | |TDengine的专门为IOT优化的存储和数据处理的设计一般可以让系统得到超出同类产品多倍数的处理速度提升。|
|要求快速处理小粒度数据| | | |这方面TDengine性能可以完全对标关系型和NoSQL型数据处理系统。|
## 系统维护需求
### 系统维护需求
|系统维护需求|不适用|可能适用|非常适用|简单说明|
|---|---|---|---|---|
|要求系统可靠运行| | | ✅ |TDengine的系统架构非常稳定可靠日常维护也简单便捷对维护人员的要求简洁明了最大程度上杜绝人为错误和事故。|
|要求运维学习成本可控| | | ✅ |同上|
|要求市场有大量人才储备| ✅ | | |TDengine作为新一代产品目前人才市场里面有经验的人员还有限。但是学习成本低我们作为厂家也提供运维的培训和辅助服务|
|要求系统可靠运行| | | √ |TDengine的系统架构非常稳定可靠日常维护也简单便捷对维护人员的要求简洁明了最大程度上杜绝人为错误和事故。|
|要求运维学习成本可控| | | √ |同上|
|要求市场有大量人才储备| √ | | |TDengine作为新一代产品目前人才市场里面有经验的人员还有限。但是学习成本低我们作为厂家也提供运维的培训和辅助服务|
## TDengine 性能指标介绍和验证方法

View File

@ -38,7 +38,7 @@ which systemd
如果系统中不存在`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>
## 轻松启动

View File

@ -4,14 +4,14 @@
TDengine采用关系型数据模型需要建库、建表。因此对于一个具体的应用场景需要考虑库的设计超级表和普通表的设计。本节不讨论细致的语法规则只介绍概念。
##创建库
## 创建库
不同类型的数据采集点往往具有不同的数据特征包括数据采集频率的高低数据保留时间的长短副本的数目数据块的大小等等。为让各种场景下TDengine都能最大效率的工作TDengine建议将不同数据特征的表创建在不同的库里因为每个库可以配置不同的存储策略。创建一个库时除SQL标准的选项外应用还可以指定保留时长、副本数、内存块个数、时间精度、文件块里最大最小记录条数、是否压缩、一个数据文件覆盖的天数等多种参数。比如
```cmd
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
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里可以有一到多个超级表。
@ -31,7 +31,7 @@ CREATE TABLE d1001 USING meters TAGS ("Beijing.Chaoyang", 2);
```
其中d1001是表名meters是超级表的表名后面紧跟标签Location的具体标签值”Beijing.Chaoyang"标签groupId的具体标签值2。虽然在创建表时需要指定标签值但可以事后修改。详细细则请见 TAOS SQL。
TDengine建议将数据采集点的全局唯一ID作为表名。但对于有的场景并没有唯一的ID可以将多个ID组合成一个唯一的ID。不建议将具有唯一性的ID作为标签值。
TDengine建议将数据采集点的全局唯一ID作为表名(比如设备序列号)。但对于有的场景并没有唯一的ID可以将多个ID组合成一个唯一的ID。不建议将具有唯一性的ID作为标签值。
**自动建表**:在某些特殊场景中,用户在写数据时并不确定某个数据采集点的表是否存在,此时可在写入数据时使用自动建表语法来创建不存在的表,若该表已存在则不会建立新表。比如:
@ -41,3 +41,4 @@ 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。
**多列模型**TDengine支持多列模型只要这些物理量是同时采集的这些量就可以作为不同列放在同一张表里。有的数据采集点有多组采集量每一组的数据采集时间是不一样的这时需要对同一个采集点建多张表。但还有一种极限的设计单列模型无论是否同时采集每个采集的物理量单独建表。TDengine建议只要采集时间一致就采用多列模型因为插入效率以及存储效率更高。

View File

@ -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 支持如下查询功能:
- 查询单列、或多列查询
- 支持值过滤条件:\>, \<, =, \<> 大于,小于,等于,不等于等等
- 支持对标签的模糊匹配
- 支持Group by, Order by, Limit, Offset
- 支持列之间的四则运算
- 支持时间戳对齐的JOIN操作
- 支持多种函数: count, max, min, avg, sum, twa, stddev, leastsquares, top, bottom, first, last, percentile, apercentile, last_row, spread, diff
- 单列、多列数据查询
- 标签和数值的多种过滤条件:\>, \<, =, \<>, like 等
- 聚合结果的分组Group by、排序Order by、约束输出Limit/Offset
- 数值列及聚合结果的四则运算
- 时间戳对齐的连接查询Join Query操作
- 多种聚合/计算函数: 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
taos> select * from d1001 where voltage > 215 order by ts desc limit 2;
ts | current | voltage | phase |
@ -27,11 +26,11 @@ Query OK, 2 row(s) in set (0.001100s)
```
为满足物联网场景的需求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下的全部或部分表进行聚合或统计操作这样大大简化应用的开发。其具体流程如下图所示
@ -39,16 +38,16 @@ TDengine对每个数据采集点单独建表但应用经常需要对数据点
<center> 多表聚合查询原理图 </center>
1应用将一个查询条件发往系统2: taosc将超级表的名字发往Meta Node管理节点)3管理节点将超级表所拥有的vnode列表发回taosc4taosc将计算的请求连同标签过滤条件发往这些vnode对应的多个数据节点5每个vnode先在内存里查找出自己节点里符合标签过滤条件的表的集合然后扫描存储的时序数据完成相应的聚合计算将结果返回给taosc6taosc将多个数据节点返回的结果做最后的聚合将其返回给应用。
1应用将一个查询条件发往系统2: taosc将超级表的名字发往 Meta Node管理节点)3管理节点将超级表所拥有的 vnode 列表发回 taosc4taosc将计算的请求连同标签过滤条件发往这些vnode对应的多个数据节点5每个vnode先在内存里查找出自己节点里符合标签过滤条件的表的集合然后扫描存储的时序数据完成相应的聚合计算将结果返回给taosc6taosc将多个数据节点返回的结果做最后的聚合将其返回给应用。
由于TDengine在vnode内将标签数据与时序数据分离存储通过先在内存里过滤标签数据将需要扫描的数据集大幅减少大幅提升聚合计算速度。同时由于数据分布在多个vnode/dnode聚合计算操作在多个vnode里并发进行又进一步提升了聚合的速度。
由于TDengine在vnode内将标签数据与时序数据分离存储通过先在内存里过滤标签数据将需要扫描的数据集大幅减少大幅提升聚合计算速度。同时由于数据分布在多个vnode/dnode聚合计算操作在多个vnode里并发进行又进一步提升了聚合的速度。
对普通表的聚合函数以及绝大部分操作都适用于超级表语法完全一样细节请看TAOS SQL。
对普通表的聚合函数以及绝大部分操作都适用于超级表,语法完全一样,细节请看 TAOS SQL。
比如在TAOS Shell查找所有智能电表采集的电压平均值并按照location分组
```mysql
taos> select avg(voltage) from meters group by location;
taos> SELECT AVG(voltage) FROM meters GROUP BY location;
avg(voltage) | location |
=============================================================
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
taos> SELECT sum(current) FROM d1001 interval(10s) ;
taos> SELECT sum(current) FROM d1001 INTERVAL(10s) ;
ts | sum(current) |
======================================================
2018-10-03 14:38:00.000 | 10.300000191 |
2018-10-03 14:38:10.000 | 24.900000572 |
Query OK, 2 row(s) in set (0.000883s)
```
降采样操作适用于超级表,比如:将所有智能电表采集的电流值每秒钟求和
降采样操作适用于超级表,比如:将所有智能电表采集的电流值每秒钟求和
```mysql
taos> SELECT sum(current) FROM meters interval(1s) ;
taos> SELECT SUM(current) FROM meters INTERVAL(1s) ;
ts | sum(current) |
======================================================
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还提供插值计算的功能。
语法规则细节请见TAOS SQL。
语法规则细节请见<a href="https://www.taosdata.com/cn/documentation20/taos-sql/">TAOS SQL </a>

View File

@ -947,7 +947,7 @@ SELECT function_list FROM stb_name
2. 在时间维度聚合中,返回的结果中时间序列严格单调递增。
3. 如果查询对象是超级表则聚合函数会作用于该超级表下满足值过滤条件的所有表的数据。如果查询中没有使用group by语句则返回的结果按照时间序列严格单调递增如果查询中使用了group by语句分组则返回结果中每个group内不按照时间序列严格单调递增。
**示例**智能电表的建表语句如下:
**示例:** 智能电表的建表语句如下:
```mysql
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);
```
##TAOS SQL 边界限制
## TAOS SQL 边界限制
- 数据库名最大长度为33
- 表名最大长度为193每行数据最大长度16k个字符
- 列名最大长度为65最多允许1024列最少需要2列第一列必须是时间戳

View File

@ -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)
```
**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
```
用户可以通过参数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。这样保证了TDengine能够在断电等因素导致的服务重启时从数据库日志文件中恢复数据避免数据的丢失。
涉及的系统配置参数有两个.
涉及的系统配置参数有两个
walLevelWAL级别1写wal, 但不执行fsync; 2写wal, 而且执行fsync。
- walLevelWAL级别0不写wal; 1写wal, 但不执行fsync; 2写wal, 而且执行fsync。
- fsync当walLevel设置为2时执行fsync的周期。设置为0表示每次写入立即执行fsync。
fsync当walLevel设置为2时执行fsync的周期。设置为0表示每次写入立即执行fsync
如果要100%的保证数据不丢失需要将walLevel设置为2fsync设置为0。这时写入速度将会下降。但如果应用侧启动的写数据的线程数达到一定的数量(超过50)那么写入数据的性能也会很不错只会比fsync设置为3000毫秒下降30%左右
**灾备**
### 灾备
TDengine的集群通过多个副本的机制来提供系统的高可性,实现灾备能力。
TDengine的集群通过多个副本的机制来提供系统的高可性,实现灾备能力。
TDengine集群是由mnode负责管理的为保证mnode的高可靠可以配置多个mnode副本副本数由系统配置参数numOfMnodes决定为了支持高可靠需要设置大于1。为保证元数据的强一致性mnode副本之间通过同步方式进行数据复制保证了元数据的强一致性。
@ -50,33 +72,7 @@ TDengine集群中的时序数据的副本数是与数据库关联的一个集
TDengine集群的节点数必须大于等于副本数否则创建表时将报错。
当TDengine集群中的节点部署在不同的物理机上比如不同的机架、或不同的IDC并设置多个副本数时就实现了异地容灾从而提供系统的高可靠性无需再使用其他软件或工具。
## 文件目录结构
安装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来配置不同的数据目录和日志目录。
当TDengine集群中的节点部署在不同的物理机上并设置多个副本数时就实现了系统的高可靠性无需再使用其他软件或工具。TDengine企业版还可以将副本部署在不同机房从而实现异地容灾。
## 服务端配置
@ -84,8 +80,8 @@ TDengine系统后台服务由taosd提供可以在配置文件taos.cfg里修
下面仅仅列出一些重要的配置参数,更多的参数请看配置文件里的说明。各个参数的详细介绍及作用请看前述章节。**注意:配置修改后,需要重启*taosd*服务才能生效。**
- first: taosd启动时主动连接的集群中第一个dnode的end point, 缺省值为 localhost:6030。
- second: taosd启动时如果first连接不上尝试连接集群中第二个dnode的end point, 缺省值为空。
- firstEp: taosd启动时主动连接的集群中第一个dnode的end point, 缺省值为 localhost:6030。
- secondEp: taosd启动时如果first连接不上尝试连接集群中第二个dnode的end point, 缺省值为空。
- fqdn数据节点的FQDN。如果为空将自动获取操作系统配置的第一个, 缺省值为空。
- serverPorttaosd启动后对外服务的端口号默认值为6030。
- httpPort: RESTful服务使用的端口号所有的HTTP请求TCP都需要向该接口发起查询/写入请求。
@ -129,7 +125,7 @@ TDengine集群中加入一个新的dnode时涉及集群相关的一些参数
- statusInterval: dnode向mnode报告状态时长。单位为秒默认值1。
- maxTablesPerVnode: 每个vnode中能够创建的最大表个数。默认值1000000。
- maxVgroupsPerDb: 每个数据库中能够使用的最大vnode个数。
- arbitrator: 系统中裁决器的end point
- arbitrator: 系统中裁决器的end point,缺省为空
- timezone时区。从系统中动态获取当前的时区设置。
- locale系统区位信息及编码格式。系统中动态获取如果自动获取失败需要用户在配置文件设置或通过API设置。
- charset字符集编码。系统中动态获取如果自动获取失败需要用户在配置文件设置或通过API设置。
@ -140,8 +136,8 @@ TDengine系统的前台交互客户端应用程序为taos它与taosd共享同
客户端配置参数列表及解释
- first: taos启动时主动连接的集群中第一个taosd实例的end point, 缺省值为 localhost:6030。
- second: taos启动时如果first连接不上尝试连接集群中第二个taosd实例的end point, 缺省值为空。
- firstEp: taos启动时主动连接的集群中第一个taosd实例的end point, 缺省值为 localhost:6030。
- secondEp: taos启动时如果first连接不上尝试连接集群中第二个taosd实例的end point, 缺省值为空。
- charset字符集编码。系统中动态获取如果自动获取失败需要用户在配置文件设置或通过API设置。
- locale系统区位信息及编码格式。系统中动态获取如果自动获取失败需要用户在配置文件设置或通过API设置。
@ -230,9 +226,7 @@ Query OK, 9 row(s) affected (0.004763s)
**taosdump工具导入**
TDengine提供了方便的数据库导入导出工具taosdump。用户可以将taosdump从一个系统导出的数据导入到其他系统中。具体使用方法请参见博客
[TDengine DUMP工具使用指南]: https://www.taosdata.com/blog/2020/03/09/1334.html
TDengine提供了方便的数据库导入导出工具taosdump。用户可以将taosdump从一个系统导出的数据导入到其他系统中。具体使用方法请参见博客<a href='https://www.taosdata.com/blog/2020/03/09/1334.html'>TDengine DUMP工具使用指南</a>
## 数据导出
@ -250,9 +244,7 @@ select * from <tb_name> >> data.csv
**用taosdump导出数据**
TDengine提供了方便的数据库导出工具taosdump。用户可以根据需要选择导出所有数据库、一个数据库或者数据库中的一张表,所有数据或一时间段的数据,甚至仅仅表的定义。具体使用方法,请参见博客:
[TDengine DUMP工具使用指南]: https://www.taosdata.com/blog/2020/03/09/1334.html
TDengine提供了方便的数据库导出工具taosdump。用户可以根据需要选择导出所有数据库、一个数据库或者数据库中的一张表,所有数据或一时间段的数据,甚至仅仅表的定义。具体使用方法,请参见博客:<a href='https://www.taosdata.com/blog/2020/03/09/1334.html'>TDengine DUMP工具使用指南</a>
## 系统连接、任务查询管理
@ -299,3 +291,32 @@ KILL STREAM <stream-id>
TDengine启动后会自动创建一个监测数据库SYS并自动将服务器的CPU、内存、硬盘空间、带宽、请求数、磁盘读写速度、慢查询等信息定时写入该数据库。TDengine还将重要的系统操作比如登录、创建、删除数据库等日志以及各种错误报警信息记录下来存放在SYS库里。系统管理员可以从CLI直接查看这个数据库也可以在WEB通过图形化界面查看这些监测信息。
这些监测信息的采集缺省是打开的但可以修改配置文件里的选项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来配置不同的数据目录和日志目录。

View File

@ -119,9 +119,9 @@ taos_consume
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后能得到通知并进行一些处理 有两种方法:
一是分别对每张子表进行查询,每次查询后记录最后一条数据的时间戳,后续只查询这个时间戳之后的数据:
@ -296,7 +296,8 @@ $ taos
您可以继续插入一些数据观察示例程序的输出。
## 缓存 (Cache)
## 缓存(Cache)
TDengine采用时间驱动缓存管理策略First-In-First-OutFIFO又称为写驱动的缓存管理机制。这种策略有别于读驱动的数据缓存模式Least-Recent-UseLRU直接将最近写入的数据保存在系统的缓存中。当缓存达到临界值的时候将最早的数据批量写入磁盘。一般意义上来说对于物联网数据的使用用户最为关心最近产生的数据即当前状态。TDengine充分利用了这一特性将最近到达的当前状态数据保存在缓存中。
TDengine通过查询函数向用户提供毫秒级的数据获取能力。直接将最近到达的数据保存在缓存中可以更加快速地响应用户针对最近一条或一批数据的查询分析整体上提供更快的数据库查询响应能力。从这个意义上来说可通过设置合适的配置参数将TDengine作为数据缓存来使用而不需要再部署额外的缓存系统可有效地简化系统架构降低运维的成本。需要注意的是TDengine重启以后系统的缓存将被清空之前缓存的数据均会被批量写入磁盘缓存的数据将不会像专门的Key-value缓存系统再将之前缓存的数据重新加载到缓存中。
@ -308,7 +309,14 @@ TDengine将内存池按块划分进行管理数据在内存块里按照列式
你可以通过函数last_row快速获取一张表或一张超级表的最后一条记录这样很便于在大屏显示各设备的实时状态或采集值。例如
```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) 。

View File

@ -4,46 +4,48 @@
### 物联网典型场景
在典型的物联网、车联网、运维监测场景中,往往有多种不同类型的数据采集设备,采集一个到多个不同的物理量。而同一种采集设备类型,往往又有多个具体的采集设备分布在不同的地点。大数据处理系统就是要将各种采集的数据汇总,然后进行计算和分析。对于同一类设备,其采集的数据都是很规则的。以智能电表为例,假设每个智能电表采集电流、电压、相位三个量,其采集的数据类似如下的表格:
| Device ID | Time Stamp | current | voltage | phase | | location | groupId |
| :-------: | :-----------: | :-----: | :-----: | :---: | :--- | :--------------: | :-----: |
| D1001 | 1538548685000 | 10.3 | 219 | 0.31 | | Beijing.Chaoyang | 2 |
| D1002 | 1538548684000 | 10.2 | 220 | 0.23 | | Beijing.Chaoyang | 3 |
| D1003 | 1538548686500 | 11.5 | 221 | 0.35 | | Beijing.Haidian | 3 |
| D1004 | 1538548685500 | 13.4 | 223 | 0.29 | | Beijing.Haidian | 2 |
| D1001 | 1538548695000 | 12.6 | 218 | 0.33 | | Beijing.Chaoyang | 2 |
| D1004 | 1538548696600 | 11.8 | 221 | 0.28 | | Beijing.Haidian | 2 |
| D1002 | 1538548696650 | 10.3 | 218 | 0.25 | | Beijing.Chaoyang | 3 |
| D1001 | 1538548696800 | 12.3 | 221 | 0.31 | | Beijing.Chaoyang | 2 |
| Device ID | Time Stamp | current | voltage | phase | location | groupId |
| :-------: | :-----------: | :-----: | :-----: | :---: | :--------------: | :-----: |
| d1001 | 1538548685000 | 10.3 | 219 | 0.31 | Beijing.Chaoyang | 2 |
| d1002 | 1538548684000 | 10.2 | 220 | 0.23 | Beijing.Chaoyang | 3 |
| d1003 | 1538548686500 | 11.5 | 221 | 0.35 | Beijing.Haidian | 3 |
| d1004 | 1538548685500 | 13.4 | 223 | 0.29 | Beijing.Haidian | 2 |
| d1001 | 1538548695000 | 12.6 | 218 | 0.33 | Beijing.Chaoyang | 2 |
| d1004 | 1538548696600 | 11.8 | 221 | 0.28 | Beijing.Haidian | 2 |
| d1002 | 1538548696650 | 10.3 | 218 | 0.25 | Beijing.Chaoyang | 3 |
| d1001 | 1538548696800 | 12.3 | 221 | 0.31 | Beijing.Chaoyang | 2 |
<center> 表1智能电表数据示例</center>
每一条记录都有设备ID时间戳采集的物理量(如上图中的电流、电压、相位还有与每个设备相关的静态标签如上述表一中的位置Location和分组groupId。每个设备是受外界的触发或按照设定的周期采集数据。采集的数据点是时序的是一个数据流。
### 数据特征
除时序特征外,仔细研究发现,物联网、车联网、运维监测类数据还具有很多其他明显的特征。
1. 数据是结构化的
1. 数据高度结构化
2. 数据极少有更新或删除操作;
3. 无需传统数据库的事务处理;
4. 相对互联网应用,写多读少;
5. 流量平稳,根据设备数量和采集频次,可以预测出来;
6. 用户关注的是一段时间的趋势,而不是某一特点时间点的值;
7. 数据有保留期限
8. 数据的查询分析一定是基于时间段和地理区域
9. 除存储查询外,还往往需要各种统计和实时计算操作;
7. 数据有保留期限;
8. 数据的查询分析一定是基于时间段和地理区域;
9. 除存储查询外,还需要各种统计和实时计算操作;
10. 数据量巨大一天采集的数据就可以超过100亿条。
充分利用上述特征TDengine采取了特殊的优化的存储和计算设计来处理时序数据,能将系统处理能力显著提高。
充分利用上述特征TDengine 采取了特殊的优化的存储和计算设计来处理时序数据,能将系统处理能力显著提高。
### 关系型数据库模型
因为采集的数据一般是结构化数据而且为降低学习门槛TDengine采用传统的关系型数据库模型管理数据。因此用户需要先创建库然后创建表之后才能插入或查询数据。TDengine采用的是结构化存储而不是NoSQL的key-value存储。
### 一个数据采集点一张表
为充分利用其数据的时序性和其他数据特点TDengine要求**对每个数据采集点单独建表**(比如有一千万个智能电表,就需创建一千万张表,上述表格中的D1001, D1002, D1003, D1004都需单独建表用来存储这个采集点所采集的时序数据。这种设计有几大优点
为充分利用其数据的时序性和其他数据特点TDengine要求**对每个数据采集点单独建表**(比如有一千万个智能电表,就需创建一千万张表,上述表格中的d1001, d1002, d1003, d1004都需单独建表用来存储这个采集点所采集的时序数据。这种设计有几大优点
1. 能保证一个采集点的数据在存储介质上是一块一块连续的。如果读取一个时间段的数据,它能大幅减少随机读取操作,成数量级的提升读取和查询速度。
2. 由于不同采集设备产生数据的过程完全独立,每个设备的数据源是唯一的,一张表也就只有一个写入者,这样就可采用无锁方式来写,写入速度就能大幅提升。
3. 对于一个数据采集点而言,其产生的数据是时序的,因此写的操作可用追加的方式实现,进一步大幅提高数据写入速度。
如果采用传统的方式,将多个设备的数据写入一张表,由于网络延时不可控,不同设备的数据到达服务器的时序是无法保证的,写入操作是要有锁保护的,而且一个设备的数据是难以保证连续存储在一起的。**采用一个采集点一张表的方式,能最大程度的保证单个数据采集点的插入和查询的性能是最优的。**
如果采用传统的方式,将多个设备的数据写入一张表,由于网络延时不可控,不同设备的数据到达服务器的时序是无法保证的,写入操作是要有锁保护的,而且一个设备的数据是难以保证连续存储在一起的。**采用一个数据采集点一张表的方式,能最大程度的保证单个数据采集点的插入和查询的性能是最优的。**
TDengine 建议用数据采集点的名字(如上表中的D1001)来做表名。每个数据采集点可能同时采集多个物理量(如上表中的curent, voltage, phase),每个物理量对应一张表中的一列,数据类型可以是整型、浮点型、字符串等。除此之外,表的第一列必须是时间戳,即数据类型为 timestamp。对采集的数据TDengine将自动按照时间戳建立索引但对采集的物理量不建任何索引。数据是用列式存储方式保存。
@ -56,13 +58,12 @@ TDengine 建议用数据采集点的名字(如上表中的D1001)来做表名。
当对多个具有相同数据类型的数据采集点进行聚合操作时TDengine将先把满足标签过滤条件的表从超级表的中查找出来然后再扫描这些表的时序数据进行聚合操作这样能将需要扫描的数据集大幅减少从而大幅提高聚合计算的性能。
##集群与基本逻辑单元
## 集群与基本逻辑单元
TDengine 的设计是基于单个硬件、软件系统不可靠,基于任何单台计算机都无法提供足够计算能力和存储能力处理海量数据的假设进行设计的。因此 TDengine 从研发的第一天起就按照分布式高可靠架构进行设计是支持水平扩展的这样任何单台或多台服务器发生硬件故障或软件错误都不影响系统的可用性和可靠性。同时通过节点虚拟化并辅以自动化负载均衡技术TDengine 能最高效率地利用异构集群中的计算和存储资源降低硬件投资。
###主要逻辑单元
### 主要逻辑单元
TDengine 分布式架构的逻辑结构图如下:
<center> <img src="../assets/structure.png"> </center>
<center> 图 1 TDengine架构示意图 </center>
一个完整的 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)的组合。通过配置不同的端口,一个物理节点(一台物理机、虚拟机或容器)可以运行多个实例,或有多个数据节点。
**虚拟节点(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 的多副本特性,可以不再需要昂贵的磁盘阵列等存储设备获得同样的数据高可靠性。虚拟节点组由管理节点创建、管理并且由管理节点分配一系统唯一的IDvnode 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 的多副本特性可以不再需要昂贵的磁盘阵列等存储设备获得同样的数据高可靠性。虚拟节点组由管理节点创建、管理并且由管理节点分配一系统唯一的IDvnode 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上都有一运行实例。
@ -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。
###一典型的操作流程
### 一典型的操作流程
为解释vnode, mnode, taosc和应用之间的关系以及各自扮演的角色下面对写入数据这个典型操作的流程进行剖析。
<center> <img src="../assets/message.png"> </center>
<center> 图 2 TDengine典型的操作流程 </center>
1. 应用通过JDBC、ODBC或其他API接口发起插入数据的请求。
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发出请求。
@ -118,7 +117,7 @@ TDengine 分布式架构的逻辑结构图如下:
## 存储模型与数据分区、分片
###存储模型
### 存储模型
TDengine存储的数据包括采集的时序数据以及库、表相关的元数据、标签数据等这些数据具体分为三部分
- 时序数据存放于vnode里由data、head和last三个文件组成数据量大查询量取决于应用场景。容许乱序写入但暂时不支持删除和更新操作。通过采用一个采集点一张表的模型一个时间段的数据是连续存储对单张表的写入是简单的追加操作一次读可以读到多条记录这样保证对单个采集点的插入和查询操作性能达到最优。
@ -130,7 +129,7 @@ TDengine存储的数据包括采集的时序数据以及库、表相关的元数
- 能够极大地降低标签数据存储的冗余度一般的NoSQL数据库或时序数据库采用的K-V存储其中的Key包含时间戳、设备ID、各种标签。每条记录都带有这些重复的内容浪费存储空间。而且如果应用要在历史数据上增加、修改或删除标签需要遍历数据重写一遍操作成本极其昂贵。
- 能够实现极为高效的多表之间的聚合查询:做多表之间聚合查询时,先把符合标签过滤条件的表查找出来,然后再查找这些表相应的数据块,这样大幅减少要扫描的数据集,从而大幅提高查询效率。而且标签数据采用全内存的结构进行管理和维护,千万级别规模的标签数据查询可以在毫秒级别返回。
###数据分片
### 数据分片
对于海量的数据管理,为实现水平扩展,一般都需要采取分片(Sharding)分区(Partitioning)策略。TDengine是通过vnode来实现数据分片的通过一个时间段一个数据文件来实现时序数据分区的。
vnode(虚拟数据节点)负责为采集的时序数据提供写入、查询和计算功能。为便于负载均衡、数据恢复、支持异构环境TDengine将一个数据节点根据其计算和存储资源切分为多个vnode。这些vnode的管理是TDengine自动完成的对应用完全透明。
@ -141,12 +140,12 @@ vnode(虚拟数据节点)负责为采集的时序数据提供写入、查询和
每张表的meda data包含schema, 标签等也存放于vnode里而不是集中存放于mnode实际上这是对Meta数据的分片这样便于高效并行的进行标签过滤操作。
###数据分区
TDengine除vnode分片之外还按照时间段进行分区。每个数据文件只包含一个时间段的时序数据时间段的长度由DB的配置参数days决定。这种按时间段分区的方法还便于高效实现数据的保留策略只要数据文件超过规定的天数系统配置参数keep),将被自动删除。而且不同的时间段可以存放于不同的路径和存储介质,以便于大数据的冷热管理,实现多级存储。
### 数据分区
TDengine除vnode分片之外对时序数据按照时间段进行分区。每个数据文件只包含一个时间段的时序数据时间段的长度由DB的配置参数days决定。这种按时间段分区的方法还便于高效实现数据的保留策略只要数据文件超过规定的天数系统配置参数keep),将被自动删除。而且不同的时间段可以存放于不同的路径和存储介质,以便于大数据的冷热管理,实现多级存储。
总的来说,**TDengine是通过vnode以及时间两个维度对大数据进行切分**,便于并行高效的管理,实现水平扩展。
###负载均衡
### 负载均衡
每个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的副本数。
@ -155,14 +154,13 @@ TDengine除vnode分片之外还按照时间段进行分区。每个数据文
负载均衡过程无需任何人工干预,应用也无需重启,将自动连接新的节点,完全透明。
##数据写入与复制流程
## 数据写入与复制流程
如果一个数据库有N个副本那一个虚拟节点组就有N个虚拟节点但是只有一个是Master其他都是slave。当应用将新的记录写入系统时只有Master vnode能接受写的请求。如果slave vnode收到写的请求系统将通知taosc需要重新定向。
###Master vnode写入流程
### Master vnode写入流程
Master Vnode遵循下面的写入流程
<center> <img src="../assets/write_master.png"> </center>
<center> 图 3 TDengine Master写入流程 </center>
1. Master vnode收到应用的数据插入请求验证OK进入下一步
2. 如果系统配置参数walLevel打开设置为2vnode将把该请求的原始数据包写入数据库日志文件WAL以保证TDengine能够在断电等因素导致的服务重启时从数据库日志文件中恢复数据避免数据的丢失
3. 如果有多个副本vnode将把数据包转发给同一虚拟节点组内slave vnodes, 该转发包带有数据的版本号(version)
@ -175,7 +173,6 @@ Master Vnode遵循下面的写入流程
<center> <img src="../assets/write_slave.png"> </center>
<center> 图 4 TDengine Slave写入流程 </center>
1. Slave vnode收到Master vnode转发了的数据插入请求。
2. 如果系统配置参数walLevl设置为2vnode将把该请求的原始数据包写入日志(WAL)
3. 写入内存更新内存中的skip list。
@ -207,24 +204,24 @@ Vnode会保持一个数据版本号(Version),对内存数据进行持久化存
3. 在线的虚拟节点数过半而且有虚拟节点是slave的话该虚拟节点自动成为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将返回错误给应用。
采用同步复制系统的性能会有所下降而且latency会增加。因为元数据要强一致Mnode之间的数据同步就是采用的同步复制。
采用同步复制系统的性能会有所下降而且latency会增加。因为元数据要强一致mnode之间的数据同步缺省就是采用的同步复制。
vnode之间的同步复制仅仅企业版支持
##缓存与持久化
###缓存
## 缓存与持久化
### 缓存
TDengine采用时间驱动缓存管理策略First-In-First-OutFIFO又称为写驱动的缓存管理机制。这种策略有别于读驱动的数据缓存模式Least-Recent-UsedLRU直接将最近写入的数据保存在系统的缓存中。当缓存达到临界值的时候将最早的数据批量写入磁盘。一般意义上来说对于物联网数据的使用用户最为关心的是刚产生的数据即当前状态。TDengine充分利用这一特性将最近到达的当前状态数据保存在缓存中。
TDengine通过查询函数向用户提供毫秒级的数据获取能力。直接将最近到达的数据保存在缓存中可以更加快速地响应用户针对最近一条或一批数据的查询分析整体上提供更快的数据库查询响应能力。从这个意义上来说**可通过设置合适的配置参数将TDengine作为数据缓存来使用而不需要再部署Redis或其他额外的缓存系统**可有效地简化系统架构降低运维的成本。需要注意的是TDengine重启以后系统的缓存将被清空之前缓存的数据均会被批量写入磁盘缓存的数据将不会像专门的Key-value缓存系统再将之前缓存的数据重新加载到缓存中。
每个vnode有自己独立的内存而且由多个固定大小的内存块组成不同vnode之间完全隔离。数据写入时类似于日志的写法数据被顺序追加写入内存但每个vnode维护有自己的skip list便于迅速查找。当一半以上的内存块写满时启动落盘操作而且后续写的操作在新的内存块进行。这样一个vnode里有一半内存块是保留有最近的数据的以达到缓存、快速查找的目的。一个vnode的内存块的个数由配置参数blocks决定内存块的大小由配置参数cache决定。
###持久化存储
### 持久化存储
TDengine采用数据驱动的方式让缓存中的数据写入硬盘进行持久化存储。当vnode中缓存的数据达到一定规模时为了不阻塞后续数据的写入TDengine也会拉起落盘线程将缓存的数据写入持久化存储。TDengine在数据落盘时会打开新的数据库日志文件在落盘成功后则会删除老的数据库日志文件避免日志文件无限制的增长。
为充分利用时序数据特点TDengine将一个vnode保存在持久化存储的数据切分成多个文件每个文件只保存固定天数的数据这个天数由系统配置参数days决定。切分成多个文件后给定查询的起止日期无需任何索引就可以立即定位需要打开哪些数据文件大大加快读取速度。
@ -239,7 +236,7 @@ TDengine采用数据驱动的方式让缓存中的数据写入硬盘进行持久
数据写入磁盘时根据系统配置参数comp决定是否压缩数据。TDengine提供了三种压缩选项无压缩、一阶段压缩和两阶段压缩分别对应comp值为0、1和2的情况。一阶段压缩根据数据的类型进行了相应的压缩压缩算法包括delta-delta编码、simple 8B方法、zig-zag编码、LZ4等算法。二阶段压缩在一阶段压缩的基础上又用通用压缩算法进行了压缩压缩率更高。
###多级存储
### 多级存储
在默认配置下TDengine会将所有数据保存在/var/lib/taos目录下而且每个vnode的数据文件保存在该目录下的不同目录。为扩大存储空间尽量减少文件读取的瓶颈提高数据吞吐率 TDengine可通过配置系统参数dataDir让多个挂载的硬盘被系统同时使用。除此之外TDengine也提供了数据分级存储的功能即根据数据文件的新老程度存储在不同的存储介质上。比如最新的数据存储在SSD上超过一周的数据存储在本地硬盘上超过4周的数据存储在网络存储设备上这样来降低存储成本而又保证高效的访问数据。数据在不同存储介质上的移动是由系统自动完成的对应用是完全透明的。数据的分级存储也是通过系统参数dataDir来配置。
dataDir的配置格式如下
@ -263,13 +260,40 @@ dataDir /mnt/disk6/taos 2
注:多级存储功能仅企业版支持
##数据查询
## 数据查询
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的索引BRINblock range index有异曲同工之妙。

View File

@ -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是被删除的节点的FQDNport是其对外服务器的端口号
###查看节点
执行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 groupvnode 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。

View File

@ -1,45 +1,80 @@
#集群管理
#集群安装、管理
多个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的创建开始的。创建第一个节点很简单确保系统配置文件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/)一章的方法进行安装、启动即可
##节点管理
###添加节点
具体有以下几个步骤:
1. 修改新节点配置文件taos.cfg, 将其中参数first与second设置为现有集群中运行节点的End Point, 然后启动taosd。second可以不用设置目的是为集群规模较大时first无法访问时该节点将尝试访问second节点。
2. 使用CLI程序taos, 登录进系统, 使用命令:
启动后请执行taos, 启动taos shell从shell里执行命令"show dnodes;",如下所示:
```
CREATE DNODE "fqdn:port"
```
将新节点的End Point添加进集群的EP列表。**"fqdn:port"需要用双引号引起来**否则出错。一个节点对外服务的fqdn和port可以通过配置文件taos.cfg进行配置。
3. 使用命令
```
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.
**示例:**假设有两个节点其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;
2. 修改host2:6050的配置文件taos.cfg, 将参数first设置为host1:6030然后启动host2:6050这个taosd实例;
3. 执行“taos -h host1" 链接到host1, 在TDengine console里执行命令
taos>
```
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"`查看节点是否加入成功.
5. 按照上述第2到第5步逐步把其他节点添加到集群中。
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
```
查看新节点是否被成功加入。
按照上述步骤可以源源不断的将新的节点加入到集群。
**提示:**
- first, second这两个参数仅仅在该节点第一次加入集群时有作用加入集群后该节点会保存最新的mnode的End Point列表不再依赖这两个参数。
- firstEp, secondEp这两个参数仅仅在该节点第一次加入集群时有作用加入集群后该节点会保存最新的mnode的End Point列表不再依赖这两个参数。
- 两个没有配置first, second参数的dnode启动后会独立运行起来。这个时候无法将其中一个节点加入到另外一个节点形成集群。**无法将两个独立的集群合并成为新的集群**。
##节点管理
###添加节点
执行CLI程序taos, 使用root账号登录进系统, 执行:
```
CREATE DNODE "fqdn:port"
```
将新节点的End Point添加进集群的EP列表。**"fqdn:port"需要用双引号引起来**否则出错。一个节点对外服务的fqdn和port可以通过配置文件taos.cfg进行配置缺省是自动获取。
###删除节点
执行CLI程序taos, 使用root账号登录进TDengine系统执行
```

View File

@ -45,11 +45,11 @@ C/C++的API类似于MySQL的C API。应用程序使用时需要包含TDengine
创建数据库连接,初始化连接上下文。其中需要用户提供的参数包含:
* ipTDengine管理主节点的IP地址
* user用户名
* pass密码
* db数据库名字如果用户没有提供也可以正常连接用户可以通过该连接创建新的数据库如果用户提供了数据库名字则说明该数据库用户已经创建好缺省使用该数据库
* port端口号
- ipTDengine管理主节点的IP地址
- user用户名
- pass密码
- db数据库名字如果用户没有提供也可以正常连接用户可以通过该连接创建新的数据库如果用户提供了数据库名字则说明该数据库用户已经创建好缺省使用该数据库
- port端口号
返回值为空表示失败。应用程序需要保存返回的参数以便后续API调用。

View File

@ -5,19 +5,19 @@
2.0版本在之前版本的基础上,进行了完全的重构,配置文件和数据文件是不兼容的。在升级之前务必进行如下操作:
1. 删除配置文件,执行 <code> sudo rm -rf /etc/taos/taos.cfg </code>
2. 删除日志文件,执行 <code> sudo rm -rf /var/log/taos/ </code>
3. 确保数据已经不再需要的前提下,删除数据文件,执行 <code> sudo rm -rf /var/lib/taos/ </code>
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", 我怎么办?
@ -68,7 +68,8 @@ windows下插入nchar类的数据中如果有中文请先确认系统的地
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驱动的编译/'>技术博客

View File

@ -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);
```
**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中的示例可以参考。
###从源代码编译blm_prometheus
### 从源代码编译blm_prometheus
用户需要从github下载[Bailongma](https://github.com/taosdata/Bailongma)的源码使用Golang语言编译器编译生成可执行文件。在开始编译前需要准备好以下条件
- Linux操作系统的服务器
- 安装好Golang, 1.10版本以上
@ -39,7 +42,7 @@ go build
一切正常的情况下就会在对应的目录下生成一个blm_prometheus的可执行程序。
###安装Prometheus
### 安装Prometheus
通过Prometheus的官网下载安装。[下载地址](https://prometheus.io/download/)
### 配置Prometheus
@ -70,7 +73,7 @@ blm_prometheus会将收到的prometheus的数据拼装成TDengine的写入请求
blm_prometheus对prometheus提供服务的端口号。
```
###启动示例
### 启动示例
通过以下命令启动一个blm_prometheus的API服务
```
@ -82,7 +85,7 @@ remote_write:
- url: "http://10.1.2.3:8088/receive"
```
###查询prometheus写入数据
### 查询prometheus写入数据
prometheus产生的数据格式如下
```
Timestamp: 1576466279341,
@ -104,10 +107,10 @@ use prometheus;
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中的示例可以参考。
###从源代码编译blm_telegraf
### 从源代码编译blm_telegraf
用户需要从github下载[Bailongma](https://github.com/taosdata/Bailongma)的源码使用Golang语言编译器编译生成可执行文件。在开始编译前需要准备好以下条件
- Linux操作系统的服务器
@ -123,7 +126,7 @@ go build
一切正常的情况下就会在对应的目录下生成一个blm_telegraf的可执行程序。
###安装Telegraf
### 安装Telegraf
目前TDengine支持Telegraf 1.7.4以上的版本。用户可以根据当前的操作系统到Telegraf官网下载安装包并执行安装。下载地址如下https://portal.influxdata.com/downloads
### 配置Telegraf
@ -142,7 +145,7 @@ go build
关于如何使用Telegraf采集数据以及更多有关使用Telegraf的信息请参考Telegraf官方的[文档](https://docs.influxdata.com/telegraf/v1.11/)。
###启动blm_telegraf程序
### 启动blm_telegraf程序
blm_telegraf程序有以下选项在启动blm_telegraf程序时可以通过设定这些选项来设定blm_telegraf的配置。
```sh
@ -165,7 +168,7 @@ blm_telegraf会将收到的telegraf的数据拼装成TDengine的写入请求
blm_telegraf对telegraf提供服务的端口号。
```
###启动示例
### 启动示例
通过以下命令启动一个blm_telegraf的API服务
```
./blm_telegraf -host 127.0.0.1 -port 8089
@ -177,7 +180,7 @@ blm_telegraf对telegraf提供服务的端口号。
url = "http://10.1.2.3:8089/telegraf"
```
###查询telegraf写入数据
### 查询telegraf写入数据
telegraf产生的数据格式如下
```
{
@ -210,7 +213,7 @@ use telegraf;
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)。

View File

@ -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大于1vnode B需要等待应用的回复确认收到确认后vnode B发送Forward Response消息给node A。
7. 如果quorum大于1vnode A需要等待vnode B或其他副本对Forward消息的确认。
8. 如果quorum大于1vnode A收到quorum-1条确认消息后调用回调函数confirmForward通知应用写入成功。
9. 如果quorum为1上述678步不会发生。
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 datamaster将通过回调函数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)就重复5678直到所有文件被处理完
对于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的需求的。目前还没有发现漏洞。

View File

@ -0,0 +1,107 @@
# TDengine 2.0 执行代码taosd的设计
逻辑上TDengine系统包含dnode, taosc和Appdnode是服务器侧执行代码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模块向下与dnodeVReaddnodeVWrite发生互动向上与子模块发生互动。它主要的功能有
- 协调各个子模块的互动。各个子模块之间都不直接调用都需要通过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配置为2fsync设置为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运行实例。

View File

@ -31,7 +31,7 @@ install_main_dir="/usr/local/taos"
bin_dir="/usr/local/taos/bin"
# 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"
nginx_port=6060
@ -79,19 +79,19 @@ osinfo=$(cat /etc/os-release | grep "NAME" | cut -d '"' -f2)
#echo "osinfo: ${osinfo}"
os_type=0
if echo $osinfo | grep -qwi "ubuntu" ; then
echo "This is ubuntu system"
# echo "This is ubuntu system"
os_type=1
elif echo $osinfo | grep -qwi "debian" ; then
echo "This is debian system"
# echo "This is debian system"
os_type=1
elif echo $osinfo | grep -qwi "Kylin" ; then
echo "This is Kylin system"
# echo "This is Kylin system"
os_type=1
elif echo $osinfo | grep -qwi "centos" ; then
echo "This is centos system"
# echo "This is centos system"
os_type=2
elif echo $osinfo | grep -qwi "fedora" ; then
echo "This is fedora system"
# echo "This is fedora system"
os_type=2
else
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
done
echo "verType=${verType} interactiveFqdn=${interactiveFqdn}"
#echo "verType=${verType} interactiveFqdn=${interactiveFqdn}"
function kill_taosd() {
pid=$(ps -ef | grep "taosd" | grep -v "grep" | awk '{print $2}')
@ -189,19 +189,19 @@ function install_bin() {
function install_lib() {
# Remove links
${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} 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
if [ "$verMode" == "cluster" ]; then
# Compatible with version 1.5
${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} chmod 777 ${v15_java_app_dir} || :
fi
#if [ "$verMode" == "cluster" ]; then
# # Compatible with version 1.5
# ${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} chmod 777 ${v15_java_app_dir} || :
#fi
}
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} 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
return 0
fi
if ((${update_flag}==1)); then
return 0
fi
if [ "$interactiveFqdn" == "no" ]; then
return 0
fi
if [ "$interactiveFqdn" == "no" ]; then
return 0
fi
#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}$"
#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 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
# 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
fi
done
fi
#else
# read -p "Please enter the correct FQDN:port: " firstEp
#fi
else
break
fi
done
}
@ -565,10 +564,9 @@ function update_TDengine() {
install_service
install_config
openresty_work=false
if [ "$verMode" == "cluster" ]; then
# Check if openresty is installed
openresty_work=false
# Check if nginx is installed successfully
if type curl &> /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
echo
echo -e "\033[44;32;1mTDengine is updated successfully!${NC}"
#echo
#echo -e "\033[44;32;1mTDengine is updated successfully!${NC}"
echo
echo -e "${GREEN_DARK}To configure TDengine ${NC}: edit /etc/taos/taos.cfg"
if ((${service_mod}==0)); then
@ -592,15 +590,12 @@ function update_TDengine() {
echo -e "${GREEN_DARK}To start TDengine ${NC}: ./taosd${NC}"
fi
if [ "$verMode" == "cluster" ]; 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}"
else
echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell${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}"
echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell${NC}"
fi
echo
echo -e "\033[44;32;1mTDengine is updated successfully!${NC}"
else
@ -643,8 +638,8 @@ function install_TDengine() {
install_bin
install_service
openresty_work=false
if [ "$verMode" == "cluster" ]; then
openresty_work=false
# Check if nginx is installed successfully
if type curl &> /dev/null; then
if curl -sSf http://127.0.0.1:${nginx_port} &> /dev/null; then
@ -671,19 +666,18 @@ function install_TDengine() {
echo -e "${GREEN_DARK}To start TDengine ${NC}: taosd${NC}"
fi
if [ "$verMode" == "cluster" ]; 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}"
else
echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell${NC}"
fi
else
echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell${NC}"
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
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
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 -e "\033[44;32;1mTDengine is installed successfully!${NC}"
echo
else # Only install client

View File

@ -106,6 +106,31 @@ function install_config() {
${csudo} mv ${cfg_dir}/taos.cfg ${cfg_dir}/taos.cfg.org
${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() {
@ -227,8 +252,8 @@ function install_TDengine() {
install_config
# Ask if to start the service
echo
echo -e "\033[44;32;1mTDengine is installed successfully!${NC}"
#echo
#echo -e "\033[44;32;1mTDengine is installed successfully!${NC}"
echo
echo -e "${GREEN_DARK}To configure TDengine ${NC}: edit /etc/taos/taos.cfg"
if ((${service_mod}==0)); then
@ -242,6 +267,12 @@ function install_TDengine() {
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 -e "\033[44;32;1mTDengine is installed successfully!${NC}"
}

View File

@ -14,10 +14,10 @@
*/
#define _DEFAULT_SOURCE
#include "os.h"
#include "tutil.h"
#include "tbalance.h"
#include "tsync.h"
#include "ttime.h"
#include "ttimer.h"
#include "tglobal.h"
#include "tdataformat.h"

View File

@ -18,7 +18,6 @@
#include "tlog.h"
#include "tscUtil.h"
#include "tsclient.h"
#include "ttime.h"
#include "com_taosdata_jdbc_TSDBJNIConnector.h"

View File

@ -27,7 +27,6 @@
#include "tscSubquery.h"
#include "tscompression.h"
#include "tsqlfunction.h"
#include "ttime.h"
#include "tutil.h"
#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);
}
/*

View File

@ -326,7 +326,7 @@ static void tscProcessServerVer(SSqlObj *pSql) {
STR_WITH_SIZE_TO_VARSTR(vx, v, t);
tscSetLocalQueryResult(pSql, vx, pExpr->aliasName, pExpr->resType, pExpr->resBytes);
tfree(vx);
taosTFree(vx);
}
static void tscProcessClientVer(SSqlObj *pSql) {
@ -342,7 +342,7 @@ static void tscProcessClientVer(SSqlObj *pSql) {
STR_WITH_SIZE_TO_VARSTR(v, version, t);
tscSetLocalQueryResult(pSql, v, pExpr->aliasName, pExpr->resType, pExpr->resBytes);
tfree(v);
taosTFree(v);
}
static void tscProcessServStatus(SSqlObj *pSql) {

View File

@ -230,7 +230,7 @@ void tscCreateLocalReducer(tExtMemBuffer **pMemBuffer, int32_t numOfBuffer, tOrd
if (ds == NULL) {
tscError("%p failed to create merge structure", pSql);
pRes->code = TSDB_CODE_TSC_OUT_OF_MEMORY;
tfree(pReducer);
taosTFree(pReducer);
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
tscDebug("%p flush data is empty, ignore %d flush record", pSql, idx);
tfree(ds);
taosTFree(ds);
continue;
}
@ -267,7 +267,7 @@ void tscCreateLocalReducer(tExtMemBuffer **pMemBuffer, int32_t numOfBuffer, tOrd
// no data actually, no need to merge result.
if (idx == 0) {
tfree(pReducer);
taosTFree(pReducer);
return;
}
@ -275,7 +275,7 @@ void tscCreateLocalReducer(tExtMemBuffer **pMemBuffer, int32_t numOfBuffer, tOrd
SCompareParam *param = malloc(sizeof(SCompareParam));
if (param == NULL) {
tfree(pReducer);
taosTFree(pReducer);
return;
}
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);
if (pReducer->pLoserTree == NULL || pRes->code != 0) {
tfree(param);
tfree(pReducer);
taosTFree(param);
taosTFree(pReducer);
return;
}
@ -332,14 +332,14 @@ void tscCreateLocalReducer(tExtMemBuffer **pMemBuffer, int32_t numOfBuffer, tOrd
if (pReducer->pTempBuffer == NULL || pReducer->discardData == NULL || pReducer->pResultBuf == NULL ||
/*pReducer->pBufForInterpo == NULL || */pReducer->pFinalRes == NULL || pReducer->prevRowOfInput == NULL) {
tfree(pReducer->pTempBuffer);
tfree(pReducer->discardData);
tfree(pReducer->pResultBuf);
tfree(pReducer->pFinalRes);
tfree(pReducer->prevRowOfInput);
tfree(pReducer->pLoserTree);
tfree(param);
tfree(pReducer);
taosTFree(pReducer->pTempBuffer);
taosTFree(pReducer->discardData);
taosTFree(pReducer->pResultBuf);
taosTFree(pReducer->pFinalRes);
taosTFree(pReducer->prevRowOfInput);
taosTFree(pReducer->pLoserTree);
taosTFree(param);
taosTFree(pReducer);
pRes->code = TSDB_CODE_TSC_OUT_OF_MEMORY;
return;
}
@ -516,38 +516,38 @@ void tscDestroyLocalReducer(SSqlObj *pSql) {
tVariantDestroy(&pCtx->tag);
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);
tfree(pLocalReducer->pResultBuf);
taosTFree(pLocalReducer->pTempBuffer);
taosTFree(pLocalReducer->pResultBuf);
if (pLocalReducer->pResInfo != NULL) {
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) {
tfree(pLocalReducer->pLoserTree->param);
tfree(pLocalReducer->pLoserTree);
taosTFree(pLocalReducer->pLoserTree->param);
taosTFree(pLocalReducer->pLoserTree);
}
tfree(pLocalReducer->pFinalRes);
tfree(pLocalReducer->discardData);
taosTFree(pLocalReducer->pFinalRes);
taosTFree(pLocalReducer->discardData);
tscLocalReducerEnvDestroy(pLocalReducer->pExtMemBuffer, pLocalReducer->pDesc, pLocalReducer->resColModel,
pLocalReducer->numOfVnode);
for (int32_t i = 0; i < pLocalReducer->numOfBuffer; ++i) {
tfree(pLocalReducer->pLocalDataSrc[i]);
taosTFree(pLocalReducer->pLocalDataSrc[i]);
}
pLocalReducer->numOfBuffer = 0;
@ -593,7 +593,7 @@ static int32_t createOrderDescriptor(tOrderDescriptor **pOrderDesc, SSqlCmd *pCm
}
*pOrderDesc = tOrderDesCreate(orderIdx, numOfGroupByCols, pModel, pQueryInfo->order.order);
tfree(orderIdx);
taosTFree(orderIdx);
if (*pOrderDesc == NULL) {
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) {
pRes->code = TSDB_CODE_TSC_OUT_OF_MEMORY;
tfree(pSchema);
taosTFree(pSchema);
return pRes->code;
}
@ -746,7 +746,7 @@ int32_t tscLocalReducerEnvCreate(SSqlObj *pSql, tExtMemBuffer ***pMemBuffer, tOr
}
*pFinalModel = createColumnModel(pSchema, size, capacity);
tfree(pSchema);
taosTFree(pSchema);
return TSDB_CODE_SUCCESS;
}
@ -765,7 +765,7 @@ void tscLocalReducerEnvDestroy(tExtMemBuffer **pMemBuffer, tOrderDescriptor *pDe
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;
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) {

View File

@ -30,7 +30,6 @@
#include "tscLog.h"
#include "tscSubquery.h"
#include "tstoken.h"
#include "ttime.h"
#include "tdataformat.h"
@ -1388,7 +1387,7 @@ static void parseFileSendDataBlock(void *param, TAOS_RES *tres, int code) {
assert(taos_errno(pSql) == code);
taos_free_result(pSql);
tfree(pSupporter);
taosTFree(pSupporter);
fclose(fp);
pParentSql->res.code = code;
@ -1453,7 +1452,7 @@ static void parseFileSendDataBlock(void *param, TAOS_RES *tres, int code) {
}
}
tfree(tokenBuf);
taosTFree(tokenBuf);
free(line);
if (count > 0) {
@ -1466,7 +1465,7 @@ static void parseFileSendDataBlock(void *param, TAOS_RES *tres, int code) {
} else {
taos_free_result(pSql);
tfree(pSupporter);
taosTFree(pSupporter);
fclose(fp);
pParentSql->fp = pParentSql->fetchFp;
@ -1496,7 +1495,7 @@ void tscProcessMultiVnodesImportFromFile(SSqlObj *pSql) {
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));
tfree(pSupporter)
taosTFree(pSupporter)
tscQueueAsyncRes(pSql);
return;

View File

@ -613,7 +613,7 @@ int taos_stmt_execute(TAOS_STMT* stmt) {
if (sql == NULL) {
ret = TSDB_CODE_TSC_OUT_OF_MEMORY;
} else {
tfree(pStmt->pSql->sqlstr);
taosTFree(pStmt->pSql->sqlstr);
pStmt->pSql->sqlstr = sql;
SSqlObj* pSql = taos_query((TAOS*)pStmt->taos, pStmt->pSql->sqlstr);
ret = taos_errno(pSql);

View File

@ -16,7 +16,6 @@
#include "os.h"
#include "tscLog.h"
#include "tsclient.h"
#include "ttime.h"
#include "ttimer.h"
#include "tutil.h"
#include "taosmsg.h"

View File

@ -29,7 +29,6 @@
#include "tsclient.h"
#include "tstoken.h"
#include "tstrbuild.h"
#include "ttime.h"
#include "ttokendef.h"
#define DEFAULT_PRIMARY_TIMESTAMP_COL_NAME "_c0"
@ -737,7 +736,7 @@ int32_t tscSetTableFullName(STableMetaInfo* pTableMetaInfo, SSQLToken* pzTableNa
assert(pTableMetaInfo->pTableMeta == NULL);
}
tfree(oldName);
taosTFree(oldName);
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);
if (ret != TSDB_CODE_SUCCESS) {
taosStringBuilderDestroy(&sb1);
tfree(segments);
taosTFree(segments);
invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg);
return ret;
@ -3661,7 +3660,7 @@ static int32_t setTableCondForSTableQuery(SSqlCmd* pCmd, SQueryInfo* pQueryInfo,
pQueryInfo->tagCond.tbnameCond.cond = strdup(str);
taosStringBuilderDestroy(&sb1);
tfree(segments);
taosTFree(segments);
return TSDB_CODE_SUCCESS;
}

View File

@ -23,7 +23,6 @@
#include "tscUtil.h"
#include "tschemautil.h"
#include "tsclient.h"
#include "ttime.h"
#include "ttimer.h"
#include "tutil.h"
#include "tlockfree.h"
@ -196,7 +195,7 @@ void tscProcessActivityTimer(void *handle, void *tmrId) {
pSql->cmd.command = TSDB_SQL_HB;
if (TSDB_CODE_SUCCESS != tscAllocPayload(&(pSql->cmd), TSDB_DEFAULT_PAYLOAD_SIZE)) {
tfree(pSql);
taosTFree(pSql);
return;
}
@ -1538,7 +1537,7 @@ int tscBuildTableMetaMsg(SSqlObj *pSql, SSqlInfo *pInfo) {
pCmd->payloadLen = pMsg - (char*)pInfoMsg;
pCmd->msgType = TSDB_MSG_TYPE_CM_TABLE_META;
tfree(tmpData);
taosTFree(tmpData);
assert(msgLen + minMsgSize() <= pCmd->allocSize);
return TSDB_CODE_SUCCESS;
@ -1572,7 +1571,7 @@ int tscBuildMultiMeterMetaMsg(SSqlObj *pSql, SSqlInfo *pInfo) {
memcpy(pInfoMsg->tableIds, tmpData, pCmd->payloadLen);
}
tfree(tmpData);
taosTFree(tmpData);
pCmd->payloadLen += sizeof(SMgmtHead) + sizeof(SCMMultiTableInfoMsg);
pCmd->msgType = TSDB_MSG_TYPE_CM_TABLES_META;
@ -1962,7 +1961,7 @@ int tscProcessShowRsp(SSqlObj *pSql) {
pCmd->numOfCols = pQueryInfo->fieldsInfo.numOfOutput;
tscFieldInfoUpdateOffset(pQueryInfo);
tfree(pTableMeta);
taosTFree(pTableMeta);
return 0;
}

View File

@ -474,7 +474,7 @@ int taos_fetch_block(TAOS_RES *res, TAOS_ROW *rows) {
pRes->rspType = 0;
pSql->numOfSubs = 0;
tfree(pSql->pSubs);
taosTFree(pSql->pSubs);
assert(pSql->fp == NULL);
@ -753,7 +753,7 @@ int taos_validate_sql(TAOS *taos, const char *sql) {
if (sqlLen > tsMaxSQLStringLen) {
tscError("%p sql too long", pSql);
pRes->code = TSDB_CODE_TSC_INVALID_SQL;
tfree(pSql);
taosTFree(pSql);
return pRes->code;
}
@ -762,7 +762,7 @@ int taos_validate_sql(TAOS *taos, const char *sql) {
pRes->code = TSDB_CODE_TSC_OUT_OF_MEMORY;
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);
tfree(pSql);
taosTFree(pSql);
return pRes->code;
}
@ -896,7 +896,7 @@ int taos_load_table_info(TAOS *taos, const char *tableNameList) {
if (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;
tfree(pSql);
taosTFree(pSql);
return pRes->code;
}
@ -904,7 +904,7 @@ int taos_load_table_info(TAOS *taos, const char *tableNameList) {
if (str == NULL) {
pRes->code = TSDB_CODE_TSC_OUT_OF_MEMORY;
tscError("%p failed to malloc sql string buffer", pSql);
tfree(pSql);
taosTFree(pSql);
return pRes->code;
}

View File

@ -21,7 +21,6 @@
#include "tsched.h"
#include "tcache.h"
#include "tsclient.h"
#include "ttime.h"
#include "ttimer.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);
taosCacheRelease(tscCacheHandle, (void**)&(pTableMetaInfo->pTableMeta), true);
tfree(pTableMetaInfo->vgroupList);
taosTFree(pTableMetaInfo->vgroupList);
tscSetRetryTimer(pStream, pStream->pSql, retryDelay);
return;
@ -260,9 +259,9 @@ static void tscProcessStreamRetrieveResult(void *param, TAOS_RES *res, int numOf
taosCacheRelease(tscCacheHandle, (void**)&(pTableMetaInfo->pTableMeta), false);
tscFreeSqlResult(pSql);
tfree(pSql->pSubs);
taosTFree(pSql->pSubs);
pSql->numOfSubs = 0;
tfree(pTableMetaInfo->vgroupList);
taosTFree(pTableMetaInfo->vgroupList);
tscSetNextLaunchTimer(pStream, pSql);
}
}
@ -592,6 +591,6 @@ void taos_close_stream(TAOS_STREAM *handle) {
tscFreeSqlObj(pSql);
pStream->pSql = NULL;
tfree(pStream);
taosTFree(pStream);
}
}

View File

@ -18,7 +18,6 @@
#include "trpc.h"
#include "tsclient.h"
#include "tsocket.h"
#include "ttime.h"
#include "ttimer.h"
#include "tutil.h"
#include "tscLog.h"

View File

@ -183,7 +183,7 @@ SJoinSupporter* tscCreateJoinSupporter(SSqlObj* pSql, SSubqueryState* pState, in
pSupporter->uid = pTableMetaInfo->pTableMeta->id.uid;
assert (pSupporter->uid != 0);
getTmpfilePath("join-", pSupporter->path);
taosGetTmpfilePath("join-", pSupporter->path);
pSupporter->f = fopen(pSupporter->path, "w");
// todo handle error
@ -215,7 +215,7 @@ static void tscDestroyJoinSupporter(SJoinSupporter* pSupporter) {
pSupporter->f = NULL;
}
tfree(pSupporter->pIdTagList);
taosTFree(pSupporter->pIdTagList);
tscTagCondRelease(&pSupporter->tagCond);
free(pSupporter);
}
@ -407,7 +407,7 @@ void freeJoinSubqueryObj(SSqlObj* pSql) {
}
}
tfree(pState);
taosTFree(pState);
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
if (!pRes->completed) {
getTmpfilePath("ts-join", pSupporter->path);
taosGetTmpfilePath("ts-join", pSupporter->path);
pSupporter->f = fopen(pSupporter->path, "w");
pRes->row = pRes->numOfRows;
@ -797,7 +797,7 @@ static void tsCompRetrieveCallback(void* param, TAOS_RES* tres, int32_t numOfRow
tscResetForNextRetrieve(&pSql->res);
assert(pSupporter->f == NULL);
getTmpfilePath("ts-join", pSupporter->path);
taosGetTmpfilePath("ts-join", pSupporter->path);
// TODO check for failure
pSupporter->f = fopen(pSupporter->path, "w");
@ -1317,12 +1317,12 @@ static void doCleanupSubqueries(SSqlObj *pSql, int32_t numOfSubs, SSubqueryState
SRetrieveSupport* pSupport = pSub->param;
tfree(pSupport->localBuffer);
taosTFree(pSupport->localBuffer);
pthread_mutex_unlock(&pSupport->queryMutex);
pthread_mutex_destroy(&pSupport->queryMutex);
tfree(pSupport);
taosTFree(pSupport);
tscFreeSqlObj(pSub);
}
@ -1358,7 +1358,7 @@ int32_t tscHandleMasterSTableQuery(SSqlObj *pSql) {
if (ret != 0) {
pRes->code = TSDB_CODE_TSC_OUT_OF_MEMORY;
tscQueueAsyncRes(pSql);
tfree(pMemoryBuf);
taosTFree(pMemoryBuf);
return ret;
}
@ -1386,7 +1386,7 @@ int32_t tscHandleMasterSTableQuery(SSqlObj *pSql) {
trs->localBuffer = (tFilePage *)calloc(1, nBufferSize + sizeof(tFilePage));
if (trs->localBuffer == NULL) {
tscError("%p failed to malloc buffer for local buffer, orderOfSub:%d, reason:%s", pSql, i, strerror(errno));
tfree(trs);
taosTFree(trs);
break;
}
@ -1404,8 +1404,8 @@ int32_t tscHandleMasterSTableQuery(SSqlObj *pSql) {
SSqlObj *pNew = tscCreateSqlObjForSubquery(pSql, trs, NULL);
if (pNew == NULL) {
tscError("%p failed to malloc buffer for subObj, orderOfSub:%d, reason:%s", pSql, i, strerror(errno));
tfree(trs->localBuffer);
tfree(trs);
taosTFree(trs->localBuffer);
taosTFree(trs);
break;
}
@ -1450,12 +1450,12 @@ static void tscFreeSubSqlObj(SRetrieveSupport *trsupport, SSqlObj *pSql) {
taos_free_result(pSql);
tfree(trsupport->localBuffer);
taosTFree(trsupport->localBuffer);
pthread_mutex_unlock(&trsupport->queryMutex);
pthread_mutex_destroy(&trsupport->queryMutex);
tfree(trsupport);
taosTFree(trsupport);
}
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,
pState->numOfTotal);
tfree(trsupport->pState);
taosTFree(trsupport->pState);
tscFreeSubSqlObj(trsupport, pSql);
// 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;
// only free once
tfree(trsupport->pState);
taosTFree(trsupport->pState);
tscFreeSubSqlObj(trsupport, pSql);
// 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);
tfree(pSupporter);
taosTFree(pSupporter);
if (atomic_sub_fetch_32(&pState->numOfRemain, 1) > 0) {
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);
// release data block data
tfree(pState);
taosTFree(pState);
// restore user defined fp
pParentObj->fp = pParentObj->fetchFp;
@ -1968,11 +1968,11 @@ int32_t tscHandleMultivnodeInsert(SSqlObj *pSql) {
_error:
for(int32_t j = 0; j < numOfSub; ++j) {
tfree(pSql->pSubs[j]->param);
taosTFree(pSql->pSubs[j]->param);
taos_free_result(pSql->pSubs[j]);
}
tfree(pState);
taosTFree(pState);
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);
if (pRes->row >= pRes->numOfRows) { // all the results has returned to invoker
tfree(pRes->tsrow);
taosTFree(pRes->tsrow);
return pRes->tsrow;
}

View File

@ -18,7 +18,6 @@
#include "tcache.h"
#include "trpc.h"
#include "tsystem.h"
#include "ttime.h"
#include "ttimer.h"
#include "tutil.h"
#include "tsched.h"

View File

@ -245,7 +245,7 @@ void tscClearInterpInfo(SQueryInfo* pQueryInfo) {
}
pQueryInfo->fillType = TSDB_FILL_NONE;
tfree(pQueryInfo->fillVal);
taosTFree(pQueryInfo->fillVal);
}
int32_t tscCreateResPointerInfo(SSqlRes* pRes, SQueryInfo* pQueryInfo) {
@ -259,9 +259,9 @@ int32_t tscCreateResPointerInfo(SSqlRes* pRes, SQueryInfo* pQueryInfo) {
// not enough memory
if (pRes->tsrow == NULL || (pRes->buffer == NULL && pRes->numOfCols > 0)) {
tfree(pRes->tsrow);
tfree(pRes->buffer);
tfree(pRes->length);
taosTFree(pRes->tsrow);
taosTFree(pRes->buffer);
taosTFree(pRes->length);
pRes->code = TSDB_CODE_TSC_OUT_OF_MEMORY;
return pRes->code;
@ -274,23 +274,23 @@ int32_t tscCreateResPointerInfo(SSqlRes* pRes, SQueryInfo* pQueryInfo) {
void tscDestroyResPointerInfo(SSqlRes* pRes) {
if (pRes->buffer != NULL) { // free all buffers containing the multibyte string
for (int i = 0; i < pRes->numOfCols; i++) {
tfree(pRes->buffer[i]);
taosTFree(pRes->buffer[i]);
}
pRes->numOfCols = 0;
}
tfree(pRes->pRsp);
tfree(pRes->tsrow);
tfree(pRes->length);
taosTFree(pRes->pRsp);
taosTFree(pRes->tsrow);
taosTFree(pRes->length);
tfree(pRes->pGroupRec);
tfree(pRes->pColumnIndex);
tfree(pRes->buffer);
taosTFree(pRes->pGroupRec);
taosTFree(pRes->pColumnIndex);
taosTFree(pRes->buffer);
if (pRes->pArithSup != NULL) {
tfree(pRes->pArithSup->data);
tfree(pRes->pArithSup);
taosTFree(pRes->pArithSup->data);
taosTFree(pRes->pArithSup);
}
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);
clearAllTableMetaInfo(pQueryInfo, (const char*)addr, false);
tfree(pQueryInfo);
taosTFree(pQueryInfo);
}
pCmd->numOfClause = 0;
tfree(pCmd->pQueryInfo);
taosTFree(pCmd->pQueryInfo);
}
void tscResetSqlCmdObj(SSqlCmd* pCmd) {
@ -357,13 +357,13 @@ void tscPartiallyFreeSqlObj(SSqlObj* pSql) {
// pSql->sqlstr will be used by tscBuildQueryStreamDesc
if (pObj->signature == pObj) {
//pthread_mutex_lock(&pObj->mutex);
tfree(pSql->sqlstr);
taosTFree(pSql->sqlstr);
//pthread_mutex_unlock(&pObj->mutex);
}
tscFreeSqlResult(pSql);
tfree(pSql->pSubs);
taosTFree(pSql->pSubs);
pSql->numOfSubs = 0;
tscResetSqlCmdObj(pCmd);
@ -383,10 +383,10 @@ void tscFreeSqlObj(SSqlObj* pSql) {
SSqlCmd* pCmd = &pSql->cmd;
memset(pCmd->payload, 0, (size_t)pCmd->allocSize);
tfree(pCmd->payload);
taosTFree(pCmd->payload);
pCmd->allocSize = 0;
tfree(pSql->sqlstr);
taosTFree(pSql->sqlstr);
sem_destroy(&pSql->rspSem);
free(pSql);
}
@ -396,12 +396,12 @@ void tscDestroyDataBlock(STableDataBlocks* pDataBlock) {
return;
}
tfree(pDataBlock->pData);
tfree(pDataBlock->params);
taosTFree(pDataBlock->pData);
taosTFree(pDataBlock->params);
// free the refcount for metermeta
taosCacheRelease(tscCacheHandle, (void**)&(pDataBlock->pTableMeta), false);
tfree(pDataBlock);
taosTFree(pDataBlock);
}
SParamInfo* tscAddParamToDataBlock(STableDataBlocks* pDataBlock, char type, uint8_t timePrec, short bytes,
@ -679,7 +679,7 @@ int32_t tscMergeTableDataBlocks(SSqlObj* pSql, SArray* pTableDataBlockList) {
taosHashCleanup(pVnodeDataBlockHashList);
tscDestroyBlockArrayList(pVnodeDataBlockList);
tfree(dataBuf->pData);
taosTFree(dataBuf->pData);
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);
tfree(pObj);
taosTFree(pObj);
}
bool tscIsInsertData(char* sqlstr) {
@ -916,7 +916,7 @@ void tscFieldInfoClear(SFieldInfo* pFieldInfo) {
if (pInfo->pArithExprInfo != NULL) {
tExprTreeDestroy(&pInfo->pArithExprInfo->pExpr, NULL);
tfree(pInfo->pArithExprInfo);
taosTFree(pInfo->pArithExprInfo);
}
}
@ -1029,7 +1029,7 @@ void* sqlExprDestroy(SSqlExpr* pExpr) {
tVariantDestroy(&pExpr->param[i]);
}
tfree(pExpr);
taosTFree(pExpr);
return NULL;
}
@ -1116,11 +1116,11 @@ SColumn* tscColumnListInsert(SArray* pColumnList, SColumnIndex* pColIndex) {
static void destroyFilterInfo(SColumnFilterInfo* pFilterInfo, int32_t numOfFilters) {
for(int32_t i = 0; i < numOfFilters; ++i) {
if (pFilterInfo[i].filterstr) {
tfree(pFilterInfo[i].pz);
taosTFree(pFilterInfo[i].pz);
}
}
tfree(pFilterInfo);
taosTFree(pFilterInfo);
}
SColumn* tscColumnClone(const SColumn* src) {
@ -1351,7 +1351,7 @@ void tscTagCondRelease(STagCond* pTagCond) {
size_t s = taosArrayGetSize(pTagCond->pCond);
for (int32_t i = 0; i < s; ++i) {
SCond* p = taosArrayGet(pTagCond->pCond, i);
tfree(p->cond);
taosTFree(p->cond);
}
taosArrayDestroy(pTagCond->pCond);
@ -1546,7 +1546,7 @@ static void freeQueryInfoImpl(SQueryInfo* pQueryInfo) {
pQueryInfo->tsBuf = tsBufDestroy(pQueryInfo->tsBuf);
tfree(pQueryInfo->fillVal);
taosTFree(pQueryInfo->fillVal);
}
void tscClearSubqueryInfo(SSqlCmd* pCmd) {
@ -1566,7 +1566,7 @@ void clearAllTableMetaInfo(SQueryInfo* pQueryInfo, const char* address, bool rem
free(pTableMetaInfo);
}
tfree(pQueryInfo->pTableMetaInfo);
taosTFree(pQueryInfo->pTableMetaInfo);
}
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);
tfree(pTableMetaInfo->vgroupList);
taosTFree(pTableMetaInfo->vgroupList);
tscColumnListDestroy(pTableMetaInfo->tagColList);
pTableMetaInfo->tagColList = NULL;
@ -2084,7 +2084,7 @@ void tscTryQueryNextClause(SSqlObj* pSql, __async_cb_func_t fp) {
pRes->numOfTotal = num;
tfree(pSql->pSubs);
taosTFree(pSql->pSubs);
pSql->numOfSubs = 0;
pSql->fp = fp;

View File

@ -1,10 +1,10 @@
#include "os.h"
#include <gtest/gtest.h>
#include <cassert>
#include <iostream>
#include "taos.h"
#include "tstoken.h"
#include "ttime.h"
#include "tutil.h"
int main(int argc, char** argv) {

View File

@ -80,7 +80,7 @@ typedef struct {
#define schemaFLen(s) ((s)->flen)
#define schemaVLen(s) ((s)->vlen)
#define schemaColAt(s, i) ((s)->columns + i)
#define tdFreeSchema(s) tfree((s))
#define tdFreeSchema(s) taosTFree((s))
STSchema *tdDupSchema(STSchema *pSchema);
int tdEncodeSchema(void **buf, STSchema *pSchema);
@ -284,7 +284,7 @@ typedef struct {
#define kvRowCpy(dst, r) memcpy((dst), (r), kvRowLen(r))
#define kvRowColVal(r, colIdx) POINTER_SHIFT(kvRowValues(r), (colIdx)->offset)
#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))
SKVRow tdKVRowDup(SKVRow row);

View File

@ -93,7 +93,7 @@ int tdInitTSchemaBuilder(STSchemaBuilder *pBuilder, int32_t version) {
void tdDestroyTSchemaBuilder(STSchemaBuilder *pBuilder) {
if (pBuilder) {
tfree(pBuilder->columns);
taosTFree(pBuilder->columns);
}
}
@ -361,8 +361,8 @@ int tdInitDataCols(SDataCols *pCols, STSchema *pSchema) {
void tdFreeDataCols(SDataCols *pCols) {
if (pCols) {
tfree(pCols->buf);
tfree(pCols->cols);
taosTFree(pCols->buf);
taosTFree(pCols->cols);
free(pCols);
}
}
@ -685,8 +685,8 @@ int tdInitKVRowBuilder(SKVRowBuilder *pBuilder) {
}
void tdDestroyKVRowBuilder(SKVRowBuilder *pBuilder) {
tfree(pBuilder->pColIdx);
tfree(pBuilder->buf);
taosTFree(pBuilder->pColIdx);
taosTFree(pBuilder->buf);
}
void tdResetKVRowBuilder(SKVRowBuilder *pBuilder) {

View File

@ -111,7 +111,7 @@ int32_t tsFsyncPeriod = TSDB_DEFAULT_FSYNC_PERIOD;
int32_t tsReplications = TSDB_DEFAULT_DB_REPLICA_OPTION;
int32_t tsQuorum = TSDB_DEFAULT_DB_QUORUM_OPTION;
int32_t tsMaxVgroupsPerDb = 0;
int32_t tsMinTablePerVnode = 100;
int32_t tsMinTablePerVnode = TSDB_TABLES_STEP;
int32_t tsMaxTablePerVnode = TSDB_DEFAULT_TABLES;
int32_t tsTableIncStepPerVnode = TSDB_TABLES_STEP;

View File

@ -128,7 +128,7 @@ void tVariantDestroy(tVariant *pVar) {
if (pVar == NULL) return;
if (pVar->nType == TSDB_DATA_TYPE_BINARY || pVar->nType == TSDB_DATA_TYPE_NCHAR) {
tfree(pVar->pz);
taosTFree(pVar->pz);
pVar->nLen = 0;
}

View File

@ -53,11 +53,6 @@
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
@ -106,31 +101,6 @@
<testFailureIgnore>true</testFailureIgnore>
</configuration>
</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>
</build>
</project>

View File

@ -10,6 +10,7 @@ const Struct = require('ref-struct');
const FieldTypes = require('./constants');
const errors = require ('./error');
const TaosObjects = require('./taosobjects');
const { NULL_POINTER } = require('ref');
module.exports = CTaosInterface;
@ -177,9 +178,10 @@ var char_arr = ArrayType(ref.types.char);
var TaosField = Struct({
'name': char_arr,
});
TaosField.fields.name.type.size = 64;
TaosField.defineProperty('bytes', ref.types.short);
TaosField.fields.name.type.size = 65;
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 ] ],
//void taos_close(TAOS *taos)
'taos_close': [ ref.types.void, [ ref.types.void_ptr ] ],
//TAOS_RES *taos_use_result(TAOS *taos);
'taos_use_result': [ ref.types.void_ptr, [ ref.types.void_ptr ] ],
//int *taos_fetch_lengths(TAOS_RES *taos);
'taos_fetch_lengths': [ ref.types.void_ptr, [ ref.types.void_ptr ] ],
//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)
'taos_affected_rows': [ ref.types.int, [ ref.types.void_ptr] ],
//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) {
return this.libtaos.taos_affected_rows(connection);
}
CTaosInterface.prototype.useResult = function useResult(connection) {
let result = this.libtaos.taos_use_result(connection);
CTaosInterface.prototype.useResult = function useResult(result) {
let fields = [];
let pfields = this.fetchFields(result);
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) {
//0 - 63 = name //64 - 65 = bytes, 66 - 67 = type
fields.push( {
name: ref.readCString(ref.reinterpret(pfields,64,i)),
bytes: pfields[i + 64],
type: pfields[i + 66]
name: ref.readCString(ref.reinterpret(pfields,65,i)),
type: pfields[i + 65],
bytes: pfields[i + 66]
})
}
}
return {result:result, fields:fields}
return fields;
}
CTaosInterface.prototype.fetchBlock = function fetchBlock(result, fields) {
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) {
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);
blocks.fill(null);
num_of_rows = Math.abs(num_of_rows);
let offset = 0;
pblock = pblock.deref();
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']] ) {
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);
offset += fields[i]['bytes'] * num_of_rows;
blocks[i] = convertFunctions[fields[i]['type']](pdata, 1, fieldlens[i], offset, isMicro);
}
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);
}
// Fetch fields count by connection, the latest query
CTaosInterface.prototype.fieldsCount = function fieldsCount(connection) {
return this.libtaos.taos_field_count(connection);
CTaosInterface.prototype.fieldsCount = function fieldsCount(result) {
return this.libtaos.taos_field_count(result);
}
CTaosInterface.prototype.fetchFields = function fetchFields(result) {
return this.libtaos.taos_fetch_fields(result);
}
CTaosInterface.prototype.errno = function errno(connection) {
return this.libtaos.taos_errno(connection);
CTaosInterface.prototype.errno = function errno(result) {
return this.libtaos.taos_errno(result);
}
CTaosInterface.prototype.errStr = function errStr(connection) {
return ref.readCString(this.libtaos.taos_errstr(connection));
CTaosInterface.prototype.errStr = function errStr(result) {
return ref.readCString(this.libtaos.taos_errstr(result));
}
// Async
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.
let row = cti.libtaos.taos_fetch_row(result2);
let fields = cti.fetchFields_a(result2);
let isMicro = (cti.libtaos.taos_result_precision(result2) == FieldTypes.C_TIMESTAMP_MICRO);
let blocks = new Array(fields.length);
blocks.fill(null);
numOfRows2 = Math.abs(numOfRows2);
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){
for (let i = 0; i < fields.length; i++) {
if (!convertFunctions[fields[i]['type']] ) {
throw new errors.DatabaseError("Invalid data type returned from database");
}
blocks[i] = convertFunctions[fields[i]['type']](row, numOfRows2, fields[i]['bytes'], offset, isMicro);
offset += fields[i]['bytes'] * numOfRows2;
let prow = ref.reinterpret(row,8,i*8);
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);
@ -440,11 +470,11 @@ CTaosInterface.prototype.fetchFields_a = function fetchFields_a (result) {
if (ref.isNull(pfields) == false) {
pfields = ref.reinterpret(pfields, 68 * pfieldscount , 0);
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( {
name: ref.readCString(ref.reinterpret(pfields,64,i)),
bytes: pfields[i + 64],
type: pfields[i + 66]
name: ref.readCString(ref.reinterpret(pfields,65,i)),
type: pfields[i + 65],
bytes: pfields[i + 66]
})
}
}

View File

@ -74,9 +74,11 @@ TDengineConnection.prototype.rollback = function rollback() {
* Clear the results from connector
* @private
*/
TDengineConnection.prototype._clearResultSet = function _clearResultSet() {
/*
TDengineConnection.prototype._clearResultSet = function _clearResultSet() {
var result = this._chandle.useResult(this._conn).result;
if (result) {
this._chandle.freeResult(result)
}
}
*/

View File

@ -98,7 +98,7 @@ TDengineCursor.prototype.execute = function execute(operation, options, callback
if (this._connection == null) {
throw new errors.ProgrammingError('Cursor is not connected');
}
this._connection._clearResultSet();
this._reset_result();
let stmt = operation;
@ -111,18 +111,18 @@ TDengineCursor.prototype.execute = function execute(operation, options, callback
});
obs.observe({ entryTypes: ['measure'] });
performance.mark('A');
res = this._chandle.query(this._connection._conn, stmt);
this._result = this._chandle.query(this._connection._conn, stmt);
performance.mark('B');
performance.measure('query', 'A', 'B');
}
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) {
let fieldCount = this._chandle.fieldsCount(this._connection._conn);
let fieldCount = this._chandle.fieldsCount(this._result);
if (fieldCount == 0) {
let affectedRowCount = this._chandle.affectedRows(this._connection._conn);
let affectedRowCount = this._chandle.affectedRows(this._result);
let response = this._createAffectedResponse(affectedRowCount, time)
if (options['quiet'] != true) {
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
}
else {
let resAndField = this._chandle.useResult(this._connection._conn, fieldCount)
this._result = resAndField.result;
this._fields = resAndField.fields;
this.fields = resAndField.fields;
this._fields = this._chandle.useResult(this._result);
this.fields = this._fields;
wrapCB(callback);
return this._result; //return a pointer to the result
}
}
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) {
let blockAndRows = this._chandle.fetchBlock(this._result, this._fields);
let block = blockAndRows.blocks;
let num_of_rows = blockAndRows.num_of_rows;
if (num_of_rows == 0) {
break;
}
this._rowcount += num_of_rows;
let numoffields = this._fields.length;
for (let i = 0; i < num_of_rows; i++) {
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];
}
data[data.length-1] = (rowBlock);
@ -221,7 +220,7 @@ TDengineCursor.prototype.fetchall = function fetchall(options, callback) {
let response = this._createSetResponse(this._rowcount, time)
console.log(response);
this._connection._clearResultSet();
// this._connection._clearResultSet();
let fields = this.fields;
this._reset_result();
this.data = data;
@ -266,13 +265,15 @@ TDengineCursor.prototype.execute_a = function execute_a (operation, options, cal
}
if (resCode >= 0) {
let fieldCount = cr._chandle.numFields(res2);
if (fieldCount == 0) {
cr._chandle.freeResult(res2);
}
else {
return res2;
}
// let fieldCount = cr._chandle.numFields(res2);
// if (fieldCount == 0) {
// //cr._chandle.freeResult(res2);
// return res2;
// }
// else {
// return res2;
// }
return res2;
}
else {
@ -381,6 +382,9 @@ TDengineCursor.prototype.stopQuery = function stopQuery(result) {
}
TDengineCursor.prototype._reset_result = function _reset_result() {
this._rowcount = -1;
if (this._result != null) {
this._chandle.freeResult(this._result);
}
this._result = null;
this._fields = null;
this.data = [];

View File

@ -1,6 +1,6 @@
{
"name": "td-connector",
"version": "1.6.1",
"name": "td2.0-connector",
"version": "2.0.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "td-connector",
"version": "2.0.0",
"name": "td2.0-connector",
"version": "2.0.1",
"description": "A Node.js connector for TDengine.",
"main": "tdengine.js",
"scripts": {

View File

@ -1,23 +1,24 @@
# TDengine Node.js connector
[![minzip](https://img.shields.io/bundlephobia/minzip/td-connector.svg)](https://github.com/taosdata/TDengine/tree/master/src/connector/nodejs) [![NPM](https://img.shields.io/npm/l/td-connector.svg)](https://github.com/taosdata/TDengine/#what-is-tdengine)
[![minzip](https://img.shields.io/bundlephobia/minzip/td2.0-connector.svg)](https://github.com/taosdata/TDengine/tree/master/src/connector/nodejs) [![NPM](https://img.shields.io/npm/l/td2.0-connector.svg)](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
To get started, just type in the following to install the connector through [npm](https://www.npmjs.com/)
```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)
### On Unix
### On Linux
- `python` (`v2.7` recommended, `v3.x.x` is **not** supported)
- `make`
- 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
@ -71,12 +72,12 @@ The following is a short summary of the basic usage of the connector, the full
### 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.
```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 cursor = conn.cursor(); // Initializing a new cursor
```

View File

@ -19,7 +19,7 @@ function randomBool() {
}
// Initialize
//c1.execute('drop database td_connector_test;');
c1.execute('create database if not exists 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));');
@ -46,12 +46,14 @@ for (let i = 0; i < 10000; i++) {
}
// 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();
console.log(c1.fields);
console.log(d);
// 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;');
var d = c1.fetchall();
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){
result.pretty();
})
c1.query('select _tinyint, _bool from all_types where _tinyint > 50 and _int < 0 limit 50;', true).then(function(result){
result.pretty();
})
c1.query('select stddev(_double), stddev(_bigint), stddev(_float) from all_types;', true).then(function(result){
result.pretty();
})
@ -83,54 +87,58 @@ q.execute().then(function(r) {
// Raw Async Testing (Callbacks, not promises)
function cb2(param, result, rowCount, rd) {
console.log('CB2 Callbacked!');
console.log("RES *", result);
console.log("Async fetched", rowCount, "rows");
console.log("Async fetched", rowCount, " rows");
console.log("Passed Param: ", param);
console.log("Fields", rd.fields);
console.log("Data", rd.data);
console.log("Fields ", rd.fields);
console.log("Data ", rd.data);
}
function cb1(param,result,code) {
console.log('Callbacked!');
console.log("RES *", result);
console.log('CB1 Callbacked!');
console.log("RES * ", result);
console.log("Status: ", code);
console.log("Passed Param", param);
c1.fetchall_a(result, cb2, param)
console.log("Passed Param ", param);
c1.fetchall_a(result, cb2, param);
}
c1.execute_a("describe td_connector_test.all_types;", cb1, {myparam:3.141});
function cb4(param, result, rowCount, rd) {
console.log('CB4 Callbacked!');
console.log("RES *", result);
console.log("Async fetched", rowCount, "rows");
console.log("Passed Param: ", param);
console.log("Fields", rd.fields);
console.log("Data", rd.data);
}
// Without directly calling fetchall_a
var thisRes;
function cb3(param,result,code) {
console.log('Callbacked!');
console.log('CB3 Callbacked!');
console.log("RES *", result);
console.log("Status: ", code);
console.log("Status:", code);
console.log("Passed Param", param);
thisRes = result;
}
//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});
console.log("Passed Param outside of callback: ", param);
console.log(param);
setTimeout(function(){
c1.fetchall_a(thisRes, cb4, param);
},100);
// 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) {
data.pretty();
})
});
c1.query('describe td_connector_test.stabletest;').execute_a().then(r=> r.pretty());
setTimeout(function(){
c1.query('drop database td_connector_test;');
},200);
setTimeout(function(){
conn.close();
},2000);
conn.close();

View File

@ -114,7 +114,7 @@ void cqClose(void *handle) {
SCqObj *pTemp = pObj;
pObj = pObj->next;
tdFreeSchema(pTemp->pSchema);
tfree(pTemp->sqlStr);
taosTFree(pTemp->sqlStr);
free(pTemp);
}

View File

@ -16,7 +16,8 @@ IF ((TD_LINUX_64) OR (TD_LINUX_32 AND TD_ARM))
AUX_SOURCE_DIRECTORY(src 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)
TARGET_LINK_LIBRARIES(taosd account)

View File

@ -75,7 +75,7 @@ void dnodeCleanupMnodePeer() {
}
taosCloseQset(tsMPeerQset);
tfree(tsMPeerPool.peerWorker);
taosTFree(tsMPeerPool.peerWorker);
dInfo("dnode mpeer is closed");
}

View File

@ -77,7 +77,7 @@ void dnodeCleanupMnodeWrite() {
}
taosCloseQset(tsMWriteQset);
tfree(tsMWritePool.writeWorker);
taosTFree(tsMWritePool.writeWorker);
dInfo("dnode mwrite is closed");
}

View File

@ -18,13 +18,11 @@
#include "cJSON.h"
#include "taoserror.h"
#include "taosmsg.h"
#include "ttime.h"
#include "ttimer.h"
#include "tsdb.h"
#include "twal.h"
#include "tqueue.h"
#include "tsync.h"
#include "ttime.h"
#include "ttimer.h"
#include "tbalance.h"
#include "tglobal.h"

View File

@ -22,6 +22,7 @@ extern "C" {
#include <stdint.h>
#include <stdbool.h>
#include "osDef.h"
#include "taos.h"
#define TSDB__packed
@ -161,7 +162,7 @@ extern tDataTypeDescriptor tDataTypeDesc[11];
bool isValidDataType(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) {
case TSDB_DATA_TYPE_BOOL:
return *(uint8_t *)val == TSDB_DATA_BOOL_NULL;

View File

@ -15,11 +15,11 @@ IF ((TD_LINUX_64) OR (TD_LINUX_32 AND TD_ARM))
LIST(REMOVE_ITEM SRC ./src/shellDarwin.c)
ADD_EXECUTABLE(shell ${SRC})
IF (TD_PAGMODE_LITE)
# IF (TD_PAGMODE_LITE)
TARGET_LINK_LIBRARIES(shell taos)
ELSE ()
TARGET_LINK_LIBRARIES(shell taos_static)
ENDIF ()
# ELSE ()
# TARGET_LINK_LIBRARIES(shell taos_static)
# ENDIF ()
SET_TARGET_PROPERTIES(shell PROPERTIES OUTPUT_NAME taos)
ELSEIF (TD_WINDOWS_64)

View File

@ -229,8 +229,8 @@ void shellReadCommand(TAOS *con, char *command) {
printf("\n");
if (isReadyGo(&cmd)) {
sprintf(command, "%s%s", cmd.buffer, cmd.command);
tfree(cmd.buffer);
tfree(cmd.command);
taosTFree(cmd.buffer);
taosTFree(cmd.command);
return;
} else {
updateBuffer(&cmd);

View File

@ -21,7 +21,6 @@
#include "os.h"
#include "shell.h"
#include "shellCommand.h"
#include "ttime.h"
#include "tutil.h"
#include "taosdef.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 ||
strcmp(command, history.hist[(history.hend + MAX_HISTORY_SIZE - 1) % MAX_HISTORY_SIZE]) != 0) {
if (history.hist[history.hend] != NULL) {
tfree(history.hist[history.hend]);
taosTFree(history.hist[history.hend]);
}
history.hist[history.hend] = strdup(command);
@ -770,7 +769,7 @@ void write_history() {
for (int i = history.hstart; i != history.hend;) {
if (history.hist[i] != NULL) {
fprintf(f, "%s\n", history.hist[i]);
tfree(history.hist[i]);
taosTFree(history.hist[i]);
}
i = (i + 1) % MAX_HISTORY_SIZE;
}

View File

@ -21,7 +21,6 @@
#include "shell.h"
#include "shellCommand.h"
#include "tglobal.h"
#include "ttime.h"
#include "tutil.h"
static char **shellSQLFiles = NULL;

View File

@ -201,8 +201,8 @@ void shellReadCommand(TAOS *con, char *command) {
printf("\n");
if (isReadyGo(&cmd)) {
sprintf(command, "%s%s", cmd.buffer, cmd.command);
tfree(cmd.buffer);
tfree(cmd.command);
taosTFree(cmd.buffer);
taosTFree(cmd.command);
return;
} else {
updateBuffer(&cmd);
@ -320,7 +320,7 @@ void *shellLoopQuery(void *arg) {
reset_terminal_mode();
} while (shellRunCommand(con, command) == 0);
tfree(command);
taosTFree(command);
exitShell();
pthread_cleanup_pop(1);

Some files were not shown because too many files have changed in this diff Show More