diff --git a/docs/zh/06-advanced/05-data-in/index.md b/docs/zh/06-advanced/05-data-in/index.md index 99dfbf098b..32d530acb5 100644 --- a/docs/zh/06-advanced/05-data-in/index.md +++ b/docs/zh/06-advanced/05-data-in/index.md @@ -1,276 +1,276 @@ ---- -sidebar_label: 数据写入 -title: 零代码数据源接入 -toc_max_heading_level: 4 ---- - -## 概述 - -TDengine Enterprise 配备了一个强大的可视化数据管理工具—taosExplorer。借助 taosExplorer,用户只须在浏览器中简单配置,就能轻松地向 TDengine 提交任务,实现以零代码方式将来自不同数据源的数据无缝导入 TDengine。在导入过程中,TDengine 会对数据进行自动提取、过滤和转换,以保证导入的数据质量。通过这种零代码数据源接入方式,TDengine 成功转型为一个卓越的时序大数据汇聚平台。用户无须部署额外的 ETL 工具,从而大大简化整体架构的设计,提高了数据处理效率。 - -下图展示了零代码接入平台的系统架构。 - -![零代码数据接入架构图](./data-in.png) - -## 支持的数据源 - -目前 TDengine 支持的数据源如下: - -1. Aveva PI System:一个工业数据管理和分析平台,前身为 OSIsoft PI System,它能够实时采集、整合、分析和可视化工业数据,助力企业实现智能化决策和精细化管理 -2. Aveva Historian:一个工业大数据分析软件,前身为 Wonderware Historian,专为工业环境设计,用于存储、管理和分析来自各种工业设备、传感器的实时和历史数据。 -3. OPC DA/UA:OPC 是 Open Platform Communications 的缩写,是一种开放式、标准化的通信协议,用于不同厂商的自动化设备之间进行数据交换。它最初由微软公司开发,旨在解决工业控制领域中不同设备之间互操作性差的问题。OPC 协议最初于 1996 年发布,当时称为 OPC DA (Data Access),主要用于实时数据采集和控制;2006 年,OPC 基金会发布了 OPC UA (Unified Architecture) 标准,它是一种基于服务的面向对象的协议,具有更高的灵活性和可扩展性,已成为 OPC 协议的主流版本。 -4. MQTT:Message Queuing Telemetry Transport 的缩写,一种基于发布/订阅模式的轻量级通讯协议,专为低开销、低带宽占用的即时通讯设计,广泛适用于物联网、小型设备、移动应用等领域。 -5. Kafka:由 Apache 软件基金会开发的一个开源流处理平台,主要用于处理实时数据,并提供一个统一、高通量、低延迟的消息系统。它具备高速度、可伸缩性、持久性和分布式设计等特点,使得它能够在每秒处理数十万次的读写操作,支持上千个客户端,同时保持数据的可靠性和可用性。 -6. OpenTSDB:基于 HBase 的分布式、可伸缩的时序数据库。它主要用于存储、索引和提供从大规模集群(包括网络设备、操作系统、应用程序等)中收集的指标数据,使这些数据更易于访问和图形化展示。 -7. CSV:Comma Separated Values 的缩写,是一种以逗号分隔的纯文本文件格式,通常用于电子表格或数据库软件。 -8. TDengine 2:泛指运行 TDengine 2.x 版本的 TDengine 实例。 -9. TDengine 3:泛指运行 TDengine 3.x 版本的 TDengine 实例。 -10. MySQL, PostgreSQL, Oracle 等关系型数据库。 - -## 数据提取、过滤和转换 - -因为数据源可以有多个,每个数据源的物理单位可能不一样,命名规则也不一样,时区也可能不同。为解决这个问题,TDengine 内置 ETL 功能,可以从数据源的数据包中解析、提取需要的数据,并进行过滤和转换,以保证写入数据的质量,提供统一的命名空间。具体的功能如下: - -1. 解析:使用 JSON Path 或正则表达式,从原始消息中解析字段 -2. 从列中提取或拆分:使用 split 或正则表达式,从一个原始字段中提取多个字段 -3. 过滤:只有表达式的值为 true 时,消息才会被写入 TDengine -4. 转换:建立解析后的字段和 TDengine 超级表字段之间的转换与映射关系。 - -下面详细讲解数据转换规则 - - -### 解析 - -仅非结构化的数据源需要这个步骤,目前 MQTT 和 Kafka 数据源会使用这个步骤提供的规则来解析非结构化数据,以初步获取结构化数据,即可以以字段描述的行列数据。在 explorer 中您需要提供示例数据和解析规则,来预览解析出以表格呈现的结构化数据。 - -#### 示例数据 - -![示例数据](./pic/transform-01.png) - -如图,textarea 输入框中就是示例数据,可以通过三种方式来获取示例数据: - -1. 直接在 textarea 中输入示例数据; -2. 点击右侧按钮 “从服务器检索” 则从配置的服务器获取示例数据,并追加到示例数据 textarea 中; -3. 上传文件,将文件内容追加到示例数据 textarea 中。 - -#### 解析 - -解析就是通过解析规则,将非结构化字符串解析为结构化数据。消息体的解析规则目前支持 JSON、Regex 和 UDT。 - -##### JSON 解析 - -如下 JSON 示例数据,可自动解析出字段:`groupid`、`voltage`、`current`、`ts`、`inuse`、`location`。 - -``` json -{"groupid": 170001, "voltage": "221V", "current": 12.3, "ts": "2023-12-18T22:12:00", "inuse": true, "location": "beijing.chaoyang.datun"} -{"groupid": 170001, "voltage": "220V", "current": 12.2, "ts": "2023-12-18T22:12:02", "inuse": true, "location": "beijing.chaoyang.datun"} -{"groupid": 170001, "voltage": "216V", "current": 12.5, "ts": "2023-12-18T22:12:04", "inuse": false, "location": "beijing.chaoyang.datun"} -``` - -如下嵌套结构的 JSON 数据,可自动解析出字段`groupid`、`data_voltage`、`data_current`、`ts`、`inuse`、`location_0_province`、`location_0_city`、`location_0_datun`,也可以选择要解析的字段,并设置解析的别名。 - -``` json -{"groupid": 170001, "data": { "voltage": "221V", "current": 12.3 }, "ts": "2023-12-18T22:12:00", "inuse": true, "location": [{"province": "beijing", "city":"chaoyang", "street": "datun"}]} -``` - -![JSON 解析](./pic/transform-02.png) - -##### Regex 正则表达式 - -可以使用正则表达式的**命名捕获组**从任何字符串(文本)字段中提取多个字段。如图所示,从 nginx 日志中提取访问ip、时间戳、访问的url等字段。 - -``` re -(?\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b)\s-\s-\s\[(?\d{2}/\w{3}/\d{4}:\d{2}:\d{2}:\d{2}\s\+\d{4})\]\s"(?[A-Z]+)\s(?[^\s"]+).*(?\d{3})\s(?\d+) -``` - -![Regex 解析](./pic/transform-03.png) - -##### UDT 自定义解析脚本 - -自定义 rhai 语法脚本解析输入数据(参考 `https://rhai.rs/book/` ),脚本目前仅支持 json 格式原始数据。 - -**输入**:脚本中可以使用参数 data, data 是原始数据 json 解析后的 Object Map; - -**输出**:输出的数据必须是数组。 - -例如对于数据,一次上报三相电压值,分别入到三个子表中。则需要对这类数据做解析 - -``` json -{ - "ts": "2024-06-27 18:00:00", - "voltage": "220.1,220.3,221.1", - "dev_id": "8208891" -} -``` - -那么可以使用如下脚本来提取三个电压数据。 - -``` -let v3 = data["voltage"].split(","); - -[ -#{"ts": data["ts"], "val": v3[0], "dev_id": data["dev_id"]}, -#{"ts": data["ts"], "val": v3[1], "dev_id": data["dev_id"]}, -#{"ts": data["ts"], "val": v3[2], "dev_id": data["dev_id"]} -] -``` - -最终解析结果如下所示: - -![UDT](./pic/transform-udf.png) - -### 提取或拆分 - -解析后的数据,可能还无法满足目标表的数据要求。比如智能表原始采集数据如下( json 格式): - -``` json -{"groupid": 170001, "voltage": "221V", "current": 12.3, "ts": "2023-12-18T22:12:00", "inuse": true, "location": "beijing.chaoyang.datun"} -{"groupid": 170001, "voltage": "220V", "current": 12.2, "ts": "2023-12-18T22:12:02", "inuse": true, "location": "beijing.chaoyang.datun"} -{"groupid": 170001, "voltage": "216V", "current": 12.5, "ts": "2023-12-18T22:12:04", "inuse": false, "location": "beijing.chaoyang.datun"} -``` - -使用 json 规则解析出的电压是字符串表达的带单位形式,最终入库希望能使用 int 类型记录电压值和电流值,便于统计分析,此时就需要对电压进一步拆分;另外日期期望拆分为日期和时间入库。 - -如下图所示可以对源字段`ts`使用 split 规则拆分成日期和时间,对字段`voltage`使用 regex 提取出电压值和电压单位。split 规则需要设置**分隔符**和**拆分数量**,拆分后的字段命名规则为`{原字段名}_{顺序号}`,Regex 规则同解析过程中的一样,使用**命名捕获组**命名提取字段。 - -![拆分和提取](./pic/transform-04.png) - -### 过滤 - -过滤功能可以设置过滤条件,满足条件的数据行 才会被写入目标表。过滤条件表达式的结果必须是 boolean 类型。在编写过滤条件前,必须确定 解析字段的类型,根据解析字段的类型,可以使用判断函数、比较操作符(`>`、`>=`、`<=`、`<`、`==`、`!=`)来判断。 - -#### 字段类型及转换 - -只有明确解析出的每个字段的类型,才能使用正确的语法做数据过滤。 - -使用 json 规则解析出的字段,按照属性值来自动设置类型: - -1. bool 类型:"inuse": true -2. int 类型:"voltage": 220 -3. float 类型:"current" : 12.2 -4. String 类型:"location": "MX001" - -使用 regex 规则解析的数据都是 string 类型。 -使用 split 和 regex 提取或拆分的数据是 string 类型。 - -如果提取出的数据类型不是预期中的类型,可以做数据类型转换。常用的数据类型转换就是把字符串转换成为数值类型。支持的转换函数如下: - -|Function|From type|To type|e.g.| -|:----|:----|:----|:----| -| parse_int | string | int | parse_int("56") // 结果为整数 56 | -| parse_float | string | float | parse_float("12.3") // 结果为浮点数 12.3 | - -#### 判断表达式 - -不同的数据类型有各自判断表达式的写法。 - -##### BOOL 类型 - -可以使用变量或者使用操作符`!`,比如对于字段 "inuse": true,可以编写以下表达式: - -> 1. inuse -> 2. !inuse - -##### 数值类型(int/float) - -数值类型支持使用比较操作符`==`、`!=`、`>`、`>=`、`<`、`<=`。 - -##### 字符串类型 - -使用比较操作符,比较字符串。 - -字符串函数 - -|Function|Description|e.g.| -|:----|:----|:----| -| is_empty | returns true if the string is empty | s.is_empty() | -| contains | checks if a certain character or sub-string occurs in the string | s.contains("substring") | -| starts_with | returns true if the string starts with a certain string | s.starts_with("prefix") | -| ends_with | returns true if the string ends with a certain string | s.ends_with("suffix") | -| len | returns the number of characters (not number of bytes) in the string,must be used with comparison operator | s.len == 5 判断字符串长度是否为5;len作为属性返回 int ,和前四个函数有区别,前四个直接返回 bool。 | - -##### 复合表达式 - -多个判断表达式,可以使用逻辑操作符(&&、||、!)来组合。 -比如下面的表达式表示获取北京市安装的并且电压值大于 200 的智能表数据。 - -> location.starts_with("beijing") && voltage > 200 - -### 映射 - -映射是将解析、提取、拆分的**源字段**对应到**目标表字段**,可以直接对应,也可以通过一些规则计算后再映射到目标表。 - -#### 选择目标超级表 - -选择目标超级表后,会加载出超级表所有的 tags 和 columns。 -源字段根据名称自动使用 mapping 规则映射到目标超级表的 tag 和 column。 -例如有如下解析、提取、拆分后的预览数据: - -#### 映射规则 - -支持的映射规则如下表所示: - -|rule|description| -|:----|:----| -| mapping | 直接映射,需要选择映射源字段。| -| value | 常量,可以输入字符串常量,也可以是数值常量,输入的常量值直接入库。| -| generator | 生成器,目前仅支持时间戳生成器 now,入库时会将当前时间入库。| -| join | 字符串连接器,可指定连接字符拼接选择的多个源字段。| -| format | **字符串格式化工具**,填写格式化字符串,比如有三个源字段 year, month, day 分别表示年月日,入库希望以yyyy-MM-dd的日期格式入库,则可以提供格式化字符串为 `${year}-${month}-${day}`。其中`${}`作为占位符,占位符中可以是一个源字段,也可以是 string 类型字段的函数处理| -| sum | 选择多个数值型字段做加法计算。| -| expr | **数值运算表达式**,可以对数值型字段做更加复杂的函数处理和数学运算。| - -##### format 中支持的字符串处理函数 - -|Function|description|e.g.| -|:----|:----|:----| -| pad(len, pad_chars) | pads the string with a character or a string to at least a specified length | "1.2".pad(5, '0') // 结果为"1.200" | -|trim|trims the string of whitespace at the beginning and end|" abc ee ".trim() // 结果为"abc ee"| -|sub_string(start_pos, len)|extracts a sub-string,两个参数:
1. start position, counting from end if < 0
2. (optional) number of characters to extract, none if ≤ 0, to end if omitted|"012345678".sub_string(5) // "5678"
"012345678".sub_string(5, 2) // "56"
"012345678".sub_string(-2) // "78"| -|replace(substring, replacement)|replaces a sub-string with another|"012345678".replace("012", "abc") // "abc345678"| - -##### expr 数学计算表达式 - -基本数学运算支持加`+`、减`-`、乘`*`、除`/`。 - -比如数据源采集数值以设置度为单位,目标库存储华氏度温度值。那么就需要对采集的温度数据做转换。 - -解析的源字段为`temperature`,则需要使用表达式 `temperature * 1.8 + 32`。 - -数值表达式中也支持使用数学函数,可用的数学函数如下表所示: - -|Function|description|e.g.| -|:----|:----|:----| -|sin、cos、tan、sinh、cosh|Trigonometry|a.sin() | -|asin、acos、atan、 asinh、acosh|arc-trigonometry|a.asin()| -|sqrt|Square root|a.sqrt() // 4.sqrt() == 2| -|exp|Exponential|a.exp()| -|ln、log|Logarithmic|a.ln() // e.ln() == 1
a.log() // 10.log() == 1| -|floor、ceiling、round、int、fraction|rounding|a.floor() // (4.2).floor() == 4
a.ceiling() // (4.2).ceiling() == 5
a.round() // (4.2).round() == 4
a.int() // (4.2).int() == 4
a.fraction() // (4.2).fraction() == 0.2| - -#### 子表名映射 - -子表名类型为字符串,可以使用映射规则中的字符串格式化 format 表达式定义子表名。 - -## 任务的创建 - -下面以 MQTT 数据源为例,介绍如何创建一个 MQTT 类型的任务,从 MQTT Broker 消费数据,并写入 TDengine。 - -1. 登录至 taosExplorer 以后,点击左侧导航栏上的“数据写入”,即可进入任务列表页面 -2. 在任务列表页面,点击“+ 新增数据源”,即可进入任务创建页面 -3. 输入任务名称后,选择类型为 MQTT, 然后可以创建一个新的代理,或者选择已创建的代理 -4. 输入 MQTT broker 的 IP 地址和端口号,例如:192.168.1.100:1883 -5. 配置认证和 SSL 加密: - - 如果 MQTT broker 开启了用户认证,则在认证部分,输入 MQTT broker 的用户名和密码; - - 如果 MQTT broker 开启了 SSL 加密,则可以打开页面上的 SSL 证书开关,并上传 CA 的证书,以及客户端的证书和私钥文件; -6. 在“采集配置“部分,可选择 MQTT 协议的版本,目前支持 3.1, 3.1.1, 5.0 三个版本;配置 Client ID 时要注意,如果对同一个 MQTT broker 创建了多个任务,Client ID 应不同,否则会造成 Client ID 冲突,导致任务无法正常运行;在对主题和 QoS 进行配置时,需要使用 `::` 的形式,即订阅的主题与 QoS 之间要使用两个冒号分隔,其中 QoS 的取值范围为 0, 1, 2, 分别代表 at most once, at lease once, exactly once;配置完成以上信息后,可以点击“检查连通性”按钮,对以上配置进行检查,如果连通性检查失败,请按照页面上返回的具体错误提示进行修改; -7. 在从 MQTT broker 同步数据的过程中,taosX 还支持对消息体中的字段进行提取,过滤、映射等操作。在位于 “Payload 转换”下方的文本框中,可以直接输入输入消息体样例,或是以上传文件的方式导入,以后还会支持直接从所配置的服务器中检索样例消息; -8. 对消息体字段的提取,目前支持 2 种方式:JSON 和正则表达式。对于简单的 key/value 格式的 JSON 数据,可以直接点击提取按钮,即可展示解析出的字段名;对于复杂的 JSON 数据,可以使用 JSON Path 提取感兴趣的字段;当使用正则表达式提取字段时,要保证正则表达式的正确性; -9. 消息体中的字段被解析后,可以基于解析出的字段名设置过滤规则,只有满足过滤规则的数据,才会写入 TDengine,否则会忽略该消息;例如:可以配置过滤规则为 voltage > 200,即只有当电压大于 200V 的数据才会被同步至 TDengine; -10. 最后,在配置完消息体中的字段和超级表中的字段的映射规则后,就可以提交任务了;除了基本的映射以外,在这里还可以对消息中字段的值进行转换,例如:可以通过表达式 (expr) 将原消息体中的电压和电流,计算为功率后再写入 TDengine; -11. 任务提交后,会自动返回任务列表页面,如果提交成功,任务的状态会切换至“运行中”,如果提交失败,可通过查看该任务的活动日志,查找错误原因; -12. 对于运行中的任务,点击指标的查看按钮,可以查看该任务的详细运行指标,弹出窗口划分为 2 个标签页,分别展示该任务多次运行的累计指标和本次运行的指标,这些指标每 2 秒钟会自动刷新一次。 - -## 任务管理 - +--- +sidebar_label: 数据接入 +title: 零代码第三方数据接入 +toc_max_heading_level: 4 +--- + +## 概述 + +TDengine Enterprise 配备了一个强大的可视化数据管理工具—taosExplorer。借助 taosExplorer,用户只须在浏览器中简单配置,就能轻松地向 TDengine 提交任务,实现以零代码方式将来自不同数据源的数据无缝导入 TDengine。在导入过程中,TDengine 会对数据进行自动提取、过滤和转换,以保证导入的数据质量。通过这种零代码数据源接入方式,TDengine 成功转型为一个卓越的时序大数据汇聚平台。用户无须部署额外的 ETL 工具,从而大大简化整体架构的设计,提高了数据处理效率。 + +下图展示了零代码接入平台的系统架构。 + +![零代码数据接入架构图](./data-in.png) + +## 支持的数据源 + +目前 TDengine 支持的数据源如下: + +1. Aveva PI System:一个工业数据管理和分析平台,前身为 OSIsoft PI System,它能够实时采集、整合、分析和可视化工业数据,助力企业实现智能化决策和精细化管理 +2. Aveva Historian:一个工业大数据分析软件,前身为 Wonderware Historian,专为工业环境设计,用于存储、管理和分析来自各种工业设备、传感器的实时和历史数据。 +3. OPC DA/UA:OPC 是 Open Platform Communications 的缩写,是一种开放式、标准化的通信协议,用于不同厂商的自动化设备之间进行数据交换。它最初由微软公司开发,旨在解决工业控制领域中不同设备之间互操作性差的问题。OPC 协议最初于 1996 年发布,当时称为 OPC DA (Data Access),主要用于实时数据采集和控制;2006 年,OPC 基金会发布了 OPC UA (Unified Architecture) 标准,它是一种基于服务的面向对象的协议,具有更高的灵活性和可扩展性,已成为 OPC 协议的主流版本。 +4. MQTT:Message Queuing Telemetry Transport 的缩写,一种基于发布/订阅模式的轻量级通讯协议,专为低开销、低带宽占用的即时通讯设计,广泛适用于物联网、小型设备、移动应用等领域。 +5. Kafka:由 Apache 软件基金会开发的一个开源流处理平台,主要用于处理实时数据,并提供一个统一、高通量、低延迟的消息系统。它具备高速度、可伸缩性、持久性和分布式设计等特点,使得它能够在每秒处理数十万次的读写操作,支持上千个客户端,同时保持数据的可靠性和可用性。 +6. OpenTSDB:基于 HBase 的分布式、可伸缩的时序数据库。它主要用于存储、索引和提供从大规模集群(包括网络设备、操作系统、应用程序等)中收集的指标数据,使这些数据更易于访问和图形化展示。 +7. CSV:Comma Separated Values 的缩写,是一种以逗号分隔的纯文本文件格式,通常用于电子表格或数据库软件。 +8. TDengine 2:泛指运行 TDengine 2.x 版本的 TDengine 实例。 +9. TDengine 3:泛指运行 TDengine 3.x 版本的 TDengine 实例。 +10. MySQL, PostgreSQL, Oracle 等关系型数据库。 + +## 数据提取、过滤和转换 + +因为数据源可以有多个,每个数据源的物理单位可能不一样,命名规则也不一样,时区也可能不同。为解决这个问题,TDengine 内置 ETL 功能,可以从数据源的数据包中解析、提取需要的数据,并进行过滤和转换,以保证写入数据的质量,提供统一的命名空间。具体的功能如下: + +1. 解析:使用 JSON Path 或正则表达式,从原始消息中解析字段 +2. 从列中提取或拆分:使用 split 或正则表达式,从一个原始字段中提取多个字段 +3. 过滤:只有表达式的值为 true 时,消息才会被写入 TDengine +4. 转换:建立解析后的字段和 TDengine 超级表字段之间的转换与映射关系。 + +下面详细讲解数据转换规则 + + +### 解析 + +仅非结构化的数据源需要这个步骤,目前 MQTT 和 Kafka 数据源会使用这个步骤提供的规则来解析非结构化数据,以初步获取结构化数据,即可以以字段描述的行列数据。在 explorer 中您需要提供示例数据和解析规则,来预览解析出以表格呈现的结构化数据。 + +#### 示例数据 + +![示例数据](./pic/transform-01.png) + +如图,textarea 输入框中就是示例数据,可以通过三种方式来获取示例数据: + +1. 直接在 textarea 中输入示例数据; +2. 点击右侧按钮 “从服务器检索” 则从配置的服务器获取示例数据,并追加到示例数据 textarea 中; +3. 上传文件,将文件内容追加到示例数据 textarea 中。 + +#### 解析 + +解析就是通过解析规则,将非结构化字符串解析为结构化数据。消息体的解析规则目前支持 JSON、Regex 和 UDT。 + +##### JSON 解析 + +如下 JSON 示例数据,可自动解析出字段:`groupid`、`voltage`、`current`、`ts`、`inuse`、`location`。 + +``` json +{"groupid": 170001, "voltage": "221V", "current": 12.3, "ts": "2023-12-18T22:12:00", "inuse": true, "location": "beijing.chaoyang.datun"} +{"groupid": 170001, "voltage": "220V", "current": 12.2, "ts": "2023-12-18T22:12:02", "inuse": true, "location": "beijing.chaoyang.datun"} +{"groupid": 170001, "voltage": "216V", "current": 12.5, "ts": "2023-12-18T22:12:04", "inuse": false, "location": "beijing.chaoyang.datun"} +``` + +如下嵌套结构的 JSON 数据,可自动解析出字段`groupid`、`data_voltage`、`data_current`、`ts`、`inuse`、`location_0_province`、`location_0_city`、`location_0_datun`,也可以选择要解析的字段,并设置解析的别名。 + +``` json +{"groupid": 170001, "data": { "voltage": "221V", "current": 12.3 }, "ts": "2023-12-18T22:12:00", "inuse": true, "location": [{"province": "beijing", "city":"chaoyang", "street": "datun"}]} +``` + +![JSON 解析](./pic/transform-02.png) + +##### Regex 正则表达式 + +可以使用正则表达式的**命名捕获组**从任何字符串(文本)字段中提取多个字段。如图所示,从 nginx 日志中提取访问ip、时间戳、访问的url等字段。 + +``` re +(?\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b)\s-\s-\s\[(?\d{2}/\w{3}/\d{4}:\d{2}:\d{2}:\d{2}\s\+\d{4})\]\s"(?[A-Z]+)\s(?[^\s"]+).*(?\d{3})\s(?\d+) +``` + +![Regex 解析](./pic/transform-03.png) + +##### UDT 自定义解析脚本 + +自定义 rhai 语法脚本解析输入数据(参考 `https://rhai.rs/book/` ),脚本目前仅支持 json 格式原始数据。 + +**输入**:脚本中可以使用参数 data, data 是原始数据 json 解析后的 Object Map; + +**输出**:输出的数据必须是数组。 + +例如对于数据,一次上报三相电压值,分别入到三个子表中。则需要对这类数据做解析 + +``` json +{ + "ts": "2024-06-27 18:00:00", + "voltage": "220.1,220.3,221.1", + "dev_id": "8208891" +} +``` + +那么可以使用如下脚本来提取三个电压数据。 + +``` +let v3 = data["voltage"].split(","); + +[ +#{"ts": data["ts"], "val": v3[0], "dev_id": data["dev_id"]}, +#{"ts": data["ts"], "val": v3[1], "dev_id": data["dev_id"]}, +#{"ts": data["ts"], "val": v3[2], "dev_id": data["dev_id"]} +] +``` + +最终解析结果如下所示: + +![UDT](./pic/transform-udf.png) + +### 提取或拆分 + +解析后的数据,可能还无法满足目标表的数据要求。比如智能表原始采集数据如下( json 格式): + +``` json +{"groupid": 170001, "voltage": "221V", "current": 12.3, "ts": "2023-12-18T22:12:00", "inuse": true, "location": "beijing.chaoyang.datun"} +{"groupid": 170001, "voltage": "220V", "current": 12.2, "ts": "2023-12-18T22:12:02", "inuse": true, "location": "beijing.chaoyang.datun"} +{"groupid": 170001, "voltage": "216V", "current": 12.5, "ts": "2023-12-18T22:12:04", "inuse": false, "location": "beijing.chaoyang.datun"} +``` + +使用 json 规则解析出的电压是字符串表达的带单位形式,最终入库希望能使用 int 类型记录电压值和电流值,便于统计分析,此时就需要对电压进一步拆分;另外日期期望拆分为日期和时间入库。 + +如下图所示可以对源字段`ts`使用 split 规则拆分成日期和时间,对字段`voltage`使用 regex 提取出电压值和电压单位。split 规则需要设置**分隔符**和**拆分数量**,拆分后的字段命名规则为`{原字段名}_{顺序号}`,Regex 规则同解析过程中的一样,使用**命名捕获组**命名提取字段。 + +![拆分和提取](./pic/transform-04.png) + +### 过滤 + +过滤功能可以设置过滤条件,满足条件的数据行 才会被写入目标表。过滤条件表达式的结果必须是 boolean 类型。在编写过滤条件前,必须确定 解析字段的类型,根据解析字段的类型,可以使用判断函数、比较操作符(`>`、`>=`、`<=`、`<`、`==`、`!=`)来判断。 + +#### 字段类型及转换 + +只有明确解析出的每个字段的类型,才能使用正确的语法做数据过滤。 + +使用 json 规则解析出的字段,按照属性值来自动设置类型: + +1. bool 类型:"inuse": true +2. int 类型:"voltage": 220 +3. float 类型:"current" : 12.2 +4. String 类型:"location": "MX001" + +使用 regex 规则解析的数据都是 string 类型。 +使用 split 和 regex 提取或拆分的数据是 string 类型。 + +如果提取出的数据类型不是预期中的类型,可以做数据类型转换。常用的数据类型转换就是把字符串转换成为数值类型。支持的转换函数如下: + +|Function|From type|To type|e.g.| +|:----|:----|:----|:----| +| parse_int | string | int | parse_int("56") // 结果为整数 56 | +| parse_float | string | float | parse_float("12.3") // 结果为浮点数 12.3 | + +#### 判断表达式 + +不同的数据类型有各自判断表达式的写法。 + +##### BOOL 类型 + +可以使用变量或者使用操作符`!`,比如对于字段 "inuse": true,可以编写以下表达式: + +> 1. inuse +> 2. !inuse + +##### 数值类型(int/float) + +数值类型支持使用比较操作符`==`、`!=`、`>`、`>=`、`<`、`<=`。 + +##### 字符串类型 + +使用比较操作符,比较字符串。 + +字符串函数 + +|Function|Description|e.g.| +|:----|:----|:----| +| is_empty | returns true if the string is empty | s.is_empty() | +| contains | checks if a certain character or sub-string occurs in the string | s.contains("substring") | +| starts_with | returns true if the string starts with a certain string | s.starts_with("prefix") | +| ends_with | returns true if the string ends with a certain string | s.ends_with("suffix") | +| len | returns the number of characters (not number of bytes) in the string,must be used with comparison operator | s.len == 5 判断字符串长度是否为5;len作为属性返回 int ,和前四个函数有区别,前四个直接返回 bool。 | + +##### 复合表达式 + +多个判断表达式,可以使用逻辑操作符(&&、||、!)来组合。 +比如下面的表达式表示获取北京市安装的并且电压值大于 200 的智能表数据。 + +> location.starts_with("beijing") && voltage > 200 + +### 映射 + +映射是将解析、提取、拆分的**源字段**对应到**目标表字段**,可以直接对应,也可以通过一些规则计算后再映射到目标表。 + +#### 选择目标超级表 + +选择目标超级表后,会加载出超级表所有的 tags 和 columns。 +源字段根据名称自动使用 mapping 规则映射到目标超级表的 tag 和 column。 +例如有如下解析、提取、拆分后的预览数据: + +#### 映射规则 + +支持的映射规则如下表所示: + +|rule|description| +|:----|:----| +| mapping | 直接映射,需要选择映射源字段。| +| value | 常量,可以输入字符串常量,也可以是数值常量,输入的常量值直接入库。| +| generator | 生成器,目前仅支持时间戳生成器 now,入库时会将当前时间入库。| +| join | 字符串连接器,可指定连接字符拼接选择的多个源字段。| +| format | **字符串格式化工具**,填写格式化字符串,比如有三个源字段 year, month, day 分别表示年月日,入库希望以yyyy-MM-dd的日期格式入库,则可以提供格式化字符串为 `${year}-${month}-${day}`。其中`${}`作为占位符,占位符中可以是一个源字段,也可以是 string 类型字段的函数处理| +| sum | 选择多个数值型字段做加法计算。| +| expr | **数值运算表达式**,可以对数值型字段做更加复杂的函数处理和数学运算。| + +##### format 中支持的字符串处理函数 + +|Function|description|e.g.| +|:----|:----|:----| +| pad(len, pad_chars) | pads the string with a character or a string to at least a specified length | "1.2".pad(5, '0') // 结果为"1.200" | +|trim|trims the string of whitespace at the beginning and end|" abc ee ".trim() // 结果为"abc ee"| +|sub_string(start_pos, len)|extracts a sub-string,两个参数:
1. start position, counting from end if < 0
2. (optional) number of characters to extract, none if ≤ 0, to end if omitted|"012345678".sub_string(5) // "5678"
"012345678".sub_string(5, 2) // "56"
"012345678".sub_string(-2) // "78"| +|replace(substring, replacement)|replaces a sub-string with another|"012345678".replace("012", "abc") // "abc345678"| + +##### expr 数学计算表达式 + +基本数学运算支持加`+`、减`-`、乘`*`、除`/`。 + +比如数据源采集数值以设置度为单位,目标库存储华氏度温度值。那么就需要对采集的温度数据做转换。 + +解析的源字段为`temperature`,则需要使用表达式 `temperature * 1.8 + 32`。 + +数值表达式中也支持使用数学函数,可用的数学函数如下表所示: + +|Function|description|e.g.| +|:----|:----|:----| +|sin、cos、tan、sinh、cosh|Trigonometry|a.sin() | +|asin、acos、atan、 asinh、acosh|arc-trigonometry|a.asin()| +|sqrt|Square root|a.sqrt() // 4.sqrt() == 2| +|exp|Exponential|a.exp()| +|ln、log|Logarithmic|a.ln() // e.ln() == 1
a.log() // 10.log() == 1| +|floor、ceiling、round、int、fraction|rounding|a.floor() // (4.2).floor() == 4
a.ceiling() // (4.2).ceiling() == 5
a.round() // (4.2).round() == 4
a.int() // (4.2).int() == 4
a.fraction() // (4.2).fraction() == 0.2| + +#### 子表名映射 + +子表名类型为字符串,可以使用映射规则中的字符串格式化 format 表达式定义子表名。 + +## 任务的创建 + +下面以 MQTT 数据源为例,介绍如何创建一个 MQTT 类型的任务,从 MQTT Broker 消费数据,并写入 TDengine。 + +1. 登录至 taosExplorer 以后,点击左侧导航栏上的“数据写入”,即可进入任务列表页面 +2. 在任务列表页面,点击“+ 新增数据源”,即可进入任务创建页面 +3. 输入任务名称后,选择类型为 MQTT, 然后可以创建一个新的代理,或者选择已创建的代理 +4. 输入 MQTT broker 的 IP 地址和端口号,例如:192.168.1.100:1883 +5. 配置认证和 SSL 加密: + - 如果 MQTT broker 开启了用户认证,则在认证部分,输入 MQTT broker 的用户名和密码; + - 如果 MQTT broker 开启了 SSL 加密,则可以打开页面上的 SSL 证书开关,并上传 CA 的证书,以及客户端的证书和私钥文件; +6. 在“采集配置“部分,可选择 MQTT 协议的版本,目前支持 3.1, 3.1.1, 5.0 三个版本;配置 Client ID 时要注意,如果对同一个 MQTT broker 创建了多个任务,Client ID 应不同,否则会造成 Client ID 冲突,导致任务无法正常运行;在对主题和 QoS 进行配置时,需要使用 `::` 的形式,即订阅的主题与 QoS 之间要使用两个冒号分隔,其中 QoS 的取值范围为 0, 1, 2, 分别代表 at most once, at lease once, exactly once;配置完成以上信息后,可以点击“检查连通性”按钮,对以上配置进行检查,如果连通性检查失败,请按照页面上返回的具体错误提示进行修改; +7. 在从 MQTT broker 同步数据的过程中,taosX 还支持对消息体中的字段进行提取,过滤、映射等操作。在位于 “Payload 转换”下方的文本框中,可以直接输入输入消息体样例,或是以上传文件的方式导入,以后还会支持直接从所配置的服务器中检索样例消息; +8. 对消息体字段的提取,目前支持 2 种方式:JSON 和正则表达式。对于简单的 key/value 格式的 JSON 数据,可以直接点击提取按钮,即可展示解析出的字段名;对于复杂的 JSON 数据,可以使用 JSON Path 提取感兴趣的字段;当使用正则表达式提取字段时,要保证正则表达式的正确性; +9. 消息体中的字段被解析后,可以基于解析出的字段名设置过滤规则,只有满足过滤规则的数据,才会写入 TDengine,否则会忽略该消息;例如:可以配置过滤规则为 voltage > 200,即只有当电压大于 200V 的数据才会被同步至 TDengine; +10. 最后,在配置完消息体中的字段和超级表中的字段的映射规则后,就可以提交任务了;除了基本的映射以外,在这里还可以对消息中字段的值进行转换,例如:可以通过表达式 (expr) 将原消息体中的电压和电流,计算为功率后再写入 TDengine; +11. 任务提交后,会自动返回任务列表页面,如果提交成功,任务的状态会切换至“运行中”,如果提交失败,可通过查看该任务的活动日志,查找错误原因; +12. 对于运行中的任务,点击指标的查看按钮,可以查看该任务的详细运行指标,弹出窗口划分为 2 个标签页,分别展示该任务多次运行的累计指标和本次运行的指标,这些指标每 2 秒钟会自动刷新一次。 + +## 任务管理 + 在任务列表页面,还可以对任务进行启动、停止、查看、删除、复制等操作,也可以查看各个任务的运行情况,包括写入的记录条数、流量等。 \ No newline at end of file diff --git a/docs/zh/07-operation/05-monitor.md b/docs/zh/07-operation/05-monitor.md index c5b4cbac06..cd8a9772cd 100644 --- a/docs/zh/07-operation/05-monitor.md +++ b/docs/zh/07-operation/05-monitor.md @@ -8,57 +8,13 @@ toc_max_heading_level: 4 至于如何获取和使用这些监控数据,用户可以使用第三方的监测工具比如 Zabbix 来获取这些保存的系统监测数据,进而将 TDengine 的运行状况无缝集成到现有的 IT 监控系统中。也可以使用 TDengine 提供的 TDinsight 插件,使用该插件用户可以通过 Grafana 平台直观地展示和管理这些监控信息,如下图所示。这为用户提供了灵活的监控选项,以满足不同场景下的运维需求。 -~[通过监控组件管理监控信息](./grafana.png) +![通过监控组件管理监控信息](./grafana.png) ## 配置 taosKeeper 因为 TDengine 的监控数据都通过 taosKeeper 上报并存储,所以本节先介绍 taosKeeper 的配置。 -taosKeeper 的配置文件默认位于 `/etc/taos/taoskeeper.toml`。 下面为一个示例配置文件,更多详细信息见参考手册。其中最为关键的一个配置项是 `database`,它决定了收集到的监控数据存储在目标系统的哪个数据库中。 - -```toml -# gin 框架是否启用 debug -debug = false - -# 服务监听端口, 默认为 6043 -port = 6043 - -# 日志级别,包含 panic、error、info、debug、trace等 -loglevel = "info" - -# 程序中使用协程池的大小 -gopoolsize = 50000 - -# 查询 TDengine 监控数据轮询间隔 -RotationInterval = "15s" - -[tdengine] -host = "127.0.0.1" -port = 6041 -username = "root" -password = "taosdata" - -# 需要被监控的 taosAdapter -[taosAdapter] -address = ["127.0.0.1:6041"] - -[metrics] -# 监控指标前缀 -prefix = "taos" - -# 集群数据的标识符 -cluster = "production" - -# 存放监控数据的数据库 -database = "log" - -# 指定需要监控的普通表 -tables = [] - -# database options for db storing metrics data -[metrics.databaseoptions] -cachemodel = "none" -``` +taosKeeper 的配置文件默认位于 `/etc/taos/taoskeeper.toml`。 详细配置见 [参考手册](../../reference/components/taoskeeper/#配置文件)。其中最为关键的一个配置项是 `database`,它决定了收集到的监控数据存储在目标系统的哪个数据库中。 ## 监控 taosd @@ -66,9 +22,9 @@ cachemodel = "none" 为了简化用户在 TDengine 监控方面的配置工作,TDengine 提供了一个名为 TDinsight 的 Grafana 插件。该插件与 taosKeeper 协同工作,能够实时监控 TDengine 的各项性能指标。 -通过集成 Grafana 和 TDengine 数据源插件,TDinsight 能够读取 taosKeeper 收集并存储的监控数据。这使得用户可以在 Grafana 平台上直观地查看 TDengine 集群的状态、节点信息、读写请求以及资源使用情况等关键指标,实现数据的可视化展示。 +通过集成 Grafana 和 TDengine 数据源插件,TDinsight 能够读取 taosKeeper 收集的监控数据。这使得用户可以在 Grafana 平台上直观地查看 TDengine 集群的状态、节点信息、读写请求以及资源使用情况等关键指标,实现数据的可视化展示。 -此外,TDinsight 还具备针对 vnode、dnode 和 mnode 节点的异常状态告警功能,为开发者提供实时的集群运行状态监控,确保 TDengine 集群的稳定性和可靠性。以下是TDinsight 的详细使用说明,以帮助你充分利用这一强大工具。 +以下是TDinsight 的详细使用说明,以帮助你充分利用这一强大工具。 #### 前置条件 @@ -76,314 +32,31 @@ cachemodel = "none" - TDengine 已安装并正常运行。 - taosAdapter 已经安装并正常运行。 - taosKeeper 已经安装并正常运行。 -- Grafana 已安装并正常运行,以下介绍以 Grafna 10.4.0 为例。 +- Grafana 已安装并正常运行,以下介绍以 Grafna 11.0.0 为例。 同时记录以下信息。 -- taosAdapter 的 RESTful 接口地址,如 http://www.example.com:6041。 +- taosAdapter 的 RESTful 接口地址,如 `http://www.example.com:6041`。 - TDengine 集群的认证信息,包括用户名及密码。 #### 导入仪表盘 TDengine 数据源插件已被提交至 Grafana 官网,完成插件的安装和数据源的创建后,可以进行 TDinsight 仪表盘的导入。 -在 Grafana 的 Home-Dashboards 页面,点击位于右上角的 New → mport 按钮,即可进入 Dashboard 的导入页面,它支持以下两种导入方式。 +在 Grafana 的 ”Home“ -> ”Dashboards“ 页面,点击位于右上角的 ”New“ -> ”import“ 按钮,即可进入 Dashboard 的导入页面,它支持以下两种导入方式。 - Dashboard ID:18180。 - Dashboard URL:https://grafana.com/grafana/dashboards/18180-tdinsight-for-3-x/ -填写以上 Dashboard ID 或 Dashboard URL 以后,点击 Load 按钮,按照向导操作,即可完成导入。导入成功后,Dashboards 列表页面会出现 TDinsight for 3.x 仪盘,点击进入后,就可以看到 TDinsight 中已创建的各个指标的面板,如下图所示: +填写以上 Dashboard ID 或 Dashboard URL 以后,点击 ”Load“ 按钮,按照向导操作,即可完成导入。导入成功后,Dashboards 列表页面会出现 ”TDinsight for 3.x“ 仪表盘,点击进入后,就可以看到 TDinsight 中已创建的各个指标的面板,如下图所示: -![TDinsight 界面示例](./tdinsight.png) +![TDinsight 界面示例](./TDinsight-1-cluster-status.webp) -**注意** 在 TDinsight 界面左上角的 Log from 下拉列表中可以选择 log 数据库。 +**注意** 在 TDinsight 界面左上角的 ”Log from“ 下拉列表中可以选择 `log` 数据库。 -### taosd 监控数据 +### TDengine V3 监控数据 -TDinsight dashboard 数据来源于 log 库(存放监控数据的默认 db,可以在 taoskeeper 配置文件中修改。以下是由 taosd 上报由 taosKeeper 存储在 log 库中的数据。 - -1. taosd\_cluster\_basic 表 - -`taosd_cluster_basic` 表记录集群基础信息。 - -|field|type|is\_tag|comment| -|:----|:---|:-----|:------| -|ts|TIMESTAMP||timestamp| -|first\_ep|VARCHAR||集群 first ep| -|first\_ep\_dnode\_id|INT||集群 first ep 的 dnode id| -|cluster_version|VARCHAR||tdengine version。例如:3.0.4.0| -|cluster\_id|VARCHAR|TAG|cluster id| - -2. taosd\_cluster\_info 表 - -`taosd_cluster_info` 表记录集群信息。 - -|field|type|is\_tag|comment| -|:----|:---|:-----|:------| -|ts|TIMESTAMP||timestamp| -|cluster_uptime|DOUBLE||当前 master 节点的uptime。单位:秒| -|dbs\_total|DOUBLE||database 总数| -|tbs\_total|DOUBLE||当前集群 table 总数| -|stbs\_total|DOUBLE||当前集群 stable 总数| -|dnodes\_total|DOUBLE||当前集群 dnode 总数| -|dnodes\_alive|DOUBLE||当前集群 dnode 存活总数| -|mnodes\_total|DOUBLE||当前集群 mnode 总数| -|mnodes\_alive|DOUBLE||当前集群 mnode 存活总数| -|vgroups\_total|DOUBLE||当前集群 vgroup 总数| -|vgroups\_alive|DOUBLE||当前集群 vgroup 存活总数| -|vnodes\_total|DOUBLE||当前集群 vnode 总数| -|vnodes\_alive|DOUBLE||当前集群 vnode 存活总数| -|connections\_total|DOUBLE||当前集群连接总数| -|topics\_total|DOUBLE||当前集群 topic 总数| -|streams\_total|DOUBLE||当前集群 stream 总数| -|grants_expire\_time|DOUBLE||认证过期时间,企业版有效,社区版为 DOUBLE 最大值| -|grants_timeseries\_used|DOUBLE||已用测点数| -|grants_timeseries\_total|DOUBLE||总测点数,开源版本为 DOUBLE 最大值| -|cluster\_id|VARCHAR|TAG|cluster id| - -3. taosd\_vgroups\_info 表 - -`taosd_vgroups_info` 表记录虚拟节点组信息。 - -|field|type|is\_tag|comment| -|:----|:---|:-----|:------| -|ts|TIMESTAMP||timestamp| -|tables\_num|DOUBLE||vgroup 中 table 数量| -|status|DOUBLE||vgroup 状态, 取值范围:unsynced = 0, ready = 1| -|vgroup\_id|VARCHAR|TAG|vgroup id| -|database\_name|VARCHAR|TAG|vgroup 所属的 database 名字| -|cluster\_id|VARCHAR|TAG|cluster id| - -4. taosd\_dnodes\_info 表 - -`taosd_dnodes_info` 记录 dnode 信息。 - -|field|type|is\_tag|comment| -|:----|:---|:-----|:------| -|ts|TIMESTAMP||timestamp| -|uptime|DOUBLE||dnode uptime,单位:秒| -|cpu\_engine|DOUBLE||taosd cpu 使用率,从 `/proc//stat` 读取| -|cpu\_system|DOUBLE||服务器 cpu 使用率,从 `/proc/stat` 读取| -|cpu\_cores|DOUBLE||服务器 cpu 核数| -|mem\_engine|DOUBLE||taosd 内存使用率,从 `/proc//status` 读取| -|mem\_free|DOUBLE||服务器可用内存,单位 KB| -|mem\_total|DOUBLE||服务器内存总量,单位 KB| -|disk\_used|DOUBLE||data dir 挂载的磁盘使用量,单位 bytes| -|disk\_total|DOUBLE||data dir 挂载的磁盘总容量,单位 bytes| -|system\_net\_in|DOUBLE||网络吞吐率,从 `/proc/net/dev` 中读取的 received bytes。单位 byte/s| -|system\_net\_out|DOUBLE||网络吞吐率,从 `/proc/net/dev` 中读取的 transmit bytes。单位 byte/s| -|io\_read|DOUBLE||io 吞吐率,从 `/proc//io` 中读取的 rchar 与上次数值计算之后,计算得到速度。单位 byte/s| -|io\_write|DOUBLE||io 吞吐率,从 `/proc//io` 中读取的 wchar 与上次数值计算之后,计算得到速度。单位 byte/s| -|io\_read\_disk|DOUBLE||磁盘 io 吞吐率,从 `/proc//io` 中读取的 read_bytes。单位 byte/s| -|io\_write\_disk|DOUBLE||磁盘 io 吞吐率,从 `/proc//io` 中读取的 write_bytes。单位 byte/s| -|vnodes\_num|DOUBLE||dnode 上 vnodes 数量| -|masters|DOUBLE||dnode 上 master node 数量| -|has\_mnode|DOUBLE||dnode 是否包含 mnode,取值范围:包含=1,不包含=0| -|has\_qnode|DOUBLE||dnode 是否包含 qnode,取值范围:包含=1,不包含=0| -|has\_snode|DOUBLE||dnode 是否包含 snode,取值范围:包含=1,不包含=0| -|has\_bnode|DOUBLE||dnode 是否包含 bnode,取值范围:包含=1,不包含=0| -|error\_log\_count|DOUBLE||error 总数| -|info\_log\_count|DOUBLE||info 总数| -|debug\_log\_count|DOUBLE||debug 总数| -|trace\_log\_count|DOUBLE||trace 总数| -|dnode\_id|VARCHAR|TAG|dnode id| -|dnode\_ep|VARCHAR|TAG|dnode endpoint| -|cluster\_id|VARCHAR|TAG|cluster id| - -5. taosd\_dnodes\_status 表 - -`taosd_dnodes_status` 表记录 dnode 状态信息。 - -|field|type|is\_tag|comment| -|:----|:---|:-----|:------| -|ts|TIMESTAMP||timestamp| -|status|DOUBLE||dnode 状态,取值范围:ready=1,offline =0| -|dnode\_id|VARCHAR|TAG|dnode id| -|dnode\_ep|VARCHAR|TAG|dnode endpoint| -|cluster\_id|VARCHAR|TAG|cluster id| - -6. taosd\_dnodes\_log\_dir 表 - -`taosd_dnodes_log_dir` 表记录 log 目录信息。 - -|field|type|is\_tag|comment| -|:----|:---|:-----|:------| -|ts|TIMESTAMP||timestamp| -|avail|DOUBLE||log 目录可用空间。单位 byte| -|used|DOUBLE||log 目录已使用空间。单位 byte| -|total|DOUBLE||log 目录空间。单位 byte| -|name|VARCHAR|TAG|log 目录名,一般为 `/var/log/taos/`| -|dnode\_id|VARCHAR|TAG|dnode id| -|dnode\_ep|VARCHAR|TAG|dnode endpoint| -|cluster\_id|VARCHAR|TAG|cluster id| - -7. taosd\_dnodes\_data\_dir 表 - -`taosd_dnodes_data_dir` 表记录 data 目录信息。 - -|field|type|is\_tag|comment| -|:----|:---|:-----|:------| -|ts|TIMESTAMP||timestamp| -|avail|DOUBLE||data 目录可用空间。单位 byte| -|used|DOUBLE||data 目录已使用空间。单位 byte| -|total|DOUBLE||data 目录空间。单位 byte| -|level|VARCHAR|TAG|0、1、2 多级存储级别| -|name|VARCHAR|TAG|data 目录,一般为 `/var/lib/taos`| -|dnode\_id|VARCHAR|TAG|dnode id| -|dnode\_ep|VARCHAR|TAG|dnode endpoint| -|cluster\_id|VARCHAR|TAG|cluster id| - -8. taosd\_mnodes\_info 表 - -`taosd_mnodes_info` 表记录 mnode 角色信息。 - -|field|type|is\_tag|comment| -|:----|:---|:-----|:------| -|ts|TIMESTAMP||timestamp| -|role|DOUBLE||mnode 角色, 取值范围:offline = 0,follower = 100,candidate = 101,leader = 102,error = 103,learner = 104| -|mnode\_id|VARCHAR|TAG|master node id| -|mnode\_ep|VARCHAR|TAG|master node endpoint| -|cluster\_id|VARCHAR|TAG|cluster id| - -9. taosd\_vnodes\_role 表 - -`taosd_vnodes_role` 表记录虚拟节点角色信息。 - -|field|type|is\_tag|comment| -|:----|:---|:-----|:------| -|ts|TIMESTAMP||timestamp| -|vnode\_role|DOUBLE||vnode 角色,取值范围:offline = 0,follower = 100,candidate = 101,leader = 102,error = 103,learner = 104| -|vgroup\_id|VARCHAR|TAG|dnode id| -|dnode\_id|VARCHAR|TAG|dnode id| -|database\_name|VARCHAR|TAG|vgroup 所属的 database 名字| -|cluster\_id|VARCHAR|TAG|cluster id| - -10. taosd\_sql\_req 表 - -`taosd_sql_req` 记录授权信息。 - -|field|type|is\_tag|comment| -|:----|:---|:-----|:------| -|ts|TIMESTAMP||timestamp| -|count|DOUBLE||sql 数量| -|result|VARCHAR|TAG|sql的执行结果,取值范围:Success, Failed| -|username|VARCHAR|TAG|执行sql的user name| -|sql\_type|VARCHAR|TAG|sql类型,取值范围:inserted_rows| -|dnode\_id|VARCHAR|TAG|dnode id| -|dnode\_ep|VARCHAR|TAG|dnode endpoint| -|vgroup\_id|VARCHAR|TAG|dnode id| -|cluster\_id|VARCHAR|TAG|cluster id| - -11. taos\_sql\_req 表 - -`taos_sql_req` 记录授权信息。 - -|field|type|is\_tag|comment| -|:----|:---|:-----|:------| -|ts|TIMESTAMP||timestamp| -|count|DOUBLE||sql 数量| -|result|VARCHAR|TAG|sql的执行结果,取值范围:Success, Failed| -|username|VARCHAR|TAG|执行sql的user name| -|sql\_type|VARCHAR|TAG|sql类型,取值范围:select, insert,delete| -|cluster\_id|VARCHAR|TAG|cluster id| - -12. taos\_slow\_sql 表 - -`taos_slow_sql` 记录授权信息。 - -|field|type|is\_tag|comment| -|:----|:---|:-----|:------| -|ts|TIMESTAMP||timestamp| -|count|DOUBLE||sql 数量| -|result|VARCHAR|TAG|sql的执行结果,取值范围:Success, Failed| -|username|VARCHAR|TAG|执行sql的user name| -|duration|VARCHAR|TAG|sql执行耗时,取值范围:3-10s,10-100s,100-1000s,1000s-| -|cluster\_id|VARCHAR|TAG|cluster id| - -13. keeper\_monitor 表 - -`keeper_monitor` 记录 taoskeeper 监控数据。 - -|field|type|is\_tag|comment| -|:----|:---|:-----|:------| -|ts|TIMESTAMP||timestamp| -|cpu|DOUBLE||cpu 使用率| -|mem|DOUBLE||内存使用率| -|identify|NCHAR|TAG|| - -14. taosadapter\_restful\_http\_request\_total 表 - -`taosadapter_restful_http_request_total` 记录 taosadapter rest 请求信息,该表为 schemaless 方式创建的表,时间戳字段名为 `_ts`。 - -|field|type|is\_tag|comment| -|:----|:---|:-----|:------| -|\_ts|TIMESTAMP||timestamp| -|gauge|DOUBLE||监控指标值| -|client\_ip|NCHAR|TAG|client ip| -|endpoint|NCHAR|TAG|taosadpater endpoint| -|request\_method|NCHAR|TAG|request method| -|request\_uri|NCHAR|TAG|request uri| -|status\_code|NCHAR|TAG|status code| - -15. taosadapter\_restful\_http\_request\_fail 表 - -`taosadapter_restful_http_request_fail` 记录 taosadapter rest 请求失败信息,该表为 schemaless 方式创建的表,时间戳字段名为 `_ts`。 - -|field|type|is\_tag|comment| -|:----|:---|:-----|:------| -|\_ts|TIMESTAMP||timestamp| -|gauge|DOUBLE||监控指标值| -|client\_ip|NCHAR|TAG|client ip| -|endpoint|NCHAR|TAG|taosadpater endpoint| -|request\_method|NCHAR|TAG|request method| -|request\_uri|NCHAR|TAG|request uri| -|status\_code|NCHAR|TAG|status code| - -16. taosadapter\_restful\_http\_request\_in\_flight 表 - -`taosadapter_restful_http_request_in_flight` 记录 taosadapter rest 实时请求信息,该表为 schemaless 方式创建的表,时间戳字段名为 `_ts`。 - -|field|type|is\_tag|comment| -|:----|:---|:-----|:------| -|\_ts|TIMESTAMP||timestamp| -|gauge|DOUBLE||监控指标值| -|endpoint|NCHAR|TAG|taosadpater endpoint| - -17. taosadapter\_restful\_http\_request\_summary\_milliseconds 表 - -`taosadapter_restful_http_request_summary_milliseconds` 记录 taosadapter rest 请求汇总信息,该表为 schemaless 方式创建的表,时间戳字段名为 `_ts`。 - -|field|type|is\_tag|comment| -|:----|:---|:-----|:------| -|\_ts|TIMESTAMP||timestamp| -|count|DOUBLE||| -|sum|DOUBLE||| -|0.5|DOUBLE||| -|0.9|DOUBLE||| -|0.99|DOUBLE||| -|0.1|DOUBLE||| -|0.2|DOUBLE||| -|endpoint|NCHAR|TAG|taosadpater endpoint| -|request\_method|NCHAR|TAG|request method| -|request\_uri|NCHAR|TAG|request uri| - -18. taosadapter\_system\_mem\_percent 表 - -`taosadapter_system_mem_percent` 表记录 taosadapter 内存使用情况,该表为 schemaless 方式创建的表,时间戳字段名为 `_ts`。 - -|field|type|is\_tag|comment| -|:----|:---|:-----|:------| -|\_ts|TIMESTAMP||timestamp| -|gauge|DOUBLE||监控指标值| -|endpoint|NCHAR|TAG|taosadpater endpoint| - -19. taosadapter\_system\_cpu\_percent 表 - -`taosadapter_system_cpu_percent` 表记录 taosadapter cpu 使用情况,该表为 schemaless 方式创建的表,时间戳字段名为 `_ts`。 - -|field|type|is\_tag|comment| -|:----|:---|:-----|:------| -|\_ts|TIMESTAMP||timestamp| -|gauge|DOUBLE||监控指标值| -|endpoint|NCHAR|TAG|taosadpater endpoint| +TDinsight dashboard 数据来源于 `log` 库(存放监控数据的默认数据库,可以在 taoskeeper 配置文件中修改)。”TDinsight for 3.x“ 仪表盘查询了 taosd 和 TaosAdapter 的监控指标。 +- taosd 的监控指标请参考 [taosd 监控指标](../../reference/components/taosd/#taosd-监控指标) +- taosAdapter 的监控指标请参考 [taosAdapter 监控指标](../../reference/components/taosadapter/#taosadapter-监控指标) ## 监控 taosX @@ -396,13 +69,13 @@ taosX 是 TDengine 中提供零代码数据接入能力的核心组件,对它 ### 版本支持 1. TDengine 企业版本 3.2.3.0 或以上版本包含的 taosX 才包含此功能。如果单独安装 taosX,需要 taosX 1.5.0 或以上版本。 -1. 需要安装 Grafana 插件 [TDengie Datasource v3.5.0](https://grafana.com/grafana/plugins/tdengine-datasource/) 或以上版本。 +2. 需要安装 Grafana 插件 [TDengie Datasource v3.5.0](https://grafana.com/grafana/plugins/tdengine-datasource/) 或以上版本。 ### 准备工作 -假设你已经部署好了 taosd,taosAdapter 和 taosAdapter。 那么还需要: -2. 启动 taosX 服务。 -3. 部署 Grafana ,安装 TDengine Datasource 插件,配置好数据源。 +假设你已经部署好了 taosd,taosAdapter 和 taosAdapter。 那么还需要: +1. 启动 taosX 服务。 +2. 部署 Grafana ,安装 TDengine Datasource 插件,配置好数据源。 ### 配置 taosX @@ -426,52 +99,50 @@ toasX 的配置文件(默认 /etc/taos/taosx.toml) 中与 monitor 相关的配 | port | --monitor-port | MONITOR_PORT | taosKeeper 服务的端口 | | 6043 | | interval | --monitor-interval | MONITTOR_INTERVAL | taosX 发送 metrics 数据到 taosKeeper 的时间间隔,单位秒 | 1-10 | 10 | -TDinsight for taosX - -"TDinsight for taosX" 专门为 taosX 监控创建的 Grafana 面板。使用前需要先导入这个面板。 - ### 基于 TDinsight 监控 tasoX +"TDinsight for taosX" 是专门为 taosX 监控创建的 Grafana 面板。使用前需要先导入这个面板。 + #### 进入面板 -1. 选择 TDengine Datasource - ![TDengine Datasource](./pic/monitor-01.jpg) -2. 点击 “Dashboard”, 选择 TDinsight for taosX 面板。(第一次使用需要先导入)。 - ![Dashboard](./pic/monitor-02.jpg) +1. 在 Grafana 界面菜单中点击 ”Data sources“, 然后选择已经配置好的 TDengine 数据源。 +2. 在数据源配置界面选择 “Dashboard” Tab, 然后导入 ”TDinsight for taosX“ 面板(第一次使用需要先导入)。 下面是一个示例图: + ![monitor rows](./pic/monitor-04.jpg) - 该面板每一行代表一个或一类监控对象。最上面是 taosX 监控行,然后是 Agent 监控行, 最后是各类数据写入任务的监控。 - :::note - 1. 如果打开这个面板后看不到任何数据,你很可能需要点击左上角的数据库列表(即 “Log from” 下拉菜单),切换到监控数据所在的数据库。 - 2. 数据库包含多少个 Agent 的数据就会自动创建多少个 Agent 行。(如上图) - - ::: + + 该面板每一行代表一个或一类监控对象。最上面是 taosX 监控行,然后是 Agent 监控行, 最后是各类数据写入任务的监控。 + :::note + - 如果打开这个面板后看不到任何数据,你很可能需要点击左上角的数据库列表(即 “Log from” 下拉菜单),切换到监控数据所在的数据库。 + - 数据库包含多少个 Agent 的数据就会自动创建多少个 Agent 行。(如上图) + ::: #### 监控示例 -1. taosX 监控示例 +1. taosX 监控示例图 -![monitor taosx](./pic/monitor-03.png) + ![monitor taosx](./pic/monitor-03.png) -2. Agent 监控示例 +2. Agent 监控示例图 -![monitor agent](./pic/monitor-09.jpg) + ![monitor agent](./pic/monitor-09.jpg) -3. TDengine2 数据源监控示例 +3. TDengine2 数据源监控示例图 -![monitor tdengine2](./pic/monitor-05.png) + ![monitor tdengine2](./pic/monitor-05.png) -:::info -监控面板只展示了数据写入任务的部分监控指标,在 Explorer 页面上有更全面的监控指标,且有每个指标的具体说明。 + :::info + 监控面板只展示了数据写入任务的部分监控指标,在 Explorer 页面上有更全面的监控指标,且有每个指标的具体说明。 -::: + ::: -3. TDengine3 数据源监控示例 -![monitor tdengine3](./pic/monitor-06.jpg) +4. TDengine3 数据源监控示例图 + + ![monitor tdengine3](./pic/monitor-06.jpg) -4. 其它数据源监控示例 -![monitor task](./pic/monitor-10.jpg) +5. 其它数据源监控示例图 + ![monitor task](./pic/monitor-10.jpg) #### 限制 diff --git a/docs/zh/07-operation/TDinsight-1-cluster-status.webp b/docs/zh/07-operation/TDinsight-1-cluster-status.webp new file mode 100644 index 0000000000..7f7792b2e9 Binary files /dev/null and b/docs/zh/07-operation/TDinsight-1-cluster-status.webp differ diff --git a/docs/zh/07-operation/tdinsight.png b/docs/zh/07-operation/tdinsight.png deleted file mode 100644 index 0d90950318..0000000000 Binary files a/docs/zh/07-operation/tdinsight.png and /dev/null differ diff --git a/docs/zh/14-reference/01-components/01-taosd.md b/docs/zh/14-reference/01-components/01-taosd.md index 0e9c4eb926..02cf2155a0 100644 --- a/docs/zh/14-reference/01-components/01-taosd.md +++ b/docs/zh/14-reference/01-components/01-taosd.md @@ -27,42 +27,42 @@ taosd 命令行参数如下 ### 连接相关 -| 参数名称 | 参数说明 | -|:-------------:|:----------------------------------------------------------------:| -|firstEp | taosd 启动时,主动连接的集群中首个 dnode 的 end point,缺省值:localhost:6030 | -|secondEp | taosd 启动时,如果 firstEp 连接不上,尝试连接集群中第二个 dnode 的 endpoint,缺省值:无| -|fqdn | 启动 taosd 后所监听的服务地址,缺省值:所在服务器上配置的第一个 hostname | -|serverPort | 启动 taosd 后所监听的端口,缺省值:6030 | -|maxShellConns | 一个 dnode 容许的连接数,取值范围为 10-5000000,缺省值:5000 | -|numOfRpcSessions | 允许一个客户端能创建的最大连接数,取值范围 100-100000,缺省值:30000 | -|timeToGetAvailableConn | 获得可用连接的最长等待时间,取值范围 10-50000000,单位为毫秒,缺省值:500000 | +| 参数名称 | 参数说明 | +| :--------------------: | :-------------------------------------------------------------------------------------: | +| firstEp | taosd 启动时,主动连接的集群中首个 dnode 的 end point,缺省值:localhost:6030 | +| secondEp | taosd 启动时,如果 firstEp 连接不上,尝试连接集群中第二个 dnode 的 endpoint,缺省值:无 | +| fqdn | 启动 taosd 后所监听的服务地址,缺省值:所在服务器上配置的第一个 hostname | +| serverPort | 启动 taosd 后所监听的端口,缺省值:6030 | +| maxShellConns | 一个 dnode 容许的连接数,取值范围为 10-5000000,缺省值:5000 | +| numOfRpcSessions | 允许一个客户端能创建的最大连接数,取值范围 100-100000,缺省值:30000 | +| timeToGetAvailableConn | 获得可用连接的最长等待时间,取值范围 10-50000000,单位为毫秒,缺省值:500000 | ### 监控相关 -| 参数名称 | 参数说明 | -|:-------------:|:----------------------------------------------------------------:| -|monitor | 是否收集监控数据并上报,0: 关闭;1:打开;缺省值:0 | -|monitorFqdn | taosKeeper 服务所在服务器的 FQDN,缺省值:无 | -|monitorPort | taosKeeper 服务所监听的端口号,缺省值:6043 | -|monitorInternal | 监控数据库记录系统参数(CPU/内存)的时间间隔,单位是秒,取值范围 1-200000 ,缺省值:30| -|telemetryReporting | 是否上传 telemetry,0: 不上传,1:上传,缺省值:1 | -|crashReporting | 是否上传 crash 信息;0: 不上传,1: 上传;缺省值: 1| +| 参数名称 | 参数说明 | +| :----------------: | :------------------------------------------------------------------------------------: | +| monitor | 是否收集监控数据并上报,0: 关闭;1:打开;缺省值:0 | +| monitorFqdn | taosKeeper 服务所在服务器的 FQDN,缺省值:无 | +| monitorPort | taosKeeper 服务所监听的端口号,缺省值:6043 | +| monitorInternal | 监控数据库记录系统参数(CPU/内存)的时间间隔,单位是秒,取值范围 1-200000 ,缺省值:30 | +| telemetryReporting | 是否上传 telemetry,0: 不上传,1:上传,缺省值:1 | +| crashReporting | 是否上传 crash 信息;0: 不上传,1: 上传;缺省值: 1 | ### 查询相关 -| 参数名称 | 参数说明 | -|:-------------:|:----------------------------------------------------------------:| -|queryPolicy | 查询策略,1: 只使用 vnode,不使用 qnode; 2: 没有扫描算子的子任务在 qnode 执行,带扫描算子的子任务在 vnode 执行; 3: vnode 只运行扫描算子,其余算子均在 qnode 执行 ;缺省值:1 | -|maxNumOfDistinctRes | 允许返回的 distinct 结果最大行数,默认值 10 万,最大允许值 1 亿 | -|countAlwaysReturnValue | ount/hyperloglog函数在输入数据为空或者NULL的情况下是否返回值,0: 返回空行,1: 返回;该参数设置为 1 时,如果查询中含有 INTERVAL 子句或者该查询使用了TSMA时, 且相应的组或窗口内数据为空或者NULL, 对应的组或窗口将不返回查询结果. 注意此参数客户端和服务端值应保持一致.| +| 参数名称 | 参数说明 | +| :--------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| queryPolicy | 查询策略,1: 只使用 vnode,不使用 qnode; 2: 没有扫描算子的子任务在 qnode 执行,带扫描算子的子任务在 vnode 执行; 3: vnode 只运行扫描算子,其余算子均在 qnode 执行 ;缺省值:1 | +| maxNumOfDistinctRes | 允许返回的 distinct 结果最大行数,默认值 10 万,最大允许值 1 亿 | +| countAlwaysReturnValue | ount/hyperloglog函数在输入数据为空或者NULL的情况下是否返回值,0: 返回空行,1: 返回;该参数设置为 1 时,如果查询中含有 INTERVAL 子句或者该查询使用了TSMA时, 且相应的组或窗口内数据为空或者NULL, 对应的组或窗口将不返回查询结果. 注意此参数客户端和服务端值应保持一致. | ### 区域相关 -| 参数名称 | 参数说明 | -|:-------------:|:----------------------------------------------------------------:| -|timezone | 时区,缺省值:当前服务器所配置的时区 | -|locale | 系统区位信息及编码格式 ,缺省值:系统中动态获取,如果自动获取失败,需要用户在配置文件设置或通过 API 设置 | -|charset | 字符集编码,缺省值:系统自动获取 | +| 参数名称 | 参数说明 | +| :------: | :------------------------------------------------------------------------------------------------------: | +| timezone | 时区,缺省值:当前服务器所配置的时区 | +| locale | 系统区位信息及编码格式 ,缺省值:系统中动态获取,如果自动获取失败,需要用户在配置文件设置或通过 API 设置 | +| charset | 字符集编码,缺省值:系统自动获取 | :::info 1. 为应对多时区的数据写入和查询问题,TDengine 采用 Unix 时间戳(Unix Timestamp)来记录和存储时间戳。Unix 时间戳的特点决定了任一时刻不论在任何时区,产生的时间戳均一致。需要注意的是,Unix 时间戳是在客户端完成转换和记录。为了确保客户端其他形式的时间转换为正确的 Unix 时间戳,需要设置正确的时区。 @@ -141,64 +141,64 @@ charset 的有效值是 UTF-8。 ### 存储相关 -| 参数名称 | 参数说明 | -|:-------------:|:----------------------------------------------------------------:| -|dataDir | 数据文件目录,所有的数据文件都将写入该目录,缺省值:/var/lib | -|tempDir | 指定所有系统运行过程中的临时文件生成的目录,缺省值:/tmp | -|minimalTmpDirGB | tempDir 所指定的临时文件目录所需要保留的最小空间,单位 GB,缺省值: 1| -|minimalDataDirGB | dataDir 指定的时序数据存储目录所需要保留的最小空间,单位 GB,缺省值: 2 | +| 参数名称 | 参数说明 | +| :--------------: | :--------------------------------------------------------------------: | +| dataDir | 数据文件目录,所有的数据文件都将写入该目录,缺省值:/var/lib | +| tempDir | 指定所有系统运行过程中的临时文件生成的目录,缺省值:/tmp | +| minimalTmpDirGB | tempDir 所指定的临时文件目录所需要保留的最小空间,单位 GB,缺省值: 1 | +| minimalDataDirGB | dataDir 指定的时序数据存储目录所需要保留的最小空间,单位 GB,缺省值: 2 | ### 集群相关 -| 参数名称 | 参数说明 | -|:-------------:|:----------------------------------------------------------------:| -|supportVnodes | dnode 支持的最大 vnode 数目,取值范围:0-4096,缺省值: CPU 核数的 2 倍 + 5 | +| 参数名称 | 参数说明 | +| :-----------: | :-------------------------------------------------------------------------: | +| supportVnodes | dnode 支持的最大 vnode 数目,取值范围:0-4096,缺省值: CPU 核数的 2 倍 + 5 | ### 性能调优 -| 参数名称 | 参数说明 | -|:-------------:|:----------------------------------------------------------------:| -|numOfCommitThreads | 写入线程的最大数量,取值范围 0-1024,缺省值为 4 | +| 参数名称 | 参数说明 | +| :----------------: | :---------------------------------------------: | +| numOfCommitThreads | 写入线程的最大数量,取值范围 0-1024,缺省值为 4 | ### 日志相关 -| 参数名称 | 参数说明 | -|:-------------:|:----------------------------------------------------------------:| -|logDir | 日志文件目录,运行日志将写入该目录,缺省值:/var/log/taos | -|minimalLogDirGB | 当日志文件夹所在磁盘可用空间大小小于该值时,停止写日志,单位GB,缺省值:1| -|numOfLogLines | 单个日志文件允许的最大行数,缺省值:10,000,000 | -|asyncLog | 日志写入模式,0: 同步,1: 异步,缺省值: 1 | -|logKeepDays | 日志文件的最长保存时间 ,单位:天,缺省值:0,意味着无限保存;当设置为大于0 的值时,日志文件会被重命名为 taosdlog.xxx,其中 xxx 为日志文件最后修改的时间戳。 | -|slowLogThreshold | 慢查询门限值,大于等于门限值认为是慢查询,单位秒,默认值: 3 | -|slowLogScope | 定启动记录哪些类型的慢查询,可选值:ALL, QUERY, INSERT, OHTERS, NONE; 默认值:ALL | -|debugFlag | 运行日志开关,131(输出错误和警告日志),135(输出错误、警告和调试日志),143(输出错误、警告、调试和跟踪日志); 默认值:131 或 135 (取决于不同模块)| -|tmrDebugFlag | 定时器模块的日志开关,取值范围同上 | -|uDebugFlag | 共用功能模块的日志开关,取值范围同上 | -|rpcDebugFlag | rpc 模块的日志开关,取值范围同上 | -|jniDebugFlag | jni 模块的日志开关,取值范围同上 | -|qDebugFlag | query 模块的日志开关,取值范围同上 | -|dDebugFlag | dnode 模块的日志开关,取值范围同上,缺省值 135 | -|vDebugFlag | vnode 模块的日志开关,取值范围同上 | -|mDebugFlag | mnode 模块的日志开关,取值范围同上 | -|wDebugFlag | wal 模块的日志开关,取值范围同上 | -|sDebugFlag | sync 模块的日志开关,取值范围同上 | -|tsdbDebugFlag | tsdb 模块的日志开关,取值范围同上 | -|tqDebugFlag | tq 模块的日志开关,取值范围同上 | -|fsDebugFlag | fs 模块的日志开关,取值范围同上 | -|udfDebugFlag | udf 模块的日志开关,取值范围同上 | -|smaDebugFlag | sma 模块的日志开关,取值范围同上 | -|idxDebugFlag | index 模块的日志开关,取值范围同上 | -|tdbDebugFlag | tdb 模块的日志开关,取值范围同上 | +| 参数名称 | 参数说明 | +| :--------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------: | +| logDir | 日志文件目录,运行日志将写入该目录,缺省值:/var/log/taos | +| minimalLogDirGB | 当日志文件夹所在磁盘可用空间大小小于该值时,停止写日志,单位GB,缺省值:1 | +| numOfLogLines | 单个日志文件允许的最大行数,缺省值:10,000,000 | +| asyncLog | 日志写入模式,0: 同步,1: 异步,缺省值: 1 | +| logKeepDays | 日志文件的最长保存时间 ,单位:天,缺省值:0,意味着无限保存;当设置为大于0 的值时,日志文件会被重命名为 taosdlog.xxx,其中 xxx 为日志文件最后修改的时间戳。 | +| slowLogThreshold | 慢查询门限值,大于等于门限值认为是慢查询,单位秒,默认值: 3 | +| slowLogScope | 定启动记录哪些类型的慢查询,可选值:ALL, QUERY, INSERT, OHTERS, NONE; 默认值:ALL | +| debugFlag | 运行日志开关,131(输出错误和警告日志),135(输出错误、警告和调试日志),143(输出错误、警告、调试和跟踪日志); 默认值:131 或 135 (取决于不同模块) | +| tmrDebugFlag | 定时器模块的日志开关,取值范围同上 | +| uDebugFlag | 共用功能模块的日志开关,取值范围同上 | +| rpcDebugFlag | rpc 模块的日志开关,取值范围同上 | +| jniDebugFlag | jni 模块的日志开关,取值范围同上 | +| qDebugFlag | query 模块的日志开关,取值范围同上 | +| dDebugFlag | dnode 模块的日志开关,取值范围同上,缺省值 135 | +| vDebugFlag | vnode 模块的日志开关,取值范围同上 | +| mDebugFlag | mnode 模块的日志开关,取值范围同上 | +| wDebugFlag | wal 模块的日志开关,取值范围同上 | +| sDebugFlag | sync 模块的日志开关,取值范围同上 | +| tsdbDebugFlag | tsdb 模块的日志开关,取值范围同上 | +| tqDebugFlag | tq 模块的日志开关,取值范围同上 | +| fsDebugFlag | fs 模块的日志开关,取值范围同上 | +| udfDebugFlag | udf 模块的日志开关,取值范围同上 | +| smaDebugFlag | sma 模块的日志开关,取值范围同上 | +| idxDebugFlag | index 模块的日志开关,取值范围同上 | +| tdbDebugFlag | tdb 模块的日志开关,取值范围同上 | ### 压缩参数 -| 参数名称 | 参数说明 | -|:-------------:|:----------------------------------------------------------------:| -| compressMsgSize | 是否对 RPC 消息进行压缩;-1: 所有消息都不压缩; 0: 所有消息都压缩; N (N>0): 只有大于 N 个字节的消息才压缩;缺省值 -1 | -| fPrecision | 设置 float 类型浮点数压缩精度 ,取值范围:0.1 ~ 0.00000001 ,默认值 0.00000001 , 小于此值的浮点数尾数部分将被截断 | -|dPrecision | 设置 double 类型浮点数压缩精度 , 取值范围:0.1 ~ 0.0000000000000001 , 缺省值 0.0000000000000001 , 小于此值的浮点数尾数部分将被截取 | -|lossyColumn | 对 float 和/或 double 类型启用 TSZ 有损压缩;取值范围: float, double, none;缺省值: none,表示关闭无损压缩 | -|ifAdtFse | 在启用 TSZ 有损压缩时,使用 FSE 算法替换 HUFFMAN 算法, FSE 算法压缩速度更快,但解压稍慢,追求压缩速度可选用此算法; 0: 关闭,1:打开;默认值为 0 | +| 参数名称 | 参数说明 | +| :-------------: | :----------------------------------------------------------------------------------------------------------------------------------------------: | +| compressMsgSize | 是否对 RPC 消息进行压缩;-1: 所有消息都不压缩; 0: 所有消息都压缩; N (N>0): 只有大于 N 个字节的消息才压缩;缺省值 -1 | +| fPrecision | 设置 float 类型浮点数压缩精度 ,取值范围:0.1 ~ 0.00000001 ,默认值 0.00000001 , 小于此值的浮点数尾数部分将被截断 | +| dPrecision | 设置 double 类型浮点数压缩精度 , 取值范围:0.1 ~ 0.0000000000000001 , 缺省值 0.0000000000000001 , 小于此值的浮点数尾数部分将被截取 | +| lossyColumn | 对 float 和/或 double 类型启用 TSZ 有损压缩;取值范围: float, double, none;缺省值: none,表示关闭无损压缩 | +| ifAdtFse | 在启用 TSZ 有损压缩时,使用 FSE 算法替换 HUFFMAN 算法, FSE 算法压缩速度更快,但解压稍慢,追求压缩速度可选用此算法; 0: 关闭,1:打开;默认值为 0 | **补充说明** @@ -216,10 +216,214 @@ lossyColumns float|double ### 其他参数 -| 参数名称 | 参数说明 | -|:-------------:|:----------------------------------------------------------------:| -|enableCoreFile | crash 时是否生成 core 文件;0: 不生成,1:生成;默认值 为 1; 不同的启动方式,生成 core 文件的目录如下:1、systemctl start taosd 启动:生成的 core 在根目录下
2、手动启动,就在 taosd 执行目录下。| -|udf | 是否启动 UDF 服务;0: 不启动,1:启动;默认值 为 0 | -|ttlChangeOnWrite | ttl 到期时间是否伴随表的修改操作改变; 0: 不改变,1:改变 ;默认值 为 | -| tmqMaxTopicNum| 订阅最多可建立的 topic 数量; 取值范围 1-10000;缺省值 为20 | -|maxTsmaNum | 集群内可创建的TSMA个数;取值范围:0-3;缺省值: 3| \ No newline at end of file +| 参数名称 | 参数说明 | +| :--------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| enableCoreFile | crash 时是否生成 core 文件;0: 不生成,1:生成;默认值 为 1; 不同的启动方式,生成 core 文件的目录如下:1、systemctl start taosd 启动:生成的 core 在根目录下
2、手动启动,就在 taosd 执行目录下。 | +| udf | 是否启动 UDF 服务;0: 不启动,1:启动;默认值 为 0 | +| ttlChangeOnWrite | ttl 到期时间是否伴随表的修改操作改变; 0: 不改变,1:改变 ;默认值 为 | +| tmqMaxTopicNum | 订阅最多可建立的 topic 数量; 取值范围 1-10000;缺省值 为20 | +| maxTsmaNum | 集群内可创建的TSMA个数;取值范围:0-3;缺省值: 3 | + + +## taosd 监控指标 + +taosd 会将监控指标上报给 taosKeeper,这些监控指标会被 taosKeeper 写入监控数据库,默认是 `log` 库,可以在 taoskeeper 配置文件中修改。以下是这些监控指标的详细介绍。 + +### taosd\_cluster\_basic 表 + +`taosd_cluster_basic` 表记录集群基础信息。 + +| field | type | is\_tag | comment | +| :------------------- | :-------- | :------ | :------------------------------ | +| ts | TIMESTAMP | | timestamp | +| first\_ep | VARCHAR | | 集群 first ep | +| first\_ep\_dnode\_id | INT | | 集群 first ep 的 dnode id | +| cluster_version | VARCHAR | | tdengine version。例如:3.0.4.0 | +| cluster\_id | VARCHAR | TAG | cluster id | + +### taosd\_cluster\_info 表 + +`taosd_cluster_info` 表记录集群信息。 + +| field | type | is\_tag | comment | +| :----------------------- | :-------- | :------ | :----------------------------------------------- | +| \_ts | TIMESTAMP | | timestamp | +| cluster_uptime | DOUBLE | | 当前 master 节点的uptime。单位:秒 | +| dbs\_total | DOUBLE | | database 总数 | +| tbs\_total | DOUBLE | | 当前集群 table 总数 | +| stbs\_total | DOUBLE | | 当前集群 stable 总数 | +| dnodes\_total | DOUBLE | | 当前集群 dnode 总数 | +| dnodes\_alive | DOUBLE | | 当前集群 dnode 存活总数 | +| mnodes\_total | DOUBLE | | 当前集群 mnode 总数 | +| mnodes\_alive | DOUBLE | | 当前集群 mnode 存活总数 | +| vgroups\_total | DOUBLE | | 当前集群 vgroup 总数 | +| vgroups\_alive | DOUBLE | | 当前集群 vgroup 存活总数 | +| vnodes\_total | DOUBLE | | 当前集群 vnode 总数 | +| vnodes\_alive | DOUBLE | | 当前集群 vnode 存活总数 | +| connections\_total | DOUBLE | | 当前集群连接总数 | +| topics\_total | DOUBLE | | 当前集群 topic 总数 | +| streams\_total | DOUBLE | | 当前集群 stream 总数 | +| grants_expire\_time | DOUBLE | | 认证过期时间,企业版有效,社区版为 DOUBLE 最大值 | +| grants_timeseries\_used | DOUBLE | | 已用测点数 | +| grants_timeseries\_total | DOUBLE | | 总测点数,开源版本为 DOUBLE 最大值 | +| cluster\_id | VARCHAR | TAG | cluster id | + +### taosd\_vgroups\_info 表 + +`taosd_vgroups_info` 表记录虚拟节点组信息。 + +| field | type | is\_tag | comment | +| :------------- | :-------- | :------ | :--------------------------------------------- | +| \_ts | TIMESTAMP | | timestamp | +| tables\_num | DOUBLE | | vgroup 中 table 数量 | +| status | DOUBLE | | vgroup 状态, 取值范围:unsynced = 0, ready = 1 | +| vgroup\_id | VARCHAR | TAG | vgroup id | +| database\_name | VARCHAR | TAG | vgroup 所属的 database 名字 | +| cluster\_id | VARCHAR | TAG | cluster id | + +### taosd\_dnodes\_info 表 + +`taosd_dnodes_info` 记录 dnode 信息。 + +| field | type | is\_tag | comment | +| :---------------- | :-------- | :------ | :------------------------------------------------------------------------------------------------ | +| \_ts | TIMESTAMP | | timestamp | +| uptime | DOUBLE | | dnode uptime,单位:秒 | +| cpu\_engine | DOUBLE | | taosd cpu 使用率,从 `/proc//stat` 读取 | +| cpu\_system | DOUBLE | | 服务器 cpu 使用率,从 `/proc/stat` 读取 | +| cpu\_cores | DOUBLE | | 服务器 cpu 核数 | +| mem\_engine | DOUBLE | | taosd 内存使用率,从 `/proc//status` 读取 | +| mem\_free | DOUBLE | | 服务器可用内存,单位 KB | +| mem\_total | DOUBLE | | 服务器内存总量,单位 KB | +| disk\_used | DOUBLE | | data dir 挂载的磁盘使用量,单位 bytes | +| disk\_total | DOUBLE | | data dir 挂载的磁盘总容量,单位 bytes | +| system\_net\_in | DOUBLE | | 网络吞吐率,从 `/proc/net/dev` 中读取的 received bytes。单位 byte/s | +| system\_net\_out | DOUBLE | | 网络吞吐率,从 `/proc/net/dev` 中读取的 transmit bytes。单位 byte/s | +| io\_read | DOUBLE | | io 吞吐率,从 `/proc//io` 中读取的 rchar 与上次数值计算之后,计算得到速度。单位 byte/s | +| io\_write | DOUBLE | | io 吞吐率,从 `/proc//io` 中读取的 wchar 与上次数值计算之后,计算得到速度。单位 byte/s | +| io\_read\_disk | DOUBLE | | 磁盘 io 吞吐率,从 `/proc//io` 中读取的 read_bytes。单位 byte/s | +| io\_write\_disk | DOUBLE | | 磁盘 io 吞吐率,从 `/proc//io` 中读取的 write_bytes。单位 byte/s | +| vnodes\_num | DOUBLE | | dnode 上 vnodes 数量 | +| masters | DOUBLE | | dnode 上 master node 数量 | +| has\_mnode | DOUBLE | | dnode 是否包含 mnode,取值范围:包含=1,不包含=0 | +| has\_qnode | DOUBLE | | dnode 是否包含 qnode,取值范围:包含=1,不包含=0 | +| has\_snode | DOUBLE | | dnode 是否包含 snode,取值范围:包含=1,不包含=0 | +| has\_bnode | DOUBLE | | dnode 是否包含 bnode,取值范围:包含=1,不包含=0 | +| error\_log\_count | DOUBLE | | error 总数 | +| info\_log\_count | DOUBLE | | info 总数 | +| debug\_log\_count | DOUBLE | | debug 总数 | +| trace\_log\_count | DOUBLE | | trace 总数 | +| dnode\_id | VARCHAR | TAG | dnode id | +| dnode\_ep | VARCHAR | TAG | dnode endpoint | +| cluster\_id | VARCHAR | TAG | cluster id | + +### taosd\_dnodes\_status 表 + +`taosd_dnodes_status` 表记录 dnode 状态信息。 + +| field | type | is\_tag | comment | +| :---------- | :-------- | :------ | :--------------------------------------- | +| \_ts | TIMESTAMP | | timestamp | +| status | DOUBLE | | dnode 状态,取值范围:ready=1,offline =0 | +| dnode\_id | VARCHAR | TAG | dnode id | +| dnode\_ep | VARCHAR | TAG | dnode endpoint | +| cluster\_id | VARCHAR | TAG | cluster id | + +### taosd\_dnodes\_log\_dir 表 + +`taosd_dnodes_log_dir` 表记录 log 目录信息。 + +| field | type | is\_tag | comment | +| :---------- | :-------- | :------ | :---------------------------------- | +| \_ts | TIMESTAMP | | timestamp | +| avail | DOUBLE | | log 目录可用空间。单位 byte | +| used | DOUBLE | | log 目录已使用空间。单位 byte | +| total | DOUBLE | | log 目录空间。单位 byte | +| name | VARCHAR | TAG | log 目录名,一般为 `/var/log/taos/` | +| dnode\_id | VARCHAR | TAG | dnode id | +| dnode\_ep | VARCHAR | TAG | dnode endpoint | +| cluster\_id | VARCHAR | TAG | cluster id | + +### taosd\_dnodes\_data\_dir 表 + +`taosd_dnodes_data_dir` 表记录 data 目录信息。 + +| field | type | is\_tag | comment | +| :---------- | :-------- | :------ | :-------------------------------- | +| \_ts | TIMESTAMP | | timestamp | +| avail | DOUBLE | | data 目录可用空间。单位 byte | +| used | DOUBLE | | data 目录已使用空间。单位 byte | +| total | DOUBLE | | data 目录空间。单位 byte | +| level | VARCHAR | TAG | 0、1、2 多级存储级别 | +| name | VARCHAR | TAG | data 目录,一般为 `/var/lib/taos` | +| dnode\_id | VARCHAR | TAG | dnode id | +| dnode\_ep | VARCHAR | TAG | dnode endpoint | +| cluster\_id | VARCHAR | TAG | cluster id | + +### taosd\_mnodes\_info 表 + +`taosd_mnodes_info` 表记录 mnode 角色信息。 + +| field | type | is\_tag | comment | +| :---------- | :-------- | :------ | :------------------------------------------------------------------------------------------------------- | +| \_ts | TIMESTAMP | | timestamp | +| role | DOUBLE | | mnode 角色, 取值范围:offline = 0,follower = 100,candidate = 101,leader = 102,error = 103,learner = 104 | +| mnode\_id | VARCHAR | TAG | master node id | +| mnode\_ep | VARCHAR | TAG | master node endpoint | +| cluster\_id | VARCHAR | TAG | cluster id | + +### taosd\_vnodes\_role 表 + +`taosd_vnodes_role` 表记录虚拟节点角色信息。 + +| field | type | is\_tag | comment | +| :------------- | :-------- | :------ | :------------------------------------------------------------------------------------------------------ | +| \_ts | TIMESTAMP | | timestamp | +| vnode\_role | DOUBLE | | vnode 角色,取值范围:offline = 0,follower = 100,candidate = 101,leader = 102,error = 103,learner = 104 | +| vgroup\_id | VARCHAR | TAG | dnode id | +| dnode\_id | VARCHAR | TAG | dnode id | +| database\_name | VARCHAR | TAG | vgroup 所属的 database 名字 | +| cluster\_id | VARCHAR | TAG | cluster id | + +### taosd\_sql\_req 表 + +`taosd_sql_req` 记录服务端 sql 请求信息。 + +| field | type | is\_tag | comment | +| :---------- | :-------- | :------ | :--------------------------------------- | +| \_ts | TIMESTAMP | | timestamp | +| count | DOUBLE | | sql 数量 | +| result | VARCHAR | TAG | sql的执行结果,取值范围:Success, Failed | +| username | VARCHAR | TAG | 执行sql的user name | +| sql\_type | VARCHAR | TAG | sql类型,取值范围:inserted_rows | +| dnode\_id | VARCHAR | TAG | dnode id | +| dnode\_ep | VARCHAR | TAG | dnode endpoint | +| vgroup\_id | VARCHAR | TAG | dnode id | +| cluster\_id | VARCHAR | TAG | cluster id | + +### taos\_sql\_req 表 + +`taos_sql_req` 记录客户端 sql 请求信息。 + +| field | type | is\_tag | comment | +| :---------- | :-------- | :------ | :---------------------------------------- | +| \_ts | TIMESTAMP | | timestamp | +| count | DOUBLE | | sql 数量 | +| result | VARCHAR | TAG | sql的执行结果,取值范围:Success, Failed | +| username | VARCHAR | TAG | 执行sql的user name | +| sql\_type | VARCHAR | TAG | sql类型,取值范围:select, insert,delete | +| cluster\_id | VARCHAR | TAG | cluster id | + +### taos\_slow\_sql 表 + +`taos_slow_sql` 记录客户端慢查询信息。 + +| field | type | is\_tag | comment | +| :---------- | :-------- | :------ | :---------------------------------------------------- | +| \_ts | TIMESTAMP | | timestamp | +| count | DOUBLE | | sql 数量 | +| result | VARCHAR | TAG | sql的执行结果,取值范围:Success, Failed | +| username | VARCHAR | TAG | 执行sql的user name | +| duration | VARCHAR | TAG | sql执行耗时,取值范围:3-10s,10-100s,100-1000s,1000s- | +| cluster\_id | VARCHAR | TAG | cluster id | + diff --git a/docs/zh/14-reference/01-components/03-taosadapter.md b/docs/zh/14-reference/01-components/03-taosadapter.md index 06ba4a9a07..84facad093 100644 --- a/docs/zh/14-reference/01-components/03-taosadapter.md +++ b/docs/zh/14-reference/01-components/03-taosadapter.md @@ -289,31 +289,32 @@ http 返回内容: ## taosAdapter 监控指标 -taosAdapter 采集 http 相关指标、CPU 百分比和内存百分比。 +taosAdapter 采集 REST/Websocket 相关请求的监控指标。将监控指标上报给 taosKeeper,这些监控指标会被 taosKeeper 写入监控数据库,默认是 `log` 库,可以在 taoskeeper 配置文件中修改。以下是这些监控指标的详细介绍。 -### http 接口 +#### adapter\_requests 表 -提供符合 [OpenMetrics](https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md) 接口: +`adapter_requests` 记录 taosadapter 监控数据。 -```text -http://:6041/metrics -``` - -### 写入 TDengine - -taosAdapter 支持将 http 监控、CPU 百分比和内存百分比写入 TDengine。 - -有关配置参数 - -| **配置项** | **描述** | **默认值** | -|-------------------------|--------------------------------------------|----------| -| monitor.collectDuration | CPU 和内存采集间隔 | 3s | -| monitor.identity | 当前taosadapter 的标识符如果不设置将使用 'hostname:port' | | -| monitor.incgroup | 是否是 cgroup 中运行(容器中运行设置为 true) | false | -| monitor.writeToTD | 是否写入到 TDengine | false | -| monitor.user | TDengine 连接用户名 | root | -| monitor.password | TDengine 连接密码 | taosdata | -| monitor.writeInterval | 写入TDengine 间隔 | 30s | +| field | type | is\_tag | comment | +| :----------------- | :----------- | :------ | :---------------------------------- | +| ts | TIMESTAMP | | timestamp | +| total | INT UNSIGNED | | 总请求数 | +| query | INT UNSIGNED | | 查询请求数 | +| write | INT UNSIGNED | | 写入请求数 | +| other | INT UNSIGNED | | 其他请求数 | +| in\_process | INT UNSIGNED | | 正在处理请求数 | +| success | INT UNSIGNED | | 成功请求数 | +| fail | INT UNSIGNED | | 失败请求数 | +| query\_success | INT UNSIGNED | | 查询成功请求数 | +| query\_fail | INT UNSIGNED | | 查询失败请求数 | +| write\_success | INT UNSIGNED | | 写入成功请求数 | +| write\_fail | INT UNSIGNED | | 写入失败请求数 | +| other\_success | INT UNSIGNED | | 其他成功请求数 | +| other\_fail | INT UNSIGNED | | 其他失败请求数 | +| query\_in\_process | INT UNSIGNED | | 正在处理查询请求数 | +| write\_in\_process | INT UNSIGNED | | 正在处理写入请求数 | +| endpoint | VARCHAR | | 请求端点 | +| req\_type | NCHAR | TAG | 请求类型:0 为 REST,1 为 Websocket | ## 结果返回条数限制 @@ -342,11 +343,11 @@ taosAdapter 从 3.0.4.0 版本开始,提供参数 `smlAutoCreateDB` 来控制 在 TDengine server 2.2.x.x 或更早期版本中,taosd 进程包含一个内嵌的 http 服务。如前面所述,taosAdapter 是一个使用 systemd 管理的独立软件,拥有自己的进程。并且两者有一些配置参数和行为是不同的,请见下表: -| **#** | **embedded httpd** | **taosAdapter** | **comment** | -|-------|---------------------|-------------------------------|------------------------------------------------------------------------------------------------| -| 1 | httpEnableRecordSql | --logLevel=debug | | -| 2 | httpMaxThreads | n/a | taosAdapter 自动管理线程池,无需此参数 | +| **#** | **embedded httpd** | **taosAdapter** | **comment** | +| ----- | ------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ | +| 1 | httpEnableRecordSql | --logLevel=debug | | +| 2 | httpMaxThreads | n/a | taosAdapter 自动管理线程池,无需此参数 | | 3 | telegrafUseFieldNum | 请参考 taosAdapter telegraf 配置方法 | -| 4 | restfulRowLimit | restfulRowLimit | 内嵌 httpd 默认输出 10240 行数据,最大允许值为 102400。taosAdapter 也提供 restfulRowLimit 但是默认不做限制。您可以根据实际场景需求进行配置 | -| 5 | httpDebugFlag | 不适用 | httpdDebugFlag 对 taosAdapter 不起作用 | -| 6 | httpDBNameMandatory | 不适用 | taosAdapter 要求 URL 中必须指定数据库名 | +| 4 | restfulRowLimit | restfulRowLimit | 内嵌 httpd 默认输出 10240 行数据,最大允许值为 102400。taosAdapter 也提供 restfulRowLimit 但是默认不做限制。您可以根据实际场景需求进行配置 | +| 5 | httpDebugFlag | 不适用 | httpdDebugFlag 对 taosAdapter 不起作用 | +| 6 | httpDBNameMandatory | 不适用 | taosAdapter 要求 URL 中必须指定数据库名 | diff --git a/docs/zh/14-reference/01-components/04-taosx.md b/docs/zh/14-reference/01-components/04-taosx.md index 166b6ea760..e378c18800 100644 --- a/docs/zh/14-reference/01-components/04-taosx.md +++ b/docs/zh/14-reference/01-components/04-taosx.md @@ -90,11 +90,11 @@ taosx privileges -i ./user-pass-privileges-backup.json -t "taos:///" 可用参数列表: -| 参数 | 说明 | -| ---- | ---- | -| -u | 包含用户基本信息(密码、是否启用等) | -| -p | 包含权限信息 | -| -w | 包含白名单信息 | +| 参数 | 说明 | +| ---- | ------------------------------------ | +| -u | 包含用户基本信息(密码、是否启用等) | +| -p | 包含权限信息 | +| -w | 包含白名单信息 | 当 `-u`/`-p` 参数应用时,将仅包含指定的信息,不带参数时,表示所有信息(用户名、密码、权限和白名单)。 @@ -324,4 +324,131 @@ Linux 下 `journalctl` 查看日志的命令如下: ```bash journalctl -u taosx [-f] -``` \ No newline at end of file +``` + +## taosX 监控指标 + +taosX 会将监控指标上报给 taosKeeper,这些监控指标会被 taosKeeper 写入监控数据库,默认是 `log` 库,可以在 taoskeeper 配置文件中修改。以下是这些监控指标的详细介绍。 + +### taosX 服务 + +| 字段 | 描述 | +| -------------------------- | ----------------------------------------------------------------------------- | +| sys_cpu_cores | 系统 CPU 核数 | +| sys_total_memory | 系统总内存,单位:字节 | +| sys_used_memory | 系统已用内存, 单位:字节 | +| sys_available_memory | 系统可用内存, 单位:字节 | +| process_uptime | taosX 运行时长,单位:秒 | +| process_id | taosX 进程 ID | +| running_tasks | taosX 当前执行任务数 | +| completed_tasks | taosX 进程在一个监控周期(比如10s)内完成的任务数 | +| failed_tasks | taosX 进程在一个监控周期(比如10s)内失败的任务数 | +| process_cpu_percent | taosX 进程占用 CPU 百分比, 单位 % | +| process_memory_percent | taosX 进程占用内存百分比, 单位 % | +| process_disk_read_bytes | taosX 进程在一个监控周期(比如10s)内从硬盘读取的字节数的平均值,单位 bytes/s | +| process_disk_written_bytes | taosX 进程在一个监控周期(比如10s)内写到硬盘的字节数的平均值,单位 bytres/s | + + +### Agent + +| 字段 | 描述 | +| -------------------------- | ----------------------------------------------------------------------------- | +| sys_cpu_cores | 系统 CPU 核数 | +| sys_total_memory | 系统总内存,单位:字节 | +| sys_used_memory | 系统已用内存, 单位:字节 | +| sys_available_memory | 系统可用内存, 单位:字节 | +| process_uptime | agent 运行时长,单位:秒 | +| process_id | agent 进程 id | +| process_cpu_percent | agent 进程占用 CPU 百分比 | +| process_memory_percent | agent 进程占用内存百分比 | +| process_uptime | 进程启动时间,单位秒 | +| process_disk_read_bytes | agent 进程在一个监控周期(比如10s)内从硬盘读取的字节数的平均值,单位 bytes/s | +| process_disk_written_bytes | agent 进程在一个监控周期(比如10s)内写到硬盘的字节数的平均值,单位 bytes/s | + +### Connector + +| 字段 | 描述 | +| -------------------------- | --------------------------------------------------------------------------------- | +| process_id | connector 进程 id | +| process_uptime | 进程启动时间,单位秒 | +| process_cpu_percent | 进程占用 CPU 百分比, 单位 % | +| process_memory_percent | 进程占用内存百分比, 单位 % | +| process_disk_read_bytes | connector 进程在一个监控周期(比如10s)内从硬盘读取的字节数的平均值,单位 bytes/s | +| process_disk_written_bytes | connector 进程在一个监控周期(比如10s)内写到硬盘的字节数的平均值,单位 bytes/s | + +### taosX 通用数据源任务 + +| 字段 | 描述 | +| -------------------- | --------------------------------------------------------------- | +| total_execute_time | 任务累计运行时间,单位毫秒 | +| total_written_rowsls | 成功写入 TDengine 的总行数(包括重复记录) | +| total_written_points | 累计写入成功点数 (等于数据块包含的行数乘以数据块包含的列数) | +| start_time | 任务启动时间 (每次重启任务会被重置) | +| written_rows | 本次运行此任务成功写入 TDengine 的总行数(包括重复记录) | +| written_points | 本次运行写入成功点数 (等于数据块包含的行数乘以数据块包含的列数) | +| execute_time | 任务本次运行时间,单位秒 | + +### taosX TDengine V2 任务 + +| 字段 | 描述 | +| --------------------- | -------------------------------------------------------------------- | +| read_concurrency | 并发读取数据源的数据 worker 数, 也等于并发写入 TDengine 的 worker 数 | +| total_stables | 需要迁移的超级表数据数量 | +| total_updated_tags | 累计更新 tag 数 | +| total_created_tables | 累计创建子表数 | +| total_tables | 需要迁移的子表数量 | +| total_finished_tables | 完成数据迁移的子表数 (任务中断重启可能大于实际值) | +| total_success_blocks | 累计写入成功的数据块数 | +| finished_tables | 本次运行完成迁移子表数 | +| success_blocks | 本次写入成功的数据块数 | +| created_tables | 本次运行创建子表数 | +| updated_tags | 本次运行更新 tag 数 | + +### taosX TDengine V3 任务 + +| 字段 | 描述 | +| ---------------------- | ------------------------------------------------------- | +| total_messages | 通过 TMQ 累计收到的消息总数 | +| total_messages_of_meta | 通过 TMQ 累计收到的 Meta 类型的消息总数 | +| total_messages_of_data | 通过 TMQ 累计收到的 Data 和 MetaData 类型的消息总数 | +| total_write_raw_fails | 累计写入 raw meta 失败的次数 | +| total_success_blocks | 累计写入成功的数据块数 | +| topics | 通过 TMQ 订阅的主题数 | +| consumers | TMQ 消费者数 | +| messages | 本次运行通过 TMQ 收到的消息总数 | +| messages_of_meta | 本次运行通过 TMQ 收到的 Meta 类型的消息总数 | +| messages_of_data | 本次运行通过 TMQ 收到的 Data 和 MetaData 类型的消息总数 | +| write_raw_fails | 本次运行写入 raw meta 失败的次数 | +| success_blocks | 本次写入成功的数据块数 | + + +### taosX 其他数据源 任务 + +这些数据源包括: InfluxDB,OpenTSDB,OPC UA,OPC DA,PI,CSV,MQTT,AVEVA Historian 和 Kafka。 + +| 字段 | 描述 | +| ----------------------- | ----------------------------------------------------------- | +| total_received_batches | 通过 IPC Stream 收到的数据总批数 | +| total_processed_batches | 已经处理的批数 | +| total_processed_rows | 已经处理的总行数(等于每批包含数据行数之和) | +| total_inserted_sqls | 执行的 INSERT SQL 总条数 | +| total_failed_sqls | 执行失败的 INSERT SQL 总条数 | +| total_created_stables | 创建的超级表总数(可能大于实际值) | +| total_created_tables | 尝试创建子表总数(可能大于实际值) | +| total_failed_rows | 写入失败的总行数 | +| total_failed_point | 写入失败的总点数 | +| total_written_blocks | 写入成功的 raw block 总数 | +| total_failed_blocks | 写入失败的 raw block 总数 | +| received_batches | 本次运行此任务通过 IPC Stream 收到的数据总批数 | +| processed_batches | 本次运行已处理批数 | +| processed_rows | 本次处理的总行数(等于包含数据的 batch 包含的数据行数之和) | +| received_records | 本次运行此任务通过 IPC Stream 收到的数据总行数 | +| inserted_sqls | 本次运行此任务执行的 INSERT SQL 总条数 | +| failed_sqls | 本次运行此任务执行失败的 INSERT SQL 总条数 | +| created_stables | 本次运行此任务尝试创建超级表数(可能大于实际值) | +| created_tables | 本次运行此任务尝试创建子表数(可能大于实际值) | +| failed_rows | 本次运行此任务写入失败的行数 | +| failed_points | 本次运行此任务写入失败的点数 | +| written_blocks | 本次运行此任务写人成功的 raw block 数 | +| failed_blocks | 本次运行此任务写入失败的 raw block 数 | + diff --git a/docs/zh/14-reference/01-components/06-taoskeeper.md b/docs/zh/14-reference/01-components/06-taoskeeper.md index a1297230c4..2877728077 100644 --- a/docs/zh/14-reference/01-components/06-taoskeeper.md +++ b/docs/zh/14-reference/01-components/06-taoskeeper.md @@ -317,3 +317,19 @@ scrape_configs: 在 Grafana Dashboard 菜单点击 `import`,dashboard ID 填写 `18587`,点击 `Load` 按钮即可导入 `TaosKeeper Prometheus Dashboard for 3.x` dashboard。 + + +## taosKeeper 监控指标 + +taosKeeper 也会将自己采集的监控数据写入监控数据库,默认是 `log` 库,可以在 taoskeeper 配置文件中修改。 + +### keeper\_monitor 表 + +`keeper_monitor` 记录 taoskeeper 监控数据。 + +| field | type | is\_tag | comment | +| :------- | :-------- | :------ | :----------- | +| ts | TIMESTAMP | | timestamp | +| cpu | DOUBLE | | cpu 使用率 | +| mem | DOUBLE | | 内存使用率 | +| identify | NCHAR | TAG | 身份标识信息 | diff --git a/docs/zh/14-reference/01-components/12-tdinsight/index.mdx b/docs/zh/14-reference/01-components/12-tdinsight/index.mdx index 4058e579de..224fd5908d 100644 --- a/docs/zh/14-reference/01-components/12-tdinsight/index.mdx +++ b/docs/zh/14-reference/01-components/12-tdinsight/index.mdx @@ -15,8 +15,8 @@ TDengine 通过 taosKeeper 将服务器的 CPU、内存、硬盘空间、带宽 首先检查下面服务: - TDengine 已经安装并正常运行,此仪表盘需要 TDengine 3.0.0.0 及以上,并开启监控上报配置,具体配置请参考:[TDengine 监控配置](../taosd/#监控相关)。 -- taosAdapter 已经安装并正常运行。具体细节请参考:[taosAdapter 使用手册](../taosadapter) -- taosKeeper 已安装并正常运行。具体细节请参考:[taosKeeper 使用手册](../taoskeeper) +- taosAdapter 已经安装并正常运行。具体细节请参考:[taosAdapter 参考手册](../taosadapter) +- taosKeeper 已安装并正常运行。具体细节请参考:[taosKeeper 参考手册](../taoskeeper) 然后记录以下信息: diff --git a/include/libs/function/taosudf.h b/include/libs/function/taosudf.h index 04b92a897a..0b59d7c2f5 100644 --- a/include/libs/function/taosudf.h +++ b/include/libs/function/taosudf.h @@ -320,6 +320,30 @@ typedef int32_t (*TScriptUdfDestoryFunc)(void *udfCtx); typedef int32_t (*TScriptOpenFunc)(SScriptUdfEnvItem *items, int numItems); typedef int32_t (*TScriptCloseFunc)(); +// clang-format off +#ifdef WINDOWS + #define fnFatal(...) {} + #define fnError(...) {} + #define fnWarn(...) {} + #define fnInfo(...) {} + #define fnDebug(...) {} + #define fnTrace(...) {} +#else + DLL_EXPORT void taosPrintLog(const char *flags, int32_t level, int32_t dflag, const char *format, ...) +#ifdef __GNUC__ + __attribute__((format(printf, 4, 5))) +#endif + ; + extern int32_t udfDebugFlag; + #define udfFatal(...) { if (udfDebugFlag & 1) { taosPrintLog("UDF FATAL ", 1, 255, __VA_ARGS__); }} + #define udfError(...) { if (udfDebugFlag & 1) { taosPrintLog("UDF ERROR ", 1, 255, __VA_ARGS__); }} + #define udfWarn(...) { if (udfDebugFlag & 2) { taosPrintLog("UDF WARN ", 2, 255, __VA_ARGS__); }} + #define udfInfo(...) { if (udfDebugFlag & 2) { taosPrintLog("UDF ", 2, 255, __VA_ARGS__); }} + #define udfDebug(...) { if (udfDebugFlag & 4) { taosPrintLog("UDF ", 4, udfDebugFlag, __VA_ARGS__); }} + #define udfTrace(...) { if (udfDebugFlag & 8) { taosPrintLog("UDF ", 8, udfDebugFlag, __VA_ARGS__); }} +#endif +// clang-format on + #ifdef __cplusplus } #endif diff --git a/include/util/tcompare.h b/include/util/tcompare.h index 80f992f646..c7a29cad57 100644 --- a/include/util/tcompare.h +++ b/include/util/tcompare.h @@ -49,6 +49,7 @@ int32_t InitRegexCache(); void DestroyRegexCache(); int32_t patternMatch(const char *pattern, size_t psize, const char *str, size_t ssize, const SPatternCompareInfo *pInfo); int32_t checkRegexPattern(const char *pPattern); +void DestoryThreadLocalRegComp(); int32_t wcsPatternMatch(const TdUcs4 *pattern, size_t psize, const TdUcs4 *str, size_t ssize, const SPatternCompareInfo *pInfo); diff --git a/include/util/tlog.h b/include/util/tlog.h index 67aafbfe44..b4dbbef532 100644 --- a/include/util/tlog.h +++ b/include/util/tlog.h @@ -74,13 +74,13 @@ void taosCloseLog(); void taosResetLog(); void taosDumpData(uint8_t *msg, int32_t len); -void taosPrintLog(const char *flags, ELogLevel level, int32_t dflag, const char *format, ...) +void taosPrintLog(const char *flags, int32_t level, int32_t dflag, const char *format, ...) #ifdef __GNUC__ __attribute__((format(printf, 4, 5))) #endif ; -void taosPrintLongString(const char *flags, ELogLevel level, int32_t dflag, const char *format, ...) +void taosPrintLongString(const char *flags, int32_t level, int32_t dflag, const char *format, ...) #ifdef __GNUC__ __attribute__((format(printf, 4, 5))) #endif diff --git a/source/client/src/clientEnv.c b/source/client/src/clientEnv.c index 0bed08b6e6..d63701c529 100644 --- a/source/client/src/clientEnv.c +++ b/source/client/src/clientEnv.c @@ -531,7 +531,7 @@ int32_t createRequest(uint64_t connId, int32_t type, int64_t reqid, SRequestObj STscObj *pTscObj = acquireTscObj(connId); if (pTscObj == NULL) { - TSC_ERR_JRET(terrno); + TSC_ERR_JRET(TSDB_CODE_TSC_DISCONNECTED); } SSyncQueryParam *interParam = taosMemoryCalloc(1, sizeof(SSyncQueryParam)); if (interParam == NULL) { diff --git a/source/libs/command/src/command.c b/source/libs/command/src/command.c index 2811402ea1..11ddc89d4c 100644 --- a/source/libs/command/src/command.c +++ b/source/libs/command/src/command.c @@ -38,7 +38,7 @@ static int32_t buildRetrieveTableRsp(SSDataBlock* pBlock, int32_t numOfCols, SRe size_t rspSize = sizeof(SRetrieveTableRsp) + blockGetEncodeSize(pBlock) + PAYLOAD_PREFIX_LEN; *pRsp = taosMemoryCalloc(1, rspSize); if (NULL == *pRsp) { - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } (*pRsp)->useconds = 0; @@ -289,7 +289,7 @@ static int32_t buildRetension(SArray* pRetension, char **ppRetentions ) { char* p1 = taosMemoryCalloc(1, 100); if(NULL == p1) { - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } int32_t len = 0; @@ -849,7 +849,7 @@ _return: static int32_t buildLocalVariablesResultDataBlock(SSDataBlock** pOutput) { SSDataBlock* pBlock = taosMemoryCalloc(1, sizeof(SSDataBlock)); if (NULL == pBlock) { - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } pBlock->info.hasVarCol = true; diff --git a/source/libs/command/src/explain.c b/source/libs/command/src/explain.c index 8d9f1fb9cc..3a73c05de2 100644 --- a/source/libs/command/src/explain.c +++ b/source/libs/command/src/explain.c @@ -227,7 +227,7 @@ int32_t qExplainGenerateResNode(SPhysiNode *pNode, SExplainGroup *group, SExplai SExplainResNode *resNode = taosMemoryCalloc(1, sizeof(SExplainResNode)); if (NULL == resNode) { qError("calloc SPhysiNodeExplainRes failed"); - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } int32_t code = 0; diff --git a/source/libs/function/src/tudf.c b/source/libs/function/src/tudf.c index ad9e5ce7d4..9a751db801 100644 --- a/source/libs/function/src/tudf.c +++ b/source/libs/function/src/tudf.c @@ -162,7 +162,7 @@ static int32_t udfSpawnUdfd(SUdfdData *pData) { fnInfo("[UDFD]Succsess to set TAOS_FQDN:%s", taosFqdn); } else { fnError("[UDFD]Failed to allocate memory for TAOS_FQDN"); - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } } @@ -837,10 +837,13 @@ int32_t convertDataBlockToUdfDataBlock(SSDataBlock *block, SUdfDataBlock *udfBlo udfBlock->numOfRows = block->info.rows; udfBlock->numOfCols = taosArrayGetSize(block->pDataBlock); udfBlock->udfCols = taosMemoryCalloc(taosArrayGetSize(block->pDataBlock), sizeof(SUdfColumn *)); + if((udfBlock->udfCols) == NULL) { + return terrno; + } for (int32_t i = 0; i < udfBlock->numOfCols; ++i) { udfBlock->udfCols[i] = taosMemoryCalloc(1, sizeof(SUdfColumn)); if(udfBlock->udfCols[i] == NULL) { - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } SColumnInfoData *col = (SColumnInfoData *)taosArrayGet(block->pDataBlock, i); SUdfColumn *udfCol = udfBlock->udfCols[i]; @@ -854,13 +857,13 @@ int32_t convertDataBlockToUdfDataBlock(SSDataBlock *block, SUdfDataBlock *udfBlo udfCol->colData.varLenCol.varOffsetsLen = sizeof(int32_t) * udfBlock->numOfRows; udfCol->colData.varLenCol.varOffsets = taosMemoryMalloc(udfCol->colData.varLenCol.varOffsetsLen); if(udfCol->colData.varLenCol.varOffsets == NULL) { - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } memcpy(udfCol->colData.varLenCol.varOffsets, col->varmeta.offset, udfCol->colData.varLenCol.varOffsetsLen); udfCol->colData.varLenCol.payloadLen = colDataGetLength(col, udfBlock->numOfRows); udfCol->colData.varLenCol.payload = taosMemoryMalloc(udfCol->colData.varLenCol.payloadLen); if(udfCol->colData.varLenCol.payload == NULL) { - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } if (col->reassigned) { for (int32_t row = 0; row < udfCol->colData.numOfRows; ++row) { @@ -882,7 +885,7 @@ int32_t convertDataBlockToUdfDataBlock(SSDataBlock *block, SUdfDataBlock *udfBlo int32_t bitmapLen = udfCol->colData.fixLenCol.nullBitmapLen; udfCol->colData.fixLenCol.nullBitmap = taosMemoryMalloc(udfCol->colData.fixLenCol.nullBitmapLen); if(udfCol->colData.fixLenCol.nullBitmap == NULL) { - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } char *bitmap = udfCol->colData.fixLenCol.nullBitmap; memcpy(bitmap, col->nullbitmap, bitmapLen); @@ -985,7 +988,7 @@ int32_t convertDataBlockToScalarParm(SSDataBlock *input, SScalarParam *output) { output->columnData = taosMemoryMalloc(sizeof(SColumnInfoData)); if(output->columnData == NULL) { - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } memcpy(output->columnData, taosArrayGet(input->pDataBlock, 0), sizeof(SColumnInfoData)); output->colAlloced = true; @@ -1724,7 +1727,7 @@ int32_t udfcStartUvTask(SClientUvTaskNode *uvTask) { if(conn == NULL) { fnError("udfc event loop start connect task malloc conn failed."); taosMemoryFree(pipe); - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } conn->pipe = pipe; conn->readBuf.len = 0; @@ -1954,7 +1957,7 @@ int32_t udfcRunUdfUvTask(SClientUdfTask *task, int8_t uvTaskType) { SClientUvTaskNode *uvTask = taosMemoryCalloc(1, sizeof(SClientUvTaskNode)); if(uvTask == NULL) { fnError("udfc client task: %p failed to allocate memory for uvTask", task); - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } fnDebug("udfc client task: %p created uvTask: %p. pipe: %p", task, uvTask, task->session->udfUvPipe); @@ -1986,13 +1989,13 @@ int32_t doSetupUdf(char udfName[], UdfcFuncHandle *funcHandle) { SClientUdfTask *task = taosMemoryCalloc(1, sizeof(SClientUdfTask)); if(task == NULL) { fnError("doSetupUdf, failed to allocate memory for task"); - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } task->session = taosMemoryCalloc(1, sizeof(SUdfcUvSession)); if(task->session == NULL) { fnError("doSetupUdf, failed to allocate memory for session"); taosMemoryFree(task); - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } task->session->udfc = &gUdfcProxy; task->type = UDF_TASK_SETUP; @@ -2037,7 +2040,7 @@ int32_t callUdf(UdfcFuncHandle handle, int8_t callType, SSDataBlock *input, SUdf SClientUdfTask *task = taosMemoryCalloc(1, sizeof(SClientUdfTask)); if(task == NULL) { fnError("udfc call udf. failed to allocate memory for task"); - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } task->session = (SUdfcUvSession *)handle; task->type = UDF_TASK_CALL; @@ -2169,7 +2172,7 @@ int32_t doTeardownUdf(UdfcFuncHandle handle) { if(task == NULL) { fnError("doTeardownUdf, failed to allocate memory for task"); taosMemoryFree(session); - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } task->session = session; task->type = UDF_TASK_TEARDOWN; diff --git a/source/libs/function/src/udfd.c b/source/libs/function/src/udfd.c index f65ff79553..2d8a926c72 100644 --- a/source/libs/function/src/udfd.c +++ b/source/libs/function/src/udfd.c @@ -409,6 +409,10 @@ int32_t udfdInitializePythonPlugin(SUdfScriptPlugin *plugin) { int16_t lenPythonPath = strlen(tsUdfdLdLibPath) + strlen(global.udfDataDir) + 1 + 1; // global.udfDataDir:tsUdfdLdLibPath char *pythonPath = taosMemoryMalloc(lenPythonPath); + if(pythonPath == NULL) { + uv_dlclose(&plugin->lib); + return terrno; + } #ifdef WINDOWS snprintf(pythonPath, lenPythonPath, "%s;%s", global.udfDataDir, tsUdfdLdLibPath); #else @@ -705,6 +709,10 @@ void udfdProcessSetupRequest(SUvUdfWork *uvUdf, SUdfRequest *request) { uv_mutex_unlock(&udf->lock); } SUdfcFuncHandle *handle = taosMemoryMalloc(sizeof(SUdfcFuncHandle)); + if(handle == NULL) { + fnError("udfdProcessSetupRequest: malloc failed."); + code = terrno; + } handle->udf = udf; _send: @@ -775,7 +783,7 @@ void udfdProcessCallRequest(SUvUdfWork *uvUdf, SUdfRequest *request) { if (outBuf.buf != NULL) { code = udf->scriptPlugin->udfAggStartFunc(&outBuf, udf->scriptUdfCtx); } else { - code = TSDB_CODE_OUT_OF_MEMORY; + code = terrno; } subRsp->resultBuf = outBuf; break; @@ -784,9 +792,13 @@ void udfdProcessCallRequest(SUvUdfWork *uvUdf, SUdfRequest *request) { SUdfDataBlock input = {0}; if (convertDataBlockToUdfDataBlock(&call->block, &input) == TSDB_CODE_SUCCESS) { SUdfInterBuf outBuf = {.buf = taosMemoryMalloc(udf->bufSize), .bufLen = udf->bufSize, .numOfResult = 0}; - code = udf->scriptPlugin->udfAggProcFunc(&input, &call->interBuf, &outBuf, udf->scriptUdfCtx); - freeUdfInterBuf(&call->interBuf); - subRsp->resultBuf = outBuf; + if (outBuf.buf != NULL) { + code = udf->scriptPlugin->udfAggProcFunc(&input, &call->interBuf, &outBuf, udf->scriptUdfCtx); + freeUdfInterBuf(&call->interBuf); + subRsp->resultBuf = outBuf; + } else { + code = terrno; + } } freeUdfDataDataBlock(&input); @@ -794,18 +806,27 @@ void udfdProcessCallRequest(SUvUdfWork *uvUdf, SUdfRequest *request) { } case TSDB_UDF_CALL_AGG_MERGE: { SUdfInterBuf outBuf = {.buf = taosMemoryMalloc(udf->bufSize), .bufLen = udf->bufSize, .numOfResult = 0}; - code = udf->scriptPlugin->udfAggMergeFunc(&call->interBuf, &call->interBuf2, &outBuf, udf->scriptUdfCtx); - freeUdfInterBuf(&call->interBuf); - freeUdfInterBuf(&call->interBuf2); - subRsp->resultBuf = outBuf; + if (outBuf.buf != NULL) { + code = udf->scriptPlugin->udfAggMergeFunc(&call->interBuf, &call->interBuf2, &outBuf, udf->scriptUdfCtx); + freeUdfInterBuf(&call->interBuf); + freeUdfInterBuf(&call->interBuf2); + subRsp->resultBuf = outBuf; + } else { + code = terrno; + } break; } case TSDB_UDF_CALL_AGG_FIN: { SUdfInterBuf outBuf = {.buf = taosMemoryMalloc(udf->bufSize), .bufLen = udf->bufSize, .numOfResult = 0}; - code = udf->scriptPlugin->udfAggFinishFunc(&call->interBuf, &outBuf, udf->scriptUdfCtx); - freeUdfInterBuf(&call->interBuf); - subRsp->resultBuf = outBuf; + if (outBuf.buf != NULL) { + code = udf->scriptPlugin->udfAggFinishFunc(&call->interBuf, &outBuf, udf->scriptUdfCtx); + freeUdfInterBuf(&call->interBuf); + subRsp->resultBuf = outBuf; + } else { + code = terrno; + } + break; } default: @@ -820,19 +841,24 @@ void udfdProcessCallRequest(SUvUdfWork *uvUdf, SUdfRequest *request) { int32_t len = encodeUdfResponse(NULL, rsp); if(len < 0) { fnError("udfdProcessCallRequest: encode udf response failed. len %d", len); - return; + goto _exit; } rsp->msgLen = len; void *bufBegin = taosMemoryMalloc(len); + if (bufBegin == NULL) { + fnError("udfdProcessCallRequest: malloc failed. len %d", len); + goto _exit; + } void *buf = bufBegin; if(encodeUdfResponse(&buf, rsp) < 0) { fnError("udfdProcessCallRequest: encode udf response failed. len %d", len); taosMemoryFree(bufBegin); - return; + goto _exit; } uvUdf->output = uv_buf_init(bufBegin, len); +_exit: switch (call->callType) { case TSDB_UDF_CALL_SCALA_PROC: { blockDataFreeRes(&call->block); @@ -906,6 +932,10 @@ _send: } rsp->msgLen = len; void *bufBegin = taosMemoryMalloc(len); + if(bufBegin == NULL) { + fnError("udfdProcessTeardownRequest: malloc failed. len %d", len); + return; + } void *buf = bufBegin; if (encodeUdfResponse(&buf, rsp) < 0) { fnError("udfdProcessTeardownRequest: encode udf response failed. len %d", len); @@ -1210,6 +1240,11 @@ void udfdSendResponse(uv_work_t *work, int status) { if (udfWork->conn != NULL) { uv_write_t *write_req = taosMemoryMalloc(sizeof(uv_write_t)); + if(write_req == NULL) { + fnError("udfd send response error, malloc failed"); + taosMemoryFree(work); + return; + } write_req->data = udfWork; int32_t code = uv_write(write_req, udfWork->conn->client, &udfWork->output, 1, udfdOnWrite); if (code != 0) { @@ -1269,7 +1304,16 @@ void udfdHandleRequest(SUdfdUvConn *conn) { int32_t inputLen = conn->inputLen; uv_work_t *work = taosMemoryMalloc(sizeof(uv_work_t)); + if(work == NULL) { + fnError("udfd malloc work failed"); + return; + } SUvUdfWork *udfWork = taosMemoryMalloc(sizeof(SUvUdfWork)); + if(udfWork == NULL) { + fnError("udfd malloc udf work failed"); + taosMemoryFree(work); + return; + } udfWork->conn = conn; udfWork->pWorkNext = conn->pWorkList; conn->pWorkList = udfWork; @@ -1334,6 +1378,10 @@ void udfdOnNewConnection(uv_stream_t *server, int status) { int32_t code = 0; uv_pipe_t *client = (uv_pipe_t *)taosMemoryMalloc(sizeof(uv_pipe_t)); + if(client == NULL) { + fnError("udfd pipe malloc failed"); + return; + } code = uv_pipe_init(global.loop, client, 0); if (code) { fnError("udfd pipe init error %s", uv_strerror(code)); @@ -1342,6 +1390,10 @@ void udfdOnNewConnection(uv_stream_t *server, int status) { } if (uv_accept(server, (uv_stream_t *)client) == 0) { SUdfdUvConn *ctx = taosMemoryMalloc(sizeof(SUdfdUvConn)); + if(ctx == NULL) { + fnError("udfd conn malloc failed"); + goto _exit; + } ctx->pWorkList = NULL; ctx->client = (uv_stream_t *)client; ctx->inputBuf = 0; @@ -1356,9 +1408,11 @@ void udfdOnNewConnection(uv_stream_t *server, int status) { taosMemoryFree(ctx); taosMemoryFree(client); } - } else { - uv_close((uv_handle_t *)client, NULL); + return; } +_exit: + uv_close((uv_handle_t *)client, NULL); + taosMemoryFree(client); } void udfdIntrSignalHandler(uv_signal_t *handle, int signum) { @@ -1411,6 +1465,10 @@ static int32_t udfdInitLog() { void udfdCtrlAllocBufCb(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { buf->base = taosMemoryMalloc(suggested_size); + if (buf->base == NULL) { + fnError("udfd ctrl pipe alloc buffer failed"); + return; + } buf->len = suggested_size; } @@ -1477,13 +1535,13 @@ static int32_t udfdGlobalDataInit() { uv_loop_t *loop = taosMemoryMalloc(sizeof(uv_loop_t)); if (loop == NULL) { fnError("udfd init uv loop failed, mem overflow"); - return -1; + return terrno; } global.loop = loop; if (uv_mutex_init(&global.scriptPluginsMutex) != 0) { fnError("udfd init script plugins mutex failed"); - return -1; + return TSDB_CODE_UDF_UV_EXEC_FAILURE; } global.udfsHash = taosHashInit(64, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), true, HASH_NO_LOCK); @@ -1494,7 +1552,7 @@ static int32_t udfdGlobalDataInit() { if (uv_mutex_init(&global.udfsMutex) != 0) { fnError("udfd init udfs mutex failed"); - return -2; + return TSDB_CODE_UDF_UV_EXEC_FAILURE; } return 0; diff --git a/source/libs/qcom/src/querymsg.c b/source/libs/qcom/src/querymsg.c index e8deed1df9..207bd91bd9 100644 --- a/source/libs/qcom/src/querymsg.c +++ b/source/libs/qcom/src/querymsg.c @@ -34,7 +34,7 @@ int32_t queryBuildUseDbOutput(SUseDbOutput *pOut, SUseDbRsp *usedbRsp) { pOut->dbVgroup = taosMemoryCalloc(1, sizeof(SDBVgInfo)); if (NULL == pOut->dbVgroup) { - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } pOut->dbVgroup->vgVersion = usedbRsp->vgVersion; @@ -509,7 +509,7 @@ int32_t queryCreateTableMetaFromMsg(STableMetaRsp *msg, bool isStb, STableMeta * STableMeta *pTableMeta = taosMemoryCalloc(1, metaSize + schemaExtSize); if (NULL == pTableMeta) { qError("calloc size[%d] failed", metaSize); - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } SSchemaExt *pSchemaExt = (SSchemaExt *)((char *)pTableMeta + metaSize); @@ -764,7 +764,7 @@ int32_t queryProcessGetTbCfgRsp(void *output, char *msg, int32_t msgSize) { STableCfgRsp *out = taosMemoryCalloc(1, sizeof(STableCfgRsp)); if(out == NULL) { - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } if (tDeserializeSTableCfgRsp(msg, msgSize, out) != 0) { qError("tDeserializeSTableCfgRsp failed, msgSize:%d", msgSize); @@ -785,7 +785,7 @@ int32_t queryProcessGetViewMetaRsp(void *output, char *msg, int32_t msgSize) { SViewMetaRsp *out = taosMemoryCalloc(1, sizeof(SViewMetaRsp)); if (out == NULL) { - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } if (tDeserializeSViewMetaRsp(msg, msgSize, out) != 0) { qError("tDeserializeSViewMetaRsp failed, msgSize:%d", msgSize); diff --git a/source/libs/scalar/src/scalar.c b/source/libs/scalar/src/scalar.c index d08f358ce0..9428f051aa 100644 --- a/source/libs/scalar/src/scalar.c +++ b/source/libs/scalar/src/scalar.c @@ -904,9 +904,8 @@ int32_t sclExecOperator(SOperatorNode *node, SScalarCtx *ctx, SScalarParam *outp terrno = TSDB_CODE_SUCCESS; SCL_ERR_JRET(OperatorFn(pLeft, pRight, output, TSDB_ORDER_ASC)); - SCL_ERR_JRET(terrno); -_return: +_return: sclFreeParamList(params, paramNum); SCL_RET(code); } diff --git a/source/util/src/tcompare.c b/source/util/src/tcompare.c index 4cb48bffe5..670a70a309 100644 --- a/source/util/src/tcompare.c +++ b/source/util/src/tcompare.c @@ -1208,20 +1208,28 @@ typedef struct UsingRegex { regex_t pRegex; int32_t lastUsedTime; } UsingRegex; +typedef UsingRegex* HashRegexPtr; typedef struct RegexCache { SHashObj *regexHash; void *regexCacheTmr; void *timer; + SRWLatch mutex; + bool exit; } RegexCache; static RegexCache sRegexCache; #define MAX_REGEX_CACHE_SIZE 20 #define REGEX_CACHE_CLEAR_TIME 30 static void checkRegexCache(void* param, void* tmrId) { + int32_t code = 0; + taosRLockLatch(&sRegexCache.mutex); + if(sRegexCache.exit) { + goto _exit; + } (void)taosTmrReset(checkRegexCache, REGEX_CACHE_CLEAR_TIME * 1000, param, sRegexCache.regexCacheTmr, &tmrId); if (taosHashGetSize(sRegexCache.regexHash) < MAX_REGEX_CACHE_SIZE) { - return; + goto _exit; } if (taosHashGetSize(sRegexCache.regexHash) >= MAX_REGEX_CACHE_SIZE) { @@ -1235,6 +1243,8 @@ static void checkRegexCache(void* param, void* tmrId) { ppUsingRegex = taosHashIterate(sRegexCache.regexHash, ppUsingRegex); } } +_exit: + taosRUnLockLatch(&sRegexCache.mutex); } void regexCacheFree(void *ppUsingRegex) { @@ -1246,30 +1256,35 @@ int32_t InitRegexCache() { sRegexCache.regexHash = taosHashInit(64, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), false, HASH_ENTRY_LOCK); if (sRegexCache.regexHash == NULL) { uError("failed to create RegexCache"); - return -1; + return terrno; } taosHashSetFreeFp(sRegexCache.regexHash, regexCacheFree); sRegexCache.regexCacheTmr = taosTmrInit(0, 0, 0, "REGEXCACHE"); if (sRegexCache.regexCacheTmr == NULL) { uError("failed to create regex cache check timer"); - terrno = TSDB_CODE_OUT_OF_MEMORY; - return -1; + return terrno; } + sRegexCache.exit = false; + taosInitRWLatch(&sRegexCache.mutex); sRegexCache.timer = taosTmrStart(checkRegexCache, REGEX_CACHE_CLEAR_TIME * 1000, NULL, sRegexCache.regexCacheTmr); if (sRegexCache.timer == NULL) { uError("failed to start regex cache timer"); - return -1; + return terrno; } - return 0; + return TSDB_CODE_SUCCESS; } void DestroyRegexCache(){ + int32_t code = 0; uInfo("[regex cache] destory regex cache"); (void)taosTmrStopA(&sRegexCache.timer); + taosWLockLatch(&sRegexCache.mutex); + sRegexCache.exit = true; taosHashCleanup(sRegexCache.regexHash); taosTmrCleanUp(sRegexCache.regexCacheTmr); + taosWUnLockLatch(&sRegexCache.mutex); } int32_t checkRegexPattern(const char *pPattern) { @@ -1290,18 +1305,17 @@ int32_t checkRegexPattern(const char *pPattern) { return TSDB_CODE_SUCCESS; } -static UsingRegex **getRegComp(const char *pPattern) { - UsingRegex **ppUsingRegex = (UsingRegex **)taosHashAcquire(sRegexCache.regexHash, pPattern, strlen(pPattern)); +int32_t getRegComp(const char *pPattern, HashRegexPtr **regexRet) { + HashRegexPtr* ppUsingRegex = (HashRegexPtr*)taosHashAcquire(sRegexCache.regexHash, pPattern, strlen(pPattern)); if (ppUsingRegex != NULL) { (*ppUsingRegex)->lastUsedTime = taosGetTimestampSec(); - return ppUsingRegex; + *regexRet = ppUsingRegex; + return TSDB_CODE_SUCCESS; } - UsingRegex *pUsingRegex = taosMemoryMalloc(sizeof(UsingRegex)); if (pUsingRegex == NULL) { - terrno = TSDB_CODE_OUT_OF_MEMORY; uError("Failed to Malloc when compile regex pattern %s.", pPattern); - return NULL; + return terrno; } int32_t cflags = REG_EXTENDED; int32_t ret = regcomp(&pUsingRegex->pRegex, pPattern, cflags); @@ -1310,8 +1324,7 @@ static UsingRegex **getRegComp(const char *pPattern) { (void)regerror(ret, &pUsingRegex->pRegex, msgbuf, tListLen(msgbuf)); uError("Failed to compile regex pattern %s. reason %s", pPattern, msgbuf); taosMemoryFree(pUsingRegex); - terrno = TSDB_CODE_PAR_REGULAR_EXPRESSION_ERROR; - return NULL; + return TSDB_CODE_PAR_REGULAR_EXPRESSION_ERROR; } while (true) { @@ -1319,8 +1332,9 @@ static UsingRegex **getRegComp(const char *pPattern) { if (code != 0 && code != TSDB_CODE_DUP_KEY) { regexCacheFree(&pUsingRegex); uError("Failed to put regex pattern %s into cache, exception internal error.", pPattern); - terrno = code; - return NULL; + return code; + } else if (code == TSDB_CODE_DUP_KEY) { + terrno = 0; } ppUsingRegex = (UsingRegex **)taosHashAcquire(sRegexCache.regexHash, pPattern, strlen(pPattern)); if (ppUsingRegex) { @@ -1334,27 +1348,68 @@ static UsingRegex **getRegComp(const char *pPattern) { } } pUsingRegex->lastUsedTime = taosGetTimestampSec(); - return ppUsingRegex; + *regexRet = ppUsingRegex; + return TSDB_CODE_SUCCESS; } void releaseRegComp(UsingRegex **regex){ taosHashRelease(sRegexCache.regexHash, regex); } +static threadlocal UsingRegex ** ppUsingRegex; +static threadlocal regex_t * pRegex; +static threadlocal char *pOldPattern = NULL; +void DestoryThreadLocalRegComp() { + if (NULL != pOldPattern) { + releaseRegComp(ppUsingRegex); + taosMemoryFree(pOldPattern); + ppUsingRegex = NULL; + pRegex = NULL; + pOldPattern = NULL; + } +} + +int32_t threadGetRegComp(regex_t **regex, const char *pPattern) { + if (NULL != pOldPattern) { + if (strcmp(pOldPattern, pPattern) == 0) { + *regex = pRegex; + return 0; + } else { + DestoryThreadLocalRegComp(); + } + } + + HashRegexPtr *ppRegex = NULL; + int32_t code = getRegComp(pPattern, &ppRegex); + if (code != TSDB_CODE_SUCCESS) { + return code; + } + pOldPattern = taosStrdup(pPattern); + if (NULL == pOldPattern) { + uError("Failed to Malloc when compile regex pattern %s.", pPattern); + return terrno; + } + ppUsingRegex = ppRegex; + pRegex = &((*ppUsingRegex)->pRegex); + *regex = &(*ppRegex)->pRegex; + return 0; +} + static int32_t doExecRegexMatch(const char *pString, const char *pPattern) { int32_t ret = 0; char msgbuf[256] = {0}; - UsingRegex **pUsingRegex = getRegComp(pPattern); - if (pUsingRegex == NULL) { - return 1; + + regex_t *regex = NULL; + ret = threadGetRegComp(®ex, pPattern); + if (ret != 0) { + return ret; } regmatch_t pmatch[1]; - ret = regexec(&(*pUsingRegex)->pRegex, pString, 1, pmatch, 0); - releaseRegComp(pUsingRegex); + ret = regexec(regex, pString, 1, pmatch, 0); if (ret != 0 && ret != REG_NOMATCH) { terrno = TSDB_CODE_PAR_REGULAR_EXPRESSION_ERROR; - (void)regerror(ret, &(*pUsingRegex)->pRegex, msgbuf, sizeof(msgbuf)); + (void)regerror(ret, regex, msgbuf, sizeof(msgbuf)); uDebug("Failed to match %s with pattern %s, reason %s", pString, pPattern, msgbuf) } @@ -1365,8 +1420,7 @@ int32_t comparestrRegexMatch(const void *pLeft, const void *pRight) { size_t sz = varDataLen(pRight); char *pattern = taosMemoryMalloc(sz + 1); if (NULL == pattern) { - terrno = TSDB_CODE_OUT_OF_MEMORY; - return 1; + return 1; // terrno has been set } (void)memcpy(pattern, varDataVal(pRight), varDataLen(pRight)); @@ -1376,8 +1430,7 @@ int32_t comparestrRegexMatch(const void *pLeft, const void *pRight) { char *str = taosMemoryMalloc(sz + 1); if (NULL == str) { taosMemoryFree(pattern); - terrno = TSDB_CODE_OUT_OF_MEMORY; - return 1; + return 1; // terrno has been set } (void)memcpy(str, varDataVal(pLeft), sz); @@ -1395,14 +1448,13 @@ int32_t comparewcsRegexMatch(const void *pString, const void *pPattern) { size_t len = varDataLen(pPattern); char *pattern = taosMemoryMalloc(len + 1); if (NULL == pattern) { - terrno = TSDB_CODE_OUT_OF_MEMORY; - return 1; + return 1; // terrno has been set } int convertLen = taosUcs4ToMbs((TdUcs4 *)varDataVal(pPattern), len, pattern); if (convertLen < 0) { taosMemoryFree(pattern); - return (terrno = TSDB_CODE_APP_ERROR); + return 1; // terrno has been set } pattern[convertLen] = 0; @@ -1411,15 +1463,14 @@ int32_t comparewcsRegexMatch(const void *pString, const void *pPattern) { char *str = taosMemoryMalloc(len + 1); if (NULL == str) { taosMemoryFree(pattern); - terrno = TSDB_CODE_OUT_OF_MEMORY; - return 1; + return 1; // terrno has been set } convertLen = taosUcs4ToMbs((TdUcs4 *)varDataVal(pString), len, str); if (convertLen < 0) { taosMemoryFree(str); taosMemoryFree(pattern); - return (terrno = TSDB_CODE_APP_ERROR); + return 1; // terrno has been set } str[convertLen] = 0; diff --git a/source/util/src/tlog.c b/source/util/src/tlog.c index 1946a0a274..86dc767adc 100644 --- a/source/util/src/tlog.c +++ b/source/util/src/tlog.c @@ -567,7 +567,7 @@ static inline void taosPrintLogImp(ELogLevel level, int32_t dflag, const char *b } } -void taosPrintLog(const char *flags, ELogLevel level, int32_t dflag, const char *format, ...) { +void taosPrintLog(const char *flags, int32_t level, int32_t dflag, const char *format, ...) { if (!(dflag & DEBUG_FILE) && !(dflag & DEBUG_SCREEN)) return; char buffer[LOG_MAX_LINE_BUFFER_SIZE]; @@ -590,7 +590,7 @@ void taosPrintLog(const char *flags, ELogLevel level, int32_t dflag, const char } } -void taosPrintLongString(const char *flags, ELogLevel level, int32_t dflag, const char *format, ...) { +void taosPrintLongString(const char *flags, int32_t level, int32_t dflag, const char *format, ...) { if (!osLogSpaceAvailable()) return; if (!(dflag & DEBUG_FILE) && !(dflag & DEBUG_SCREEN)) return; diff --git a/source/util/src/tworker.c b/source/util/src/tworker.c index 258d53c335..edf5b0b970 100644 --- a/source/util/src/tworker.c +++ b/source/util/src/tworker.c @@ -106,6 +106,7 @@ static void *tQWorkerThreadFp(SQueueWorker *worker) { } destroyThreadLocalGeosCtx(); + DestoryThreadLocalRegComp(); return NULL; } @@ -237,6 +238,7 @@ static void *tAutoQWorkerThreadFp(SQueueWorker *worker) { taosUpdateItemSize(qinfo.queue, 1); } + DestoryThreadLocalRegComp(); return NULL; } @@ -664,6 +666,7 @@ static void *tQueryAutoQWorkerThreadFp(SQueryAutoQWorker *worker) { } destroyThreadLocalGeosCtx(); + DestoryThreadLocalRegComp(); return NULL; } diff --git a/source/util/test/CMakeLists.txt b/source/util/test/CMakeLists.txt index e8aabfe338..0d8774ba41 100644 --- a/source/util/test/CMakeLists.txt +++ b/source/util/test/CMakeLists.txt @@ -119,6 +119,13 @@ add_test( COMMAND bufferTest ) +add_executable(regexTest "regexTest.cpp") +target_link_libraries(regexTest os util gtest_main ) +add_test( + NAME regexTest + COMMAND regexTest +) + #add_executable(decompressTest "decompressTest.cpp") #target_link_libraries(decompressTest os util common gtest_main) #add_test( diff --git a/source/util/test/regexTest.cpp b/source/util/test/regexTest.cpp new file mode 100644 index 0000000000..5fe3701700 --- /dev/null +++ b/source/util/test/regexTest.cpp @@ -0,0 +1,344 @@ + +#include +#include +#include +#include +#include +#include "os.h" +#include "tutil.h" +#include "regex.h" +#include "osDef.h" +#include "tcompare.h" + +extern "C" { + typedef struct UsingRegex UsingRegex; + typedef struct HashRegexPtr HashRegexPtr; + int32_t getRegComp(const char *pPattern, HashRegexPtr **regexRet); + int32_t threadGetRegComp(regex_t **regex, const char *pPattern); +} + +class regexTest { + public: + regexTest() { (void)InitRegexCache(); } + ~regexTest() { (void)DestroyRegexCache(); } +}; +static regexTest test; + +static threadlocal regex_t pRegex; +static threadlocal char *pOldPattern = NULL; + +void DestoryThreadLocalRegComp1() { + if (NULL != pOldPattern) { + regfree(&pRegex); + taosMemoryFree(pOldPattern); + pOldPattern = NULL; + } +} + +static regex_t *threadGetRegComp1(const char *pPattern) { + if (NULL != pOldPattern) { + if( strcmp(pOldPattern, pPattern) == 0) { + return &pRegex; + } else { + DestoryThreadLocalRegComp1(); + } + } + pOldPattern = (char*)taosMemoryMalloc(strlen(pPattern) + 1); + if (NULL == pOldPattern) { + uError("Failed to Malloc when compile regex pattern %s.", pPattern); + return NULL; + } + strcpy(pOldPattern, pPattern); + int32_t cflags = REG_EXTENDED; + int32_t ret = regcomp(&pRegex, pPattern, cflags); + if (ret != 0) { + char msgbuf[256] = {0}; + regerror(ret, &pRegex, msgbuf, tListLen(msgbuf)); + uError("Failed to compile regex pattern %s. reason %s", pPattern, msgbuf); + DestoryThreadLocalRegComp1(); + return NULL; + } + return &pRegex; +} + +TEST(testCase, regexCacheTest1) { + int times = 100000; + char s1[] = "abc"; + auto start = std::chrono::high_resolution_clock::now(); + + uint64_t t0 = taosGetTimestampUs(); + for (int i = 0; i < times; i++) { + HashRegexPtr* ret = NULL; + int32_t code = getRegComp(s1, &ret); + if (code != 0) { + FAIL() << "Failed to compile regex pattern " << s1; + } + } + uint64_t t1 = taosGetTimestampUs(); + + printf("%s regex(current) %d times:%" PRIu64 " us.\n", s1, times, t1 - t0); + + uint64_t t2 = taosGetTimestampUs(); + for(int i = 0; i < times; i++) { + regex_t* rex = threadGetRegComp1(s1); + } + uint64_t t3 = taosGetTimestampUs(); + + printf("%s regex(before) %d times:%" PRIu64 " us.\n", s1, times, t3 - t2); + + t2 = taosGetTimestampUs(); + for(int i = 0; i < times; i++) { + regex_t* rex = NULL; + (void)threadGetRegComp(&rex, s1); + } + t3 = taosGetTimestampUs(); + + printf("%s regex(new) %d times:%" PRIu64 " us.\n", s1, times, t3 - t2); +} + +TEST(testCase, regexCacheTest2) { + int times = 100000; + char s1[] = "abc%*"; + auto start = std::chrono::high_resolution_clock::now(); + + uint64_t t0 = taosGetTimestampUs(); + for (int i = 0; i < times; i++) { + HashRegexPtr* ret = NULL; + int32_t code = getRegComp(s1, &ret); + if (code != 0) { + FAIL() << "Failed to compile regex pattern " << s1; + } + } + uint64_t t1 = taosGetTimestampUs(); + + printf("%s regex(current) %d times:%" PRIu64 " us.\n", s1, times, t1 - t0); + + uint64_t t2 = taosGetTimestampUs(); + for(int i = 0; i < times; i++) { + regex_t* rex = threadGetRegComp1(s1); + } + uint64_t t3 = taosGetTimestampUs(); + + printf("%s regex(before) %d times:%" PRIu64 " us.\n", s1, times, t3 - t2); + + t2 = taosGetTimestampUs(); + for(int i = 0; i < times; i++) { + regex_t* rex = NULL; + (void)threadGetRegComp(&rex, s1); + } + t3 = taosGetTimestampUs(); + + printf("%s regex(new) %d times:%" PRIu64 " us.\n", s1, times, t3 - t2); +} + +TEST(testCase, regexCacheTest3) { + int times = 100000; + char s1[] = "abc%*"; + char s2[] = "abc"; + auto start = std::chrono::high_resolution_clock::now(); + + uint64_t t0 = taosGetTimestampUs(); + for (int i = 0; i < times; i++) { + HashRegexPtr* ret = NULL; + int32_t code = getRegComp(s1, &ret); + if (code != 0) { + FAIL() << "Failed to compile regex pattern " << s1; + } + } + uint64_t t1 = taosGetTimestampUs(); + + printf("'%s' and '%s' take place by turn regex(current) %d times:%" PRIu64 " us.\n", s1, s2, times, t1 - t0); + + uint64_t t2 = taosGetTimestampUs(); + for(int i = 0; i < times; i++) { + regex_t* rex = threadGetRegComp1(s1); + rex = threadGetRegComp1(s2); + } + uint64_t t3 = taosGetTimestampUs(); + + printf("'%s' and '%s' take place by turn regex(before) %d times:%" PRIu64 " us.\n", s1, s2, times, t3 - t2); + + t2 = taosGetTimestampUs(); + for(int i = 0; i < times; i++) { + regex_t* rex = NULL; + (void)threadGetRegComp(&rex, s1); + (void)threadGetRegComp(&rex, s2); + } + t3 = taosGetTimestampUs(); + + printf("'%s' and '%s' take place by turn regex(new) %d times:%" PRIu64 " us.\n", s1, s2, times, t3 - t2); +} + +TEST(testCase, regexCacheTest4) { + int times = 100; + int count = 1000; + char s1[] = "abc%*"; + char s2[] = "abc"; + auto start = std::chrono::high_resolution_clock::now(); + + uint64_t t0 = taosGetTimestampUs(); + for (int i = 0; i < times; i++) { + for (int j = 0; j < count; ++j) { + HashRegexPtr* ret = NULL; + int32_t code = getRegComp(s1, &ret); + if (code != 0) { + FAIL() << "Failed to compile regex pattern " << s1; + } + } + for (int j = 0; j < count; ++j) { + HashRegexPtr* ret = NULL; + int32_t code = getRegComp(s2, &ret); + if (code != 0) { + FAIL() << "Failed to compile regex pattern " << s2; + } + } + } + uint64_t t1 = taosGetTimestampUs(); + + printf("'%s' and '%s' take place by turn(per %d count) regex(current) %d times:%" PRIu64 " us.\n", s1, s2, count, times, t1 - t0); + + uint64_t t2 = taosGetTimestampUs(); + for (int i = 0; i < times; i++) { + for (int j = 0; j < count; ++j) { + regex_t* rex = threadGetRegComp1(s1); + } + for (int j = 0; j < count; ++j) { + regex_t* rex = threadGetRegComp1(s2); + } + } + uint64_t t3 = taosGetTimestampUs(); + + printf("'%s' and '%s' take place by turn(per %d count) regex(before) %d times:%" PRIu64 " us.\n", s1, s2, count, times, t3 - t2); + + t2 = taosGetTimestampUs(); + for (int i = 0; i < times; i++) { + for (int j = 0; j < count; ++j) { + regex_t* rex = NULL; + (void)threadGetRegComp(&rex, s1); + } + for (int j = 0; j < count; ++j) { + regex_t* rex = NULL; + (void)threadGetRegComp(&rex, s2); + } + } + t3 = taosGetTimestampUs(); + + printf("'%s' and '%s' take place by turn(per %d count) regex(new) %d times:%" PRIu64 " us.\n", s1, s2, count, times, t3 - t2); +} + +// It is not a good idea to test this case, because it will take a long time. +/* +TEST(testCase, regexCacheTest5) { + int times = 10000; + int count = 10000; + char s1[] = "abc%*"; + char s2[] = "abc"; + auto start = std::chrono::high_resolution_clock::now(); + + uint64_t t0 = taosGetTimestampUs(); + for (int i = 0; i < times; i++) { + for (int j = 0; j < count; ++j) { + HashRegexPtr* ret = NULL; + int32_t code = getRegComp(s1, &ret); + if (code != 0) { + FAIL() << "Failed to compile regex pattern " << s1; + } + } + for (int j = 0; j < count; ++j) { + HashRegexPtr* ret = NULL; + int32_t code = getRegComp(s2, &ret); + if (code != 0) { + FAIL() << "Failed to compile regex pattern " << s2; + } + } + } + uint64_t t1 = taosGetTimestampUs(); + + printf("'%s' and '%s' take place by turn(per %d count) regex(current) %d times:%" PRIu64 " us.\n", s1, s2, count, times, t1 - t0); + + uint64_t t2 = taosGetTimestampUs(); + for (int i = 0; i < times; i++) { + for (int j = 0; j < count; ++j) { + regex_t* rex = threadGetRegComp1(s1); + } + for (int j = 0; j < count; ++j) { + regex_t* rex = threadGetRegComp1(s2); + } + } + uint64_t t3 = taosGetTimestampUs(); + + printf("'%s' and '%s' take place by turn(per %d count) regex(before) %d times:%" PRIu64 " us.\n", s1, s2, count, times, t3 - t2); + + t2 = taosGetTimestampUs(); + for (int i = 0; i < times; i++) { + for (int j = 0; j < count; ++j) { + regex_t* rex = NULL; + (void)threadGetRegComp(&rex, s1); + } + for (int j = 0; j < count; ++j) { + regex_t* rex = NULL; + (void)threadGetRegComp(&rex, s2); + } + } + t3 = taosGetTimestampUs(); + + printf("'%s' and '%s' take place by turn(per %d count) regex(new) %d times:%" PRIu64 " us.\n", s1, s2, count, times, t3 - t2); +} + +TEST(testCase, regexCacheTest6) { + int times = 10000; + int count = 1000; + char s1[] = "abc%*"; + char s2[] = "abc"; + auto start = std::chrono::high_resolution_clock::now(); + + uint64_t t0 = taosGetTimestampUs(); + for (int i = 0; i < times; i++) { + for (int j = 0; j < count; ++j) { + HashRegexPtr* ret = NULL; + int32_t code = getRegComp(s1, &ret); + if (code != 0) { + FAIL() << "Failed to compile regex pattern " << s1; + } + } + for (int j = 0; j < count; ++j) { + HashRegexPtr* ret = NULL; + int32_t code = getRegComp(s2, &ret); + if (code != 0) { + FAIL() << "Failed to compile regex pattern " << s2; + } + } + } + uint64_t t1 = taosGetTimestampUs(); + + printf("'%s' and '%s' take place by turn(per %d count) regex(current) %d times:%" PRIu64 " us.\n", s1, s2, count, times, t1 - t0); + + uint64_t t2 = taosGetTimestampUs(); + for (int i = 0; i < times; i++) { + for (int j = 0; j < count; ++j) { + regex_t* rex = threadGetRegComp1(s1); + } + for (int j = 0; j < count; ++j) { + regex_t* rex = threadGetRegComp1(s2); + } + } + uint64_t t3 = taosGetTimestampUs(); + + printf("'%s' and '%s' take place by turn(per %d count) regex(before) %d times:%" PRIu64 " us.\n", s1, s2, count, times, t3 - t2); + + t2 = taosGetTimestampUs(); + for (int i = 0; i < times; i++) { + for (int j = 0; j < count; ++j) { + regex_t* rex = NULL; + (void)threadGetRegComp(&rex, s1); + } + for (int j = 0; j < count; ++j) { + regex_t* rex = NULL; + (void)threadGetRegComp(&rex, s2); + } + } + t3 = taosGetTimestampUs(); + + printf("'%s' and '%s' take place by turn(per %d count) regex(new) %d times:%" PRIu64 " us.\n", s1, s2, count, times, t3 - t2); +} +*/ diff --git a/tests/script/sh/bit_and.c b/tests/script/sh/bit_and.c index f3bf71ce94..2cf2157e1c 100644 --- a/tests/script/sh/bit_and.c +++ b/tests/script/sh/bit_and.c @@ -8,13 +8,17 @@ DLL_EXPORT int32_t bit_and_init() { return 0; } DLL_EXPORT int32_t bit_and_destroy() { return 0; } DLL_EXPORT int32_t bit_and(SUdfDataBlock* block, SUdfColumn* resultCol) { + udfTrace("block:%p, processing begins, rows:%d cols:%d", block, block->numOfRows, block->numOfCols); + if (block->numOfCols < 2) { + udfError("block:%p, cols:%d needs to be greater than 2", block, block->numOfCols); return TSDB_CODE_UDF_INVALID_INPUT; } for (int32_t i = 0; i < block->numOfCols; ++i) { SUdfColumn* col = block->udfCols[i]; - if (!(col->colMeta.type == TSDB_DATA_TYPE_INT)) { + if (col->colMeta.type != TSDB_DATA_TYPE_INT) { + udfError("block:%p, col:%d type:%d should be int(%d)", block, i, col->colMeta.type, TSDB_DATA_TYPE_INT); return TSDB_CODE_UDF_INVALID_INPUT; } } @@ -23,25 +27,35 @@ DLL_EXPORT int32_t bit_and(SUdfDataBlock* block, SUdfColumn* resultCol) { for (int32_t i = 0; i < block->numOfRows; ++i) { if (udfColDataIsNull(block->udfCols[0], i)) { + udfTrace("block:%p, row:%d result is null since col:0 is null", block, i); udfColDataSetNull(resultCol, i); continue; } + int32_t result = *(int32_t*)udfColDataGetData(block->udfCols[0], i); - int j = 1; + udfTrace("block:%p, row:%d col:0 data:%d", block, i, result); + + int32_t j = 1; for (; j < block->numOfCols; ++j) { if (udfColDataIsNull(block->udfCols[j], i)) { + udfTrace("block:%p, row:%d result is null since col:%d is null", block, i, j); udfColDataSetNull(resultCol, i); break; } char* colData = udfColDataGetData(block->udfCols[j], i); result &= *(int32_t*)colData; + udfTrace("block:%p, row:%d col:%d data:%d", block, i, j, *(int32_t*)colData); } + if (j == block->numOfCols) { udfColDataSet(resultCol, i, (char*)&result, false); + udfTrace("block:%p, row:%d result is %d", block, i, result); } } + resultData->numOfRows = block->numOfRows; + udfTrace("block:%p, processing completed, rows:%d, cols:%d,", block, block->numOfRows, block->numOfCols); return TSDB_CODE_SUCCESS; }