Merge branch '3.0' into fix/3_liaohj
This commit is contained in:
commit
29a6b072a8
|
@ -8,31 +8,33 @@ import java.sql.SQLException;
|
|||
import java.util.Properties;
|
||||
|
||||
public class WSConnectExample {
|
||||
// ANCHOR: main
|
||||
public static void main(String[] args) throws SQLException {
|
||||
// use
|
||||
// String jdbcUrl = "jdbc:TAOS-RS://localhost:6041/dbName?user=root&password=taosdata";
|
||||
// if you want to connect a specified database named "dbName".
|
||||
String jdbcUrl = "jdbc:TAOS-RS://localhost:6041?user=root&password=taosdata";
|
||||
Properties connProps = new Properties();
|
||||
connProps.setProperty(TSDBDriver.PROPERTY_KEY_BATCH_LOAD, "true");
|
||||
connProps.setProperty(TSDBDriver.PROPERTY_KEY_ENABLE_AUTO_RECONNECT, "true");
|
||||
connProps.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8");
|
||||
connProps.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC-8");
|
||||
// ANCHOR: main
|
||||
public static void main(String[] args) throws SQLException {
|
||||
// use
|
||||
// String jdbcUrl =
|
||||
// "jdbc:TAOS-RS://localhost:6041/dbName?user=root&password=taosdata&batchfetch=true";
|
||||
// if you want to connect a specified database named "dbName".
|
||||
String jdbcUrl = "jdbc:TAOS-RS://localhost:6041?user=root&password=taosdata&batchfetch=true";
|
||||
Properties connProps = new Properties();
|
||||
connProps.setProperty(TSDBDriver.PROPERTY_KEY_ENABLE_AUTO_RECONNECT, "true");
|
||||
connProps.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8");
|
||||
connProps.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC-8");
|
||||
|
||||
try (Connection conn = DriverManager.getConnection(jdbcUrl, connProps)){
|
||||
System.out.println("Connected to " + jdbcUrl + " successfully.");
|
||||
try (Connection conn = DriverManager.getConnection(jdbcUrl, connProps)) {
|
||||
System.out.println("Connected to " + jdbcUrl + " successfully.");
|
||||
|
||||
// you can use the connection for execute SQL here
|
||||
// you can use the connection for execute SQL here
|
||||
|
||||
} catch (SQLException ex) {
|
||||
// handle any errors, please refer to the JDBC specifications for detailed exceptions info
|
||||
System.out.println("Failed to connect to " + jdbcUrl + "; ErrCode:" + ex.getErrorCode() + "; ErrMessage: " + ex.getMessage());
|
||||
throw ex;
|
||||
} catch (Exception ex){
|
||||
System.out.println("Failed to connect to " + jdbcUrl + "; ErrMessage: " + ex.getMessage());
|
||||
throw ex;
|
||||
} catch (SQLException ex) {
|
||||
// handle any errors, please refer to the JDBC specifications for detailed
|
||||
// exceptions info
|
||||
System.out.println("Failed to connect to " + jdbcUrl + "; ErrCode:" + ex.getErrorCode() + "; ErrMessage: "
|
||||
+ ex.getMessage());
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
System.out.println("Failed to connect to " + jdbcUrl + "; ErrMessage: " + ex.getMessage());
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
// ANCHOR_END: main
|
||||
// ANCHOR_END: main
|
||||
}
|
||||
|
|
|
@ -259,17 +259,19 @@ dotnet add package TDengine.Connector
|
|||
## 建立连接
|
||||
|
||||
在执行这一步之前,请确保有一个正在运行的,且可以访问到的 TDengine,而且服务端的 FQDN 配置正确。以下示例代码,都假设 TDengine 安装在本机,且 FQDN(默认 localhost) 和 serverPort(默认 6030) 都使用默认配置。
|
||||
|
||||
### 连接参数
|
||||
连接的配置项较多,因此在建立连接之前,我们能先介绍一下各语言连接器建立连接使用的参数。
|
||||
|
||||
<Tabs defaultValue="java" groupId="lang">
|
||||
<TabItem label="Java" value="java">
|
||||
Java 连接器建立连接的参数有 URL 和 Properties。
|
||||
TDengine 的 JDBC URL 规范格式为:
|
||||
`jdbc:[TAOS|TAOS-RS]://[host_name]:[port]/[database_name]?[user={user}|&password={password}|&charset={charset}|&cfgdir={config_dir}|&locale={locale}|&timezone={timezone}|&batchfetch={batchfetch}]`
|
||||
|
||||
Java 连接器建立连接的参数有 URL 和 Properties。
|
||||
TDengine 的 JDBC URL 规范格式为:
|
||||
`jdbc:[TAOS|TAOS-RS]://[host_name]:[port]/[database_name]?[user={user}|&password={password}|&charset={charset}|&cfgdir={config_dir}|&locale={locale}|&timezone={timezone}]`
|
||||
URL 和 Properties 的详细参数说明和如何使用详见 [url 规范](../../reference/connector/java/#url-规范)
|
||||
|
||||
URL 和 Properties 的详细参数说明和如何使用详见 [url 规范](../../reference/connector/java/#url-规范)
|
||||
**注**:REST 连接中增加 `batchfetch` 参数并设置为 true,将开启 WebSocket 连接。
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Python" value="python">
|
||||
|
@ -343,7 +345,7 @@ DSN 的详细说明和如何使用详见 [连接功能](../../reference/connecto
|
|||
- **database**: 数据库名称。
|
||||
- **params**: 其他参数。 例如token。
|
||||
|
||||
- 完整 D 示例:
|
||||
- 完整 DSN 示例:
|
||||
|
||||
```js
|
||||
ws://root:taosdata@localhost:6041
|
||||
|
|
|
@ -90,7 +90,7 @@ curl --location -uroot:taosdata 'http://127.0.0.1:6041/rest/sql/power' \
|
|||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
> **注意**:如果不使用 `USE power` 指定数据库,则后续对表的操作都需要增加数据库名称作为前缀,如 `power.meters`。
|
||||
> **注意**:建议采用 `<dbName>.<tableName>` 的格式构造SQL语句,不推荐在应用中采用 `USE DBName` 方式访问。
|
||||
|
||||
## 插入数据
|
||||
下面以智能电表为例,展示如何使用连接器执行 SQL 来插入数据到 `power` 数据库的 `meters` 超级表。样例使用 TDengine 自动建表 SQL 语法,写入 d1001 子表中 3 条数据,写入 d1002 子表中 1 条数据,然后打印出实际插入数据条数。
|
||||
|
|
|
@ -169,7 +169,7 @@ st,t1=3,t2=4,t3=t3 c1=3i64,c6="passit" 1626006833640000000
|
|||
```
|
||||
|
||||
|
||||
执行带有 reqId 的无模式写入,此 reqId 可用于请求链路追踪。
|
||||
执行带有 reqId 的无模式写入,最后一个参数 reqId 可用于请求链路追踪。
|
||||
|
||||
```java
|
||||
writer.write(lineDemo, SchemalessProtocolType.LINE, SchemalessTimestampType.NANO_SECONDS, 1L);
|
||||
|
@ -213,7 +213,7 @@ writer.write(lineDemo, SchemalessProtocolType.LINE, SchemalessTimestampType.NANO
|
|||
{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/SchemalessJniTest.java:schemaless}}
|
||||
```
|
||||
|
||||
执行带有 reqId 的无模式写入,此 reqId 可用于请求链路追踪。
|
||||
执行带有 reqId 的无模式写入,最后一个参数 reqId 可用于请求链路追踪。
|
||||
|
||||
```java
|
||||
writer.write(lineDemo, SchemalessProtocolType.LINE, SchemalessTimestampType.NANO_SECONDS, 1L);
|
||||
|
|
|
@ -410,7 +410,7 @@ def finish(buf: bytes) -> output_type:
|
|||
#### 示例一
|
||||
|
||||
编写一个只接收一个整数的 UDF 函数: 输入 n, 输出 ln(n^2 + 1)。
|
||||
首先编写一个 Python 文件,存在系统某个目录,比如 /root/udf/myfun.py 内容如下
|
||||
首先编写一个 Python 文件,存在系统某个目录,比如 /root/udf/myfun.py 内容如下。
|
||||
|
||||
```python
|
||||
from math import log
|
||||
|
@ -426,23 +426,25 @@ def process(block):
|
|||
return [log(block.data(i, 0) ** 2 + 1) for i in range(rows)]
|
||||
```
|
||||
|
||||
这个文件包含 3 个函数, init 和 destroy 都是空函数,它们是 UDF 的生命周期函数,即使什么都不做也要定义。最关键的是 process 函数, 它接受一个数据块,这个数据块对象有两个方法:
|
||||
这个文件包含 3 个函数, init 和 destroy 都是空函数,它们是 UDF 的生命周期函数,即使什么都不做也要定义。最关键的是 process 函数, 它接受一个数据块,这个数据块对象有两个方法。
|
||||
1. shape() 返回数据块的行数和列数
|
||||
2. data(i, j) 返回 i 行 j 列的数据
|
||||
标量函数的 process 方法传人的数据块有多少行,就需要返回多少个数据。上述代码中我们忽略的列数,因为我们只想对每行的第一个数做计算。
|
||||
接下来我们创建对应的 UDF 函数,在 TDengine CLI 中执行下面语句:
|
||||
|
||||
标量函数的 process 方法传入的数据块有多少行,就需要返回多少行数据。上述代码忽略列数,因为只需对每行的第一列做计算。
|
||||
|
||||
接下来创建对应的 UDF 函数,在 TDengine CLI 中执行下面语句。
|
||||
|
||||
```sql
|
||||
create function myfun as '/root/udf/myfun.py' outputtype double language 'Python'
|
||||
```
|
||||
其输出如下
|
||||
其输出如下。
|
||||
|
||||
```shell
|
||||
taos> create function myfun as '/root/udf/myfun.py' outputtype double language 'Python';
|
||||
Create OK, 0 row(s) affected (0.005202s)
|
||||
```
|
||||
|
||||
看起来很顺利,接下来 show 一下系统中所有的自定义函数,确认创建成功:
|
||||
看起来很顺利,接下来查看系统中所有的自定义函数,确认创建成功。
|
||||
|
||||
```text
|
||||
taos> show functions;
|
||||
|
@ -452,7 +454,7 @@ taos> show functions;
|
|||
Query OK, 1 row(s) in set (0.005767s)
|
||||
```
|
||||
|
||||
接下来就来测试一下这个函数,测试之前先执行下面的 SQL 命令,制造些测试数据,在 TDengine CLI 中执行下述命令
|
||||
生成测试数据,可以在 TDengine CLI 中执行下述命令。
|
||||
|
||||
```sql
|
||||
create database test;
|
||||
|
@ -462,7 +464,7 @@ insert into t values('2023-05-03 08:09:10', 2, 3, 4);
|
|||
insert into t values('2023-05-10 07:06:05', 3, 4, 5);
|
||||
```
|
||||
|
||||
测试 myfun 函数:
|
||||
测试 myfun 函数。
|
||||
|
||||
```sql
|
||||
taos> select myfun(v1, v2) from t;
|
||||
|
@ -470,14 +472,13 @@ taos> select myfun(v1, v2) from t;
|
|||
DB error: udf function execution failure (0.011088s)
|
||||
```
|
||||
|
||||
不幸的是执行失败了,什么原因呢?
|
||||
查看 udfd 进程的日志
|
||||
不幸的是执行失败了,什么原因呢?查看 udfd 进程的日志。
|
||||
|
||||
```shell
|
||||
tail -10 /var/log/taos/udfd.log
|
||||
```
|
||||
|
||||
发现以下错误信息:
|
||||
发现以下错误信息。
|
||||
|
||||
```text
|
||||
05/24 22:46:28.733545 01665799 UDF ERROR can not load library libtaospyudf.so. error: operation not permitted
|
||||
|
@ -486,7 +487,7 @@ tail -10 /var/log/taos/udfd.log
|
|||
|
||||
错误很明确:没有加载到 Python 插件 libtaospyudf.so,如果遇到此错误,请参考前面的准备环境一节。
|
||||
|
||||
修复环境错误后再次执行,如下:
|
||||
修复环境错误后再次执行,如下。
|
||||
|
||||
```sql
|
||||
taos> select myfun(v1) from t;
|
||||
|
@ -501,7 +502,7 @@ taos> select myfun(v1) from t;
|
|||
|
||||
#### 示例二
|
||||
|
||||
上面的 myfun 虽然测试测试通过了,但是有两个缺点:
|
||||
上面的 myfun 虽然测试测试通过了,但是有两个缺点。
|
||||
|
||||
1. 这个标量函数只接受 1 列数据作为输入,如果用户传入了多列也不会抛异常。
|
||||
|
||||
|
@ -514,8 +515,7 @@ taos> select myfun(v1, v2) from t;
|
|||
2.302585093 |
|
||||
```
|
||||
|
||||
2. 没有处理 null 值。我们期望如果输入有 null,则会抛异常终止执行。
|
||||
因此 process 函数改进如下:
|
||||
2. 没有处理 null 值。我们期望如果输入有 null,则会抛异常终止执行。因此 process 函数改进如下。
|
||||
|
||||
```python
|
||||
def process(block):
|
||||
|
@ -525,13 +525,13 @@ def process(block):
|
|||
return [ None if block.data(i, 0) is None else log(block.data(i, 0) ** 2 + 1) for i in range(rows)]
|
||||
```
|
||||
|
||||
然后执行下面的语句更新已有的 UDF:
|
||||
执行如下语句更新已有的 UDF。
|
||||
|
||||
```sql
|
||||
create or replace function myfun as '/root/udf/myfun.py' outputtype double language 'Python';
|
||||
```
|
||||
|
||||
再传入 myfun 两个参数,就会执行失败了
|
||||
再传入 myfun 两个参数,就会执行失败了。
|
||||
|
||||
```sql
|
||||
taos> select myfun(v1, v2) from t;
|
||||
|
@ -539,7 +539,7 @@ taos> select myfun(v1, v2) from t;
|
|||
DB error: udf function execution failure (0.014643s)
|
||||
```
|
||||
|
||||
但遗憾的是我们自定义的异常信息没有展示给用户,而是在插件的日志文件 /var/log/taos/taospyudf.log 中:
|
||||
自定义的异常信息打印在插件的日志文件 /var/log/taos/taospyudf.log 中。
|
||||
|
||||
```text
|
||||
2023-05-24 23:21:06.790 ERROR [1666188] [doPyUdfScalarProc@507] call pyUdfScalar proc function. context 0x7faade26d180. error: Exception: require 1 parameter but given 2
|
||||
|
@ -554,18 +554,17 @@ At:
|
|||
|
||||
#### 示例三
|
||||
|
||||
编写一个 UDF:输入(x1, x2, ..., xn), 输出每个值和它们的序号的乘积的和: 1 * x1 + 2 * x2 + ... + n * xn。如果 x1 至 xn 中包含 null,则结果为 null。
|
||||
这个示例与示例一的区别是,可以接受任意多列作为输入,且要处理每一列的值。编写 UDF 文件 /root/udf/nsum.py:
|
||||
输入(x1, x2, ..., xn), 输出每个值和它们的序号的乘积的和:1 * x1 + 2 * x2 + ... + n * xn。如果 x1 至 xn 中包含 null,则结果为 null。
|
||||
|
||||
本例与示例一的区别是,可以接受任意多列作为输入,且要处理每一列的值。编写 UDF 文件 /root/udf/nsum.py。
|
||||
|
||||
```python
|
||||
def init():
|
||||
pass
|
||||
|
||||
|
||||
def destroy():
|
||||
pass
|
||||
|
||||
|
||||
def process(block):
|
||||
rows, cols = block.shape()
|
||||
result = []
|
||||
|
@ -581,13 +580,13 @@ def process(block):
|
|||
return result
|
||||
```
|
||||
|
||||
创建 UDF:
|
||||
创建 UDF。
|
||||
|
||||
```sql
|
||||
create function nsum as '/root/udf/nsum.py' outputtype double language 'Python';
|
||||
```
|
||||
|
||||
测试 UDF:
|
||||
测试 UDF。
|
||||
|
||||
```sql
|
||||
taos> insert into t values('2023-05-25 09:09:15', 6, null, 8);
|
||||
|
@ -606,22 +605,20 @@ Query OK, 4 row(s) in set (0.010653s)
|
|||
#### 示例四
|
||||
|
||||
编写一个 UDF,输入一个时间戳,输出距离这个时间最近的下一个周日。比如今天是 2023-05-25, 则下一个周日是 2023-05-28。
|
||||
完成这个函数要用到第三方库 momen。先安装这个库:
|
||||
完成这个函数要用到第三方库 momen。先安装这个库。
|
||||
|
||||
```shell
|
||||
pip3 install moment
|
||||
```
|
||||
|
||||
然后编写 UDF 文件 /root/udf/nextsunday.py
|
||||
然后编写 UDF 文件 /root/udf/nextsunday.py。
|
||||
|
||||
```python
|
||||
import moment
|
||||
|
||||
|
||||
def init():
|
||||
pass
|
||||
|
||||
|
||||
def destroy():
|
||||
pass
|
||||
|
||||
|
@ -636,13 +633,13 @@ def process(block):
|
|||
for i in range(rows)]
|
||||
```
|
||||
|
||||
UDF 框架会将 TDengine 的 timestamp 类型映射为 Python 的 int 类型,所以这个函数只接受一个表示毫秒数的整数。process 方法先做参数检查,然后用 moment 包替换时间的星期为星期日,最后格式化输出。输出的字符串长度是固定的 10 个字符长,因此可以这样创建 UDF 函数:
|
||||
UDF 框架会将 TDengine 的 timestamp 类型映射为 Python 的 int 类型,所以这个函数只接受一个表示毫秒数的整数。process 方法先做参数检查,然后用 moment 包替换时间的星期为星期日,最后格式化输出。输出的字符串长度是固定的 10 个字符长,因此可以这样创建 UDF 函数。
|
||||
|
||||
```sql
|
||||
create function nextsunday as '/root/udf/nextsunday.py' outputtype binary(10) language 'Python';
|
||||
```
|
||||
|
||||
此时测试函数,如果你是用 systemctl 启动的 taosd,肯定会遇到错误:
|
||||
此时测试函数,如果你是用 systemctl 启动的 taosd,肯定会遇到错误。
|
||||
|
||||
```sql
|
||||
taos> select ts, nextsunday(ts) from t;
|
||||
|
@ -651,11 +648,11 @@ DB error: udf function execution failure (1.123615s)
|
|||
```
|
||||
|
||||
```shell
|
||||
tail -20 taospyudf.log
|
||||
tail -20 taospyudf.log
|
||||
2023-05-25 11:42:34.541 ERROR [1679419] [PyUdf::PyUdf@217] py udf load module failure. error ModuleNotFoundError: No module named 'moment'
|
||||
```
|
||||
|
||||
这是因为 “moment” 所在位置不在 python udf 插件默认的库搜索路径中。怎么确认这一点呢?通过以下命令搜索 taospyudf.log:
|
||||
这是因为 “moment” 所在位置不在 python udf 插件默认的库搜索路径中。怎么确认这一点呢?通过以下命令搜索 taospyudf.log。
|
||||
|
||||
```shell
|
||||
grep 'sys path' taospyudf.log | tail -1
|
||||
|
@ -668,7 +665,7 @@ grep 'sys path' taospyudf.log | tail -1
|
|||
```
|
||||
|
||||
发现 python udf 插件默认搜索的第三方库安装路径是: /lib/python3/dist-packages,而 moment 默认安装到了 /usr/local/lib/python3.8/dist-packages。下面我们修改 python udf 插件默认的库搜索路径。
|
||||
先打开 python3 命令行,查看当前的 sys.path
|
||||
先打开 python3 命令行,查看当前的 sys.path。
|
||||
|
||||
```python
|
||||
>>> import sys
|
||||
|
@ -676,13 +673,13 @@ grep 'sys path' taospyudf.log | tail -1
|
|||
'/usr/lib/python3.8:/usr/lib/python3.8/lib-dynload:/usr/local/lib/python3.8/dist-packages:/usr/lib/python3/dist-packages'
|
||||
```
|
||||
|
||||
复制上面脚本的输出的字符串,然后编辑 /var/taos/taos.cfg 加入以下配置:
|
||||
复制上面脚本的输出的字符串,然后编辑 /var/taos/taos.cfg 加入以下配置。
|
||||
|
||||
```shell
|
||||
UdfdLdLibPath /usr/lib/python3.8:/usr/lib/python3.8/lib-dynload:/usr/local/lib/python3.8/dist-packages:/usr/lib/python3/dist-packages
|
||||
```
|
||||
|
||||
保存后执行 systemctl restart taosd, 再测试就不报错了:
|
||||
保存后执行 systemctl restart taosd, 再测试就不报错了。
|
||||
|
||||
```sql
|
||||
taos> select ts, nextsunday(ts) from t;
|
||||
|
@ -698,7 +695,7 @@ Query OK, 4 row(s) in set (1.011474s)
|
|||
#### 示例五
|
||||
|
||||
编写一个聚合函数,计算某一列最大值和最小值的差。
|
||||
聚合函数与标量函数的区别是:标量函数是多行输入对应多个输出,聚合函数是多行输入对应一个输出。聚合函数的执行过程有点像经典的 map-reduce 框架的执行过程,框架把数据分成若干块,每个 mapper 处理一个块,reducer 再把 mapper 的结果做聚合。不一样的地方在于,对于 TDengine Python UDF 中的 reduce 函数既有 map 的功能又有 reduce 的功能。reduce 函数接受两个参数:一个是自己要处理的数据,一个是别的任务执行 reduce 函数的处理结果。如下面的示例 /root/udf/myspread.py:
|
||||
聚合函数与标量函数的区别是:标量函数是多行输入对应多个输出,聚合函数是多行输入对应一个输出。聚合函数的执行过程有点像经典的 map-reduce 框架的执行过程,框架把数据分成若干块,每个 mapper 处理一个块,reducer 再把 mapper 的结果做聚合。不一样的地方在于,对于 TDengine Python UDF 中的 reduce 函数既有 map 的功能又有 reduce 的功能。reduce 函数接受两个参数:一个是自己要处理的数据,一个是别的任务执行 reduce 函数的处理结果。如下面的示例 /root/udf/myspread.py。
|
||||
|
||||
```python
|
||||
import io
|
||||
|
@ -707,26 +704,21 @@ import pickle
|
|||
|
||||
LOG_FILE: io.TextIOBase = None
|
||||
|
||||
|
||||
def init():
|
||||
global LOG_FILE
|
||||
LOG_FILE = open("/var/log/taos/spread.log", "wt")
|
||||
log("init function myspead success")
|
||||
|
||||
|
||||
def log(o):
|
||||
LOG_FILE.write(str(o) + '\n')
|
||||
|
||||
|
||||
def destroy():
|
||||
log("close log file: spread.log")
|
||||
LOG_FILE.close()
|
||||
|
||||
|
||||
def start():
|
||||
return pickle.dumps((-math.inf, math.inf))
|
||||
|
||||
|
||||
def reduce(block, buf):
|
||||
max_number, min_number = pickle.loads(buf)
|
||||
log(f"initial max_number={max_number}, min_number={min_number}")
|
||||
|
@ -741,26 +733,26 @@ def reduce(block, buf):
|
|||
min_number = v
|
||||
return pickle.dumps((max_number, min_number))
|
||||
|
||||
|
||||
def finish(buf):
|
||||
max_number, min_number = pickle.loads(buf)
|
||||
return max_number - min_number
|
||||
```
|
||||
|
||||
在这个示例中我们不光定义了一个聚合函数,还添加记录执行日志的功能,讲解如下:
|
||||
1. init 函数不再是空函数,而是打开了一个文件用于写执行日志
|
||||
2. log 函数是记录日志的工具,自动将传入的对象转成字符串,加换行符输出
|
||||
3. destroy 函数用来在执行结束关闭文件
|
||||
4. start 返回了初始的 buffer,用来存聚合函数的中间结果,我们把最大值初始化为负无穷大,最小值初始化为正无穷大
|
||||
5. reduce 处理每个数据块并聚合结果
|
||||
6. finish 函数将最终的 buffer 转换成最终的输出
|
||||
执行下面的 SQL语句创建对应的 UDF:
|
||||
在这个示例中,我们不但定义了一个聚合函数,还增加了记录执行日志的功能。
|
||||
1. init 函数打开一个文件用于记录日志
|
||||
2. log 函数记录日志,自动将传入的对象转成字符串,加换行符输出
|
||||
3. destroy 函数在执行结束后关闭日志文件
|
||||
4. start 函数返回初始的 buffer,用来存聚合函数的中间结果,把最大值初始化为负无穷大,最小值初始化为正无穷大
|
||||
5. reduce 函数处理每个数据块并聚合结果
|
||||
6. finish 函数将 buffer 转换成最终的输出
|
||||
|
||||
执行下面 SQL 语句创建对应的 UDF。
|
||||
|
||||
```sql
|
||||
create or replace aggregate function myspread as '/root/udf/myspread.py' outputtype double bufsize 128 language 'Python';
|
||||
```
|
||||
|
||||
这个 SQL 语句与创建标量函数的 SQL 语句有两个重要区别:
|
||||
这个 SQL 语句与创建标量函数的 SQL 语句有两个重要区别。
|
||||
1. 增加了 aggregate 关键字
|
||||
2. 增加了 bufsize 关键字,用来指定存储中间结果的内存大小,这个数值可以大于实际使用的数值。本例中间结果是两个浮点数组成的 tuple,序列化后实际占用大小只有 32 个字节,但指定的 bufsize 是128,可以用 python 命令行打印实际占用的字节数
|
||||
|
||||
|
@ -785,7 +777,7 @@ taos> select spread(v1) from t;
|
|||
Query OK, 1 row(s) in set (0.005501s)
|
||||
```
|
||||
|
||||
最后,查看我们自己打印的执行日志,从日志可以看出,reduce 函数被执行了 3 次。执行过程中 max 值被更新了 4 次, min 值只被更新 1 次。
|
||||
最后,查看执行日志,可以看到 reduce 函数被执行了 3 次,执行过程中 max 值被更新了 4 次,min 值只被更新 1 次。
|
||||
|
||||
```shell
|
||||
root@slave11 /var/log/taos $ cat spread.log
|
||||
|
@ -854,7 +846,7 @@ pycumsum 使用 numpy 计算输入列所有数据的累积和。
|
|||
|
||||
创建标量函数的 SQL 语法如下。
|
||||
```sql
|
||||
CREATE OR REPLACE FUNCTION function_name AS library_path OUTPUTTYPE output_type LANGUAGE 'Python';
|
||||
CREATE [OR REPLACE] FUNCTION function_name AS library_path OUTPUTTYPE output_type LANGUAGE 'Python';
|
||||
```
|
||||
各参数说明如下。
|
||||
- or replace:如果函数已经存在,则会修改已有的函数属性。
|
||||
|
@ -867,8 +859,9 @@ CREATE OR REPLACE FUNCTION function_name AS library_path OUTPUTTYPE output_type
|
|||
### 创建聚合函数
|
||||
|
||||
创建聚合函数的 SQL 语法如下。
|
||||
|
||||
```sql
|
||||
CREATE OR REPLACE AGGREGATE FUNCTION function_name library_path OUTPUTTYPE output_type LANGUAGE 'Python';
|
||||
CREATE [OR REPLACE] AGGREGATE FUNCTION function_name library_path OUTPUTTYPE output_type BUFSIZE buffer_size LANGUAGE 'Python';
|
||||
```
|
||||
|
||||
其中,buffer_size 表示中间计算结果的缓冲区大小,单位是字节。其他参数的含义与标量函数相同。
|
||||
|
@ -880,7 +873,7 @@ CREATE AGGREGATE FUNCTION l2norm AS "/home/taos/udf_example/libl2norm.so" OUTPUT
|
|||
|
||||
### 删除 UDF
|
||||
|
||||
删除指定名称的 UDF 的 SQL 语法如下:
|
||||
删除指定名称的 UDF 的 SQL 语法如下。
|
||||
```sql
|
||||
DROP FUNCTION function_name;
|
||||
```
|
||||
|
|
|
@ -17,6 +17,12 @@ TDengine 通过 taosKeeper 将服务器的 CPU、内存、硬盘空间、带宽
|
|||
- TDengine 已经安装并正常运行,此仪表盘需要 TDengine 3.0.0.0 及以上,并开启监控上报配置,具体配置请参考:[TDengine 监控配置](../taosd/#监控相关)。
|
||||
- taosAdapter 已经安装并正常运行。具体细节请参考:[taosAdapter 参考手册](../taosadapter)
|
||||
- taosKeeper 已安装并正常运行。具体细节请参考:[taosKeeper 参考手册](../taoskeeper)
|
||||
- Grafana 服务已安装并正常运行。我们建议您使用最新的 Grafana 版本,TDInsight 支持 Grafana 7.5 及以上版本。
|
||||
:::info
|
||||
|
||||
下文介绍中,都以 Grafana v11.0.0 版本为例,其他版本功能可能有差异,请参考 [Grafana 官网](https://grafana.com/docs/grafana/latest/)。
|
||||
|
||||
:::
|
||||
|
||||
然后记录以下信息:
|
||||
|
||||
|
@ -24,22 +30,11 @@ TDengine 通过 taosKeeper 将服务器的 CPU、内存、硬盘空间、带宽
|
|||
- taosAdapter 集群认证信息,可使用用户名及密码。
|
||||
- taosKeeper 记录监控指标的数据库名称。
|
||||
|
||||
## 安装和启动 Grafana
|
||||
|
||||
我们建议您使用最新的 Grafana 版本,TDInsight 支持 Grafana 7.5 及以上版本。您可以在任何[支持的操作系统](https://grafana.com/docs/grafana/latest/installation/requirements/#supported-operating-systems)中,按照 [Grafana 官方文档安装说明](https://grafana.com/docs/grafana/latest/installation/) 安装 Grafana。
|
||||
安装后请参考 [启动 Grafana](https://grafana.com/docs/grafana/latest/setup-grafana/start-restart-grafana/) 启动 Grafana 服务。
|
||||
|
||||
安装完成后就可以在 Web 浏览器中打开 Grafana 网址,默认是:`http://localhost:3000`。 默认用户名/密码都是 `admin`。Grafana 会要求在首次登录后更改密码。
|
||||
|
||||
:::info
|
||||
|
||||
下文介绍中,都以 Grafana v11.0.0 版本为例,其他版本功能可能有差异,请参考 [Grafana 官网](https://grafana.com/docs/grafana/latest/)。
|
||||
|
||||
:::
|
||||
|
||||
## 安装 TDengine 数据源插件
|
||||
|
||||
TDInsight 支持图形界面安装、手动安装和脚本安装三种安装方式,一般建议图形界面安装。对于 Grafana 8.5 以下版本可以使用手动安装和脚本安装方式。
|
||||
TDengine 数据源插件支持图形界面安装、手动安装和脚本安装三种安装方式,一般建议图形界面安装。对于 Grafana 8.5 以下版本可以使用手动安装和脚本安装方式。
|
||||
|
||||
<Tabs defaultValue="manual" groupId="deploy">
|
||||
<TabItem value="gui" label="图形界面安装">
|
||||
|
@ -108,13 +103,13 @@ chmod +x TDinsight.sh
|
|||
点击 `Save & Test` 进行测试,成功会提示:`TDengine Data source is working`。
|
||||
|
||||
|
||||
## 导入 TDengine V3 仪表盘
|
||||
## 导入 TDinsightV3 仪表盘
|
||||
|
||||
在配置 TDengine 数据源界面,点击 “Dashboards” tab,再点击 ”import” 导入 ”TDengine for 3.x” 仪表盘。
|
||||
导入成功后可以进入这个 dashboard,在左上角 ”Log from“ 选项中选择 taosKeeper 中设置的记录监控指标的数据库就可以看到监控结果。
|
||||
|
||||
|
||||
## TDengine V3 仪表盘详情
|
||||
## TDinsightV3 仪表盘详情
|
||||
|
||||
TDinsight 仪表盘旨在提供 TDengine 相关资源的使用情况和状态,比如 dnodes、 mnodes、 vnodes 和数据库等。
|
||||
主要分为集群状态、DNodes 概述、MNode 概述、请求、数据库、DNode 资源使用情况和 taosAdapter 监控信息。下面我们分别详细介绍。
|
||||
|
|
|
@ -13,26 +13,26 @@ database_options:
|
|||
database_option ...
|
||||
|
||||
database_option: {
|
||||
BUFFER value
|
||||
VGROUPS value
|
||||
| PRECISION {'ms' | 'us' | 'ns'}
|
||||
| REPLICA value
|
||||
| BUFFER value
|
||||
| PAGES value
|
||||
| PAGESIZE value
|
||||
| CACHEMODEL {'none' | 'last_row' | 'last_value' | 'both'}
|
||||
| CACHESIZE value
|
||||
| COMP {0 | 1 | 2}
|
||||
| DURATION value
|
||||
| WAL_FSYNC_PERIOD value
|
||||
| MAXROWS value
|
||||
| MINROWS value
|
||||
| KEEP value
|
||||
| PAGES value
|
||||
| PAGESIZE value
|
||||
| PRECISION {'ms' | 'us' | 'ns'}
|
||||
| REPLICA value
|
||||
| WAL_LEVEL {1 | 2}
|
||||
| VGROUPS value
|
||||
| SINGLE_STABLE {0 | 1}
|
||||
| STT_TRIGGER value
|
||||
| SINGLE_STABLE {0 | 1}
|
||||
| TABLE_PREFIX value
|
||||
| TABLE_SUFFIX value
|
||||
| TSDB_PAGESIZE value
|
||||
| WAL_LEVEL {1 | 2}
|
||||
| WAL_FSYNC_PERIOD value
|
||||
| WAL_RETENTION_PERIOD value
|
||||
| WAL_RETENTION_SIZE value
|
||||
}
|
||||
|
@ -40,7 +40,14 @@ database_option: {
|
|||
|
||||
### 参数说明
|
||||
|
||||
- VGROUPS:数据库中初始 vgroup 的数目。
|
||||
- PRECISION:数据库的时间戳精度。ms 表示毫秒,us 表示微秒,ns 表示纳秒,默认 ms 毫秒。
|
||||
- REPLICA:表示数据库副本数,取值为 1、2 或 3,默认为 1; 2 仅在企业版 3.3.0.0 及以后版本中可用。在集群中使用,副本数必须小于或等于 DNODE 的数目。且使用时存在以下限制:
|
||||
- 暂不支持对双副本数据库相关 Vgroup 进行 SPLITE VGROUP 或 REDISTRIBUTE VGROUP 操作
|
||||
- 单副本数据库可变更为双副本数据库,但不支持从双副本变更为其它副本数,也不支持从三副本变更为双副本
|
||||
- BUFFER: 一个 VNODE 写入内存池大小,单位为 MB,默认为 256,最小为 3,最大为 16384。
|
||||
- PAGES:一个 VNODE 中元数据存储引擎的缓存页个数,默认为 256,最小 64。一个 VNODE 元数据存储占用 PAGESIZE \* PAGES,默认情况下为 1MB 内存。
|
||||
- PAGESIZE:一个 VNODE 中元数据存储引擎的页大小,单位为 KB,默认为 4 KB。范围为 1 到 16384,即 1 KB 到 16 MB。
|
||||
- CACHEMODEL:表示是否在内存中缓存子表的最近数据。默认为 none。
|
||||
- none:表示不缓存。
|
||||
- last_row:表示缓存子表最近一行数据。这将显著改善 LAST_ROW 函数的性能表现。
|
||||
|
@ -53,27 +60,20 @@ database_option: {
|
|||
- 1:表示一阶段压缩。
|
||||
- 2:表示两阶段压缩。
|
||||
- DURATION:数据文件存储数据的时间跨度。可以使用加单位的表示形式,如 DURATION 100h、DURATION 10d 等,支持 m(分钟)、h(小时)和 d(天)三个单位。不加时间单位时默认单位为天,如 DURATION 50 表示 50 天。
|
||||
- WAL_FSYNC_PERIOD:当 WAL_LEVEL 参数设置为 2 时,用于设置落盘的周期。默认为 3000,单位毫秒。最小为 0,表示每次写入立即落盘;最大为 180000,即三分钟。
|
||||
- MAXROWS:文件块中记录的最大条数,默认为 4096 条。
|
||||
- MINROWS:文件块中记录的最小条数,默认为 100 条。
|
||||
- KEEP:表示数据文件保存的天数,缺省值为 3650,取值范围 [1, 365000],且必须大于或等于3倍的 DURATION 参数值。数据库会自动删除保存时间超过 KEEP 值的数据。KEEP 可以使用加单位的表示形式,如 KEEP 100h、KEEP 10d 等,支持 m(分钟)、h(小时)和 d(天)三个单位。也可以不写单位,如 KEEP 50,此时默认单位为天。企业版支持[多级存储](https://docs.taosdata.com/tdinternal/arch/#%E5%A4%9A%E7%BA%A7%E5%AD%98%E5%82%A8)功能, 因此, 可以设置多个保存时间(多个以英文逗号分隔,最多 3 个,满足 keep 0 \<= keep 1 \<= keep 2,如 KEEP 100h,100d,3650d); 社区版不支持多级存储功能(即使配置了多个保存时间, 也不会生效, KEEP 会取最大的保存时间)。
|
||||
- PAGES:一个 VNODE 中元数据存储引擎的缓存页个数,默认为 256,最小 64。一个 VNODE 元数据存储占用 PAGESIZE \* PAGES,默认情况下为 1MB 内存。
|
||||
- PAGESIZE:一个 VNODE 中元数据存储引擎的页大小,单位为 KB,默认为 4 KB。范围为 1 到 16384,即 1 KB 到 16 MB。
|
||||
- PRECISION:数据库的时间戳精度。ms 表示毫秒,us 表示微秒,ns 表示纳秒,默认 ms 毫秒。
|
||||
- REPLICA:表示数据库副本数,取值为 1、2 或 3,默认为 1; 2 仅在企业版 3.3.0.0 及以后版本中可用。在集群中使用,副本数必须小于或等于 DNODE 的数目。且使用时存在以下限制:
|
||||
- 暂不支持对双副本数据库相关 Vgroup 进行 SPLITE VGROUP 或 REDISTRIBUTE VGROUP 操作
|
||||
- 单副本数据库可变更为双副本数据库,但不支持从双副本变更为其它副本数,也不支持从三副本变更为双副本
|
||||
- WAL_LEVEL:WAL 级别,默认为 1。
|
||||
- 1:写 WAL,但不执行 fsync。
|
||||
- 2:写 WAL,而且执行 fsync。
|
||||
- VGROUPS:数据库中初始 vgroup 的数目。
|
||||
- STT_TRIGGER:表示落盘文件触发文件合并的个数。默认为 1,范围 1 到 16。对于少表高频场景,此参数建议使用默认配置,或较小的值;而对于多表低频场景,此参数建议配置较大的值。
|
||||
- SINGLE_STABLE:表示此数据库中是否只可以创建一个超级表,用于超级表列非常多的情况。
|
||||
- 0:表示可以创建多张超级表。
|
||||
- 1:表示只可以创建一张超级表。
|
||||
- STT_TRIGGER:表示落盘文件触发文件合并的个数。默认为 1,范围 1 到 16。对于少表高频场景,此参数建议使用默认配置,或较小的值;而对于多表低频场景,此参数建议配置较大的值。
|
||||
- TABLE_PREFIX:当其为正值时,在决定把一个表分配到哪个 vgroup 时要忽略表名中指定长度的前缀;当其为负值时,在决定把一个表分配到哪个 vgroup 时只使用表名中指定长度的前缀;例如,假定表名为 "v30001",当 TSDB_PREFIX = 2 时 使用 "0001" 来决定分配到哪个 vgroup ,当 TSDB_PREFIX = -2 时使用 "v3" 来决定分配到哪个 vgroup
|
||||
- TABLE_SUFFIX:当其为正值时,在决定把一个表分配到哪个 vgroup 时要忽略表名中指定长度的后缀;当其为负值时,在决定把一个表分配到哪个 vgroup 时只使用表名中指定长度的后缀;例如,假定表名为 "v30001",当 TSDB_SUFFIX = 2 时 使用 "v300" 来决定分配到哪个 vgroup ,当 TSDB_SUFFIX = -2 时使用 "01" 来决定分配到哪个 vgroup。
|
||||
- TSDB_PAGESIZE:一个 VNODE 中时序数据存储引擎的页大小,单位为 KB,默认为 4 KB。范围为 1 到 16384,即 1 KB到 16 MB。
|
||||
- WAL_LEVEL:WAL 级别,默认为 1。
|
||||
- 1:写 WAL,但不执行 fsync。
|
||||
- 2:写 WAL,而且执行 fsync。
|
||||
- WAL_FSYNC_PERIOD:当 WAL_LEVEL 参数设置为 2 时,用于设置落盘的周期。默认为 3000,单位毫秒。最小为 0,表示每次写入立即落盘;最大为 180000,即三分钟。
|
||||
- WAL_RETENTION_PERIOD: 为了数据订阅消费,需要WAL日志文件额外保留的最大时长策略。WAL日志清理,不受订阅客户端消费状态影响。单位为 s。默认为 3600,表示在 WAL 保留最近 3600 秒的数据,请根据数据订阅的需要修改这个参数为适当值。
|
||||
- WAL_RETENTION_SIZE:为了数据订阅消费,需要WAL日志文件额外保留的最大累计大小策略。单位为 KB。默认为 0,表示累计大小无上限。
|
||||
### 创建数据库示例
|
||||
|
|
|
@ -199,7 +199,7 @@ Websocket 和 REST 连接使用驱动类 `com.taosdata.jdbc.rs.RestfulDriver`。
|
|||
|
||||
#### URL 规范
|
||||
TDengine 的 JDBC URL 规范格式为:
|
||||
`jdbc:[TAOS|TAOS-RS]://[host_name]:[port]/[database_name]?[user={user}|&password={password}|&charset={charset}|&cfgdir={config_dir}|&locale={locale}|&timezone={timezone}]`
|
||||
`jdbc:[TAOS|TAOS-RS]://[host_name]:[port]/[database_name]?[user={user}|&password={password}|&charset={charset}|&cfgdir={config_dir}|&locale={locale}|&timezone={timezone}|&batchfetch={batchfetch}]`
|
||||
|
||||
对于建立连接,原生连接与 REST 连接有细微不同。 Websocket 和 REST 连接使用驱动类 `com.taosdata.jdbc.rs.RestfulDriver`。原生连接使用驱动类 `com.taosdata.jdbc.TSDBDriver`。
|
||||
|
||||
|
|
|
@ -28,14 +28,14 @@ TDengine 提供了丰富的应用程序开发接口,为了便于用户快速
|
|||
|
||||
TDengine 版本更新往往会增加新的功能特性,列表中的连接器版本为连接器最佳适配版本。
|
||||
|
||||
| **TDengine 版本** | **Java** | **Python** | **Go** | **C#** | **Node.js** | **Rust** |
|
||||
| ---------------------- | ------------- | ------------------------------------------- | ------------ | ------------- | --------------- | -------- |
|
||||
| **3.3.0.0 及以上** | 3.3.2.0及以上 | taospy 2.7.15及以上,taos-ws-py 0.3.2及以上 | 3.5.5及以上 | 3.1.3及以上 | 3.1.0及以上 | 当前版本 |
|
||||
| **3.0.0.0 及以上** | 3.0.2以上 | 当前版本 | 3.0 分支 | 3.0.0 | 3.1.0 | 当前版本 |
|
||||
| **2.4.0.14 及以上** | 2.0.38 | 当前版本 | develop 分支 | 1.0.2 - 1.0.6 | 2.0.10 - 2.0.12 | 当前版本 |
|
||||
| **2.4.0.4 - 2.4.0.13** | 2.0.37 | 当前版本 | develop 分支 | 1.0.2 - 1.0.6 | 2.0.10 - 2.0.12 | 当前版本 |
|
||||
| **2.2.x.x ** | 2.0.36 | 当前版本 | master 分支 | n/a | 2.0.7 - 2.0.9 | 当前版本 |
|
||||
| **2.0.x.x ** | 2.0.34 | 当前版本 | master 分支 | n/a | 2.0.1 - 2.0.6 | 当前版本 |
|
||||
| **TDengine 版本** | **Java** | **Python** | **Go** | **C#** | **Node.js** | **Rust** |
|
||||
| ---------------------- | ----------- | ------------------------------------------- | ------------ | ------------- | --------------- | -------- |
|
||||
| **3.3.0.0 及以上** | 3.3.0及以上 | taospy 2.7.15及以上,taos-ws-py 0.3.2及以上 | 3.5.5及以上 | 3.1.3及以上 | 3.1.0及以上 | 当前版本 |
|
||||
| **3.0.0.0 及以上** | 3.0.2以上 | 当前版本 | 3.0 分支 | 3.0.0 | 3.1.0 | 当前版本 |
|
||||
| **2.4.0.14 及以上** | 2.0.38 | 当前版本 | develop 分支 | 1.0.2 - 1.0.6 | 2.0.10 - 2.0.12 | 当前版本 |
|
||||
| **2.4.0.4 - 2.4.0.13** | 2.0.37 | 当前版本 | develop 分支 | 1.0.2 - 1.0.6 | 2.0.10 - 2.0.12 | 当前版本 |
|
||||
| **2.2.x.x ** | 2.0.36 | 当前版本 | master 分支 | n/a | 2.0.7 - 2.0.9 | 当前版本 |
|
||||
| **2.0.x.x ** | 2.0.34 | 当前版本 | master 分支 | n/a | 2.0.1 - 2.0.6 | 当前版本 |
|
||||
|
||||
## 功能特性
|
||||
|
||||
|
@ -43,31 +43,36 @@ TDengine 版本更新往往会增加新的功能特性,列表中的连接器
|
|||
|
||||
### 使用原生接口(taosc)
|
||||
|
||||
| **功能特性** | **Java** | **Python** | **Go** | **C#** | **Node.js** | **Rust** |
|
||||
| ------------------- | -------- | ---------- | ------ | ------ | ----------- | -------- |
|
||||
| **连接管理** | 支持 | 支持 | 支持 | 支持 | 不支持 | 支持 |
|
||||
| **普通查询** | 支持 | 支持 | 支持 | 支持 | 不支持 | 支持 |
|
||||
| **参数绑定** | 支持 | 支持 | 支持 | 支持 | 不支持 | 支持 |
|
||||
| **数据订阅(TMQ)** | 支持 | 支持 | 支持 | 支持 | 不支持 | 支持 |
|
||||
| **Schemaless** | 支持 | 支持 | 支持 | 支持 | 不支持 | 支持 |
|
||||
| **功能特性** | **Java** | **Python** | **Go** | **C#** | **Rust** |
|
||||
| ------------------- | -------- | ---------- | ------ | ------ | -------- |
|
||||
| **连接管理** | 支持 | 支持 | 支持 | 支持 | 支持 |
|
||||
| **普通查询** | 支持 | 支持 | 支持 | 支持 | 支持 |
|
||||
| **参数绑定** | 支持 | 支持 | 支持 | 支持 | 支持 |
|
||||
| **数据订阅(TMQ)** | 支持 | 支持 | 支持 | 支持 | 支持 |
|
||||
| **Schemaless** | 支持 | 支持 | 支持 | 支持 | 支持 |
|
||||
|
||||
:::info
|
||||
由于不同编程语言数据库框架规范不同,并不意味着所有 C/C++ 接口都需要对应封装支持。
|
||||
:::
|
||||
|
||||
### 使用 http (REST 或 WebSocket) 接口
|
||||
### 使用 http REST 接口
|
||||
|
||||
| **功能特性** | **Java** | **Python** | **Go** | **C#** | **Node.js** | **Rust** |
|
||||
| ------------------------------ | -------- | ---------- | ------ | ------ | ----------- | -------- |
|
||||
| **连接管理** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 |
|
||||
| **普通查询** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 |
|
||||
| **参数绑定** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 |
|
||||
| **数据订阅(TMQ)** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 |
|
||||
| **Schemaless** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 |
|
||||
| **批量拉取(基于 WebSocket)** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 |
|
||||
| **功能特性** | **Java** | **Python** | **Go** | **C#** |
|
||||
| ------------ | -------- | ---------- | ------ | ------ |
|
||||
| **连接管理** | 支持 | 支持 | 支持 | 支持 |
|
||||
| **普通查询** | 支持 | 支持 | 支持 | 支持 |
|
||||
|
||||
### 使用 Websocket 接口
|
||||
|
||||
| **功能特性** | **Java** | **Python** | **Go** | **C#** | **Node.js** | **Rust** |
|
||||
| ------------------- | -------- | ---------- | ------ | ------ | ----------- | -------- |
|
||||
| **连接管理** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 |
|
||||
| **普通查询** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 |
|
||||
| **参数绑定** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 |
|
||||
| **数据订阅(TMQ)** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 |
|
||||
| **Schemaless** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 |
|
||||
|
||||
:::warning
|
||||
|
||||
- 无论选用何种编程语言的连接器,2.0 及以上版本的 TDengine 推荐数据库应用的每个线程都建立一个独立的连接,或基于线程建立连接池,以避免连接内的“USE statement”状态量在线程之间相互干扰(但连接的查询和写入操作都是线程安全的)。
|
||||
|
||||
:::
|
||||
|
|
|
@ -7,13 +7,18 @@ toc_max_heading_level: 4
|
|||
import Tabs from "@theme/Tabs";
|
||||
import TabItem from "@theme/TabItem";
|
||||
|
||||
TDengine 能够与开源数据可视化系统 [Grafana](https://www.grafana.com/) 快速集成搭建数据监测报警系统,整个过程无需任何代码开发,TDengine 中数据表的内容可以在仪表盘(DashBoard)上进行可视化展现。关于 TDengine 插件的使用您可以在 [GitHub](https://github.com/taosdata/grafanaplugin/blob/master/README.md) 中了解更多。
|
||||
## 概述
|
||||
本文档介绍如何将 TDengine 数据源与开源数据可视化系统 [Grafana](https://www.grafana.com/) 集成,以实现数据的可视化和监测报警系统的搭建。通过 TDengine 插件,您可以轻松地将 TDengine 数据表的数据展示在 Grafana 仪表盘上,且无需进行复杂的开发工作。
|
||||
|
||||
## Grafana 版本要求
|
||||
当前 TDengine 支持 Grafana 7.5 及以上版本,建议使用最新版本。请根据您的系统环境下载并安装对应版本的 Grafana。
|
||||
|
||||
|
||||
## 前置条件
|
||||
|
||||
要让 Grafana 能正常添加 TDengine 数据源,需要以下几方面的准备工作。
|
||||
|
||||
- Grafana 服务已经部署并正常运行。目前 TDengine 支持 Grafana 7.5 以上的版本。用户可以根据当前的操作系统,到 Grafana 官网下载安装包,并执行安装。下载地址如下:[https://grafana.com/grafana/download](https://grafana.com/grafana/download) 。
|
||||
- Grafana 服务已经部署并正常运行。
|
||||
:::info
|
||||
|
||||
**注意**:要确保启动 Grafana 的账号有其安装目录的写权限,否则可能后面无法安装插件。
|
||||
|
@ -22,8 +27,6 @@ TDengine 能够与开源数据可视化系统 [Grafana](https://www.grafana.com/
|
|||
- TDengine 集群已经部署并正常运行。
|
||||
- taosAdapter 已经安装并正常运行。具体细节请参考 [taosAdapter 的使用手册](../../../reference/components/taosadapter)
|
||||
|
||||
|
||||
|
||||
记录以下信息:
|
||||
|
||||
- TDengine 集群 REST API 地址,如:`http://tdengine.local:6041`。
|
||||
|
@ -173,14 +176,22 @@ docker run -d \
|
|||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
:::info
|
||||
|
||||
下文介绍中,都以 Grafana v11.0.0 版本为例,其他版本功能可能有差异,请参考 [Grafana 官网](https://grafana.com/docs/grafana/latest/)。
|
||||
|
||||
:::
|
||||
|
||||
## 内置变量和自定义变量
|
||||
## Dashboard 使用指南
|
||||
|
||||
本节内容按如下方式组织:
|
||||
1. 介绍基础知识,包括 Grafana 的内置变量和自定义变量,TDengine 对与时序查询的特色语法支持。
|
||||
2. 介绍如何使用 TDengine 数据源在 Grafana 中创建 Dashboard,然后给出使用时序查询的特色语法和如何分组展示数据。
|
||||
3. 由于配置的 Dashbord 在页面显示时会定时查询 TDengine 来刷新显示,如果 SQL 编写不当会导致严重的性能问题,我们给出了性能优化建议。
|
||||
4. 最后我们以 TDengine 监控面板 TDinsight 为例介绍了如何导入我们提供的 DashBoard。
|
||||
|
||||
### Grafana 内置变量和自定义变量
|
||||
|
||||
Grafana 中的 Variable(变量)功能非常强大,可以在 Dashboard 的查询、面板标题、标签等地方使用,用来创建更加动态和交互式的 Dashbord,提高用户体验和效率。
|
||||
|
||||
变量的主要作用和特点包括:
|
||||
|
@ -191,26 +202,30 @@ Grafana 中的 Variable(变量)功能非常强大,可以在 Dashboard 的
|
|||
|
||||
- 灵活的配置选项:变量提供了多种配置选项,如预定义的静态值列表、从数据源动态查询值、正则表达式过滤等,使得变量的应用更加灵活和强大。
|
||||
|
||||
|
||||
Grafana 提供了内置变量和自定义变量,它们都可以可以在编写 SQL 时引用,引用的方式是 `$variableName`,`variableName` 是变量的名字,其他引用方式请参考 [引用方式](https://grafana.com/docs/grafana/latest/dashboards/variables/variable-syntax/)。
|
||||
|
||||
### 内置变量
|
||||
#### 内置变量
|
||||
|
||||
Grafana 内置了 `from`、`to` 和 `interval` 等变量,都取自于 Grafana 插件面板。其含义如下:
|
||||
- `from` 查询范围的起始时间
|
||||
- `to` 查询范围的结束时间
|
||||
- `interval` 窗口切分间隔
|
||||
|
||||
对于每个查询都建议设置查询范围的起始时间和结束时间,可以有效的减少 TDengine 服务端执行查询扫描的数据量。`interval` 是窗口切分的大小,在 Grafana 11 版本中,其大小为时间间隔和返回点数计算而得。
|
||||
对于每个查询都建议设置查询范围的起始时间和结束时间,可以有效的减少 TDengine 服务端执行查询扫描的数据量。`interval` 是窗口切分的大小,在 Grafana 11 版本中,其大小为时间间隔和返回点数计算而得。
|
||||
|
||||
除了上述三个常用变量,Grafana 还提供了如 `__timezone`, `__org`, `__user` 等变量,详情请参考 [内置变量](https://grafana.com/docs/grafana/latest/dashboards/variables/add-template-variables/#global-variables)。
|
||||
|
||||
### 自定义变量
|
||||
#### 自定义变量
|
||||
|
||||
我们可以在 Dashbord 中增加自定义变量。自定义变量和内置变量的使用方式没有区别,都是在 SQL 中用 `$variableName` 进行引用。
|
||||
自定义变量支持多种类型,常见的类型包括 `Query`(查询)、`Constant`(常量)、`Interval`(间隔)、`Data source`(数据源)等。
|
||||
自定义变量可以引用其他自定义变量,比如一个变量表示区域,另一个变量可以引用区域的值,来查询这个区域的设备。
|
||||
#### 添加查询类型变量
|
||||
|
||||
##### 添加查询类型变量
|
||||
|
||||
在 Dashbord 的配置中,选择 【Variables】,然后点击 【New variable】:
|
||||
1. 在 “Name“ 字段中,输入你的变量名,此处我们设置变量名为 `selected_groups`。
|
||||
2. 在 【Select variable type】下拉菜单中,选择 ”Query“(查询)。
|
||||
1. 在 “Name” 字段中,输入你的变量名,此处我们设置变量名为 `selected_groups`。
|
||||
2. 在 【Select variable type】下拉菜单中,选择 “Query”(查询)。
|
||||
根据选择的变量类型,配置相应的选项。例如,如果选择了 “Query” 类型,你需要指定数据源和用于获取变量值的查询语句。此处我们还以智能电表为例,设置查询类型,选择数据源后,配置 SQL 为 `select distinct(groupid) from power.meters where groupid < 3 and ts > $from and ts < $to;`
|
||||
3. 点击底部的【Run Query】后,可以在 “Preview of values”(值预览)部分,查看到根据你的配置生成的变量值。
|
||||
4. 还有其他配置不再赘述,完成配置后,点击页面底部的【Apply】(应用)按钮,然后点击右上角的【Save dashboard】保存。
|
||||
|
@ -219,9 +234,10 @@ Grafana 内置了 `from`、`to` 和 `interval` 等变量,都取自于 Grafana
|
|||
|
||||
我们还可以再新增自定义变量来引用这个 `selected_groups` 变量,比如我们新增一个名为 `tbname_max_current` 的查询变量,其 SQL 为 `select tbname from power.meters where groupid = $selected_groups and ts > $from and ts < $to;`
|
||||
|
||||
#### 添加间隔类型变量
|
||||
##### 添加间隔类型变量
|
||||
|
||||
我们可以自定义时间窗口间隔,可以更加贴合业务需求。
|
||||
1. 在 “Name“ 字段中,输入变量名为 `interval`。
|
||||
1. 在 “Name” 字段中,输入变量名为 `interval`。
|
||||
2. 在 【Select variable type】下拉菜单中,选择 “Interval”(间隔)。
|
||||
3. 在 【Interval options】选项中输入 `1s,2s,5s,10s,15s,30s,1m`。
|
||||
4. 还有其他配置不再赘述,完成配置后,点击页面底部的【Apply】(应用)按钮,然后点击右上角的【Save dashboard】保存。
|
||||
|
@ -234,7 +250,8 @@ Grafana 内置了 `from`、`to` 和 `interval` 等变量,都取自于 Grafana
|
|||
|
||||
:::
|
||||
|
||||
## TDengine 时间序列查询支持
|
||||
### TDengine 时序查询支持
|
||||
|
||||
TDengine 在支持标准 SQL 的基础之上,还提供了一系列满足时序业务场景需求的特色查询语法,这些语法能够为时序场景的应用的开发带来极大的便利。
|
||||
- `partition by` 子句可以按一定的维度对数据进行切分,然后在切分出的数据空间内再进行一系列的计算。绝大多数情况可以替代 `group by`。
|
||||
- `interval` 子句用于产生相等时间周期的窗口
|
||||
|
@ -243,19 +260,21 @@ TDengine 在支持标准 SQL 的基础之上,还提供了一系列满足时序
|
|||
|
||||
上述特性详细介绍可以参考 [特色查询](../../../reference/taos-sql/distinguished/)。
|
||||
|
||||
## 创建 Dashboard
|
||||
### 创建 Dashboard
|
||||
|
||||
回到主界面创建 Dashboard,点击【Add Query】进入面板查询页面:
|
||||
有了前面的基础知识,我们可以配置基于 TDengine 数据源的时间序列数据展示 Dashbord。
|
||||
在 Grafana 主界面创建 Dashboard,点击【Add Query】进入面板查询页面:
|
||||
|
||||

|
||||
|
||||
如上图所示,在 ”Query“ 中选中 `TDengine` 数据源,在下方查询框可输入相应 SQL 进行查询。 我们继续用智能电表来举例,为了展示曲线美观,此处**用了虚拟数据**。
|
||||
如上图所示,在 “Query” 中选中 `TDengine` 数据源,在下方查询框可输入相应 SQL 进行查询。 我们继续用智能电表来举例,为了展示曲线美观,此处**用了虚拟数据**。
|
||||
|
||||
#### 时间序列数据展示
|
||||
|
||||
## 时间序列数据展示
|
||||
假设我们想查询一段时间内的平均电流大小,时间窗口按 `$interval` 切分,若某一时间窗口区间数据缺失,填充 null。
|
||||
- “INPUT SQL“:输入要查询的语句(该 SQL 语句的结果集应为两列多行),此处输入:`select _wstart as ts, avg(current) as current from power.meters where groupid in ($selected_groups) and ts > $from and ts < $to interval($interval) fill(null)` ,其中,from、to 和 interval 为 Grafana 内置变量,selected_groups 为自定义变量。
|
||||
- “ALIAS BY“:可设置当前查询别名。
|
||||
- “GENERATE SQL“: 点击该按钮会自动替换相应变量,并生成最终执行的语句。
|
||||
- “INPUT SQL”:输入要查询的语句(该 SQL 语句的结果集应为两列多行),此处输入:`select _wstart as ts, avg(current) as current from power.meters where groupid in ($selected_groups) and ts > $from and ts < $to interval($interval) fill(null)` ,其中,from、to 和 interval 为 Grafana 内置变量,selected_groups 为自定义变量。
|
||||
- “ALIAS BY”:可设置当前查询别名。
|
||||
- “GENERATE SQL”: 点击该按钮会自动替换相应变量,并生成最终执行的语句。
|
||||
|
||||
在顶部的自定义变量中,若选择 `selected_groups` 的值为 1,则查询 `meters` 超级表中 `groupid` 为 1 的所有设备电流平均值变化如下图:
|
||||
|
||||
|
@ -267,11 +286,12 @@ TDengine 在支持标准 SQL 的基础之上,还提供了一系列满足时序
|
|||
|
||||
:::
|
||||
|
||||
## 时间序列数据分组展示
|
||||
#### 时间序列数据分组展示
|
||||
|
||||
假设我们想查询一段时间内的平均电流大小,按 `groupid` 分组展示,我们可以修改之前的 SQL 为 `select _wstart as ts, groupid, avg(current) as current from power.meters where ts > $from and ts < $to partition by groupid interval($interval) fill(null)`
|
||||
|
||||
- “Group by column(s)“: **半角**逗号分隔的 `group by` 或 `partition by` 列名。如果是 `group by` 或 `partition by` 查询语句,设置 “Group by“ 列,可以展示多维数据。此处设置 “Group by“ 列名为 `groupid`,可以按 `groupid` 分组展示数据。
|
||||
- “Group By Format“: `Group by` 或 `Partition by` 场景下多维数据 legend 格式化格式。例如上述 INPUT SQL,将 “Group By Format“ 设置为 `groupid-{{groupid}}`,展示的 legend 名字为格式化的分组名。
|
||||
- “Group by column(s)”: **半角**逗号分隔的 `group by` 或 `partition by` 列名。如果是 `group by` 或 `partition by` 查询语句,设置 “Group by” 列,可以展示多维数据。此处设置 “Group by” 列名为 `groupid`,可以按 `groupid` 分组展示数据。
|
||||
- “Group By Format”: `Group by` 或 `Partition by` 场景下多维数据 legend 格式化格式。例如上述 INPUT SQL,将 “Group By Format” 设置为 `groupid-{{groupid}}`,展示的 legend 名字为格式化的分组名。
|
||||
|
||||
完成设置后,按照 `groupid` 分组展示如下图:
|
||||
|
||||
|
@ -279,12 +299,12 @@ TDengine 在支持标准 SQL 的基础之上,还提供了一系列满足时序
|
|||
|
||||
> 关于如何使用 Grafana 创建相应的监测界面以及更多有关使用 Grafana 的信息,请参考 Grafana 官方的[文档](https://grafana.com/docs/)。
|
||||
|
||||
## 性能建议
|
||||
### 性能优化建议
|
||||
|
||||
- **所有查询加上时间范围**,在时序数据库中,如果不加查询的时间范围,会扫表导致性能低下。常见的 SQL 写法是 `select column_name from db.table where ts > $from and ts < $to;`
|
||||
- 对于最新状态类型的查询,我们一般建议在**创建数据库的时候打开缓存**(`CACHEMODEL` 设置为 last_row 或者 both),常见的 SQL 写法是 `select last(column_name) from db.table where ts > $from and ts < $to;`
|
||||
|
||||
|
||||
## 导入 Dashboard
|
||||
### 导入 Dashboard
|
||||
|
||||
在数据源配置页面,您可以为该数据源导入 TDinsight 面板,作为 TDengine 集群的监控可视化工具。如果 TDengine 服务端为 3.0 版本请选择 `TDinsight for 3.x` 导入。注意 TDinsight for 3.x 需要运行和配置 taoskeeper。
|
||||
|
||||
|
@ -299,30 +319,36 @@ TDengine 在支持标准 SQL 的基础之上,还提供了一系列满足时序
|
|||
- [15167](https://grafana.com/grafana/dashboards/15167): TDinsight
|
||||
- [16388](https://grafana.com/grafana/dashboards/16388): Telegraf 采集节点信息的数据展示
|
||||
|
||||
## 告警配置简介
|
||||
### 告警配置流程
|
||||
## 告警配置
|
||||
|
||||
TDengine Grafana 插件支持告警,如果要配置告警,需要以下几个步骤:
|
||||
1. 配置联络点(“Contact points“):配置通知渠道,包括 DingDing、Email、Slack、WebHook、Prometheus Alertmanager 等
|
||||
2. 配置告警通知策略(“Notification policies“):配置告警发送到哪个通道的路由,以及发送通知的时间和重复频率
|
||||
3. 配置 “Alert rules“:配置详细的告警规则
|
||||
1. 配置联络点(“Contact points”):配置通知渠道,包括 DingDing、Email、Slack、WebHook、Prometheus Alertmanager 等
|
||||
2. 配置告警通知策略(“Notification policies”):配置告警发送到哪个通道的路由,以及发送通知的时间和重复频率
|
||||
3. 配置告警规则(“Alert rules”):配置详细的告警规则
|
||||
3.1 配置告警名称
|
||||
3.2 配置查询及告警触发条件
|
||||
3.2 配置查询和告警触发条件
|
||||
3.3 配置规则评估策略
|
||||
3.4 配置标签和告警通道
|
||||
3.5 配置通知文案
|
||||
|
||||
### 告警配置界面
|
||||
在Grafana 11 告警界面一共有 6 个 Tab,分别是 “Alert rules“、“Contact points“、“Notification policies“、“Silences“、 “Groups“ 和 “Settings“。
|
||||
- “Alert rules“ 告警规则列表,用于展示和配置告警规则
|
||||
- “Contact points“ 通知渠道,包括 DingDing、Email、Slack、WebHook、Prometheus Alertmanager 等
|
||||
- “Notification policies“ 配置告警发送到哪个通道的路由,以及发送通知的时间和重复频率
|
||||
- “Silences“ 配置告警静默时间段
|
||||
- “Groups“ 告警组,配置的告警触发后会在这里分组显示
|
||||
- “Settings“ 提供通过 JSON 方式修改告警配置
|
||||
### 告警配置界面介绍
|
||||
|
||||
在Grafana 11 告警界面一共有 6 个 Tab,分别是 “Alert rules”、“Contact points”、“Notification policies”、“Silences”、 “Groups” 和 “Settings”。
|
||||
- “Alert rules” 告警规则列表,用于展示和配置告警规则
|
||||
- “Contact points” 通知渠道,包括 DingDing、Email、Slack、WebHook、Prometheus Alertmanager 等
|
||||
- “Notification policies” 配置告警发送到哪个通道的路由,以及发送通知的时间和重复频率
|
||||
- “Silences” 配置告警静默时间段
|
||||
- “Groups” 告警组,配置的告警触发后会在这里分组显示
|
||||
- “Settings” 提供通过 JSON 方式修改告警配置
|
||||
|
||||
### 配置联络点
|
||||
|
||||
本节以邮件和飞书为例配置联络点。
|
||||
|
||||
#### 配置邮件联络点
|
||||
|
||||
在 Grafana 服务的配置文件中添加 SMTP/Emailing 和 Alerting 模块。(以 Linux 系统为例,其配置文件一般位于 `/etc/grafana/grafana.ini`)
|
||||
|
||||
## 配置邮件联络点
|
||||
### Grafana Server 配置文件修改
|
||||
在 Grafana 服务的配置文件中添加 SMTP/Emailing 和 Alerting 模块,以 Linux 系统为例,其配置文件一般位于 `/etc/grafana/grafana.ini`
|
||||
在配置文件中增加下面内容:
|
||||
|
||||
```ini
|
||||
|
@ -336,106 +362,110 @@ skip_verify = true
|
|||
from_address = sender@foxmail.com
|
||||
```
|
||||
|
||||
然后重启 Grafana 服务即可, 以 Linux 系统为例,执行 `systemctl restart grafana-server.service`
|
||||
然后重启 Grafana 服务(以 Linux 系统为例,执行 `systemctl restart grafana-server.service`)即可添加完成
|
||||
|
||||
### Grafana 页面创建新联络点
|
||||
|
||||
在 Grafana 页面找到 “Home“ -> “Alerting“ -> “Contact points“,创建新联络点
|
||||
”Name“: Email Contact Point
|
||||
“Integration“:选择联络类型,这里选择 Email,填写邮件接收地址,完成后保存联络点
|
||||
在 Grafana 页面找到 “Home” -> “Alerting” -> “Contact points”,创建新联络点
|
||||
“Name”: Email Contact Point
|
||||
“Integration”:选择联络类型,这里选择 Email,填写邮件接收地址,完成后保存联络点
|
||||
|
||||

|
||||
|
||||
## 配置飞书联络点
|
||||
#### 配置飞书联络点
|
||||
|
||||
### 飞书机器人配置
|
||||
1. “飞书工作台“ -> “获取应用“ -> “搜索飞书机器人助手“ -> “新建指令“
|
||||
按照以下步骤配置飞书机器人:
|
||||
|
||||
1. “飞书工作台” -> “获取应用” -> “搜索飞书机器人助手” -> “新建指令”
|
||||
2. 选择触发器:Grafana
|
||||
3. 选择操作:通过官方机器人发送消息,填写发送对象和发送内容
|
||||
|
||||

|
||||
|
||||
### Grafana 配置飞书联络点
|
||||
|
||||
在 Grafana 页面找到 “Home“ -> “Alerting“ -> “Contact points“ 创建新联络点
|
||||
“Name“:Feishu Contact Point
|
||||
“Integration“:选择联络类型,这里选择 Webhook,并填写 URL (在飞书机器人助手的 Grafana 触发器 Webhook 地址),完成后保存联络点
|
||||
在 Grafana 页面找到 “Home” -> “Alerting” -> “Contact points” 创建新联络点
|
||||
“Name”:Feishu Contact Point
|
||||
“Integration”:选择联络类型,这里选择 Webhook,并填写 URL (在飞书机器人助手的 Grafana 触发器 Webhook 地址),完成后保存联络点
|
||||
|
||||

|
||||
|
||||
## 通知策略
|
||||
### 配置告警通知策略
|
||||
|
||||
配置好联络点后,可以看到已有一个Default Policy
|
||||
|
||||

|
||||
|
||||
点击右侧 ”...“ -> ”Edit“,然后编辑默认通知策略,弹出配置窗口:
|
||||
点击右侧 “...” -> ”Edit”,然后编辑默认通知策略,弹出配置窗口:
|
||||
|
||||

|
||||
|
||||
然后配置下列参数:
|
||||
- “Group wait“: 发送首次告警之前的等待时间。
|
||||
- “Group interval“: 发送第一个告警后,为该组发送下一批新告警的等待时间。
|
||||
- “Repeat interval“: 成功发送告警后再次重复发送告警的等待时间。
|
||||
- “Group wait”: 发送首次告警之前的等待时间。
|
||||
- “Group interval”: 发送第一个告警后,为该组发送下一批新告警的等待时间。
|
||||
- “Repeat interval”: 成功发送告警后再次重复发送告警的等待时间。
|
||||
|
||||
## 配置告警规则
|
||||
### 配置告警规则
|
||||
|
||||
### 配置查询和告警触发条件
|
||||
以配置智能电表告警为例,告警规则的配置主要包括告警名称、查询和告警触发条件、规则评估策略、标签和告警通道、通知文案。
|
||||
|
||||
在需要配置告警的面板中选择 “Edit“ -> “Alert“ -> “New alert rule“。
|
||||
#### 配置告警名称
|
||||
|
||||
在需要配置告警的面板中选择 “Edit” -> “Alert” -> “New alert rule”。
|
||||
|
||||
“Enter alert rule name“ (输入告警规则名称):此处以智能电表为例输入 `power meters alert`
|
||||
|
||||
#### 配置查询和告警触发条件
|
||||
|
||||
在 “Define query and alert condition” (定义查询和告警触发条件) 中配置告警规则。
|
||||
1. 选择数据源:`TDengine Datasource`
|
||||
2. 查询语句:
|
||||
|
||||
1. “Enter alert rule name“ (输入告警规则名称):此处以智能电表为例输入 `power meters alert`
|
||||
2. “Define query and alert condition“ (定义查询和告警触发条件)
|
||||
2.1 选择数据源:`TDengine Datasource`
|
||||
2.2 查询语句:
|
||||
```sql
|
||||
select _wstart as ts, groupid, avg(current) as current from power.meters where ts > $from and ts < $to partition by groupid interval($interval) fill(null)
|
||||
```
|
||||
2.3 设置 ”Expression“(表达式):`Threshold is above 100`
|
||||
2.4 点击【Set as alert condition】
|
||||
2.5 “Preview“:查看设置的规则的结果
|
||||
|
||||
3. 设置 “Expression”(表达式):`Threshold is above 100`
|
||||
4. 点击【Set as alert condition】
|
||||
5. “Preview”:查看设置的规则的结果
|
||||
|
||||
完成设置后可以看到下面图片展示:
|
||||
|
||||

|
||||
|
||||
### 配置表达式和计算规则
|
||||
|
||||
Grafana 的 “Expression“(表达式)支持对数据做各种操作和计算,其类型分为:
|
||||
1. “Reduce“:将所选时间范围内的时间序列值聚合为单个值
|
||||
1.1 “Function“ 用来设置聚合方法,支持 Min、Max、Last、Mean、Sum 和 Count。
|
||||
1.2 “Mode“ 支持下面三种:
|
||||
- “Strict“:如果查询不到数据,数据会赋值为 NaN。
|
||||
- “Drop Non-numeric Value“:去掉非法数据结果。
|
||||
- “Replace Non-numeric Value“:如果是非法数据,使用固定值进行替换。
|
||||
2. “Threshold“:检查时间序列数据是否符合阈值判断条件。当条件为假时返回 0,为真则返回1。支持下列方式:
|
||||
Grafana 的 “Expression”(表达式)支持对数据做各种操作和计算,其类型分为:
|
||||
1. “Reduce”:将所选时间范围内的时间序列值聚合为单个值
|
||||
1.1 “Function” 用来设置聚合方法,支持 Min、Max、Last、Mean、Sum 和 Count。
|
||||
1.2 “Mode” 支持下面三种:
|
||||
- “Strict”:如果查询不到数据,数据会赋值为 NaN。
|
||||
- “Drop Non-numeric Value”:去掉非法数据结果。
|
||||
- “Replace Non-numeric Value”:如果是非法数据,使用固定值进行替换。
|
||||
2. “Threshold”:检查时间序列数据是否符合阈值判断条件。当条件为假时返回 0,为真则返回1。支持下列方式:
|
||||
- Is above (x > y)
|
||||
- Is below (x < y)
|
||||
- Is within range (x > y1 AND x < y2)
|
||||
- Is outside range (x < y1 AND x > y2)
|
||||
3. “Math“:对时间序列的数据进行数学运算。
|
||||
4. “Resample“:更改每个时间序列中的时间戳使其具有一致的时间间隔,以便在它们之间执行数学运算。
|
||||
5. “Classic condition (legacy)“: 可配置多个逻辑条件,判断是否触发告警。
|
||||
3. “Math”:对时间序列的数据进行数学运算。
|
||||
4. “Resample”:更改每个时间序列中的时间戳使其具有一致的时间间隔,以便在它们之间执行数学运算。
|
||||
5. “Classic condition (legacy)”: 可配置多个逻辑条件,判断是否触发告警。
|
||||
|
||||
如上节截图显示,此处我们设置最大值超过 100 触发告警。
|
||||
|
||||
### 配置评估策略
|
||||
#### 配置规则评估策略
|
||||
|
||||

|
||||
|
||||
完成下面配置:
|
||||
- “Folder“:设置告警规则所属目录。
|
||||
- “Evaluation group“:设置告警规则评估组。“Evaluation group“ 可以选择已有组或者新建组,新建组可以设置组名和评估时间间隔。
|
||||
- “Pending period“:在告警规则的阈值被触发后,异常值持续多长时间可以触发告警,合理设置可以避免误报。
|
||||
- “Folder”:设置告警规则所属目录。
|
||||
- “Evaluation group”:设置告警规则评估组。“Evaluation group” 可以选择已有组或者新建组,新建组可以设置组名和评估时间间隔。
|
||||
- “Pending period”:在告警规则的阈值被触发后,异常值持续多长时间可以触发告警,合理设置可以避免误报。
|
||||
|
||||
#### 配置标签和告警通道
|
||||
|
||||
### 配置标签和告警通道
|
||||

|
||||
|
||||
完成下面配置:
|
||||
- “Labels“ 将标签添加到规则中,以便进行搜索、静默或路由到通知策略。
|
||||
- “Contact point“ 选择联络点,当告警发生时通过设置的联络点进行通知。
|
||||
- “Labels” 将标签添加到规则中,以便进行搜索、静默或路由到通知策略。
|
||||
- “Contact point” 选择联络点,当告警发生时通过设置的联络点进行通知。
|
||||
|
||||
### 配置通知文案
|
||||
#### 配置通知文案
|
||||
|
||||

|
||||
|
||||
设置 “Summary” 和 ”Description” 后,若告警触发,将会收到告警通知。
|
||||
设置 “Summary” 和 “Description” 后,若告警触发,将会收到告警通知。
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<dependency>
|
||||
<groupId>com.taosdata.jdbc</groupId>
|
||||
<artifactId>taos-jdbcdriver</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<version>3.3.0</version>
|
||||
</dependency>
|
||||
<!-- druid -->
|
||||
<dependency>
|
||||
|
|
|
@ -22,7 +22,7 @@ public static void main(String[] args) throws Exception {
|
|||
dataSource.setMinIdle(10);
|
||||
dataSource.setMaxActive(10);
|
||||
dataSource.setMaxWait(30000);
|
||||
dataSource.setValidationQuery("SELECT SERVER_STATUS()");
|
||||
dataSource.setValidationQuery("SELECT SERVER_VERSION()");
|
||||
|
||||
Connection connection = dataSource.getConnection(); // get connection
|
||||
Statement statement = connection.createStatement(); // get statement
|
||||
|
|
|
@ -21,7 +21,7 @@ public static void main(String[] args) throws SQLException {
|
|||
config.setConnectionTimeout(30000); //maximum wait milliseconds for get connection from pool
|
||||
config.setMaxLifetime(0); // maximum life time for each connection
|
||||
config.setIdleTimeout(0); // max idle time for recycle idle connection
|
||||
config.setConnectionTestQuery("SELECT SERVER_STATUS()"); //validation query
|
||||
config.setConnectionTestQuery("SELECT SERVER_VERSION()"); //validation query
|
||||
|
||||
HikariDataSource ds = new HikariDataSource(config); //create datasource
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ public class DruidPoolBuilder {
|
|||
dataSource.setMinIdle(poolSize);
|
||||
dataSource.setMaxActive(poolSize);
|
||||
dataSource.setMaxWait(30000);
|
||||
dataSource.setValidationQuery("select server_status()");
|
||||
dataSource.setValidationQuery("select SERVER_VERSION()");
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ public class HikariCpBuilder {
|
|||
config.setConnectionTimeout(30000); //maximum wait milliseconds for get connection from pool
|
||||
config.setMaxLifetime(0); // maximum life time for each connection
|
||||
config.setIdleTimeout(0); // max idle time for recycle idle connection
|
||||
config.setConnectionTestQuery("select server_status()"); //validation query
|
||||
config.setConnectionTestQuery("select SERVER_VERSION()"); //validation query
|
||||
|
||||
HikariDataSource ds = new HikariDataSource(config);
|
||||
return ds;
|
||||
|
|
|
@ -22,6 +22,6 @@
|
|||
<!--proxool自动侦察各个连接状态的时间间隔(毫秒),侦察到空闲的连接就马上回收,超时的销毁 默认30秒-->
|
||||
<house-keeping-sleep-time>30000</house-keeping-sleep-time>
|
||||
<!--用于保持连接的测试语句 -->
|
||||
<house-keeping-test-sql>select server_status()</house-keeping-test-sql>
|
||||
<house-keeping-test-sql>select server_version()</house-keeping-test-sql>
|
||||
</proxool>
|
||||
</something-else-entirely>
|
|
@ -2908,6 +2908,7 @@ int32_t filterGenerateColRange(SFilterInfo *info, SFilterGroupCtx **gRes, int32_
|
|||
info->colRangeNum = colNum;
|
||||
info->colRange = taosMemoryCalloc(colNum, POINTER_BYTES);
|
||||
if (info->colRange == NULL) {
|
||||
info->colRangeNum = 0;
|
||||
FLT_ERR_JRET(TSDB_CODE_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
|
|
|
@ -1287,7 +1287,7 @@ SSyncNode* syncNodeOpen(SSyncInfo* pSyncInfo, int32_t vnodeVersion) {
|
|||
}
|
||||
|
||||
// tools
|
||||
(void)syncRespMgrCreate(pSyncNode, SYNC_RESP_TTL_MS, &pSyncNode->pSyncRespMgr); // TODO: check return value
|
||||
(void)syncRespMgrCreate(pSyncNode, SYNC_RESP_TTL_MS, &pSyncNode->pSyncRespMgr); // TODO: check return value
|
||||
if (pSyncNode->pSyncRespMgr == NULL) {
|
||||
sError("vgId:%d, failed to create SyncRespMgr", pSyncNode->vgId);
|
||||
goto _error;
|
||||
|
@ -1407,7 +1407,8 @@ int32_t syncNodeRestore(SSyncNode* pSyncNode) {
|
|||
pSyncNode->commitIndex = TMAX(pSyncNode->commitIndex, commitIndex);
|
||||
sInfo("vgId:%d, restore sync until commitIndex:%" PRId64, pSyncNode->vgId, pSyncNode->commitIndex);
|
||||
|
||||
if (pSyncNode->fsmState != SYNC_FSM_STATE_INCOMPLETE && (code = syncLogBufferCommit(pSyncNode->pLogBuf, pSyncNode, pSyncNode->commitIndex)) < 0) {
|
||||
if (pSyncNode->fsmState != SYNC_FSM_STATE_INCOMPLETE &&
|
||||
(code = syncLogBufferCommit(pSyncNode->pLogBuf, pSyncNode, pSyncNode->commitIndex)) < 0) {
|
||||
TAOS_RETURN(code);
|
||||
}
|
||||
|
||||
|
@ -2187,7 +2188,7 @@ void syncNodeCandidate2Leader(SSyncNode* pSyncNode) {
|
|||
}
|
||||
|
||||
SyncIndex lastIndex = pSyncNode->pLogStore->syncLogLastIndex(pSyncNode->pLogStore);
|
||||
ASSERT(lastIndex >= 0);
|
||||
// ASSERT(lastIndex >= 0);
|
||||
sInfo("vgId:%d, become leader. term:%" PRId64 ", commit index:%" PRId64 ", last index:%" PRId64 "", pSyncNode->vgId,
|
||||
raftStoreGetTerm(pSyncNode), pSyncNode->commitIndex, lastIndex);
|
||||
}
|
||||
|
|
|
@ -892,7 +892,7 @@ int32_t syncLogReplRecover(SSyncLogReplMgr* pMgr, SSyncNode* pNode, SyncAppendEn
|
|||
|
||||
if (pMsg->matchIndex < pNode->pLogBuf->matchIndex) {
|
||||
code = syncLogReplGetPrevLogTerm(pMgr, pNode, index + 1, &term);
|
||||
if (term < 0 && (errno == ENFILE || errno == EMFILE)) {
|
||||
if (term < 0 && (errno == ENFILE || errno == EMFILE || errno == ENOENT)) {
|
||||
sError("vgId:%d, failed to get prev log term since %s. index:%" PRId64, pNode->vgId, tstrerror(code), index + 1);
|
||||
TAOS_RETURN(code);
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ int32_t tScalableBfInit(uint64_t expectedEntries, double errorRate, SScalableBf*
|
|||
int32_t lino = 0;
|
||||
const uint32_t defaultSize = 8;
|
||||
if (expectedEntries < 1 || errorRate <= 0 || errorRate >= 1.0) {
|
||||
code = TSDB_CODE_FAILED;
|
||||
code = TSDB_CODE_INVALID_PARA;
|
||||
QUERY_CHECK_CODE(code, lino, _error);
|
||||
}
|
||||
SScalableBf* pSBf = taosMemoryCalloc(1, sizeof(SScalableBf));
|
||||
|
@ -71,7 +71,7 @@ int32_t tScalableBfPutNoCheck(SScalableBf* pSBf, const void* keyBuf, uint32_t le
|
|||
int32_t code = TSDB_CODE_SUCCESS;
|
||||
int32_t lino = 0;
|
||||
if (pSBf->status == SBF_INVALID) {
|
||||
code = TSDB_CODE_FAILED;
|
||||
code = TSDB_CODE_OUT_OF_BUFFER;
|
||||
QUERY_CHECK_CODE(code, lino, _error);
|
||||
}
|
||||
int32_t size = taosArrayGetSize(pSBf->bfArray);
|
||||
|
@ -92,7 +92,7 @@ int32_t tScalableBfPutNoCheck(SScalableBf* pSBf, const void* keyBuf, uint32_t le
|
|||
|
||||
_error:
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
uError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
|
||||
uDebug("%s failed at line %d since %s", __func__, lino, tstrerror(code));
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ int32_t tScalableBfPut(SScalableBf* pSBf, const void* keyBuf, uint32_t len, int3
|
|||
int32_t code = TSDB_CODE_SUCCESS;
|
||||
int32_t lino = 0;
|
||||
if (pSBf->status == SBF_INVALID) {
|
||||
code = TSDB_CODE_FAILED;
|
||||
code = TSDB_CODE_OUT_OF_BUFFER;
|
||||
QUERY_CHECK_CODE(code, lino, _end);
|
||||
}
|
||||
uint64_t h1 = (uint64_t)pSBf->hashFn1(keyBuf, len);
|
||||
|
@ -153,7 +153,7 @@ static int32_t tScalableBfAddFilter(SScalableBf* pSBf, uint64_t expectedEntries,
|
|||
int32_t code = TSDB_CODE_SUCCESS;
|
||||
int32_t lino = 0;
|
||||
if (taosArrayGetSize(pSBf->bfArray) >= pSBf->maxBloomFilters) {
|
||||
code = TSDB_CODE_FAILED;
|
||||
code = TSDB_CODE_OUT_OF_BUFFER;
|
||||
QUERY_CHECK_CODE(code, lino, _error);
|
||||
}
|
||||
|
||||
|
@ -163,7 +163,7 @@ static int32_t tScalableBfAddFilter(SScalableBf* pSBf, uint64_t expectedEntries,
|
|||
|
||||
if (taosArrayPush(pSBf->bfArray, &pNormalBf) == NULL) {
|
||||
tBloomFilterDestroy(pNormalBf);
|
||||
code = TSDB_CODE_OUT_OF_MEMORY;
|
||||
code = terrno;
|
||||
QUERY_CHECK_CODE(code, lino, _error);
|
||||
}
|
||||
pSBf->numBits += pNormalBf->numBits;
|
||||
|
@ -217,7 +217,7 @@ int32_t tScalableBfDecode(SDecoder* pDecoder, SScalableBf** ppSBf) {
|
|||
pSBf->bfArray = NULL;
|
||||
int32_t size = 0;
|
||||
if (tDecodeI32(pDecoder, &size) < 0) {
|
||||
code = TSDB_CODE_FAILED;
|
||||
code = terrno;
|
||||
QUERY_CHECK_CODE(code, lino, _error);
|
||||
}
|
||||
if (size == 0) {
|
||||
|
@ -242,19 +242,19 @@ int32_t tScalableBfDecode(SDecoder* pDecoder, SScalableBf** ppSBf) {
|
|||
}
|
||||
}
|
||||
if (tDecodeU32(pDecoder, &pSBf->growth) < 0) {
|
||||
code = TSDB_CODE_FAILED;
|
||||
code = terrno;
|
||||
QUERY_CHECK_CODE(code, lino, _error);
|
||||
}
|
||||
if (tDecodeU64(pDecoder, &pSBf->numBits) < 0) {
|
||||
code = TSDB_CODE_FAILED;
|
||||
code = terrno;
|
||||
QUERY_CHECK_CODE(code, lino, _error);
|
||||
}
|
||||
if (tDecodeU32(pDecoder, &pSBf->maxBloomFilters) < 0) {
|
||||
code = TSDB_CODE_FAILED;
|
||||
code = terrno;
|
||||
QUERY_CHECK_CODE(code, lino, _error);
|
||||
}
|
||||
if (tDecodeI8(pDecoder, &pSBf->status) < 0) {
|
||||
code = TSDB_CODE_FAILED;
|
||||
code = terrno;
|
||||
QUERY_CHECK_CODE(code, lino, _error);
|
||||
}
|
||||
(*ppSBf) = pSBf;
|
||||
|
|
|
@ -163,6 +163,7 @@
|
|||
,,y,system-test,./pytest.sh python3 ./test.py -f 1-insert/stt_blocks_check.py
|
||||
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/out_of_order.py -Q 3
|
||||
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/out_of_order.py
|
||||
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/agg_null.py
|
||||
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/insert_null_none.py
|
||||
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/insert_null_none.py -R
|
||||
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/insert_null_none.py -Q 2
|
||||
|
|
|
@ -9,3 +9,4 @@ requests
|
|||
pexpect
|
||||
faker
|
||||
pyopenssl
|
||||
hyperloglog
|
|
@ -0,0 +1,136 @@
|
|||
###################################################################
|
||||
# Copyright (c) 2016 by TAOS Technologies, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This file is proprietary and confidential to TAOS Technologies.
|
||||
# No part of this file may be reproduced, stored, transmitted,
|
||||
# disclosed or used in any form or by any means other than as
|
||||
# expressly provided by the written permission from Jianhui Tao
|
||||
#
|
||||
###################################################################
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import numpy as np
|
||||
from util.log import *
|
||||
from util.cases import *
|
||||
from util.sql import *
|
||||
from util.common import *
|
||||
from util.sqlset import *
|
||||
from hyperloglog import HyperLogLog
|
||||
'''
|
||||
Test case for TS-5150
|
||||
'''
|
||||
class TDTestCase:
|
||||
def init(self, conn, logSql, replicaVar=1):
|
||||
self.replicaVar = int(replicaVar)
|
||||
tdLog.debug("start to execute %s" % __file__)
|
||||
tdSql.init(conn.cursor())
|
||||
self.ts = 1537146000000
|
||||
def initdabase(self):
|
||||
tdSql.execute('create database if not exists db_test vgroups 2 buffer 10')
|
||||
tdSql.execute('use db_test')
|
||||
tdSql.execute('create stable stb(ts timestamp, delay int) tags(groupid int)')
|
||||
tdSql.execute('create table t1 using stb tags(1)')
|
||||
tdSql.execute('create table t2 using stb tags(2)')
|
||||
tdSql.execute('create table t3 using stb tags(3)')
|
||||
tdSql.execute('create table t4 using stb tags(4)')
|
||||
tdSql.execute('create table t5 using stb tags(5)')
|
||||
tdSql.execute('create table t6 using stb tags(6)')
|
||||
def insert_data(self):
|
||||
for i in range(5000):
|
||||
tdSql.execute(f"insert into t1 values({self.ts + i * 1000}, {i%5})")
|
||||
tdSql.execute(f"insert into t2 values({self.ts + i * 1000}, {i%5})")
|
||||
tdSql.execute(f"insert into t3 values({self.ts + i * 1000}, {i%5})")
|
||||
|
||||
def verify_agg_null(self):
|
||||
for i in range(20):
|
||||
col_val_list = []
|
||||
tdSql.query(f'select CASE WHEN delay != 0 THEN delay ELSE NULL END from stb where ts between {1537146000000 + i * 1000} and {1537146000000 + (i+10) * 1000}')
|
||||
for col_va in tdSql.queryResult:
|
||||
if col_va[0] is not None:
|
||||
col_val_list.append(col_va[0])
|
||||
tdSql.query(f'SELECT APERCENTILE(CASE WHEN delay != 0 THEN delay ELSE NULL END,50) AS apercentile,\
|
||||
MAX(CASE WHEN delay != 0 THEN delay ELSE NULL END) AS maxDelay,\
|
||||
MIN(CASE WHEN delay != 0 THEN delay ELSE NULL END) AS minDelay,\
|
||||
AVG(CASE WHEN delay != 0 THEN delay ELSE NULL END) AS avgDelay,\
|
||||
STDDEV(CASE WHEN delay != 0 THEN delay ELSE NULL END) AS jitter,\
|
||||
COUNT(CASE WHEN delay = 0 THEN 1 ELSE NULL END) AS timeoutCount,\
|
||||
COUNT(*) AS totalCount ,\
|
||||
ELAPSED(ts) AS elapsed_time,\
|
||||
SPREAD(CASE WHEN delay != 0 THEN delay ELSE NULL END) AS spread,\
|
||||
SUM(CASE WHEN delay != 0 THEN delay ELSE NULL END) AS sum,\
|
||||
HYPERLOGLOG(CASE WHEN delay != 0 THEN delay ELSE NULL END) AS hyperloglog from stb where ts between {1537146000000 + i * 1000} and {1537146000000 + (i+10) * 1000}')
|
||||
#verify apercentile
|
||||
apercentile_res = tdSql.queryResult[0][0]
|
||||
approximate_median = np.percentile(col_val_list, 50)
|
||||
assert np.abs(apercentile_res - approximate_median) < 1
|
||||
#verify max
|
||||
max_res = tdSql.queryResult[0][1]
|
||||
tdSql.checkEqual(max_res,max(col_val_list))
|
||||
#verify min
|
||||
min_res = tdSql.queryResult[0][2]
|
||||
tdSql.checkEqual(min_res,min(col_val_list))
|
||||
#verify avg
|
||||
avg_res = tdSql.queryResult[0][3]
|
||||
tdSql.checkEqual(avg_res,np.average(col_val_list))
|
||||
#verify stddev
|
||||
stddev_res = tdSql.queryResult[0][4]
|
||||
assert np.abs(stddev_res - np.std(col_val_list)) < 0.0001
|
||||
#verify count of 0 + count of !0 == count(*)
|
||||
count_res = tdSql.queryResult[0][6]
|
||||
tdSql.checkEqual(count_res,len(col_val_list)+tdSql.queryResult[0][5])
|
||||
#verify elapsed
|
||||
elapsed_res = tdSql.queryResult[0][7]
|
||||
assert elapsed_res == 10000
|
||||
#verify spread
|
||||
spread_res = tdSql.queryResult[0][8]
|
||||
tdSql.checkEqual(spread_res,max(col_val_list) - min(col_val_list))
|
||||
#verify sum
|
||||
sum_res = tdSql.queryResult[0][9]
|
||||
tdSql.checkEqual(sum_res,sum(col_val_list))
|
||||
#verify hyperloglog
|
||||
error_rate = 0.01
|
||||
hll = HyperLogLog(error_rate)
|
||||
for col_val in col_val_list:
|
||||
hll.add(col_val)
|
||||
hll_res = tdSql.queryResult[0][10]
|
||||
assert np.abs(hll_res - hll.card()) < 0.01
|
||||
#verify leastsquares
|
||||
tdSql.query(f'SELECT leastsquares(CASE WHEN delay != 0 THEN delay ELSE NULL END,1,1) from stb where ts between {1537146000000 + i * 1000} and {1537146000000 + (i+10) * 1000}')
|
||||
cleaned_data = tdSql.queryResult[0][0].strip('{}').replace(' ', '')
|
||||
pairs = cleaned_data.split(',')
|
||||
slope = None
|
||||
intercept = None
|
||||
for pair in pairs:
|
||||
key, value = pair.split(':')
|
||||
key = key.strip()
|
||||
value = float(value.strip())
|
||||
if key == 'slop':
|
||||
slope = value
|
||||
elif key == 'intercept':
|
||||
intercept = value
|
||||
assert slope != 0
|
||||
assert intercept != 0
|
||||
#verify histogram
|
||||
tdSql.query(f'SELECT histogram(CASE WHEN delay != 0 THEN delay ELSE NULL END, "user_input", "[1,3,5,7]", 1) from stb where ts between {1537146000000 + i * 1000} and {1537146000000 + (i+10) * 1000}')
|
||||
cleaned_data = tdSql.queryResult[0][0].strip('{}').replace(' ', '')
|
||||
pairs = cleaned_data.split(',')
|
||||
count = None
|
||||
for pair in pairs:
|
||||
key, value = pair.split(':')
|
||||
key = key.strip()
|
||||
if key == 'count':
|
||||
count = float(value.strip())
|
||||
assert count != 0
|
||||
def run(self):
|
||||
self.initdabase()
|
||||
self.insert_data()
|
||||
self.verify_agg_null()
|
||||
def stop(self):
|
||||
tdSql.close()
|
||||
tdLog.success(f"{__file__} successfully executed")
|
||||
|
||||
|
||||
tdCases.addLinux(__file__, TDTestCase())
|
||||
tdCases.addWindows(__file__, TDTestCase())
|
Loading…
Reference in New Issue