From 3a63fd5b18efb12d9077681c7b23a4f85fe723ec Mon Sep 17 00:00:00 2001 From: gccgdb1234 Date: Wed, 3 Jul 2024 16:33:55 +0800 Subject: [PATCH] docs: overwrite docs in 3.0 branch with docs in main branch --- docs/en/05-get-started/01-docker.md | 13 +- docs/en/05-get-started/03-package.md | 102 +- .../07-develop/01-connect/_connect_rust.mdx | 4 +- .../01-connect/connection-type-en.webp | Bin 0 -> 14352 bytes docs/en/07-develop/01-connect/index.md | 27 +- docs/en/08-client-libraries/04-java.mdx | 930 ++--------------- docs/en/08-client-libraries/05-go.mdx | 794 +-------------- docs/en/08-client-libraries/06-rust.mdx | 121 +-- docs/en/08-client-libraries/07-python.mdx | 528 ++-------- docs/en/08-client-libraries/08-node.mdx | 491 ++++----- docs/en/08-client-libraries/09-csharp.mdx | 43 +- docs/en/08-client-libraries/50-odbc.mdx | 29 +- docs/en/08-client-libraries/_request_id.mdx | 7 + .../assets/odbc-ws-config-en.webp | Bin 21396 -> 18240 bytes docs/en/08-client-libraries/index.mdx | 10 +- docs/en/10-deployment/01-deploy.md | 6 - docs/en/12-taos-sql/02-database.md | 2 +- docs/en/12-taos-sql/03-table.md | 2 + docs/en/12-taos-sql/04-stable.md | 2 + docs/en/12-taos-sql/06-select.md | 8 +- docs/en/12-taos-sql/08-delete-data.mdx | 2 + docs/en/12-taos-sql/10-function.md | 2 +- docs/en/12-taos-sql/12-distinguished.md | 2 +- docs/en/12-taos-sql/14-stream.md | 4 + docs/en/12-taos-sql/21-node.md | 4 +- docs/en/12-taos-sql/22-meta.md | 10 +- docs/en/12-taos-sql/25-grant.md | 51 +- docs/en/12-taos-sql/26-udf.md | 2 +- docs/en/13-operation/10-monitor.md | 257 ++--- .../14-reference/02-rest-api/02-rest-api.mdx | 79 +- docs/en/14-reference/08-taos-shell.md | 1 + docs/en/14-reference/14-taosKeeper.md | 53 +- docs/en/14-reference/15-explorer.md | 53 + docs/en/20-third-party/75-powerbi.md | 60 +- docs/en/20-third-party/76-yonghongbi.mdx | 64 ++ docs/en/20-third-party/powerbi-step-en.png | Bin 0 -> 13485 bytes docs/en/20-third-party/powerbi-step-en.webp | Bin 37206 -> 0 bytes docs/en/20-third-party/yonghongbi-step-en.png | Bin 0 -> 13775 bytes docs/en/21-tdinternal/01-arch.md | 4 +- docs/en/28-releases/01-tdengine.md | 14 + docs/examples/go/demo/consumer/main.go | 113 +++ docs/examples/go/demo/consumerws/main.go | 115 +++ docs/examples/go/demo/query/main.go | 76 ++ docs/examples/go/demo/sml/main.go | 41 + docs/examples/go/demo/smlws/main.go | 54 + docs/examples/go/demo/stmt/main.go | 81 ++ docs/examples/go/demo/stmtws/main.go | 95 ++ .../websocketexample/json_line_example.js | 53 + .../node/websocketexample/line_example.js | 49 + .../node/websocketexample/nodejsChecker.js | 77 ++ .../node/websocketexample/sql_example.js | 143 +++ .../node/websocketexample/stmt_example.js | 60 ++ .../websocketexample/telnet_line_example.js | 58 ++ .../node/websocketexample/tmq_example.js | 90 ++ .../python/connect_native_reference.py | 16 +- docs/examples/python/create_db_native.py | 26 + docs/examples/python/create_db_rest.py | 18 + docs/examples/python/create_db_ws.py | 22 + docs/examples/python/insert_native.py | 76 ++ docs/examples/python/insert_rest.py | 48 + docs/examples/python/insert_ws.py | 71 ++ docs/examples/python/schemaless_native.py | 36 + docs/examples/python/schemaless_ws.py | 46 + docs/examples/python/stmt_native.py | 53 + docs/examples/python/stmt_ws.py | 52 + docs/examples/python/tmq_native.py | 84 ++ docs/examples/rust/nativeexample/Cargo.toml | 4 +- docs/zh/05-get-started/01-docker.md | 16 +- docs/zh/05-get-started/03-package.md | 98 +- .../07-develop/01-connect/_connect_rust.mdx | 4 +- .../01-connect/connection-type-zh.webp | Bin 0 -> 13774 bytes docs/zh/07-develop/01-connect/index.md | 25 +- docs/zh/07-develop/02-model/index.mdx | 12 +- .../03-insert-data/01-sql-writing.mdx | 8 +- .../03-insert-data/20-kafka-writting.mdx | 2 +- .../03-insert-data/30-influxdb-line.mdx | 6 +- .../03-insert-data/40-opentsdb-telnet.mdx | 2 +- .../03-insert-data/50-opentsdb-json.mdx | 2 +- docs/zh/07-develop/04-query-data/index.mdx | 6 +- docs/zh/07-develop/07-tmq.mdx | 4 +- docs/zh/07-develop/09-udf.md | 4 +- docs/zh/07-develop/index.md | 2 +- docs/zh/08-connector/02-rest-api.mdx | 85 +- docs/zh/08-connector/10-cpp.mdx | 4 +- docs/zh/08-connector/14-java.mdx | 931 ++---------------- docs/zh/08-connector/20-go.mdx | 800 +-------------- docs/zh/08-connector/26-rust.mdx | 126 +-- docs/zh/08-connector/30-python.mdx | 541 ++-------- docs/zh/08-connector/35-node.mdx | 507 +++++----- docs/zh/08-connector/40-csharp.mdx | 41 +- docs/zh/08-connector/45-php.mdx | 2 +- docs/zh/08-connector/50-odbc.mdx | 27 +- docs/zh/08-connector/_05-schemaless.mdx | 2 +- docs/zh/08-connector/_preparation.mdx | 2 +- docs/zh/08-connector/_request_id.mdx | 7 + .../assets/odbc-native-config-zh.webp | Bin 6306 -> 15080 bytes .../assets/odbc-ws-config-zh.webp | Bin 6234 -> 15954 bytes docs/zh/08-connector/index.md | 18 +- docs/zh/10-deployment/01-deploy.md | 8 +- docs/zh/10-deployment/03-k8s.md | 4 +- docs/zh/12-taos-sql/01-data-type.md | 8 +- docs/zh/12-taos-sql/02-database.md | 6 +- docs/zh/12-taos-sql/03-table.md | 4 +- docs/zh/12-taos-sql/04-stable.md | 2 + docs/zh/12-taos-sql/06-select.md | 6 +- docs/zh/12-taos-sql/08-delete-data.mdx | 2 + docs/zh/12-taos-sql/10-function.md | 10 +- docs/zh/12-taos-sql/14-stream.md | 5 +- docs/zh/12-taos-sql/16-operators.md | 6 +- docs/zh/12-taos-sql/21-node.md | 4 +- docs/zh/12-taos-sql/22-meta.md | 13 +- docs/zh/12-taos-sql/27-indexing.md | 2 +- docs/zh/12-taos-sql/29-changes.md | 2 +- docs/zh/12-taos-sql/index.md | 2 +- docs/zh/14-reference/04-taosadapter.md | 6 +- docs/zh/14-reference/05-taosbenchmark.md | 86 +- docs/zh/14-reference/06-taosdump.md | 2 +- docs/zh/14-reference/08-taos-shell.md | 1 + docs/zh/14-reference/12-config/index.md | 4 +- .../13-schemaless/13-schemaless.md | 2 +- docs/zh/14-reference/14-taosKeeper.md | 55 +- docs/zh/14-reference/15-explorer.md | 53 + docs/zh/14-reference/_collectd.mdx | 6 +- docs/zh/14-reference/_icinga2.mdx | 4 +- docs/zh/14-reference/_prometheus.mdx | 4 +- docs/zh/14-reference/_statsd.mdx | 4 +- docs/zh/14-reference/_tcollector.mdx | 2 +- docs/zh/14-reference/_telegraf.mdx | 2 +- docs/zh/17-operation/06-monitor.md | 266 ++--- docs/zh/17-operation/17-diagnose.md | 4 +- docs/zh/20-third-party/01-grafana.mdx | 4 +- docs/zh/20-third-party/02-prometheus.md | 2 +- docs/zh/20-third-party/03-telegraf.md | 4 +- docs/zh/20-third-party/05-collectd.md | 2 +- docs/zh/20-third-party/06-statsd.md | 2 +- docs/zh/20-third-party/07-icinga2.md | 2 +- docs/zh/20-third-party/08-tcollector.md | 2 +- docs/zh/20-third-party/09-emq-broker.md | 4 +- docs/zh/20-third-party/11-kafka.md | 12 +- docs/zh/20-third-party/13-dbeaver.md | 2 +- docs/zh/20-third-party/50-qstudio.md | 2 +- docs/zh/20-third-party/75-powerbi.md | 56 +- docs/zh/20-third-party/76-yonghongbi.mdx | 64 ++ docs/zh/20-third-party/_deploytaosadapter.mdx | 2 +- docs/zh/20-third-party/powerbi-step-zh.png | Bin 0 -> 13402 bytes docs/zh/20-third-party/powerbi-step-zh.webp | Bin 39838 -> 0 bytes docs/zh/20-third-party/yonghongbi-step-zh.png | Bin 0 -> 13563 bytes docs/zh/25-application/01-telegraf.md | 2 +- docs/zh/25-application/02-collectd.md | 2 +- docs/zh/25-application/03-immigrate.md | 2 +- docs/zh/27-train-faq/01-faq.md | 13 +- docs/zh/28-releases/01-tdengine.md | 20 +- 152 files changed, 3881 insertions(+), 5625 deletions(-) create mode 100644 docs/en/07-develop/01-connect/connection-type-en.webp create mode 100644 docs/en/08-client-libraries/_request_id.mdx create mode 100644 docs/en/14-reference/15-explorer.md create mode 100644 docs/en/20-third-party/76-yonghongbi.mdx create mode 100644 docs/en/20-third-party/powerbi-step-en.png delete mode 100644 docs/en/20-third-party/powerbi-step-en.webp create mode 100644 docs/en/20-third-party/yonghongbi-step-en.png create mode 100644 docs/examples/go/demo/consumer/main.go create mode 100644 docs/examples/go/demo/consumerws/main.go create mode 100644 docs/examples/go/demo/query/main.go create mode 100644 docs/examples/go/demo/sml/main.go create mode 100644 docs/examples/go/demo/smlws/main.go create mode 100644 docs/examples/go/demo/stmt/main.go create mode 100644 docs/examples/go/demo/stmtws/main.go create mode 100644 docs/examples/node/websocketexample/json_line_example.js create mode 100644 docs/examples/node/websocketexample/line_example.js create mode 100644 docs/examples/node/websocketexample/nodejsChecker.js create mode 100644 docs/examples/node/websocketexample/sql_example.js create mode 100644 docs/examples/node/websocketexample/stmt_example.js create mode 100644 docs/examples/node/websocketexample/telnet_line_example.js create mode 100644 docs/examples/node/websocketexample/tmq_example.js create mode 100644 docs/examples/python/create_db_native.py create mode 100644 docs/examples/python/create_db_rest.py create mode 100644 docs/examples/python/create_db_ws.py create mode 100644 docs/examples/python/insert_native.py create mode 100644 docs/examples/python/insert_rest.py create mode 100644 docs/examples/python/insert_ws.py create mode 100644 docs/examples/python/schemaless_native.py create mode 100644 docs/examples/python/schemaless_ws.py create mode 100644 docs/examples/python/stmt_native.py create mode 100644 docs/examples/python/stmt_ws.py create mode 100644 docs/examples/python/tmq_native.py create mode 100644 docs/zh/07-develop/01-connect/connection-type-zh.webp create mode 100644 docs/zh/08-connector/_request_id.mdx create mode 100644 docs/zh/14-reference/15-explorer.md create mode 100644 docs/zh/20-third-party/76-yonghongbi.mdx create mode 100644 docs/zh/20-third-party/powerbi-step-zh.png delete mode 100644 docs/zh/20-third-party/powerbi-step-zh.webp create mode 100644 docs/zh/20-third-party/yonghongbi-step-zh.png diff --git a/docs/en/05-get-started/01-docker.md b/docs/en/05-get-started/01-docker.md index 761faf4a05..aed9e4b9f5 100644 --- a/docs/en/05-get-started/01-docker.md +++ b/docs/en/05-get-started/01-docker.md @@ -27,7 +27,7 @@ docker pull tdengine/tdengine:3.0.1.4 And then run the following command: ```shell -docker run -d -p 6030:6030 -p 6041:6041 -p 6043-6049:6043-6049 -p 6043-6049:6043-6049/udp tdengine/tdengine +docker run -d -p 6030:6030 -p 6041:6041 -p 6043-6060:6043-6060 -p 6043-6060:6043-6060/udp tdengine/tdengine ``` Note that TDengine Server 3.0 uses TCP port 6030. Port 6041 is used by taosAdapter for the REST API service. Ports 6043 through 6049 are used by taosAdapter for other connections. You can open these ports as needed. @@ -36,7 +36,7 @@ If you need to persist data to a specific directory on your local machine, pleas ```shell docker run -d -v ~/data/taos/dnode/data:/var/lib/taos \ -v ~/data/taos/dnode/log:/var/log/taos \ - -p 6030:6030 -p 6041:6041 -p 6043-6049:6043-6049 -p 6043-6049:6043-6049/udp tdengine/tdengine + -p 6030:6030 -p 6041:6041 -p 6043-6060:6043-6060 -p 6043-6060:6043-6060/udp tdengine/tdengine ``` :::note @@ -62,7 +62,7 @@ You can now access TDengine or run other Linux commands. Note: For information about installing docker, see the [official documentation](https://docs.docker.com/get-docker/). -## Open the TDengine CLI +## TDengine Command Line Interface On the container, run the following command to open the TDengine CLI: @@ -73,6 +73,12 @@ taos> ``` +## TDegnine Graphic User Interface + +From TDengine 3.3.0.0, there is a new componenet called `taos-explorer` added in the TDengine docker image. You can use it to manage the databases, super tables, child tables, and data in your TDengine system. There are also some features only available in TDengine Enterprise Edition, please contact TDengine sales team in case you need these features. + +To use taos-explorer in the container, you need to access the host port mapped from container port 6060. Assuming the host name is abc.com, and the port used on host is 6060, you need to access `http://abc.com:6060`. taos-explorer uses port 6060 by default in the container. When you use it the first time, you need to register with your enterprise email, then can logon using your user name and password in the TDengine database management system. + ## Test data insert performance After your TDengine Server is running normally, you can run the taosBenchmark utility to test its performance: @@ -125,6 +131,7 @@ SELECT FIRST(ts), AVG(current), MAX(voltage), MIN(phase) FROM test.d10 INTERVAL( In the query above you are selecting the first timestamp (ts) in the interval, another way of selecting this would be `\_wstart` which will give the start of the time window. For more information about windowed queries, see [Time-Series Extensions](../../taos-sql/distinguished/). + ## Additional Information For more information about deploying TDengine in a Docker environment, see [Deploying TDengine with Docker](../../deployment/docker). diff --git a/docs/en/05-get-started/03-package.md b/docs/en/05-get-started/03-package.md index c6f3e60932..cbb88fa472 100644 --- a/docs/en/05-get-started/03-package.md +++ b/docs/en/05-get-started/03-package.md @@ -35,6 +35,10 @@ gcc version - 9.3.1 or above; ## Installation +**Note** + +Since TDengine 3.0.6.0, we don't provide standalone taosTools pacakge for downloading. However, all the tools included in the taosTools pacakge can be found in TDengine-server pacakge. + @@ -119,11 +123,18 @@ This installation method is supported only for Debian and Ubuntu. -Note: TDengine only supports Windows Server 2016/2019 and Windows 10/11 on the Windows platform. +**Note** +- TDengine only supports Windows Server 2016/2019 and Windows 10/11 on the Windows platform. +- Since TDengine 3.1.0.0, we wonly provide client package for Windows. If you need to run TDenginer server on Windows, please contact TDengine sales team to upgrade to TDengine Enterprise. +- To run on Windows, the Microsoft Visual C++ Runtime library is required. If the Microsoft Visual C++ Runtime Library is missing on your platform, you can download and install it from [VC Runtime Library](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170). + +Follow the steps below: 1. Download the Windows installation package. 2. Run the downloaded package to install TDengine. +Note: From version 3.0.1.7, only TDengine client pacakge can be downloaded for Windows platform. If you want to run TDengine servers on Windows, please contact our sales team to upgrade to TDengine Enterprise. + @@ -153,38 +164,26 @@ After the installation is complete, run the following command to start the TDeng ```bash systemctl start taosd +systemctl start taosadapter +systemctl start taoskeeper +systemctl start taos-explorer ``` -Run the following command to confirm that TDengine is running normally: +Or you can run a scrip to start all the above services together ```bash +start-all.sh +``` + +systemctl can also be used to stop, restart a specific service or check its status, like below using `taosd` as example: + +```bash +systemctl start taosd +systemctl stop taosd +systemctl restart taosd systemctl status taosd ``` -Output similar to the following indicates that TDengine is running normally: - -``` -Active: active (running) -``` - -Output similar to the following indicates that TDengine has not started successfully: - -``` -Active: inactive (dead) -``` - -After confirming that TDengine is running, run the `taos` command to access the TDengine CLI. - -The following `systemctl` commands can help you manage TDengine service: - -- Start TDengine Server: `systemctl start taosd` - -- Stop TDengine Server: `systemctl stop taosd` - -- Restart TDengine Server: `systemctl restart taosd` - -- Check TDengine Server status: `systemctl status taosd` - :::info - The `systemctl` command requires _root_ privileges. If you are not logged in as the _root_ user, use the `sudo` command. @@ -193,35 +192,38 @@ The following `systemctl` commands can help you manage TDengine service: ::: -## Command Line Interface (CLI) - -You can use the TDengine CLI to monitor your TDengine deployment and execute ad hoc queries. To open the CLI, you can execute `taos` in terminal. - After the installation is complete, please run `sc start taosd` or run `C:\TDengine\taosd.exe` with administrator privilege to start TDengine Server. Please run `sc start taosadapter` or run `C:\TDengine\taosadapter.exe` with administrator privilege to start taosAdapter to provide http/REST service. -## Command Line Interface (CLI) - -You can use the TDengine CLI to monitor your TDengine deployment and execute ad hoc queries. To open the CLI, you can run `taos.exe` in the `C:\TDengine` directory of the Windows terminal to start the TDengine command line. - -After the installation is complete, double-click the /applications/TDengine to start the program, or run `launchctl start com.tdengine.taosd` to start TDengine Server. +After the installation is complete, double-click the /applications/TDengine to start the program, or run `sudo launchctl start ` to start TDengine services. -The following `launchctl` commands can help you manage TDengine service: +```bash +sudo launchctl start com.tdengine.taosd +sudo launchctl start com.tdengine.taosadapter +sudo launchctl start com.tdengine.taoskeeper +sudo launchctl start com.tdengine.taos-explorer +``` -- Start TDengine Server: `sudo launchctl start com.tdengine.taosd` +Or you can run a scrip to start all the above services together +```bash +start-all.sh +``` -- Stop TDengine Server: `sudo launchctl stop com.tdengine.taosd` +The following `launchctl` commands can help you manage TDengine service, using `taosd` service as an example below: -- Check TDengine Server status: `sudo launchctl list | grep taosd` - -- Check TDengine Server status details: `launchctl print system/com.tdengine.taosd` +```bash +sudo launchctl start com.tdengine.taosd +sudo launchctl stop com.tdengine.taosd +sudo launchctl list | grep taosd +sudo launchctl print system/com.tdengine.taosd +``` :::info - Please use `sudo` to run `launchctl` to manage _com.tdengine.taosd_ with administrator privileges. @@ -232,24 +234,20 @@ The following `launchctl` commands can help you manage TDengine service: ::: -## Command Line Interface (CLI) - -You can use the TDengine CLI to monitor your TDengine deployment and execute ad hoc queries. To open the CLI, you can execute `taos` in terminal. -```bash -taos -``` -The TDengine CLI displays a welcome message and version information to indicate that its connection to the TDengine service was successful. If an error message is displayed, see the [FAQ](../../train-faq/faq) for troubleshooting information. At the following prompt, you can execute SQL commands. +## TDengine Command Line Interface + +You can use the TDengine CLI to monitor your TDengine deployment and execute ad hoc queries. To open the CLI, you can execute `taos` (Linux/Mac) or `taos.exe` (Windows) in terminal. The prompt of TDengine CLI is like below: ```cmd taos> ``` -For example, you can create and delete databases and tables and run all types of queries. Each SQL command must be end with a semicolon (;). For example: +Using TDengine CLI, you can create and delete databases and tables and run all types of queries. Each SQL command must be end with a semicolon (;). For example: ```sql CREATE DATABASE demo; @@ -269,6 +267,12 @@ Query OK, 2 row(s) in set (0.003128s) You can also can monitor the deployment status, add and remove user accounts, and manage running instances. You can run the TDengine CLI on either machines. For more information, see [TDengine CLI](../../reference/taos-shell/). +## TDengine Graphic User Interface + +From TDengine 3.3.0.0, there is a new componenet called `taos-explorer` added in the TDengine docker image. You can use it to manage the databases, super tables, child tables, and data in your TDengine system. There are also some features only available in TDengine Enterprise Edition, please contact TDengine sales team in case you need these features. + +To use taos-explorer in the container, you need to access the host port mapped from container port 6060. Assuming the host name is abc.com, and the port used on host is 6060, you need to access `http://abc.com:6060`. taos-explorer uses port 6060 by default in the container. When you use it the first time, you need to register with your enterprise email, then can logon using your user name and password in the TDengine + ## Test data insert performance After your TDengine Server is running normally, you can run the taosBenchmark utility to test its performance: diff --git a/docs/en/07-develop/01-connect/_connect_rust.mdx b/docs/en/07-develop/01-connect/_connect_rust.mdx index 5746968263..1a895cf932 100644 --- a/docs/en/07-develop/01-connect/_connect_rust.mdx +++ b/docs/en/07-develop/01-connect/_connect_rust.mdx @@ -1,8 +1,8 @@ -```rust title="Native Connection/REST Connection" +```rust title="Native Connection" {{#include docs/examples/rust/nativeexample/examples/connect.rs}} ``` :::note -For Rust client library, the connection depends on the feature being used. If "rest" feature is enabled, then only the implementation for "rest" is compiled and packaged. +For Rust client library, the connection depends on the feature being used. If "ws" feature is enabled, then only the implementation for "websocket" is compiled and packaged. ::: diff --git a/docs/en/07-develop/01-connect/connection-type-en.webp b/docs/en/07-develop/01-connect/connection-type-en.webp new file mode 100644 index 0000000000000000000000000000000000000000..995222610c43087811debdca81943f27eee90de7 GIT binary patch literal 14352 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 literal 0 HcmV?d00001 diff --git a/docs/en/07-develop/01-connect/index.md b/docs/en/07-develop/01-connect/index.md index 32a70a680c..ec3ed7b6dd 100644 --- a/docs/en/07-develop/01-connect/index.md +++ b/docs/en/07-develop/01-connect/index.md @@ -26,17 +26,24 @@ Any application running on any platform can access TDengine through the REST API ## Establish Connection -There are two ways for a client library to establish connections to TDengine: +There are three ways for a client library to establish connections to TDengine: -1. REST connection through the REST API provided by the taosAdapter component. -2. Native connection through the TDengine client driver (taosc). +1. Native connection through the TDengine client driver (taosc). +2. REST connection through the REST API provided by the taosAdapter component. +3. Websocket connection provided by the taosAdapter component. -For REST and native connections, client libraries provide similar APIs for performing operations and running SQL statements on your databases. The main difference is the method of establishing the connection, which is not visible to users. +![TDengine connection type](connection-type-en.webp) + +For these ways of connections, client libraries provide similar APIs for performing operations and running SQL statements on your databases. The main difference is the method of establishing the connection, which is not visible to users. Key differences: -3. The REST connection is more accessible with cross-platform support, however it results in a 30% performance downgrade. -1. The TDengine client driver (taosc) has the highest performance with all the features of TDengine like [Parameter Binding](../../client-libraries/cpp#parameter-binding-api), [Subscription](../../client-libraries/cpp#subscription-api), etc. +1. For a Native connection, the client driver taosc and the server TDengine version must be compatible. +2. For a REST connection, users do not need to install the client driver taosc, providing the advantage of cross-platform ease of use. However, functions such as data subscription and binary data types are not available. Additionally, compared to Native and Websocket connections, a REST connection has the worst performance. +3. For a Websocket connection, users also do not need to install the client driver taosc. +4. To connect to a cloud service instance, you need to use the REST connection or Websocket connection. + +Normally we recommend using **Websocket connection**. ## Install Client Driver taosc @@ -123,18 +130,18 @@ require github.com/taosdata/driver-go/v3 latest -Just need to add `libtaos` dependency in `Cargo.toml`. +Just need to add `taos` dependency in `Cargo.toml`. ```toml title=Cargo.toml [dependencies] -libtaos = { version = "0.4.2"} +taos = { version = "*"} ``` :::info -Rust client library uses different features to distinguish the way to establish connection. To establish REST connection, please enable `rest` feature. +Rust client library uses different features to distinguish the way to establish connection. To establish Websocket connection, please enable `ws` feature. ```toml -libtaos = { version = "*", features = ["rest"] } +taos = { version = "*", default-features = false, features = ["ws"] } ``` ::: diff --git a/docs/en/08-client-libraries/04-java.mdx b/docs/en/08-client-libraries/04-java.mdx index b0a2671d94..8aa4a8994e 100644 --- a/docs/en/08-client-libraries/04-java.mdx +++ b/docs/en/08-client-libraries/04-java.mdx @@ -7,17 +7,9 @@ toc_max_heading_level: 4 import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; +import RequestId from "./_request_id.mdx"; -`taos-jdbcdriver` is the official Java client library for TDengine. Java developers can use it to develop applications that access data in TDengine. `taos-jdbcdriver` implements standard JDBC driver interfaces and two connection methods: One is **native connection**, which connects to TDengine instances natively through the TDengine client driver (taosc), supporting data writing, querying, subscriptions, schemaless writing, and bind interface. The second is **REST connection** which is implemented through taosAdapter. The set of features implemented by the REST connection differs slightly from those implemented by the native connection. - -![TDengine Java client library](tdengine-jdbc-connector.webp) - -The preceding figure shows the two ways in which a Java application can access TDengine. - -- JDBC native connection: Java applications use TSDBDriver on physical node 1 (pnode1) to call client-driven directly (`libtaos.so` or `taos.dll`) APIs to send writing and query requests to taosd instances located on physical node 2 (pnode2). -- JDBC REST connection: The Java application encapsulates the SQL as a REST request via RestfulDriver, sends it to the REST server (taosAdapter) on physical node 2. taosAdapter forwards the request to TDengine server and returns the result. - -The REST connection, which does not rely on TDengine client drivers, is more convenient and flexible, in addition to being cross-platform. However the performance is about 30% lower than that of the native connection. +`taos-jdbcdriver` is the official Java client library for TDengine. Java developers can use it to develop applications that access data in TDengine. `taos-jdbcdriver` implements standard JDBC driver interfaces. :::info TDengine's JDBC driver implementation is as consistent as possible with the relational database driver. Still, there are differences in the use scenarios and technical characteristics of TDengine and relational object databases. So 'taos-jdbcdriver' also has some differences from traditional JDBC drivers. It is important to keep the following points in mind: @@ -27,6 +19,20 @@ TDengine's JDBC driver implementation is as consistent as possible with the rela ::: +## Connection types + +`taos-jdbcdriver` mainly provides 3 connection types, among which we recommend using **websocket connection**. +- **Native connection**, which connects to TDengine instances natively through the TDengine client driver (taosc), supporting data writing, querying, subscriptions, schemaless writing, and bind interface. +- **REST connection**, which is implemented through taosAdapter. Some features like schemaless and subscriptions are not supported. +- **Websocket connection** which is implemented through taosAdapter. The set of features implemented by the WebSocket connection differs slightly from those implemented by the native connection. + +For a detailed introduction of the connection types, please refer to: [Establish Connection](../../develop/connect/#establish-connection) + +## Compatibility with JDBC and JRE Versions + +- JDBC: implements JDBC 4.2 version, some features like schemaless and data subscription are provided individually. +- JRE: supports JRE 8 or higher. + ## Supported platforms Native connections are supported on the same platforms as the TDengine client driver. @@ -49,7 +55,7 @@ REST connection supports all platforms that can run Java. | 3.2.1 | JDBC REST connection supports schemaless/prepareStatement over WebSocket | 3.0.3.0 or later | | 3.2.0 | This version has been deprecated | - | | 3.1.0 | JDBC REST connection supports subscription over WebSocket | - | -| 3.0.1 - 3.0.4 | fix the resultSet data is parsed incorrectly sometimes. 3.0.1 is compiled on JDK 11, you are advised to use other version in the JDK 8 environment | - | +| 3.0.1 - 3.0.4 | Fixed the issue of result set data sometimes parsing incorrectly. 3.0.1 is compiled on JDK 11, you are advised to use other version in the JDK 8 environment | - | | 3.0.0 | Support for TDengine 3.0 | 3.0.0.0 or later | | 2.0.42 | Fix wasNull interface return value in WebSocket connection | - | | 2.0.41 | Fix decode method of username and password in REST connection | - | @@ -58,23 +64,12 @@ REST connection supports all platforms that can run Java. | 2.0.37 | Support json tags | - | | 2.0.36 | Support schemaless writing | - | -**Note**: adding `batchfetch` to the REST connection and setting it to true will enable the WebSocket connection. - ### Handling exceptions After an error is reported, the error message and error code can be obtained through SQLException. ```java -try (Statement statement = connection.createStatement()) { - // executeQuery - ResultSet resultSet = statement.executeQuery(sql); - // print result - printResult(resultSet); -} catch (SQLException e) { - System.out.println("ERROR Message: " + e.getMessage()); - System.out.println("ERROR Code: " + e.getErrorCode()); - e.printStackTrace(); -} +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/JdbcBasicDemo.java:jdbc_exception}} ``` There are four types of error codes that the JDBC client library can report: @@ -211,16 +206,18 @@ TDengine's JDBC URL specification format is: For establishing connections, native connections differ slightly from REST connections. +**Note**: adding `batchfetch` to the REST connection and setting it to true will enable the WebSocket connection. + ```java Class.forName("com.taosdata.jdbc.TSDBDriver"); -String jdbcUrl = "jdbc:TAOS://taosdemo.com:6030/test?user=root&password=taosdata"; +String jdbcUrl = "jdbc:TAOS://taosdemo.com:6030/power?user=root&password=taosdata"; Connection conn = DriverManager.getConnection(jdbcUrl); ``` -In the above example, TSDBDriver, which uses a JDBC native connection, establishes a connection to a hostname `taosdemo.com`, port `6030` (the default port for TDengine), and a database named `test`. In this URL, the user name `user` is specified as `root`, and the `password` is `taosdata`. +In the above example, TSDBDriver, which uses a JDBC native connection, establishes a connection to a hostname `taosdemo.com`, port `6030` (the default port for TDengine), and a database named `power`. In this URL, the user name `user` is specified as `root`, and the `password` is `taosdata`. Note: With JDBC native connections, taos-jdbcdriver relies on the client driver (`libtaos.so` on Linux; `taos.dll` on Windows; `libtaos.dylib` on macOS). @@ -244,7 +241,7 @@ When you use a JDBC native connection to connect to a TDengine cluster, you can ```java public Connection getConn() throws Exception{ Class.forName("com.taosdata.jdbc.TSDBDriver"); - String jdbcUrl = "jdbc:TAOS://:/test?user=root&password=taosdata"; + String jdbcUrl = "jdbc:TAOS://:/power?user=root&password=taosdata"; Properties connProps = new Properties(); connProps.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8"); connProps.setProperty(TSDBDriver.PROPERTY_KEY_LOCALE, "en_US.UTF-8"); @@ -270,7 +267,7 @@ secondEp cluster_node2:6030 # locale en_US.UTF-8 ``` -In the above example, JDBC uses the client's configuration file to establish a connection to a hostname `cluster_node1`, port 6030, and a database named `test`. When the firstEp node in the cluster fails, JDBC attempts to connect to the cluster using secondEp. +In the above example, JDBC uses the client's configuration file to establish a connection to a hostname `cluster_node1`, port 6030, and a database named `power`. When the firstEp node in the cluster fails, JDBC attempts to connect to the cluster using secondEp. In TDengine, as long as one node in firstEp and secondEp is valid, the connection to the cluster can be established normally. @@ -281,11 +278,11 @@ The configuration file here refers to the configuration file on the machine wher ```java Class.forName("com.taosdata.jdbc.rs.RestfulDriver"); -String jdbcUrl = "jdbc:TAOS-RS://taosdemo.com:6041/test?user=root&password=taosdata"; +String jdbcUrl = "jdbc:TAOS-RS://taosdemo.com:6041/power?user=root&password=taosdata"; Connection conn = DriverManager.getConnection(jdbcUrl); ``` -In the above example, a RestfulDriver with a JDBC REST connection is used to establish a connection to a database named `test` with hostname `taosdemo.com` on port `6041`. The URL specifies the user name as `root` and the password as `taosdata`. +In the above example, a RestfulDriver with a JDBC REST connection is used to establish a connection to a database named `power` with hostname `taosdemo.com` on port `6041`. The URL specifies the user name as `root` and the password as `taosdata`. There is no dependency on the client driver when Using a JDBC REST connection. Compared to a JDBC native connection, only the following are required: @@ -313,10 +310,10 @@ The configuration parameters in the URL are as follows: - Unlike the native connection method, the REST interface is stateless. When using the JDBC REST connection, you need to specify the database name of the table and super table in SQL. For example: ```sql -INSERT INTO test.t1 USING test.weather (ts, temperature) TAGS('California.SanFrancisco') VALUES(now, 24.6); +INSERT INTO power.d1001 USING power.meters TAGS(2,'California.SanFrancisco') VALUES (NOW, 10.30000, 219, 0.31000); ``` -- If the dbname is specified in the URL, the JDBC REST connection uses /rest/sql/dbname as the default URL for RESTful requests. In this case, it is not necessary to specify the dbname in SQL. For example, if the URL is `jdbc:TAOS-RS://127.0.0.1:6041/test`, then the SQL can be executed: insert into test using weather(ts, temperature) tags('California.SanFrancisco') values(now, 24.6); +- If the dbname is specified in the URL, the JDBC REST connection uses /rest/sql/dbname as the default URL for RESTful requests. In this case, it is not necessary to specify the dbname in SQL. For example, if the URL is `jdbc:TAOS-RS://127.0.0.1:6041/power`, then the SQL can be executed: INSERT INTO d1001 USING meters TAGS(2,'California.SanFrancisco') VALUES (NOW, 10.30000, 219, 0.31000); ::: @@ -330,25 +327,24 @@ In addition to getting the connection from the specified URL, you can use Proper Note: - The client parameter set in the application is process-level. If you want to update the parameters of the client, you need to restart the application. This is because the client parameter is a global parameter that takes effect only the first time the application is set. -- The following sample code is based on taos-jdbcdriver-3.1.0. +- The following sample code is based on taos-jdbcdriver-3.1.0 version or above. ```java public Connection getConn() throws Exception{ Class.forName("com.taosdata.jdbc.TSDBDriver"); - String jdbcUrl = "jdbc:TAOS://taosdemo.com:6030/test?user=root&password=taosdata"; + String jdbcUrl = "jdbc:TAOS://taosdemo.com:6030/power?user=root&password=taosdata"; Properties connProps = new Properties(); connProps.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8"); connProps.setProperty(TSDBDriver.PROPERTY_KEY_LOCALE, "en_US.UTF-8"); connProps.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC-8"); connProps.setProperty("debugFlag", "135"); - connProps.setProperty("maxSQLLength", "1048576"); Connection conn = DriverManager.getConnection(jdbcUrl, connProps); return conn; } public Connection getRestConn() throws Exception{ Class.forName("com.taosdata.jdbc.rs.RestfulDriver"); - String jdbcUrl = "jdbc:TAOS-RS://taosdemo.com:6041/test?user=root&password=taosdata"; + String jdbcUrl = "jdbc:TAOS-RS://taosdemo.com:6041/power?user=root&password=taosdata"; Properties connProps = new Properties(); connProps.setProperty(TSDBDriver.PROPERTY_KEY_BATCH_LOAD, "true"); Connection conn = DriverManager.getConnection(jdbcUrl, connProps); @@ -356,7 +352,7 @@ public Connection getRestConn() throws Exception{ } ``` -In the above example, a connection is established to `taosdemo.com`, port is 6030/6041, and database named `test`. The connection specifies the user name as `root` and the password as `taosdata` in the URL and specifies the character set, language environment, time zone, and whether to enable bulk fetching in the connProps.The url specifies the user name as `root` and the password as `taosdata`. +In the above example, a connection is established to `taosdemo.com`, port is 6030/6041, and database named `power`. The connection specifies the user name as `root` and the password as `taosdata` in the URL and specifies the character set, language environment, time zone, and whether to enable bulk fetching in the connProps.The url specifies the user name as `root` and the password as `taosdata`. The configuration parameters in properties are as follows. @@ -399,64 +395,34 @@ For example, if you specify the password as `taosdata` in the URL and specify th ### Create database and tables ```java -Statement stmt = conn.createStatement(); - -// create database -stmt.executeUpdate("create database if not exists db"); - -// use database -stmt.executeUpdate("use db"); - -// create table -stmt.executeUpdate("create table if not exists tb (ts timestamp, temperature int, humidity float)"); +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/JdbcBasicDemo.java:create_db_and_table}} ``` -> **Note**: If you do not use `use db` to specify the database, all subsequent operations on the table need to add the database name as a prefix, such as db.tb. +> **Note**: If you do not use `USE power` to specify the database, all subsequent operations on the table need to add the database name as a prefix, such as power.meters. ### Insert data ```java -// insert data -int affectedRows = stmt.executeUpdate("insert into tb values(now, 23, 10.3) (now + 1s, 20, 9.3)"); - -System.out.println("insert " + affectedRows + " rows."); +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/JdbcBasicDemo.java:insert_data}} ``` -> now is an internal function. The default is the current time of the client's computer. -> `now + 1s` represents the current time of the client plus 1 second, followed by the number representing the unit of time: a (milliseconds), s (seconds), m (minutes), h (hours), d (days), w (weeks), n (months), y (years). +> NOW is an internal function. The default is the current time of the client's computer. +> `NOW + 1s` represents the current time of the client plus 1 second, followed by the number representing the unit of time: a (milliseconds), s (seconds), m (minutes), h (hours), d (days), w (weeks), n (months), y (years). ### Querying data ```java -// insert data -ResultSet resultSet = stmt.executeQuery("select * from tb"); - -Timestamp ts = null; -int temperature = 0; -float humidity = 0; -while(resultSet.next()){ - - ts = resultSet.getTimestamp(1); - temperature = resultSet.getInt(2); - humidity = resultSet.getFloat("humidity"); - - System.out.printf("%s, %d, %s\n", ts, temperature, humidity); -} +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/JdbcBasicDemo.java:query_data}} ``` > The query is consistent with operating a relational database. When using subscripts to get the contents of the returned fields, you have to start from 1. However, we recommend using the field names to get the values of the fields in the result set. -### execute SQL with reqId +### Execute SQL with reqId -This reqId can be used to request link tracing. + ```java -AbstractStatement aStmt = (AbstractStatement) connection.createStatement(); -aStmt.execute("create database if not exists db", 1L); -aStmt.executeUpdate("use db", 2L); -try (ResultSet rs = aStmt.executeQuery("select * from tb", 3L)) { - Timestamp ts = rs.getTimestamp(1); -} +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/JdbcBasicDemo.java:with_reqid}} ``` ### Writing data via parameter binding @@ -466,7 +432,7 @@ TDengine has significantly improved the bind APIs to support data writing (INSER **Note:** - JDBC REST connections do not currently support bind interface -- The following sample code is based on taos-jdbcdriver-3.2.1 +- The following sample code is based on taos-jdbcdriver-3.2.1 version or above - The setString method should be called for binary type data, and the setNString method should be called for nchar type data - Do not use `db.?` in prepareStatement when specify the database with the table name, should directly use `?`, then specify the database in setTableName, for example: `prepareStatement.setTableName("db.t1")`. @@ -474,312 +440,23 @@ TDengine has significantly improved the bind APIs to support data writing (INSER ```java -public class ParameterBindingDemo { - - private static final String host = "127.0.0.1"; - private static final Random random = new Random(System.currentTimeMillis()); - private static final int BINARY_COLUMN_SIZE = 50; - private static final String[] schemaList = { - "create table stable1(ts timestamp, f1 tinyint, f2 smallint, f3 int, f4 bigint) tags(t1 tinyint, t2 smallint, t3 int, t4 bigint)", - "create table stable2(ts timestamp, f1 float, f2 double) tags(t1 float, t2 double)", - "create table stable3(ts timestamp, f1 bool) tags(t1 bool)", - "create table stable4(ts timestamp, f1 binary(" + BINARY_COLUMN_SIZE + ")) tags(t1 binary(" + BINARY_COLUMN_SIZE + "))", - "create table stable5(ts timestamp, f1 nchar(" + BINARY_COLUMN_SIZE + ")) tags(t1 nchar(" + BINARY_COLUMN_SIZE + "))", - "create table stable6(ts timestamp, f1 varbinary(" + BINARY_COLUMN_SIZE + ")) tags(t1 varbinary(" + BINARY_COLUMN_SIZE + "))", - "create table stable7(ts timestamp, f1 geometry(" + BINARY_COLUMN_SIZE + ")) tags(t1 geometry(" + BINARY_COLUMN_SIZE + "))", - }; - private static final int numOfSubTable = 10, numOfRow = 10; - - public static void main(String[] args) throws SQLException { - - String jdbcUrl = "jdbc:TAOS://" + host + ":6030/"; - Connection conn = DriverManager.getConnection(jdbcUrl, "root", "taosdata"); - - init(conn); - - bindInteger(conn); - bindFloat(conn); - bindBoolean(conn); - bindBytes(conn); - bindString(conn); - bindVarbinary(conn); - bindGeometry(conn); - - clean(conn); - conn.close(); - } - - private static void init(Connection conn) throws SQLException { - clean(conn); - try (Statement stmt = conn.createStatement()) { - stmt.execute("create database if not exists test_parabind"); - stmt.execute("use test_parabind"); - for (int i = 0; i < schemaList.length; i++) { - stmt.execute(schemaList[i]); - } - } - } - private static void clean(Connection conn) throws SQLException { - try (Statement stmt = conn.createStatement()) { - stmt.execute("drop database if exists test_parabind"); - } - } - - private static void bindInteger(Connection conn) throws SQLException { - String sql = "insert into ? using stable1 tags(?,?,?,?) values(?,?,?,?,?)"; - - try (TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class)) { - - for (int i = 1; i <= numOfSubTable; i++) { - // set table name - pstmt.setTableName("t1_" + i); - // set tags - pstmt.setTagByte(0, Byte.parseByte(Integer.toString(random.nextInt(Byte.MAX_VALUE)))); - pstmt.setTagShort(1, Short.parseShort(Integer.toString(random.nextInt(Short.MAX_VALUE)))); - pstmt.setTagInt(2, random.nextInt(Integer.MAX_VALUE)); - pstmt.setTagLong(3, random.nextLong()); - // set columns - ArrayList tsList = new ArrayList<>(); - long current = System.currentTimeMillis(); - for (int j = 0; j < numOfRow; j++) - tsList.add(current + j); - pstmt.setTimestamp(0, tsList); - - ArrayList f1List = new ArrayList<>(); - for (int j = 0; j < numOfRow; j++) - f1List.add(Byte.parseByte(Integer.toString(random.nextInt(Byte.MAX_VALUE)))); - pstmt.setByte(1, f1List); - - ArrayList f2List = new ArrayList<>(); - for (int j = 0; j < numOfRow; j++) - f2List.add(Short.parseShort(Integer.toString(random.nextInt(Short.MAX_VALUE)))); - pstmt.setShort(2, f2List); - - ArrayList f3List = new ArrayList<>(); - for (int j = 0; j < numOfRow; j++) - f3List.add(random.nextInt(Integer.MAX_VALUE)); - pstmt.setInt(3, f3List); - - ArrayList f4List = new ArrayList<>(); - for (int j = 0; j < numOfRow; j++) - f4List.add(random.nextLong()); - pstmt.setLong(4, f4List); - - // add column - pstmt.columnDataAddBatch(); - } - // execute column - pstmt.columnDataExecuteBatch(); - } - } - - private static void bindFloat(Connection conn) throws SQLException { - String sql = "insert into ? using stable2 tags(?,?) values(?,?,?)"; - - TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class); - - for (int i = 1; i <= numOfSubTable; i++) { - // set table name - pstmt.setTableName("t2_" + i); - // set tags - pstmt.setTagFloat(0, random.nextFloat()); - pstmt.setTagDouble(1, random.nextDouble()); - // set columns - ArrayList tsList = new ArrayList<>(); - long current = System.currentTimeMillis(); - for (int j = 0; j < numOfRow; j++) - tsList.add(current + j); - pstmt.setTimestamp(0, tsList); - - ArrayList f1List = new ArrayList<>(); - for (int j = 0; j < numOfRow; j++) - f1List.add(random.nextFloat()); - pstmt.setFloat(1, f1List); - - ArrayList f2List = new ArrayList<>(); - for (int j = 0; j < numOfRow; j++) - f2List.add(random.nextDouble()); - pstmt.setDouble(2, f2List); - - // add column - pstmt.columnDataAddBatch(); - } - // execute - pstmt.columnDataExecuteBatch(); - // close if no try-with-catch statement is used - pstmt.close(); - } - - private static void bindBoolean(Connection conn) throws SQLException { - String sql = "insert into ? using stable3 tags(?) values(?,?)"; - - try (TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class)) { - for (int i = 1; i <= numOfSubTable; i++) { - // set table name - pstmt.setTableName("t3_" + i); - // set tags - pstmt.setTagBoolean(0, random.nextBoolean()); - // set columns - ArrayList tsList = new ArrayList<>(); - long current = System.currentTimeMillis(); - for (int j = 0; j < numOfRow; j++) - tsList.add(current + j); - pstmt.setTimestamp(0, tsList); - - ArrayList f1List = new ArrayList<>(); - for (int j = 0; j < numOfRow; j++) - f1List.add(random.nextBoolean()); - pstmt.setBoolean(1, f1List); - - // add column - pstmt.columnDataAddBatch(); - } - // execute - pstmt.columnDataExecuteBatch(); - } - } - - private static void bindBytes(Connection conn) throws SQLException { - String sql = "insert into ? using stable4 tags(?) values(?,?)"; - - try (TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class)) { - - for (int i = 1; i <= numOfSubTable; i++) { - // set table name - pstmt.setTableName("t4_" + i); - // set tags - pstmt.setTagString(0, new String("abc")); - - // set columns - ArrayList tsList = new ArrayList<>(); - long current = System.currentTimeMillis(); - for (int j = 0; j < numOfRow; j++) - tsList.add(current + j); - pstmt.setTimestamp(0, tsList); - - ArrayList f1List = new ArrayList<>(); - for (int j = 0; j < numOfRow; j++) { - f1List.add(new String("abc")); - } - pstmt.setString(1, f1List, BINARY_COLUMN_SIZE); - - // add column - pstmt.columnDataAddBatch(); - } - // execute - pstmt.columnDataExecuteBatch(); - } - } - - private static void bindString(Connection conn) throws SQLException { - String sql = "insert into ? using stable5 tags(?) values(?,?)"; - - try (TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class)) { - - for (int i = 1; i <= numOfSubTable; i++) { - // set table name - pstmt.setTableName("t5_" + i); - // set tags - pstmt.setTagNString(0, "California.SanFrancisco"); - - // set columns - ArrayList tsList = new ArrayList<>(); - long current = System.currentTimeMillis(); - for (int j = 0; j < numOfRow; j++) - tsList.add(current + j); - pstmt.setTimestamp(0, tsList); - - ArrayList f1List = new ArrayList<>(); - for (int j = 0; j < numOfRow; j++) { - f1List.add("California.LosAngeles"); - } - pstmt.setNString(1, f1List, BINARY_COLUMN_SIZE); - - // add column - pstmt.columnDataAddBatch(); - } - // execute - pstmt.columnDataExecuteBatch(); - } - } - - private static void bindVarbinary(Connection conn) throws SQLException { - String sql = "insert into ? using stable6 tags(?) values(?,?)"; - - try (TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class)) { - - for (int i = 1; i <= numOfSubTable; i++) { - // set table name - pstmt.setTableName("t6_" + i); - // set tags - byte[] bTag = new byte[]{0,2,3,4,5}; - bTag[0] = (byte) i; - pstmt.setTagVarbinary(0, bTag); - - // set columns - ArrayList tsList = new ArrayList<>(); - long current = System.currentTimeMillis(); - for (int j = 0; j < numOfRow; j++) - tsList.add(current + j); - pstmt.setTimestamp(0, tsList); - - ArrayList f1List = new ArrayList<>(); - for (int j = 0; j < numOfRow; j++) { - byte[] v = new byte[]{0,2,3,4,5,6}; - v[0] = (byte)j; - f1List.add(v); - } - pstmt.setVarbinary(1, f1List, BINARY_COLUMN_SIZE); - - // add column - pstmt.columnDataAddBatch(); - } - // execute - pstmt.columnDataExecuteBatch(); - } - } - - private static void bindGeometry(Connection conn) throws SQLException { - String sql = "insert into ? using stable7 tags(?) values(?,?)"; - - try (TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class)) { - - byte[] g1 = StringUtils.hexToBytes("0101000000000000000000F03F0000000000000040"); - byte[] g2 = StringUtils.hexToBytes("0102000020E610000002000000000000000000F03F000000000000004000000000000008400000000000001040"); - List listGeo = new ArrayList<>(); - listGeo.add(g1); - listGeo.add(g2); - - for (int i = 1; i <= 2; i++) { - // set table name - pstmt.setTableName("t7_" + i); - // set tags - pstmt.setTagGeometry(0, listGeo.get(i - 1)); - - // set columns - ArrayList tsList = new ArrayList<>(); - long current = System.currentTimeMillis(); - for (int j = 0; j < numOfRow; j++) - tsList.add(current + j); - pstmt.setTimestamp(0, tsList); - - ArrayList f1List = new ArrayList<>(); - for (int j = 0; j < numOfRow; j++) { - f1List.add(listGeo.get(i - 1)); - } - pstmt.setGeometry(1, f1List, BINARY_COLUMN_SIZE); - - // add column - pstmt.columnDataAddBatch(); - } - // execute - pstmt.columnDataExecuteBatch(); - } - } -} +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/ParameterBindingBasicDemo.java:para_bind}} ``` -**Note**: both String and byte[] require the user to declare the width of the corresponding column in the size parameter of the table definition +This is the [Detailed Example](https://github.com/taosdata/TDengine/blob/main/examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/ParameterBindingFullDemo.java) + + + + + +```java +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/WSParameterBindingBasicDemo.java:para_bind}} +``` + +This is the [Detailed Example](https://github.com/taosdata/TDengine/blob/main/examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/WSParameterBindingFullDemo.java) + + + The methods to set VALUES columns: @@ -797,178 +474,7 @@ public void setNString(int columnIndex, ArrayList list, int size) throws public void setVarbinary(int columnIndex, ArrayList list, int size) throws SQLException public void setGeometry(int columnIndex, ArrayList list, int size) throws SQLException ``` - - - - -```java -public class ParameterBindingDemo { - private static final String host = "127.0.0.1"; - private static final Random random = new Random(System.currentTimeMillis()); - private static final int BINARY_COLUMN_SIZE = 30; - private static final String[] schemaList = { - "create table stable1(ts timestamp, f1 tinyint, f2 smallint, f3 int, f4 bigint) tags(t1 tinyint, t2 smallint, t3 int, t4 bigint)", - "create table stable2(ts timestamp, f1 float, f2 double) tags(t1 float, t2 double)", - "create table stable3(ts timestamp, f1 bool) tags(t1 bool)", - "create table stable4(ts timestamp, f1 binary(" + BINARY_COLUMN_SIZE + ")) tags(t1 binary(" + BINARY_COLUMN_SIZE + "))", - "create table stable5(ts timestamp, f1 nchar(" + BINARY_COLUMN_SIZE + ")) tags(t1 nchar(" + BINARY_COLUMN_SIZE + "))" - }; - private static final int numOfSubTable = 10, numOfRow = 10; - - public static void main(String[] args) throws SQLException { - - String jdbcUrl = "jdbc:TAOS-RS://" + host + ":6041/?batchfetch=true"; - Connection conn = DriverManager.getConnection(jdbcUrl, "root", "taosdata"); - - init(conn); - - bindInteger(conn); - - bindFloat(conn); - - bindBoolean(conn); - - bindBytes(conn); - - bindString(conn); - - conn.close(); - } - - private static void init(Connection conn) throws SQLException { - try (Statement stmt = conn.createStatement()) { - stmt.execute("drop database if exists test_ws_parabind"); - stmt.execute("create database if not exists test_ws_parabind"); - stmt.execute("use test_ws_parabind"); - for (int i = 0; i < schemaList.length; i++) { - stmt.execute(schemaList[i]); - } - } - } - - private static void bindInteger(Connection conn) throws SQLException { - String sql = "insert into ? using stable1 tags(?,?,?,?) values(?,?,?,?,?)"; - - try (TSWSPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSWSPreparedStatement.class)) { - - for (int i = 1; i <= numOfSubTable; i++) { - // set table name - pstmt.setTableName("t1_" + i); - // set tags - pstmt.setTagByte(1, Byte.parseByte(Integer.toString(random.nextInt(Byte.MAX_VALUE)))); - pstmt.setTagShort(2, Short.parseShort(Integer.toString(random.nextInt(Short.MAX_VALUE)))); - pstmt.setTagInt(3, random.nextInt(Integer.MAX_VALUE)); - pstmt.setTagLong(4, random.nextLong()); - // set columns - long current = System.currentTimeMillis(); - for (int j = 0; j < numOfRow; j++) { - pstmt.setTimestamp(1, new Timestamp(current + j)); - pstmt.setByte(2, Byte.parseByte(Integer.toString(random.nextInt(Byte.MAX_VALUE)))); - pstmt.setShort(3, Short.parseShort(Integer.toString(random.nextInt(Short.MAX_VALUE)))); - pstmt.setInt(4, random.nextInt(Integer.MAX_VALUE)); - pstmt.setLong(5, random.nextLong()); - pstmt.addBatch(); - } - pstmt.executeBatch(); - } - } - } - - private static void bindFloat(Connection conn) throws SQLException { - String sql = "insert into ? using stable2 tags(?,?) values(?,?,?)"; - - try(TSWSPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSWSPreparedStatement.class)) { - - for (int i = 1; i <= numOfSubTable; i++) { - // set table name - pstmt.setTableName("t2_" + i); - // set tags - pstmt.setTagFloat(1, random.nextFloat()); - pstmt.setTagDouble(2, random.nextDouble()); - // set columns - long current = System.currentTimeMillis(); - for (int j = 0; j < numOfRow; j++) { - pstmt.setTimestamp(1, new Timestamp(current + j)); - pstmt.setFloat(2, random.nextFloat()); - pstmt.setDouble(3, random.nextDouble()); - pstmt.addBatch(); - } - pstmt.executeBatch(); - } - } - } - - private static void bindBoolean(Connection conn) throws SQLException { - String sql = "insert into ? using stable3 tags(?) values(?,?)"; - - try (TSWSPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSWSPreparedStatement.class)) { - for (int i = 1; i <= numOfSubTable; i++) { - // set table name - pstmt.setTableName("t3_" + i); - // set tags - pstmt.setTagBoolean(1, random.nextBoolean()); - // set columns - long current = System.currentTimeMillis(); - for (int j = 0; j < numOfRow; j++) { - pstmt.setTimestamp(1, new Timestamp(current + j)); - pstmt.setBoolean(2, random.nextBoolean()); - pstmt.addBatch(); - } - pstmt.executeBatch(); - } - } - } - - private static void bindBytes(Connection conn) throws SQLException { - String sql = "insert into ? using stable4 tags(?) values(?,?)"; - - try (TSWSPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSWSPreparedStatement.class)) { - - for (int i = 1; i <= numOfSubTable; i++) { - // set table name - pstmt.setTableName("t4_" + i); - // set tags - pstmt.setTagString(1, new String("abc")); - - // set columns - long current = System.currentTimeMillis(); - for (int j = 0; j < numOfRow; j++) { - pstmt.setTimestamp(1, new Timestamp(current + j)); - pstmt.setString(2, "abc"); - pstmt.addBatch(); - } - pstmt.executeBatch(); - } - } - } - - private static void bindString(Connection conn) throws SQLException { - String sql = "insert into ? using stable5 tags(?) values(?,?)"; - - try (TSWSPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSWSPreparedStatement.class)) { - - for (int i = 1; i <= numOfSubTable; i++) { - // set table name - pstmt.setTableName("t5_" + i); - // set tags - pstmt.setTagNString(1, "California.SanFrancisco"); - - // set columns - long current = System.currentTimeMillis(); - for (int j = 0; j < numOfRow; j++) { - pstmt.setTimestamp(0, new Timestamp(current + j)); - pstmt.setNString(1, "California.SanFrancisco"); - pstmt.addBatch(); - } - pstmt.executeBatch(); - } - } - } -} -``` - - - +**Note**: both String and byte[] require the user to declare the width of the corresponding column in the size parameter of the table definition The methods to set TAGS values: @@ -997,65 +503,14 @@ TDengine supports schemaless writing. It is compatible with InfluxDB's Line Prot ```java -public class SchemalessJniTest { - private static final String host = "127.0.0.1"; - private static final String lineDemo = "st,t1=3i64,t2=4f64,t3=\"t3\" c1=3i64,c3=L\"passit\",c2=false,c4=4f64 1626006833639000000"; - private static final String telnetDemo = "stb0_0 1626006833 4 host=host0 interface=eth0"; - private static final String jsonDemo = "{\"metric\": \"meter_current\",\"timestamp\": 1346846400,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"California.SanFrancisco\", \"id\": \"d1001\"}}"; - - public static void main(String[] args) throws SQLException { - final String url = "jdbc:TAOS://" + host + ":6030/?user=root&password=taosdata"; - try (Connection connection = DriverManager.getConnection(url)) { - init(connection); - - SchemalessWriter writer = new SchemalessWriter(connection); - writer.write(lineDemo, SchemalessProtocolType.LINE, SchemalessTimestampType.NANO_SECONDS); - writer.write(telnetDemo, SchemalessProtocolType.TELNET, SchemalessTimestampType.MILLI_SECONDS); - writer.write(jsonDemo, SchemalessProtocolType.JSON, SchemalessTimestampType.NOT_CONFIGURED); - } - } - - private static void init(Connection connection) throws SQLException { - try (Statement stmt = connection.createStatement()) { - stmt.executeUpdate("drop database if exists test_schemaless"); - stmt.executeUpdate("create database if not exists test_schemaless"); - stmt.executeUpdate("use test_schemaless"); - } - } -} +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/SchemalessJniTest.java:schemaless}} ``` ```java -public class SchemalessWsTest { - private static final String host = "127.0.0.1"; - private static final String lineDemo = "st,t1=3i64,t2=4f64,t3=\"t3\" c1=3i64,c3=L\"passit\",c2=false,c4=4f64 1626006833639000000"; - private static final String telnetDemo = "stb0_0 1626006833 4 host=host0 interface=eth0"; - private static final String jsonDemo = "{\"metric\": \"meter_current\",\"timestamp\": 1626846400,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"California.SanFrancisco\", \"id\": \"d1001\"}}"; - - public static void main(String[] args) throws SQLException { - final String url = "jdbc:TAOS-RS://" + host + ":6041/?user=root&password=taosdata&batchfetch=true"; - try(Connection connection = DriverManager.getConnection(url)){ - init(connection); - - try(SchemalessWriter writer = new SchemalessWriter(connection, "test_ws_schemaless")){ - writer.write(lineDemo, SchemalessProtocolType.LINE, SchemalessTimestampType.NANO_SECONDS); - writer.write(telnetDemo, SchemalessProtocolType.TELNET, SchemalessTimestampType.MILLI_SECONDS); - writer.write(jsonDemo, SchemalessProtocolType.JSON, SchemalessTimestampType.SECONDS); - } - } - } - - private static void init(Connection connection) throws SQLException { - try (Statement stmt = connection.createStatement()) { - stmt.executeUpdate("drop database if exists test_ws_schemaless"); - stmt.executeUpdate("create database if not exists test_ws_schemaless keep 36500"); - stmt.executeUpdate("use test_ws_schemaless"); - } - } -} +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/SchemalessWsTest.java:schemaless}} ``` @@ -1076,28 +531,16 @@ The TDengine Java client library supports subscription functionality with the fo #### Create a Topic ```java -Connection connection = DriverManager.getConnection(url, properties); -Statement statement = connection.createStatement(); -statement.executeUpdate("create topic if not exists topic_speed as select ts, speed from speed_table"); +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/ConsumerLoopImp.java:create_topic}} ``` -The three parameters of the `subscribe()` method have the following meanings. - -- topic_speed: the subscribed topic (name). This is the unique identifier of the subscription. -- sql: the query statement of the subscription which can only be a _select_ statement. Only the original data should be queried, and data can only be queried in temporal order.. - -The preceding example uses the SQL statement `select ts, speed from speed_table` and creates a subscription named `topic_speed`. +The preceding example uses the SQL statement `SELECT ts, current, voltage, phase, groupid, location FROM meters` and creates a topic named `topic_meters`. +> **Note**: the query statement of the topic which can only be a _SELECT_ statement. Only the original data should be queried, and data can only be queried in temporal order. #### Create a Consumer ```java -Properties config = new Properties(); -config.setProperty("bootstrap.servers", "localhost:6030"); -config.setProperty("enable.auto.commit", "true"); -config.setProperty("group.id", "group1"); -config.setProperty("value.deserializer", "com.taosdata.jdbc.tmq.ConsumerTest.ResultDeserializer"); - -TaosConsumer consumer = new TaosConsumer<>(config); +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/AbsConsumerLoop.java:create_consumer}} ``` - bootstrap.servers: `ip:port` where the TDengine server is located, or `ip:port` where the taosAdapter is located if WebSocket connection is used. @@ -1107,22 +550,22 @@ TaosConsumer consumer = new TaosConsumer<>(config); - td.connect.type: Specifies the type connect with TDengine, `jni` or `WebSocket`. default is `jni` - httpConnectTimeout: WebSocket connection timeout in milliseconds, the default value is 5000 ms. It only takes effect when using WebSocket type. - messageWaitTimeout: socket timeout in milliseconds, the default value is 10000 ms. It only takes effect when using WebSocket type. -- httpPoolSize: Maximum number of concurrent requests on the a connection。It only takes effect when using WebSocket type. -- For more information, see [Consumer Parameters](../../develop/tmq). Note that the default value of auto.offset.reset in data subscription on the TDengine server has changed since version 3.2.0.0. +- httpPoolSize: Maximum number of concurrent requests on the a connection。It only takes effect when using WebSocket type. +- TSDBDriver.PROPERTY_KEY_ENABLE_COMPRESSION: Whether to enable compression during transmission. It only takes effect when using Websocket connections. true: enabled, false: disabled. The default is false. +- TSDBDriver.PROPERTY_KEY_ENABLE_AUTO_RECONNECT: Whether to enable automatic reconnection. It only takes effect when using Websocket connections. true: enabled, false: disabled. The default is false. +- TSDBDriver.PROPERTY_KEY_RECONNECT_INTERVAL_MS: The interval for automatic reconnection retries, in milliseconds, with a default value of 2000. It only takes effect when PROPERTY_KEY_ENABLE_AUTO_RECONNECT is true. +- TSDBDriver.PROPERTY_KEY_RECONNECT_RETRY_COUNT: The number of automatic reconnection retries, with a default value of 3. It only takes effect when PROPERTY_KEY_ENABLE_AUTO_RECONNECT is true. + +For more information, see [Consumer Parameters](../../develop/tmq/#create-a-consumer). Note that the default value of auto.offset.reset in data subscription on the TDengine server has changed since version 3.2.0.0. #### Subscribe to consume data ```java -while(true) { - ConsumerRecords records = consumer.poll(Duration.ofMillis(100)); - for (ConsumerRecord record : records) { - ResultBean bean = record.value(); - process(bean); - } -} +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/AbsConsumerLoop.java:poll_data}} ``` -`poll` obtains one message each time it is run. +The parameters of the subscribe method are defined as: a list of topics to subscribe, and it supports subscribing to multiple topics at the same time. +`poll` retrieves a single message with each execution, and a single message may contain multiple records. #### Assignment subscription Offset @@ -1145,24 +588,7 @@ void seekToEnd(Collection partitions) throws SQLException; Example usage is as follows. ```java -String topic = "offset_seek_test"; -Map offset = null; -try (TaosConsumer consumer = new TaosConsumer<>(properties)) { - consumer.subscribe(Collections.singletonList(topic)); - for (int i = 0; i < 10; i++) { - if (i == 3) { - // Saving consumption position - offset = consumer.position(topic); - } - if (i == 5) { - // reset consumption to the previously saved position - for (Map.Entry entry : offset.entrySet()) { - consumer.seek(entry.getKey(), entry.getValue()); - } - } - ConsumerRecords records = consumer.poll(Duration.ofMillis(500)); - } -} +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/ConsumerOffsetSeek.java:consumer_seek}} ``` #### Commit offset @@ -1196,166 +622,14 @@ For more information, see [Data Subscription](../../develop/tmq). In addition to the native connection, the Java client library also supports subscribing via websocket. ```java -public abstract class ConsumerLoop { - private final TaosConsumer consumer; - private final List topics; - private final AtomicBoolean shutdown; - private final CountDownLatch shutdownLatch; - - public ConsumerLoop() throws SQLException { - Properties config = new Properties(); - config.setProperty("td.connect.type", "jni"); - config.setProperty("bootstrap.servers", "localhost:6030"); - config.setProperty("td.connect.user", "root"); - config.setProperty("td.connect.pass", "taosdata"); - config.setProperty("auto.offset.reset", "latest"); - config.setProperty("msg.with.table.name", "true"); - config.setProperty("enable.auto.commit", "true"); - config.setProperty("auto.commit.interval.ms", "1000"); - config.setProperty("group.id", "group1"); - config.setProperty("client.id", "1"); - config.setProperty("value.deserializer", "com.taosdata.jdbc.tmq.ConsumerTest.ConsumerLoop$ResultDeserializer"); - config.setProperty("value.deserializer.encoding", "UTF-8"); - - this.consumer = new TaosConsumer<>(config); - this.topics = Collections.singletonList("topic_speed"); - this.shutdown = new AtomicBoolean(false); - this.shutdownLatch = new CountDownLatch(1); - } - - public abstract void process(ResultBean result); - - public void pollData() throws SQLException { - try { - consumer.subscribe(topics); - - while (!shutdown.get()) { - ConsumerRecords records = consumer.poll(Duration.ofMillis(100)); - for (ConsumerRecord record : records) { - ResultBean bean = record.value(); - process(bean); - } - } - consumer.unsubscribe(); - } finally { - consumer.close(); - shutdownLatch.countDown(); - } - } - - public void shutdown() throws InterruptedException { - shutdown.set(true); - shutdownLatch.await(); - } - - public static class ResultDeserializer extends ReferenceDeserializer { - - } - - public static class ResultBean { - private Timestamp ts; - private int speed; - - public Timestamp getTs() { - return ts; - } - - public void setTs(Timestamp ts) { - this.ts = ts; - } - - public int getSpeed() { - return speed; - } - - public void setSpeed(int speed) { - this.speed = speed; - } - } -} +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/AbsConsumerLoopFull.java:consumer_demo}} ``` ```java -public abstract class ConsumerLoop { - private final TaosConsumer consumer; - private final List topics; - private final AtomicBoolean shutdown; - private final CountDownLatch shutdownLatch; - - public ConsumerLoop() throws SQLException { - Properties config = new Properties(); - config.setProperty("td.connect.type", "ws"); - config.setProperty("bootstrap.servers", "localhost:6041"); - config.setProperty("td.connect.user", "root"); - config.setProperty("td.connect.pass", "taosdata"); - config.setProperty("auto.offset.reset", "latest"); - config.setProperty("msg.with.table.name", "true"); - config.setProperty("enable.auto.commit", "true"); - config.setProperty("auto.commit.interval.ms", "1000"); - config.setProperty("group.id", "group2"); - config.setProperty("client.id", "1"); - config.setProperty("value.deserializer", "com.taosdata.jdbc.tmq.ConsumerTest.ConsumerLoop$ResultDeserializer"); - config.setProperty("value.deserializer.encoding", "UTF-8"); - - this.consumer = new TaosConsumer<>(config); - this.topics = Collections.singletonList("topic_speed"); - this.shutdown = new AtomicBoolean(false); - this.shutdownLatch = new CountDownLatch(1); - } - - public abstract void process(ResultBean result); - - public void pollData() throws SQLException { - try { - consumer.subscribe(topics); - - while (!shutdown.get()) { - ConsumerRecords records = consumer.poll(Duration.ofMillis(100)); - for (ConsumerRecord record : records) { - ResultBean bean = record.value(); - process(bean); - } - } - consumer.unsubscribe(); - } finally { - consumer.close(); - shutdownLatch.countDown(); - } - } - - public void shutdown() throws InterruptedException { - shutdown.set(true); - shutdownLatch.await(); - } - - public static class ResultDeserializer extends ReferenceDeserializer { - - } - - public static class ResultBean { - private Timestamp ts; - private int speed; - - public Timestamp getTs() { - return ts; - } - - public void setTs(Timestamp ts) { - this.ts = ts; - } - - public int getSpeed() { - return speed; - } - - public void setSpeed(int speed) { - this.speed = speed; - } - } -} +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/AbsWsConsumerLoop.java:consumer_demo}} ``` @@ -1370,30 +644,7 @@ public abstract class ConsumerLoop { Example usage is as follows. ```java - public static void main(String[] args) throws SQLException { - HikariConfig config = new HikariConfig(); - // jdbc properties - config.setJdbcUrl("jdbc:TAOS://127.0.0.1:6030/log"); - config.setUsername("root"); - config.setPassword("taosdata"); - // connection pool configurations - config.setMinimumIdle(10); //minimum number of idle connection - config.setMaximumPoolSize(10); //maximum number of connection in the pool - config.setConnectionTimeout(30000); //maximum wait milliseconds for get connection from pool - config.setMaxLifetime(0); // maximum life time for each connection - config.setIdleTimeout(0); // max idle time for recycle idle connection - config.setConnectionTestQuery("select server_status()"); //validation query - - HikariDataSource ds = new HikariDataSource(config); //create datasource - - Connection connection = ds.getConnection(); // get connection - Statement statement = connection.createStatement(); // get statement - - //query or insert - // ... - - connection.close(); // put back to connection pool -} +{{#include examples/JDBC/connectionPools/src/main/java/com/taosdata/example/HikariDemo.java:connection_pool}} ``` > getConnection(), you need to call the close() method after you finish using it. It doesn't close the connection. It just puts it back into the connection pool. @@ -1404,28 +655,7 @@ Example usage is as follows. Example usage is as follows. ```java -public static void main(String[] args) throws Exception { - - DruidDataSource dataSource = new DruidDataSource(); - // jdbc properties - dataSource.setDriverClassName("com.taosdata.jdbc.TSDBDriver"); - dataSource.setUrl(url); - dataSource.setUsername("root"); - dataSource.setPassword("taosdata"); - // pool configurations - dataSource.setInitialSize(10); - dataSource.setMinIdle(10); - dataSource.setMaxActive(10); - dataSource.setMaxWait(30000); - dataSource.setValidationQuery("select server_status()"); - - Connection connection = dataSource.getConnection(); // get connection - Statement statement = connection.createStatement(); // get statement - //query or insert - // ... - - connection.close(); // put back to connection pool -} +{{#include examples/JDBC/connectionPools/src/main/java/com/taosdata/example/DruidDemo.java:connection_pool}} ``` > For more questions about using druid, please see [Official Instructions](https://github.com/alibaba/druid). @@ -1435,10 +665,10 @@ public static void main(String[] args) throws Exception { The source code of the sample application is under `TDengine/examples/JDBC`: - JDBCDemo: JDBC sample source code. -- JDBCConnectorChecker: JDBC installation checker source and jar package. - connectionPools: using taos-jdbcdriver in connection pools such as HikariCP, Druid, dbcp, c3p0, etc. - SpringJdbcTemplate: using taos-jdbcdriver in Spring JdbcTemplate. - mybatisplus-demo: using taos-jdbcdriver in Springboot + Mybatis. +- springbootdemo: using taos-jdbcdriver in Springboot. - consumer-demo: consumer TDengine data example, the consumption rate can be controlled by parameters. [JDBC example](https://github.com/taosdata/TDengine/tree/3.0/examples/JDBC) diff --git a/docs/en/08-client-libraries/05-go.mdx b/docs/en/08-client-libraries/05-go.mdx index 528a21707e..be87fbc7cb 100644 --- a/docs/en/08-client-libraries/05-go.mdx +++ b/docs/en/08-client-libraries/05-go.mdx @@ -13,14 +13,23 @@ import GoInfluxLine from "../07-develop/03-insert-data/_go_line.mdx" import GoOpenTSDBTelnet from "../07-develop/03-insert-data/_go_opts_telnet.mdx" import GoOpenTSDBJson from "../07-develop/03-insert-data/_go_opts_json.mdx" import GoQuery from "../07-develop/04-query-data/_go.mdx" +import RequestId from "./_request_id.mdx"; `driver-go` is the official Go language client library for TDengine. It implements the [database/sql](https://golang.org/pkg/database/sql/) package, the generic Go language interface to SQL databases. Go developers can use it to develop applications that access TDengine cluster data. -`driver-go` provides two ways to establish connections. One is **native connection**, which connects to TDengine instances natively through the TDengine client driver (taosc), supporting data writing, querying, subscriptions, schemaless writing, and bind interface. The other is the **REST connection**, which connects to TDengine instances via the REST interface provided by taosAdapter. The set of features implemented by the REST connection differs slightly from those implemented by the native connection. +## Connection types -This article describes how to install `driver-go` and connect to TDengine clusters and perform basic operations such as data query and data writing through `driver-go`. +`driver-go` provides 3 connection types. -The source code of `driver-go` is hosted on [GitHub](https://github.com/taosdata/driver-go). +* **Native connection**, which connects to TDengine instances natively through the TDengine client driver (taosc), supporting data writing, querying, subscriptions, schemaless writing, and bind interface. +* **REST connection**, which is implemented through taosAdapter. Some features like schemaless and subscriptions are not supported. +* **Websocket connection** which is implemented through taosAdapter. The set of features implemented by the WebSocket connection differs slightly from those implemented by the native connection. + +For a detailed introduction of the connection types, please refer to: [Establish Connection](../../develop/connect/#establish-connection) + +## Compatibility + +Supports minimum Go version 1.14, it is recommended to use the latest Go version ## Supported platforms @@ -233,45 +242,27 @@ The Go client library does not support this feature ### Create database and tables ```go -var taosDSN = "root:taosdata@tcp(localhost:6030)/" -taos, err := sql.Open("taosSql", taosDSN) -if err != nil { - log.Fatalln("failed to connect TDengine, err:", err) -} -defer taos.Close() -_, err := taos.Exec("CREATE DATABASE power") -if err != nil { - log.Fatalln("failed to create database, err:", err) -} -_, err = taos.Exec("CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)") -if err != nil { - log.Fatalln("failed to create stable, err:", err) -} +{{#include docs/examples/go/demo/query/main.go:create_db_and_table}} ``` ### Insert data - +```go +{{#include docs/examples/go/demo/query/main.go:insert_data}} +``` ### Querying data - +```go +{{#include docs/examples/go/demo/query/main.go:query_data}} +``` ### execute SQL with reqId -This reqId can be used to request link tracing. + ```go -db, err := sql.Open("taosSql", "root:taosdata@tcp(localhost:6030)/") -if err != nil { - panic(err) -} -defer db.Close() -ctx := context.WithValue(context.Background(), common.ReqIDKey, common.GetReqID()) -_, err = db.ExecContext(ctx, "create database if not exists example_taos_sql") -if err != nil { - panic(err) -} +{{#include docs/examples/go/demo/query/main.go:with_reqid}} ``` ### Writing data via parameter binding @@ -280,375 +271,14 @@ if err != nil { ```go -package main - -import ( - "time" - - "github.com/taosdata/driver-go/v3/af" - "github.com/taosdata/driver-go/v3/common" - "github.com/taosdata/driver-go/v3/common/param" -) - -func main() { - db, err := af.Open("", "root", "taosdata", "", 0) - if err != nil { - panic(err) - } - defer db.Close() - _, err = db.Exec("create database if not exists example_stmt") - if err != nil { - panic(err) - } - _, err = db.Exec("create table if not exists example_stmt.tb1(ts timestamp," + - "c1 bool," + - "c2 tinyint," + - "c3 smallint," + - "c4 int," + - "c5 bigint," + - "c6 tinyint unsigned," + - "c7 smallint unsigned," + - "c8 int unsigned," + - "c9 bigint unsigned," + - "c10 float," + - "c11 double," + - "c12 binary(20)," + - "c13 nchar(20)" + - ")") - if err != nil { - panic(err) - } - stmt := db.InsertStmt() - err = stmt.Prepare("insert into example_stmt.tb1 values(?,?,?,?,?,?,?,?,?,?,?,?,?,?)") - if err != nil { - panic(err) - } - now := time.Now() - params := make([]*param.Param, 14) - params[0] = param.NewParam(2). - AddTimestamp(now, common.PrecisionMilliSecond). - AddTimestamp(now.Add(time.Second), common.PrecisionMilliSecond) - params[1] = param.NewParam(2).AddBool(true).AddNull() - params[2] = param.NewParam(2).AddTinyint(2).AddNull() - params[3] = param.NewParam(2).AddSmallint(3).AddNull() - params[4] = param.NewParam(2).AddInt(4).AddNull() - params[5] = param.NewParam(2).AddBigint(5).AddNull() - params[6] = param.NewParam(2).AddUTinyint(6).AddNull() - params[7] = param.NewParam(2).AddUSmallint(7).AddNull() - params[8] = param.NewParam(2).AddUInt(8).AddNull() - params[9] = param.NewParam(2).AddUBigint(9).AddNull() - params[10] = param.NewParam(2).AddFloat(10).AddNull() - params[11] = param.NewParam(2).AddDouble(11).AddNull() - params[12] = param.NewParam(2).AddBinary([]byte("binary")).AddNull() - params[13] = param.NewParam(2).AddNchar("nchar").AddNull() - - paramTypes := param.NewColumnType(14). - AddTimestamp(). - AddBool(). - AddTinyint(). - AddSmallint(). - AddInt(). - AddBigint(). - AddUTinyint(). - AddUSmallint(). - AddUInt(). - AddUBigint(). - AddFloat(). - AddDouble(). - AddBinary(6). - AddNchar(5) - err = stmt.BindParam(params, paramTypes) - if err != nil { - panic(err) - } - err = stmt.AddBatch() - if err != nil { - panic(err) - } - err = stmt.Execute() - if err != nil { - panic(err) - } - err = stmt.Close() - if err != nil { - panic(err) - } - // select * from example_stmt.tb1 -} +{{#include docs/examples/go/demo/stmt/main.go}} ``` ```go -package main - -import ( - "database/sql" - "fmt" - "time" - - "github.com/taosdata/driver-go/v3/common" - "github.com/taosdata/driver-go/v3/common/param" - _ "github.com/taosdata/driver-go/v3/taosRestful" - "github.com/taosdata/driver-go/v3/ws/stmt" -) - -func main() { - db, err := sql.Open("taosRestful", "root:taosdata@http(localhost:6041)/") - if err != nil { - panic(err) - } - defer db.Close() - prepareEnv(db) - - config := stmt.NewConfig("ws://127.0.0.1:6041/rest/stmt", 0) - config.SetConnectUser("root") - config.SetConnectPass("taosdata") - config.SetConnectDB("example_ws_stmt") - config.SetMessageTimeout(common.DefaultMessageTimeout) - config.SetWriteWait(common.DefaultWriteWait) - config.SetErrorHandler(func(connector *stmt.Connector, err error) { - panic(err) - }) - config.SetCloseHandler(func() { - fmt.Println("stmt connector closed") - }) - - connector, err := stmt.NewConnector(config) - if err != nil { - panic(err) - } - now := time.Now() - { - stmt, err := connector.Init() - if err != nil { - panic(err) - } - err = stmt.Prepare("insert into ? using all_json tags(?) values(?,?,?,?,?,?,?,?,?,?,?,?,?,?)") - if err != nil { - panic(err) - } - err = stmt.SetTableName("tb1") - if err != nil { - panic(err) - } - err = stmt.SetTags(param.NewParam(1).AddJson([]byte(`{"tb":1}`)), param.NewColumnType(1).AddJson(0)) - if err != nil { - panic(err) - } - params := []*param.Param{ - param.NewParam(3).AddTimestamp(now, 0).AddTimestamp(now.Add(time.Second), 0).AddTimestamp(now.Add(time.Second*2), 0), - param.NewParam(3).AddBool(true).AddNull().AddBool(true), - param.NewParam(3).AddTinyint(1).AddNull().AddTinyint(1), - param.NewParam(3).AddSmallint(1).AddNull().AddSmallint(1), - param.NewParam(3).AddInt(1).AddNull().AddInt(1), - param.NewParam(3).AddBigint(1).AddNull().AddBigint(1), - param.NewParam(3).AddUTinyint(1).AddNull().AddUTinyint(1), - param.NewParam(3).AddUSmallint(1).AddNull().AddUSmallint(1), - param.NewParam(3).AddUInt(1).AddNull().AddUInt(1), - param.NewParam(3).AddUBigint(1).AddNull().AddUBigint(1), - param.NewParam(3).AddFloat(1).AddNull().AddFloat(1), - param.NewParam(3).AddDouble(1).AddNull().AddDouble(1), - param.NewParam(3).AddBinary([]byte("test_binary")).AddNull().AddBinary([]byte("test_binary")), - param.NewParam(3).AddNchar("test_nchar").AddNull().AddNchar("test_nchar"), - } - paramTypes := param.NewColumnType(14). - AddTimestamp(). - AddBool(). - AddTinyint(). - AddSmallint(). - AddInt(). - AddBigint(). - AddUTinyint(). - AddUSmallint(). - AddUInt(). - AddUBigint(). - AddFloat(). - AddDouble(). - AddBinary(0). - AddNchar(0) - err = stmt.BindParam(params, paramTypes) - if err != nil { - panic(err) - } - err = stmt.AddBatch() - if err != nil { - panic(err) - } - err = stmt.Exec() - if err != nil { - panic(err) - } - affected := stmt.GetAffectedRows() - fmt.Println("all_json affected rows:", affected) - err = stmt.Close() - if err != nil { - panic(err) - } - } - { - stmt, err := connector.Init() - if err != nil { - panic(err) - } - err = stmt.Prepare("insert into ? using all_all tags(?,?,?,?,?,?,?,?,?,?,?,?,?,?) values(?,?,?,?,?,?,?,?,?,?,?,?,?,?)") - err = stmt.SetTableName("tb1") - if err != nil { - panic(err) - } - - err = stmt.SetTableName("tb2") - if err != nil { - panic(err) - } - err = stmt.SetTags( - param.NewParam(14). - AddTimestamp(now, 0). - AddBool(true). - AddTinyint(2). - AddSmallint(2). - AddInt(2). - AddBigint(2). - AddUTinyint(2). - AddUSmallint(2). - AddUInt(2). - AddUBigint(2). - AddFloat(2). - AddDouble(2). - AddBinary([]byte("tb2")). - AddNchar("tb2"), - param.NewColumnType(14). - AddTimestamp(). - AddBool(). - AddTinyint(). - AddSmallint(). - AddInt(). - AddBigint(). - AddUTinyint(). - AddUSmallint(). - AddUInt(). - AddUBigint(). - AddFloat(). - AddDouble(). - AddBinary(0). - AddNchar(0), - ) - if err != nil { - panic(err) - } - params := []*param.Param{ - param.NewParam(3).AddTimestamp(now, 0).AddTimestamp(now.Add(time.Second), 0).AddTimestamp(now.Add(time.Second*2), 0), - param.NewParam(3).AddBool(true).AddNull().AddBool(true), - param.NewParam(3).AddTinyint(1).AddNull().AddTinyint(1), - param.NewParam(3).AddSmallint(1).AddNull().AddSmallint(1), - param.NewParam(3).AddInt(1).AddNull().AddInt(1), - param.NewParam(3).AddBigint(1).AddNull().AddBigint(1), - param.NewParam(3).AddUTinyint(1).AddNull().AddUTinyint(1), - param.NewParam(3).AddUSmallint(1).AddNull().AddUSmallint(1), - param.NewParam(3).AddUInt(1).AddNull().AddUInt(1), - param.NewParam(3).AddUBigint(1).AddNull().AddUBigint(1), - param.NewParam(3).AddFloat(1).AddNull().AddFloat(1), - param.NewParam(3).AddDouble(1).AddNull().AddDouble(1), - param.NewParam(3).AddBinary([]byte("test_binary")).AddNull().AddBinary([]byte("test_binary")), - param.NewParam(3).AddNchar("test_nchar").AddNull().AddNchar("test_nchar"), - } - paramTypes := param.NewColumnType(14). - AddTimestamp(). - AddBool(). - AddTinyint(). - AddSmallint(). - AddInt(). - AddBigint(). - AddUTinyint(). - AddUSmallint(). - AddUInt(). - AddUBigint(). - AddFloat(). - AddDouble(). - AddBinary(0). - AddNchar(0) - err = stmt.BindParam(params, paramTypes) - if err != nil { - panic(err) - } - err = stmt.AddBatch() - if err != nil { - panic(err) - } - err = stmt.Exec() - if err != nil { - panic(err) - } - affected := stmt.GetAffectedRows() - fmt.Println("all_all affected rows:", affected) - err = stmt.Close() - if err != nil { - panic(err) - } - - } -} - -func prepareEnv(db *sql.DB) { - steps := []string{ - "create database example_ws_stmt", - "create table example_ws_stmt.all_json(ts timestamp," + - "c1 bool," + - "c2 tinyint," + - "c3 smallint," + - "c4 int," + - "c5 bigint," + - "c6 tinyint unsigned," + - "c7 smallint unsigned," + - "c8 int unsigned," + - "c9 bigint unsigned," + - "c10 float," + - "c11 double," + - "c12 binary(20)," + - "c13 nchar(20)" + - ")" + - "tags(t json)", - "create table example_ws_stmt.all_all(" + - "ts timestamp," + - "c1 bool," + - "c2 tinyint," + - "c3 smallint," + - "c4 int," + - "c5 bigint," + - "c6 tinyint unsigned," + - "c7 smallint unsigned," + - "c8 int unsigned," + - "c9 bigint unsigned," + - "c10 float," + - "c11 double," + - "c12 binary(20)," + - "c13 nchar(20)" + - ")" + - "tags(" + - "tts timestamp," + - "tc1 bool," + - "tc2 tinyint," + - "tc3 smallint," + - "tc4 int," + - "tc5 bigint," + - "tc6 tinyint unsigned," + - "tc7 smallint unsigned," + - "tc8 int unsigned," + - "tc9 bigint unsigned," + - "tc10 float," + - "tc11 double," + - "tc12 binary(20)," + - "tc13 nchar(20))", - } - for _, step := range steps { - _, err := db.Exec(step) - if err != nil { - panic(err) - } - } -} - +{{#include docs/examples/go/demo/stmtws/main.go}} ``` @@ -661,98 +291,14 @@ func prepareEnv(db *sql.DB) { ```go -import ( - "fmt" - - "github.com/taosdata/driver-go/v3/af" -) - -func main() { - conn, err := af.Open("localhost", "root", "taosdata", "", 6030) - if err != nil { - fmt.Println("fail to connect, err:", err) - } - defer conn.Close() - _, err = conn.Exec("create database if not exists example") - if err != nil { - panic(err) - } - _, err = conn.Exec("use example") - if err != nil { - panic(err) - } - influxdbData := "st,t1=3i64,t2=4f64,t3=\"t3\" c1=3i64,c3=L\"passit\",c2=false,c4=4f64 1626006833639000000" - err = conn.InfluxDBInsertLines([]string{influxdbData}, "ns") - if err != nil { - panic(err) - } - telnetData := "stb0_0 1626006833 4 host=host0 interface=eth0" - err = conn.OpenTSDBInsertTelnetLines([]string{telnetData}) - if err != nil { - panic(err) - } - jsonData := "{\"metric\": \"meter_current\",\"timestamp\": 1626846400,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"California.SanFrancisco\", \"id\": \"d1001\"}}" - err = conn.OpenTSDBInsertJsonPayload(jsonData) - if err != nil { - panic(err) - } -} +{{#include docs/examples/go/demo/sml/main.go}} ``` ```go -import ( - "database/sql" - "log" - "time" - - "github.com/taosdata/driver-go/v3/common" - _ "github.com/taosdata/driver-go/v3/taosWS" - "github.com/taosdata/driver-go/v3/ws/schemaless" -) - -func main() { - db, err := sql.Open("taosWS", "root:taosdata@ws(localhost:6041)/") - if err != nil { - log.Fatal(err) - } - defer db.Close() - _, err = db.Exec("create database if not exists schemaless_ws") - if err != nil { - log.Fatal(err) - } - s, err := schemaless.NewSchemaless(schemaless.NewConfig("ws://localhost:6041/rest/schemaless", 1, - schemaless.SetDb("schemaless_ws"), - schemaless.SetReadTimeout(10*time.Second), - schemaless.SetWriteTimeout(10*time.Second), - schemaless.SetUser("root"), - schemaless.SetPassword("taosdata"), - schemaless.SetErrorHandler(func(err error) { - log.Fatal(err) - }), - )) - if err != nil { - panic(err) - } - influxdbData := "st,t1=3i64,t2=4f64,t3=\"t3\" c1=3i64,c3=L\"passit\",c2=false,c4=4f64 1626006833639000000" - telnetData := "stb0_0 1626006833 4 host=host0 interface=eth0" - jsonData := "{\"metric\": \"meter_current\",\"timestamp\": 1626846400,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"California.SanFrancisco\", \"id\": \"d1001\"}}" - - err = s.Insert(influxdbData, schemaless.InfluxDBLineProtocol, "ns", 0, common.GetReqID()) - if err != nil { - panic(err) - } - err = s.Insert(telnetData, schemaless.OpenTSDBTelnetLineProtocol, "ms", 0, common.GetReqID()) - if err != nil { - panic(err) - } - err = s.Insert(jsonData, schemaless.OpenTSDBJsonFormatProtocol, "ms", 0, common.GetReqID()) - if err != nil { - panic(err) - } -} +{{#include docs/examples/go/demo/smlws/main.go}} ``` @@ -774,89 +320,31 @@ The TDengine Go client library supports subscription functionality with the foll #### Create a Topic ```go - db, err := af.Open("", "root", "taosdata", "", 0) - if err != nil { - panic(err) - } - defer db.Close() - _, err = db.Exec("create database if not exists example_tmq WAL_RETENTION_PERIOD 86400") - if err != nil { - panic(err) - } - _, err = db.Exec("create topic if not exists example_tmq_topic as DATABASE example_tmq") - if err != nil { - panic(err) - } +{{#include docs/examples/go/demo/consumer/main.go:create_topic}} ``` #### Create a Consumer ```go - consumer, err := tmq.NewConsumer(&tmqcommon.ConfigMap{ - "group.id": "test", - "auto.offset.reset": "latest", - "td.connect.ip": "127.0.0.1", - "td.connect.user": "root", - "td.connect.pass": "taosdata", - "td.connect.port": "6030", - "client.id": "test_tmq_client", - "enable.auto.commit": "false", - "msg.with.table.name": "true", - }) - if err != nil { - panic(err) - } +{{#include docs/examples/go/demo/consumer/main.go:create_consumer}} ``` #### Subscribe to consume data ```go - err = consumer.Subscribe("example_tmq_topic", nil) - if err != nil { - panic(err) - } - for i := 0; i < 5; i++ { - ev := consumer.Poll(500) - if ev != nil { - switch e := ev.(type) { - case *tmqcommon.DataMessage: - fmt.Printf("get message:%v\n", e) - case tmqcommon.Error: - fmt.Fprintf(os.Stderr, "%% Error: %v: %v\n", e.Code(), e) - panic(e) - } - consumer.Commit() - } - } +{{#include docs/examples/go/demo/consumer/main.go:poll_data}} ``` #### Assignment subscription Offset ```go - partitions, err := consumer.Assignment() - if err != nil { - panic(err) - } - for i := 0; i < len(partitions); i++ { - fmt.Println(partitions[i]) - err = consumer.Seek(tmqcommon.TopicPartition{ - Topic: partitions[i].Topic, - Partition: partitions[i].Partition, - Offset: 0, - }, 0) - if err != nil { - panic(err) - } - } +{{#include docs/examples/go/demo/consumer/main.go:consumer_seek}} ``` #### Close subscriptions ```go - err = consumer.Close() - if err != nil { - panic(err) - } +{{#include docs/examples/go/demo/consumer/main.go:consumer_close}} ``` #### Full Sample Code @@ -865,232 +353,14 @@ The TDengine Go client library supports subscription functionality with the foll ```go -package main - -import ( - "fmt" - "os" - "time" - - "github.com/taosdata/driver-go/v3/af" - "github.com/taosdata/driver-go/v3/af/tmq" - tmqcommon "github.com/taosdata/driver-go/v3/common/tmq" -) - -func main() { - db, err := af.Open("", "root", "taosdata", "", 0) - if err != nil { - panic(err) - } - defer db.Close() - _, err = db.Exec("create database if not exists example_tmq WAL_RETENTION_PERIOD 86400") - if err != nil { - panic(err) - } - _, err = db.Exec("create topic if not exists example_tmq_topic as DATABASE example_tmq") - if err != nil { - panic(err) - } - consumer, err := tmq.NewConsumer(&tmqcommon.ConfigMap{ - "group.id": "test", - "auto.offset.reset": "latest", - "td.connect.ip": "127.0.0.1", - "td.connect.user": "root", - "td.connect.pass": "taosdata", - "td.connect.port": "6030", - "client.id": "test_tmq_client", - "enable.auto.commit": "false", - "msg.with.table.name": "true", - }) - if err != nil { - panic(err) - } - err = consumer.Subscribe("example_tmq_topic", nil) - if err != nil { - panic(err) - } - _, err = db.Exec("create table example_tmq.t1 (ts timestamp,v int)") - if err != nil { - panic(err) - } - go func() { - for { - _, err = db.Exec("insert into example_tmq.t1 values(now,1)") - if err != nil { - panic(err) - } - time.Sleep(time.Millisecond * 100) - } - }() - - for i := 0; i < 5; i++ { - ev := consumer.Poll(500) - if ev != nil { - switch e := ev.(type) { - case *tmqcommon.DataMessage: - fmt.Printf("get message:%v\n", e) - case tmqcommon.Error: - fmt.Fprintf(os.Stderr, "%% Error: %v: %v\n", e.Code(), e) - panic(e) - } - consumer.Commit() - } - } - partitions, err := consumer.Assignment() - if err != nil { - panic(err) - } - for i := 0; i < len(partitions); i++ { - fmt.Println(partitions[i]) - err = consumer.Seek(tmqcommon.TopicPartition{ - Topic: partitions[i].Topic, - Partition: partitions[i].Partition, - Offset: 0, - }, 0) - if err != nil { - panic(err) - } - } - - partitions, err = consumer.Assignment() - if err != nil { - panic(err) - } - for i := 0; i < len(partitions); i++ { - fmt.Println(partitions[i]) - } - - err = consumer.Close() - if err != nil { - panic(err) - } -} +{{#include docs/examples/go/demo/consumer/main.go}} ``` ```go -package main - -import ( - "database/sql" - "fmt" - "time" - - "github.com/taosdata/driver-go/v3/common" - tmqcommon "github.com/taosdata/driver-go/v3/common/tmq" - _ "github.com/taosdata/driver-go/v3/taosRestful" - "github.com/taosdata/driver-go/v3/ws/tmq" -) - -func main() { - db, err := sql.Open("taosRestful", "root:taosdata@http(localhost:6041)/") - if err != nil { - panic(err) - } - defer db.Close() - prepareEnv(db) - consumer, err := tmq.NewConsumer(&tmqcommon.ConfigMap{ - "ws.url": "ws://127.0.0.1:6041/rest/tmq", - "ws.message.channelLen": uint(0), - "ws.message.timeout": common.DefaultMessageTimeout, - "ws.message.writeWait": common.DefaultWriteWait, - "td.connect.user": "root", - "td.connect.pass": "taosdata", - "group.id": "example", - "client.id": "example_consumer", - "auto.offset.reset": "latest", - }) - if err != nil { - panic(err) - } - err = consumer.Subscribe("example_ws_tmq_topic", nil) - if err != nil { - panic(err) - } - - _, err = db.Exec("create table example_ws_tmq.t_all(ts timestamp," + - "c1 bool," + - "c2 tinyint," + - "c3 smallint," + - "c4 int," + - "c5 bigint," + - "c6 tinyint unsigned," + - "c7 smallint unsigned," + - "c8 int unsigned," + - "c9 bigint unsigned," + - "c10 float," + - "c11 double," + - "c12 binary(20)," + - "c13 nchar(20)" + - ")") - if err != nil { - panic(err) - } - go func() { - for { - _, err = db.Exec("insert into example_ws_tmq.t_all values(now,true,2,3,4,5,6,7,8,9,10.123,11.123,'binary','nchar')") - if err != nil { - panic(err) - } - time.Sleep(time.Millisecond * 100) - } - - }() - for i := 0; i < 5; i++ { - ev := consumer.Poll(500) - if ev != nil { - switch e := ev.(type) { - case *tmqcommon.DataMessage: - fmt.Printf("get message:%v\n", e) - case tmqcommon.Error: - fmt.Printf("%% Error: %v: %v\n", e.Code(), e) - panic(e) - } - consumer.Commit() - } - } - partitions, err := consumer.Assignment() - if err != nil { - panic(err) - } - for i := 0; i < len(partitions); i++ { - fmt.Println(partitions[i]) - err = consumer.Seek(tmqcommon.TopicPartition{ - Topic: partitions[i].Topic, - Partition: partitions[i].Partition, - Offset: 0, - }, 0) - if err != nil { - panic(err) - } - } - - partitions, err = consumer.Assignment() - if err != nil { - panic(err) - } - for i := 0; i < len(partitions); i++ { - fmt.Println(partitions[i]) - } - - err = consumer.Close() - if err != nil { - panic(err) - } -} - -func prepareEnv(db *sql.DB) { - _, err := db.Exec("create database example_ws_tmq WAL_RETENTION_PERIOD 86400") - if err != nil { - panic(err) - } - _, err = db.Exec("create topic example_ws_tmq_topic as database example_ws_tmq") - if err != nil { - panic(err) - } -} +{{#include docs/examples/go/demo/consumerws/main.go}} ``` diff --git a/docs/en/08-client-libraries/06-rust.mdx b/docs/en/08-client-libraries/06-rust.mdx index cb27a2529d..9a74c4c096 100644 --- a/docs/en/08-client-libraries/06-rust.mdx +++ b/docs/en/08-client-libraries/06-rust.mdx @@ -13,15 +13,24 @@ import RustInsert from "../07-develop/03-insert-data/_rust_sql.mdx" import RustBind from "../07-develop/03-insert-data/_rust_stmt.mdx" import RustSml from "../07-develop/03-insert-data/_rust_schemaless.mdx" import RustQuery from "../07-develop/04-query-data/_rust.mdx" +import RequestId from "./_request_id.mdx"; [![Crates.io](https://img.shields.io/crates/v/taos)](https://crates.io/crates/taos) ![Crates.io](https://img.shields.io/crates/d/taos) [![docs.rs](https://img.shields.io/docsrs/taos)](https://docs.rs/taos) `taos` is the official Rust client library for TDengine. Rust developers can develop applications to access the TDengine instance data. -`taos` provides two ways to establish connections. One is the **Native Connection**, which connects to TDengine instances via the TDengine client driver (taosc). The other is the **WebSocket connection**, which connects to TDengine instances via the WebSocket interface provided by taosAdapter. You can specify a connection type with Cargo features. By default, both types are supported. The Websocket connection can be used on any platform. The native connection can be used on any platform that the TDengine Client supports. - The source code for the Rust client library is located on [GitHub](https://github.com/taosdata/taos-connector-rust). +## Connection types + +`taos` provides two ways to establish connections, among which we recommend using **websocket connection**. +- **Native Connection**, which connects to TDengine instances via the TDengine client driver (taosc). +- **WebSocket connection**, which connects to TDengine instances via the WebSocket interface provided by taosAdapter. + +You can specify a connection type with Cargo features. By default, both types are supported. + +For a detailed introduction of the connection types, please refer to: [Establish Connection](../../develop/connect/#establish-connection) + ## Supported platforms Native connections are supported on the same platforms as the TDengine client driver. @@ -31,8 +40,11 @@ Websocket connections are supported on all platforms that can run Go. | connector-rust version | TDengine version | major features | | :----------------: | :--------------: | :--------------------------------------------------: | -| v0.9.2 | 3.0.7.0 or later | STMT: Get tag_fields and col_fields under ws. | -| v0.8.12 | 3.0.5.0 | TMQ: Get consuming progress and seek offset to consume. | +| v0.12.0 | 3.2.3.0 or later | WS supports compression | +| v0.11.0 | 3.2.0.0 | TMQ feature optimization | +| v0.10.0 | 3.1.0.0 | WS endpoint changes | +| v0.9.2 | 3.0.7.0 | STMT: Get tag_fields and col_fields under ws. | +| v0.8.12 | 3.0.5.0 | TMQ: Get consuming progress and seek offset to consume. | | v0.8.0 | 3.0.4.0 | Support schemaless insert. | | v0.7.6 | 3.0.3.0 | Support req_id in query. | | v0.6.0 | 3.0.0.0 | Base features. | @@ -269,52 +281,28 @@ There are two ways to query data: Using built-in types or the [serde](https://se ### Create database and tables ```rust -use taos::*; - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - let dsn = "taos://localhost:6030"; - let builder = TaosBuilder::from_dsn(dsn)?; - - let taos = builder.build()?; - - let db = "query"; - - // create database - taos.exec_many([ - format!("DROP DATABASE IF EXISTS `{db}`"), - format!("CREATE DATABASE `{db}`"), - format!("USE `{db}`"), - ]) - .await?; - - // create table - taos.exec_many([ - // create super table - "CREATE TABLE `meters` (`ts` TIMESTAMP, `current` FLOAT, `voltage` INT, `phase` FLOAT) \ - TAGS (`groupid` INT, `location` BINARY(16))", - // create child table - "CREATE TABLE `d0` USING `meters` TAGS(0, 'Los Angles')", - ]).await?; -} +{{#include docs/examples/rust/nativeexample/examples/query.rs:create_db_and_table}} ``` > The query is consistent with operating a relational database. When using subscripts to get the contents of the returned fields, you have to start from 1. However, we recommend using the field names to get the values of the fields in the result set. ### Insert data - - +```rust +{{#include docs/examples/rust/nativeexample/examples/query.rs:insert_data}} +``` ### Query data - +```rust +{{#include docs/examples/rust/nativeexample/examples/query.rs:query_data}} +``` ### execute SQL with req_id -This req_id can be used to request link tracing. + ```rust -let rs = taos.query_with_req_id("select * from stable where tag1 is null", 1)?; +{{#include docs/examples/rust/nativeexample/examples/query.rs:query_with_req_id}} ``` ### Writing data via parameter binding @@ -323,13 +311,17 @@ TDengine has significantly improved the bind APIs to support data writing (INSER Parameter binding details see [API Reference](#bind-interface) - +```rust +{{#include docs/examples/rust/nativeexample/examples/stmt.rs}} +``` ### Schemaless Writing TDengine supports schemaless writing. It is compatible with InfluxDB's Line Protocol, OpenTSDB's telnet line protocol, and OpenTSDB's JSON format protocol. For more information, see [Schemaless Writing](../../reference/schemaless/). - +```rust +{{#include docs/examples/rust/nativeexample/examples/schemaless.rs}} +``` ### Schemaless with req_id @@ -352,25 +344,15 @@ TDengine starts subscriptions through [TMQ](../../taos-sql/tmq/). #### Create a Topic ```rust -taos.exec_many([ - // create topic for subscription - format!("CREATE TOPIC tmq_meters with META AS DATABASE {db}") -]) -.await?; +{{#include docs/examples/rust/nativeexample/examples/tmq.rs:create_topic}} ``` #### Create a Consumer -You create a TMQ connection by using a DSN. - -```rust -let tmq = TmqBuilder::from_dsn("taos://localhost:6030/?group.id=test")?; -``` - Create a consumer: ```rust -let mut consumer = tmq.build()?; +{{#include docs/examples/rust/nativeexample/examples/tmq.rs:create_consumer}} ``` #### Subscribe to consume data @@ -378,40 +360,13 @@ let mut consumer = tmq.build()?; A single consumer can subscribe to one or more topics. ```rust -consumer.subscribe(["tmq_meters"]).await?; +{{#include docs/examples/rust/nativeexample/examples/tmq.rs:subscribe}} ``` The TMQ is of [futures::Stream](https://docs.rs/futures/latest/futures/stream/index.html) type. You can use the corresponding API to consume each message in the queue and then use `.commit` to mark them as consumed. ```rust -{ - let mut stream = consumer.stream(); - - while let Some((offset, message)) = stream.try_next().await? { - // get information from offset - - // the topic - let topic = offset.topic(); - // the vgroup id, like partition id in kafka. - let vgroup_id = offset.vgroup_id(); - println!("* in vgroup id {vgroup_id} of topic {topic}\n"); - - if let Some(data) = message.into_data() { - while let Some(block) = data.fetch_raw_block().await? { - // one block for one table, get table name if needed - let name = block.table_name(); - let records: Vec = block.deserialize().try_collect()?; - println!( - "** table: {}, got {} records: {:#?}\n", - name.unwrap(), - records.len(), - records - ); - } - } - consumer.commit(offset).await?; - } -} +{{#include docs/examples/rust/nativeexample/examples/tmq.rs:consume}} ``` Get assignments: @@ -419,7 +374,7 @@ Get assignments: Version requirements connector-rust >= v0.8.8, TDengine >= 3.0.5.0 ```rust -let assignments = consumer.assignments().await.unwrap(); +{{#include docs/examples/rust/nativeexample/examples/tmq.rs:assignments}} ``` #### Assignment subscription Offset @@ -429,13 +384,13 @@ Seek offset: Version requirements connector-rust >= v0.8.8, TDengine >= 3.0.5.0 ```rust -consumer.offset_seek(topic, vgroup_id, offset).await; +{{#include docs/examples/rust/nativeexample/examples/tmq.rs:seek_offset}} ``` #### Close subscriptions ```rust -consumer.unsubscribe().await; +{{#include docs/examples/rust/nativeexample/examples/tmq.rs:unsubscribe}} ``` The following parameters can be configured for the TMQ DSN. Only `group.id` is mandatory. diff --git a/docs/en/08-client-libraries/07-python.mdx b/docs/en/08-client-libraries/07-python.mdx index 220d676c2b..cc2b7a7aab 100644 --- a/docs/en/08-client-libraries/07-python.mdx +++ b/docs/en/08-client-libraries/07-python.mdx @@ -6,15 +6,25 @@ description: This document describes taospy, the TDengine Python client library. import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem"; +import RequestId from "./_request_id.mdx"; + +`taospy` is the official Python client library for TDengine. taospy provides a rich API that makes it easy for Python applications to use TDengine. + +The source code for the Python client library is hosted on [GitHub](https://github.com/taosdata/taos-connector-python). + +## Connection types + +`taospy` mainly provides 3 connection types, among which we recommend using **websocket connection**. +- **Native connection**, which correspond to the `taos` modules of the `taospy` package, connects to TDengine instances natively through the TDengine client driver (taosc), supporting data writing, querying, subscriptions, schemaless writing, and bind interface. +- **REST connection**, which correspond to the `taosrest` modules of the `taospy` package, which is implemented through taosAdapter. Some features like schemaless and subscriptions are not supported. +- **Websocket connection** `taos-ws-py` is an optional package to enable using WebSocket to connect TDengine, which is implemented through taosAdapter. The set of features implemented by the WebSocket connection differs slightly from those implemented by the native connection. + +For a detailed introduction of the connection types, please refer to: [Establish Connection](../../develop/connect/#establish-connection) -`taospy` is the official Python client library for TDengine. taospy provides a rich API that makes it easy for Python applications to use TDengine. `taospy` wraps both the [native interface](../cpp) and [REST interface](../../reference/rest-api) of TDengine, which correspond to the `taos` and `taosrest` modules of the `taospy` package, respectively. In addition to wrapping the native and REST interfaces, `taospy` also provides a set of programming interfaces that conforms to the [Python Data Access Specification (PEP 249)](https://peps.python.org/pep-0249/). It is easy to integrate `taospy` with many third-party tools, such as [SQLAlchemy](https://www.sqlalchemy.org/) and [pandas](https://pandas.pydata.org/). -`taos-ws-py` is an optional package to enable using WebSocket to connect TDengine. - The direct connection to the server using the native interface provided by the client driver is referred to hereinafter as a "native connection"; the connection to the server using the REST or WebSocket interface provided by taosAdapter is referred to hereinafter as a "REST connection" or "WebSocket connection". -The source code for the Python client library is hosted on [GitHub](https://github.com/taosdata/taos-connector-python). ## Supported platforms - The [supported platforms](../#supported-platforms) for the native connection are the same as the ones supported by the TDengine client. @@ -348,13 +358,7 @@ If the configuration parameters are duplicated in the parameters or client confi ```python -conn = taos.connect() -# Execute a sql, ignore the result set, just get affected rows. It's useful for DDL and DML statement. -conn.execute("DROP DATABASE IF EXISTS test") -conn.execute("CREATE DATABASE test") -# change database. same as execute "USE db" -conn.select_db("test") -conn.execute("CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT) TAGS (location INT)") +{{#include docs/examples/python/create_db_native.py}} ``` @@ -362,12 +366,7 @@ conn.execute("CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT) TAGS (locat ```python -conn = taosrest.connect(url="http://localhost:6041") -# Execute a sql, ignore the result set, just get affected rows. It's useful for DDL and DML statement. -conn.execute("DROP DATABASE IF EXISTS test") -conn.execute("CREATE DATABASE test") -conn.execute("USE test") -conn.execute("CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT) TAGS (location INT)") +{{#include docs/examples/python/create_db_rest.py}} ``` @@ -375,12 +374,7 @@ conn.execute("CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT) TAGS (locat ```python -conn = taosws.connect("taosws://localhost:6041") -# Execute a sql, ignore the result set, just get affected rows. It's useful for DDL and DML statement. -conn.execute("DROP DATABASE IF EXISTS test") -conn.execute("CREATE DATABASE test") -conn.execute("USE test") -conn.execute("CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT) TAGS (location INT)") +{{#include docs/examples/python/create_db_ws.py}} ``` @@ -388,100 +382,35 @@ conn.execute("CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT) TAGS (locat ### Insert data -```python -conn.execute("INSERT INTO t1 USING weather TAGS(1) VALUES (now, 23.5) (now+1m, 23.5) (now+2m, 24.4)") -``` - -::: -now is an internal function. The default is the current time of the client's computer. now + 1s represents the current time of the client plus 1 second, followed by the number representing the unit of time: a (milliseconds), s (seconds), m (minutes), h (hours), d (days), w (weeks), n (months), y (years). -::: - - -### Basic Usage - -##### TaosConnection class - -The `TaosConnection` class contains both an implementation of the PEP249 Connection interface (e.g., the `cursor()` method and the `close()` method) and many extensions (e.g., the `execute()`, `query()`, `schemaless_insert()`, and `subscribe()` methods). - -```python title="execute method" -{{#include docs/examples/python/connection_usage_native_reference.py:insert}} +```python +{{#include docs/examples/python/insert_native.py:insert}} ``` -```python title="query method" -{{#include docs/examples/python/connection_usage_native_reference.py:query}} -``` - -:::tip -The queried results can only be fetched once. For example, only one of `fetch_all()` and `fetch_all_into_dict()` can be used in the example above. Repeated fetches will result in an empty list. -::: - -##### Use of TaosResult class - -In the above example of using the `TaosConnection` class, we have shown two ways to get the result of a query: `fetch_all()` and `fetch_all_into_dict()`. In addition, `TaosResult` also provides methods to iterate through the result set by rows (`rows_iter`) or by data blocks (`blocks_iter`). Using these two methods will be more efficient in scenarios where the query has a large amount of data. - -```python title="blocks_iter method" -{{#include docs/examples/python/result_set_examples.py}} -``` -##### Use of the TaosCursor class - -The `TaosConnection` class and the `TaosResult` class already implement all the functionality of the native interface. If you are familiar with the interfaces in the PEP249 specification, you can also use the methods provided by the `TaosCursor` class. - -```python title="Use of TaosCursor" -{{#include docs/examples/python/cursor_usage_native_reference.py}} -``` - -:::note -The TaosCursor class uses native connections for write and query operations. In a client-side multi-threaded scenario, this cursor instance must remain thread exclusive and cannot be shared across threads for use, otherwise, it will result in errors in the returned results. - -The best practice for TaosCursor is to create a cursor at the beginning of a query and close it immediately after use. Please avoid reusing the same cursor for multiple executions. -::: - + -##### Use of the RestClient class - -The `RestClient` class is a direct wrapper for the [REST API](../../reference/rest-api). It contains only a `sql()` method for executing arbitrary SQL statements and returning the result. - -```python title="Use of RestClient" -{{#include docs/examples/python/rest_client_example.py}} +```python +{{#include docs/examples/python/insert_rest.py:insert}} ``` -For a more detailed description of the `sql()` method, please refer to [RestClient](https://docs.taosdata.com/api/taospy/taosrest/restclient.html). - -##### Use of TaosRestCursor class - -The `TaosRestCursor` class is an implementation of the PEP249 Cursor interface. - -```python title="Use of TaosRestCursor" -{{#include docs/examples/python/connect_rest_examples.py:basic}} -``` -- `cursor.execute`: Used to execute arbitrary SQL statements. -- `cursor.rowcount` : For write operations, returns the number of successful rows written. For query operations, returns the number of rows in the result set. -- `cursor.description` : Returns the description of the field. Please refer to [TaosRestCursor](https://docs.taosdata.com/api/taospy/taosrest/cursor.html) for the specific format of the description information. - -:::note -The best practice for TaosRestCursor is to create a cursor at the beginning of a query and close it immediately after use. Please avoid reusing the same cursor for multiple executions. -::: - + -The `Connection` class contains both an implementation of the PEP249 Connection interface (e.g., the `cursor()` method and the `close()` method) and many extensions (e.g., the `execute()`, `query()`, `schemaless_insert()`, and `subscribe()` methods). - ```python -{{#include docs/examples/python/connect_websocket_examples.py:basic}} +{{#include docs/examples/python/insert_ws.py:insert}} ``` -- `conn.execute`: can use to execute arbitrary SQL statements, and return the number of rows affected. -- `conn.query`: can use to execute query SQL statements, and return the query results. - +> NOW is an internal function. The default is the current time of the client's computer. +> `NOW + 1s` represents the current time of the client plus 1 second, followed by the number representing the unit of time: a (milliseconds), s (seconds), m (minutes), h (hours), d (days), w (weeks), n (months), y (years). + ### Querying Data @@ -490,7 +419,7 @@ The `Connection` class contains both an implementation of the PEP249 Connection The `query` method of the `TaosConnection` class can be used to query data and return the result data of type `TaosResult`. ```python -{{#include docs/examples/python/connection_usage_native_reference.py:query}} +{{#include docs/examples/python/insert_native.py:query}} ``` :::tip @@ -504,7 +433,7 @@ The queried results can only be fetched once. For example, only one of `fetch_al The `RestClient` class is a direct wrapper for the [REST API](../../reference/rest-api). It contains only a `sql()` method for executing arbitrary SQL statements and returning the result. ```python -{{#include docs/examples/python/rest_client_example.py}} +{{#include docs/examples/python/insert_rest.py:query}} ``` For a more detailed description of the `sql()` method, please refer to [RestClient](https://docs.taosdata.com/api/taospy/taosrest/restclient.html). @@ -516,7 +445,7 @@ For a more detailed description of the `sql()` method, please refer to [RestClie The `query` method of the `TaosConnection` class can be used to query data and return the result data of type `TaosResult`. ```python -{{#include docs/examples/python/connect_websocket_examples.py:basic}} +{{#include docs/examples/python/insert_ws.py:query}} ``` @@ -524,61 +453,25 @@ The `query` method of the `TaosConnection` class can be used to query data and r ### Execute SQL with reqId -By using the optional req_id parameter, you can specify a request ID that can be used for tracing. + -##### TaosConnection class - As the way to connect introduced above but add `req_id` argument. -```python title="execute method" -{{#include docs/examples/python/connection_usage_native_reference_with_req_id.py:insert}} -``` - -```python title="query method" -{{#include docs/examples/python/connection_usage_native_reference_with_req_id.py:query}} -``` - -##### Use of TaosResult class - -As the way to fetch data introduced above but add `req_id` argument. - -```python title="blocks_iter method" -{{#include docs/examples/python/result_set_with_req_id_examples.py}} -``` -##### Use of the TaosCursor class - -The `TaosConnection` class and the `TaosResult` class already implement all the functionality of the native interface. If you are familiar with the interfaces in the PEP249 specification, you can also use the methods provided by the `TaosCursor` class. - -```python title="Use of TaosCursor" -{{#include docs/examples/python/cursor_usage_native_reference_with_req_id.py}} +```python +{{#include docs/examples/python/insert_native.py:req_id}} ``` -##### Use of the RestClient class - -The `RestClient` class is a direct wrapper for the [REST API](../../reference/rest-api). It contains only a `sql()` method for executing arbitrary SQL statements and returning the result. - -```python title="Use of RestClient" -{{#include docs/examples/python/rest_client_with_req_id_example.py}} -``` - -For a more detailed description of the `sql()` method, please refer to [RestClient](https://docs.taosdata.com/api/taospy/taosrest/restclient.html). - -##### Use of TaosRestCursor class - As the way to connect introduced above but add `req_id` argument. -```python title="Use of TaosRestCursor" -{{#include docs/examples/python/connect_rest_with_req_id_examples.py:basic}} +```python +{{#include docs/examples/python/insert_rest.py:req_id}} ``` -- `cursor.execute`: Used to execute arbitrary SQL statements. -- `cursor.rowcount` : For write operations, returns the number of successful rows written. For query operations, returns the number of rows in the result set. -- `cursor.description` : Returns the description of the field. Please refer to [TaosRestCursor](https://docs.taosdata.com/api/taospy/taosrest/cursor.html) for the specific format of the description information. @@ -587,36 +480,7 @@ As the way to connect introduced above but add `req_id` argument. As the way to connect introduced above but add `req_id` argument. ```python -{{#include docs/examples/python/connect_websocket_with_req_id_examples.py:basic}} -``` - -- `conn.execute`: can use to execute arbitrary SQL statements, and return the number of rows affected. -- `conn.query`: can use to execute query SQL statements, and return the query results. - - - - -### Used with pandas - - - - -```python -{{#include docs/examples/python/conn_native_pandas.py}} -``` - - - - -```python -{{#include docs/examples/python/conn_rest_pandas.py}} -``` - - - - -```python -{{#include docs/examples/python/conn_websocket_pandas.py}} +{{#include docs/examples/python/insert_ws.py:req_id}} ``` @@ -629,126 +493,15 @@ The Python client library provides a parameter binding api for inserting data. S -##### Create Stmt - -Call the `statement` method in `Connection` to create the `stmt` for parameter binding. - -``` -import taos - -conn = taos.connect() -stmt = conn.statement("insert into log values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)") -``` - -##### parameter binding - -Call the `new_multi_binds` function to create the parameter list for parameter bindings. - -``` -params = new_multi_binds(16) -params[0].timestamp((1626861392589, 1626861392590, 1626861392591)) -params[1].bool((True, None, False)) -params[2].tinyint([-128, -128, None]) # -128 is tinyint null -params[3].tinyint([0, 127, None]) -params[4].smallint([3, None, 2]) -params[5].int([3, 4, None]) -params[6].bigint([3, 4, None]) -params[7].tinyint_unsigned([3, 4, None]) -params[8].smallint_unsigned([3, 4, None]) -params[9].int_unsigned([3, 4, None]) -params[10].bigint_unsigned([3, 4, None]) -params[11].float([3, None, 1]) -params[12].double([3, None, 1.2]) -params[13].binary(["abc", "dddafadfadfadfadfa", None]) -params[14].nchar(["涛思数据", None, "a long string with 中文字符"]) -params[15].timestamp([None, None, 1626861392591]) -``` - -Call the `bind_param` (for a single row) method or the `bind_param_batch` (for multiple rows) method to set the values. - -``` -stmt.bind_param_batch(params) -``` - -##### execute sql - -Call `execute` method to execute sql. - -``` -stmt.execute() -``` - -##### Close Stmt - -``` -stmt.close() -``` - -##### Example - ```python -{{#include docs/examples/python/stmt_example.py}} +{{#include docs/examples/python/stmt_native.py:stmt}} ``` -##### Create Stmt - -Call the `statement` method in `Connection` to create the `stmt` for parameter binding. - -``` -import taosws - -conn = taosws.connect('taosws://localhost:6041/test') -stmt = conn.statement() -``` - -##### Prepare sql - -Call `prepare` method in stmt to prepare sql. - -``` -stmt.prepare("insert into t1 values (?, ?, ?, ?)") -``` - -##### parameter binding - -Call the `bind_param` method to bind parameters. - -``` -stmt.bind_param([ - taosws.millis_timestamps_to_column([1686844800000, 1686844801000, 1686844802000, 1686844803000]), - taosws.ints_to_column([1, 2, 3, 4]), - taosws.floats_to_column([1.1, 2.2, 3.3, 4.4]), - taosws.varchar_to_column(['a', 'b', 'c', 'd']), -]) -``` - -Call the `add_batch` method to add parameters to the batch. - -``` -stmt.add_batch() -``` - -##### execute sql - -Call `execute` method to execute sql. - -``` -stmt.execute() -``` - -##### Close Stmt - -``` -stmt.close() -``` - -##### Example - ```python -{{#include docs/examples/python/stmt_websocket_example.py}} +{{#include docs/examples/python/stmt_ws.py:stmt}} ``` @@ -758,46 +511,18 @@ stmt.close() Client library support schemaless insert. - - -##### Simple insert + ```python -{{#include docs/examples/python/schemaless_insert.py}} -``` - -##### Insert with ttl argument - -```python -{{#include docs/examples/python/schemaless_insert_ttl.py}} -``` - -##### Insert with req_id argument - -```python -{{#include docs/examples/python/schemaless_insert_req_id.py}} +{{#include docs/examples/python/schemaless_native.py}} ``` - - -##### Simple insert + ```python -{{#include docs/examples/python/schemaless_insert_raw.py}} -``` - -##### Insert with ttl argument - -```python -{{#include docs/examples/python/schemaless_insert_raw_ttl.py}} -``` - -##### Insert with req_id argument - -```python -{{#include docs/examples/python/schemaless_insert_raw_req_id.py}} +{{#include docs/examples/python/schemaless_ws.py}} ``` @@ -808,11 +533,12 @@ Client library support schemaless insert. There is a optional parameter called `req_id` in `schemaless_insert` and `schemaless_insert_raw` method. This reqId can be used to request link tracing. ```python -{{#include docs/examples/python/schemaless_insert_req_id.py}} -``` - -```python -{{#include docs/examples/python/schemaless_insert_raw_req_id.py}} +conn.schemaless_insert( + lines=lineDemo, + protocol=taos.SmlProtocol.LINE_PROTOCOL, + precision=taos.SmlPrecision.NANO_SECONDS, + req_id=1, +) ``` ### Data Subscription @@ -821,194 +547,56 @@ Client library support data subscription. For more information about subscroptio #### Create a Topic -To create topic, please refer to [Data Subscription](../../develop/tmq/#create-a-topic). +```python +{{#include docs/examples/python/tmq_native.py:create_topic}} +``` #### Create a Consumer - - - - -The consumer in the client library contains the subscription api. The syntax for creating a consumer is consumer = Consumer(configs). For more subscription api parameters, please refer to [Data Subscription](../../develop/tmq/#create-a-consumer). - ```python -from taos.tmq import Consumer - -consumer = Consumer({"group.id": "local", "td.connect.ip": "127.0.0.1"}) +{{#include docs/examples/python/tmq_native.py:create_consumer}} ``` - - - - -In addition to native connections, the client library also supports subscriptions via websockets. - -The syntax for creating a consumer is "consumer = Consumer(conf=configs)". You need to specify that the `td.connect.websocket.scheme` parameter is set to "ws" in the configuration. For more subscription api parameters, please refer to [Data Subscription](../../develop/tmq/#create-a-consumer). - -```python -import taosws - -consumer = taosws.Consumer(conf={"group.id": "local", "td.connect.websocket.scheme": "ws"}) -``` - - - #### Subscribe to a Topic - - - - -The `subscribe` function is used to subscribe to a list of topics. - ```python -consumer.subscribe(['topic1', 'topic2']) +{{#include docs/examples/python/tmq_native.py:subscribe}} ``` - - - -The `subscribe` function is used to subscribe to a list of topics. - -```python -consumer.subscribe(['topic1', 'topic2']) -``` - - - - #### Consume messages - - - - -The `poll` function is used to consume data in tmq. The parameter of the `poll` function is a value of type float representing the timeout in seconds. It returns a `Message` before timing out, or `None` on timing out. You have to handle error messages in response data. - ```python -while True: - message = consumer.poll(1) - if not message: - continue - err = message.error() - if err is not None: - raise err - val = message.value() - - for block in val: - print(block.fetchall()) +{{#include docs/examples/python/tmq_native.py:consume}} ``` - - - -The `poll` function is used to consume data in tmq. The parameter of the `poll` function is a value of type float representing the timeout in seconds. It returns a `Message` before timing out, or `None` on timing out. - -```python -while True: - message = consumer.poll(1) - if not message: - continue - - for block in message: - for row in block: - print(row) -``` - - - - #### Assignment subscription Offset - - - - The `assignment` function is used to get the assignment of the topic. ```python -assignments = consumer.assignment() +{{#include docs/examples/python/tmq_native.py:assignment}} ``` The `seek` function is used to reset the assignment of the topic. ```python -tp = TopicPartition(topic='topic1', partition=0, offset=0) -consumer.seek(tp) +{{#include docs/examples/python/tmq_native.py:consume}} ``` - - - -The `assignment` function is used to get the assignment of the topic. - -```python -assignments = consumer.assignment() -``` - -The `seek` function is used to reset the assignment of the topic. - -```python -consumer.seek(topic='topic1', partition=0, offset=0) -``` - - - - #### Close subscriptions - - - - You should unsubscribe to the topics and close the consumer after consuming. ```python -consumer.unsubscribe() -consumer.close() +{{#include docs/examples/python/tmq_native.py:unsubscribe}} ``` - - - -You should unsubscribe to the topics and close the consumer after consuming. - -```python -consumer.unsubscribe() -consumer.close() -``` - - - - #### Full Sample Code - - - - ```python -{{#include docs/examples/python/tmq_example.py}} +{{#include docs/examples/python/tmq_native.py}} ``` -```python -{{#include docs/examples/python/tmq_assignment_example.py:taos_get_assignment_and_seek_demo}} -``` - - - - -```python -{{#include docs/examples/python/tmq_websocket_example.py}} -``` - -```python -{{#include docs/examples/python/tmq_websocket_assgnment_example.py:taosws_get_assignment_and_seek_demo}} -``` - - - - ### Other sample programs | Example program links | Example program content | diff --git a/docs/en/08-client-libraries/08-node.mdx b/docs/en/08-client-libraries/08-node.mdx index 5c8e9d87af..80ebd80825 100644 --- a/docs/en/08-client-libraries/08-node.mdx +++ b/docs/en/08-client-libraries/08-node.mdx @@ -7,35 +7,30 @@ toc_max_heading_level: 4 import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem"; +import RequestId from "./_request_id.mdx"; -import Preparition from "./_preparation.mdx"; -import NodeInsert from "../07-develop/03-insert-data/_js_sql.mdx"; -import NodeInfluxLine from "../07-develop/03-insert-data/_js_line.mdx"; -import NodeOpenTSDBTelnet from "../07-develop/03-insert-data/_js_opts_telnet.mdx"; -import NodeOpenTSDBJson from "../07-develop/03-insert-data/_js_opts_json.mdx"; -import NodeQuery from "../07-develop/04-query-data/_js.mdx"; +`@tdengine/websocket` is the official Node.js client library for TDengine. Node.js developers can develop applications to access the TDengine instance data. -`@tdengine/client` and `@tdengine/rest` are the official Node.js client libraries. Node.js developers can develop applications to access TDengine instance data. Note: The client libraries for TDengine 3.0 are different than those for TDengine 2.x. The new client libraries do not support TDengine 2.x. +The source code for the Node.js client library is hosted on [GitHub](https://github.com/taosdata/taos-connector-node/tree/main). -`@tdengine/client` is **native connection**, which connects to TDengine instances natively through the TDengine client driver (taosc), supporting data writing, querying, subscriptions, schemaless writing, and bind interface. `@tdengine/rest` is the **REST connection**, which connects to TDengine instances via the REST interface provided by taosAdapter. The REST client library can run on any platform, but performance is slightly degraded, and the interface implements a somewhat different set of functional features than the native interface. +## Connection types -The source code for the Node.js client libraries is located on [GitHub](https://github.com/taosdata/taos-connector-node/tree/3.0). +Node.js connector supports only websocket connection through taosAdapter. + +For a detailed introduction of the connection types, please refer to: [Establish Connection](../../develop/connect/#establish-connection) ## Supported platforms -The platforms supported by the native client library are the same as those supported by the TDengine client driver. -The REST client library supports all platforms that can run Node.js. +Node.js client library needs to be run with Node.js 14 or higher version. -## Version support +## Recent update logs -Please refer to [version support list](../#version-support) +| Node.js connector version | major changes | TDengine 版本 | +| :-----------------------: | :------------------: | :----------------:| +| 3.1.0 | new version, supports websocket | 3.2.0.0 or later | ## Supported features - - - - 1. Connection Management 2. General Query 3. Continuous Query @@ -43,294 +38,300 @@ Please refer to [version support list](../#version-support) 5. Subscription 6. Schemaless - - +## Handling exceptions -1. Connection Management -2. General Query -3. Continuous Query +After an error is reported, the error message and error code can be obtained through try catch. The Node.js client library error code is between 100 and 110, while the other error codes are for the TDengine function module. - - +Please refer to the table below for error code, error description and corresponding suggestions. + +| Error Code | Description | Suggested Actions | +| ---------- | -------------------------------------------------------------| -------------------------------------------------------------------------------------------------- | +| 100 | invalid variables | The parameter is invalid. Check the interface specification and adjust the parameter type and size.| +| 101 | invalid url | URL error, please check if the url is correct. | +| 102 | received server data but did not find a callback for processing | Client waiting timeout, please check network and TaosAdapter status. | +| 103 | invalid message type | Please check if the client version and server version match. | +| 104 | connection creation failed | Connection creation failed. Please check the network and TaosAdapter status. | +| 105 | websocket request timeout | Increase the execution time by adding the messageWaitTimeout parameter, or check the connection to the TaosAdapter.| +| 106 | authentication fail | Authentication failed, please check if the username and password are correct. | +| 107 | unknown sql type in tdengine | Check the data type supported by TDengine. | +| 108 | connection has been closed | The connection has been closed, check the connection status, or recreate the connection to execute the relevant instructions. | +| 109 | fetch block data parse fail | Please check if the client version and server version match. | +| 110 | websocket connection has reached its maximum limit | Please check if the connection has been closed after use | + +## Data Type Mapping + +The table below despicts the mapping between TDengine data type and Node.js data type. + +| TDengine Data Type | Node.js Data Type| +|-------------------|-------------| +| TIMESTAMP | bigint | +| TINYINT | number | +| SMALLINT | number | +| INT | number | +| BIGINT | bigint | +| TINYINT UNSIGNED | number | +| SMALLINT UNSIGNED | number | +| INT UNSIGNED | number | +| BIGINT UNSIGNED | bigint | +| FLOAT | number | +| DOUBLE | number | +| BOOL | boolean | +| BINARY | string | +| NCHAR | string | +| JSON | string | +| VARBINARY | ArrayBuffer | +| GEOMETRY | ArrayBuffer | + +**Note**: Only TAG supports JSON types ## Installation Steps ### Pre-installation preparation -- Install the Node.js development environment -- If you are using the REST client library, skip this step. However, if you use the native client library, please install the TDengine client driver. Please refer to [Install Client Driver](../#install-client-driver) for more details. We use [node-gyp](https://github.com/nodejs/node-gyp) to interact with TDengine instances and also need to install some dependencies mentioned below depending on the specific OS. +- Install the Node.js development environment, using version 14 or above. Download link: https://nodejs.org/en/download/ - - - -- `python` (recommended for `v2.7` , `v3.x.x` currently not supported) -- `@tdengine/client` 3.0.0 supports Node.js LTS v10.9.0 or later and Node.js LTS v12.8.0 or later. Older versions may be incompatible. -- `make` -- C compiler, [GCC](https://gcc.gnu.org) v4.8.5 or later. - - - - - -- `python` (recommended for `v2.7` , `v3.x.x` currently not supported) -- `@tdengine/client` 3.0.0 currently supports Node.js from v12.22.12, but only later versions of v12. Other versions may be incompatible. -- `make` -- C compiler, [GCC](https://gcc.gnu.org) v4.8.5 or later. - - - - - -- Installation method 1 - -Use Microsoft's [windows-build-tools](https://github.com/felixrieseberg/windows-build-tools) to execute `npm install --global --production` from the `cmd` command-line interface to install all the necessary tools. - -- Installation method 2 - -Manually install the following tools. - -- Install Visual Studio related: [Visual Studio Build Tools](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools) or [Visual Studio 2017 Community](https://visualstudio.microsoft.com/pl/thank-you-downloading-visual-studio/?sku=Community) -- Install [Python](https://www.python.org/downloads/) 2.7 (`v3.x.x` is not supported) and execute `npm config set python python2.7`. -- Go to the `cmd` command-line interface, `npm config set msvs_version 2017` - -Refer to Microsoft's Node.js User Manual [Microsoft's Node.js Guidelines for Windows](https://github.com/Microsoft/nodejs-guidelines/blob/master/windows-environment.md#compiling-native-addon-modules). - -If using ARM64 Node.js on Windows 10 ARM, you must add "Visual C++ compilers and libraries for ARM64" and "Visual C++ ATL for ARM64". - - - - -### Install via npm - - - +### Install Node.js client library via npm ```bash -npm install @tdengine/client +npm install @tdengine/websocket ``` - - - -```bash -npm install @tdengine/rest -``` - - - - ### Verify - - - After installing the TDengine client, use the `nodejsChecker.js` program to verify that the current environment supports Node.js access to TDengine. Verification in details: -- Create an installation test folder such as `~/tdengine-test`. Download the [nodejsChecker.js source code](https://github.com/taosdata/taos-connector-node/blob/3.0/nodejs/examples/nodejsChecker.js) to your local machine. +- Create an installation test folder such as `~/tdengine-test`. Download the [nodejsChecker.js](https://github.com/taosdata/TDengine/tree/main/docs/examples/node/websocketexample/nodejsChecker.js) to your local machine. - Execute the following command from the command-line. ```bash -npm init -y -npm install @tdengine/client -node nodejsChecker.js host=localhost + npm init -y + npm install @tdengine/websocket + node nodejsChecker.js host=localhost ``` - After executing the above steps, the command-line will output the result of `nodejsChecker.js` connecting to the TDengine instance and performing a simple insert and query. - - - -After installing the TDengine client, use the `restChecker.js` program to verify that the current environment supports Node.js access to TDengine. - -Verification in details: - -- Create an installation test folder such as `~/tdengine-test`. Download the [restChecker.js source code](https://github.com/taosdata/TDengine/tree/3.0/docs/examples/node/restexample/restChecker.js) to your local. - -- Execute the following command from the command-line. - -```bash -npm init -y -npm install @tdengine/rest -node restChecker.js -``` - -- After executing the above steps, the command-line will output the result of `restChecker.js` connecting to the TDengine instance and performing a simple insert and query. - - - - ## Establishing a connection -Please choose to use one of the client libraries. +Install and import the `@tdengine/websocket` package. - - - -Install and import the `@tdengine/client` package. +**Note**: After using the Node.js client library, it is necessary to call taos.destroy() Release connector resources. ```javascript -//A cursor also needs to be initialized in order to interact with TDengine from Node.js. -const taos = require("@tdengine/client"); -var conn = taos.connect({ - host: "127.0.0.1", - user: "root", - password: "taosdata", - config: "/etc/taos", - port: 0, -}); -var cursor = conn.cursor(); // Initializing a new cursor +const taos = require("@tdengine/websocket"); -//Close a connection -conn.close(); +//database operations...... + +taos.destroy(); ``` - - - -Install and import the `@tdengine/rest` package. - ```javascript -//A cursor also needs to be initialized in order to interact with TDengine from Node.js. -import { options, connect } from "@tdengine/rest"; -options.path = "/rest/sql"; -// set host -options.host = "localhost"; -// set other options like user/passwd - -let conn = connect(options); -let cursor = conn.cursor(); +WSConfig configures the Websocket parameters as follows: + getToken(): string | undefined | null; + setToken(token: string): void; + getUser(): string | undefined | null; + setUser(user: string): void; + getPwd(): string | undefined | null; + setPwd(pws: string): void; + getDb(): string | undefined | null; + setDb(db: string): void; + getUrl(): string; + setUrl(url: string): void; + setTimeOut(ms: number): void; + getTimeOut(): number | undefined | null; ``` - - +```javascript +{{#include docs/examples/node/websocketexample/sql_example.js:createConnect}} +``` ## Usage examples -### Write data +### Create database and tables -#### SQL Write - - - - - - - - - -```js -{{#include docs/examples/node/restexample/insert_example.js}} +```javascript +{{#include docs/examples/node/websocketexample/sql_example.js:create_db_and_table}} ``` - - +**Note**: If you do not use `USE power` to specify the database, all subsequent operations on the table need to add the database name as a prefix, such as power.meters. -#### InfluxDB line protocol write +### Insert data - - +```javascript +{{#include docs/examples/node/websocketexample/sql_example.js:insertData}} +``` - - - - - -#### OpenTSDB Telnet line protocol write - - - - - - - - - -#### OpenTSDB JSON line protocol write - - - - - - - - +> NOW is an internal function. The default is the current time of the client's computer. +> `NOW + 1s` represents the current time of the client plus 1 second, followed by the number representing the unit of time: a (milliseconds), s (seconds), m (minutes), h (hours), d (days), w (weeks), n (months), y (years). ### Querying data - - - - - - - - - -```js -{{#include docs/examples/node/restexample/query_example.js}} +```javascript +{{#include docs/examples/node/websocketexample/sql_example.js:queryData}} ``` - - +> Discovered data structure +```javascript +wsRow:meta:=> [ + { name: 'ts', type: 'TIMESTAMP', length: 8 }, + { name: 'current', type: 'FLOAT', length: 4 }, + { name: 'voltage', type: 'INT', length: 4 }, + { name: 'phase', type: 'FLOAT', length: 4 }, + { name: 'location', type: 'VARCHAR', length: 64}, + { name: 'groupid', type: 'INT', length: 4 } +] +wsRow:data:=> [ + [ 1714013737536n, 12.3, 221, 0.31, 'California.SanFrancisco', 3 ] +] +``` + +### Execute SQL with reqId + + + +```javascript +{{#include docs/examples/node/websocketexample/sql_example.js:sqlWithReqid}} +``` + +### Writing data via parameter binding + +The Node.js client library provides a parameter binding api for inserting data. Similar to most databases, TDengine currently only supports the question mark `?` to indicate the parameters to be bound. + +**Note**: Do not use `db.?` in prepareStatement when specify the database with the table name, should directly use `?`, then specify the database in setTableName, for example: `prepareStatement.setTableName("db.t1")`. + +Sample Code: + +```javascript +{{#include docs/examples/node/websocketexample/stmt_example.js}} +``` + +The methods to set TAGS values or VALUES columns: + +```javascript +setBoolean(params: any[]): void; +setTinyInt(params: any[]): void; +setUTinyInt(params: any[]): void; +setSmallInt(params: any[]): void; +setUSmallInt(params: any[]): void; +setInt(params: any[]): void; +setUInt(params: any[]): void; +setBigint(params: any[]): void; +setUBigint(params: any[]): void; +setFloat(params: any[]): void; +setDouble(params: any[]): void; +setVarchar(params: any[]): void; +setBinary(params: any[]): void; +setNchar(params: any[]): void; +setJson(params: any[]): void; +setVarBinary(params: any[]): void; +setGeometry(params: any[]): void; +setTimestamp(params: any[]): void; +``` + +**Note**: Only TAG supports JSON types + +### Schemaless Writing + +TDengine supports schemaless writing. It is compatible with InfluxDB's Line Protocol, OpenTSDB's telnet line protocol, and OpenTSDB's JSON format protocol. For more information, see [Schemaless Writing](../../reference/schemaless/). + +```javascript +{{#include docs/examples/node/websocketexample/line_example.js}} +``` + +### Schemaless with reqId + +This reqId can be used to request link tracing. + +```javascript +await wsSchemaless.schemalessInsert([influxdbData], SchemalessProto.InfluxDBLineProtocol, Precision.NANO_SECONDS, ttl, reqId); +await wsSchemaless.schemalessInsert([telnetData], SchemalessProto.OpenTSDBTelnetLineProtocol, Precision.NANO_SECONDS, ttl, reqId); +await wsSchemaless.schemalessInsert([jsonData], SchemalessProto.OpenTSDBJsonFormatProtocol, Precision.NANO_SECONDS, ttl, reqId); +``` + +### Data Subscription + +The TDengine Node.js client library supports subscription functionality with the following application API. + +#### Create a Topic + +```javascript +{{#include docs/examples/node/websocketexample/tmq_example.js:create_topic}} +``` + +#### Create a Consumer + +```javascript +{{#include docs/examples/node/websocketexample/tmq_example.js:create_consumer}} +``` + +**Parameter Description** + +- taos.TMQConstants.CONNECT_USER: username. +- taos.TMQConstants.CONNECT_PASS: password. +- taos.TMQConstants.GROUP_ID: Specifies the group that the consumer is in. +- taos.TMQConstants.CLIENT_ID: client id. +- taos.TMQConstants.WS_URL: The URL address of TaosAdapter. +- taos.TMQConstants.AUTO_OFFSET_RESET: When offset does not exist, where to start consumption, the optional value is earliest or latest, the default is latest. +- taos.TMQConstants.ENABLE_AUTO_COMMIT: Specifies whether to commit automatically. +- taos.TMQConstants.AUTO_COMMIT_INTERVAL_MS: Automatic submission interval, the default value is 5000 ms. +- taos.TMQConstants.CONNECT_MESSAGE_TIMEOUT: socket timeout in milliseconds, the default value is 10000 ms. It only takes effect when using WebSocket type. + +For more information, see [Consumer Parameters](../../develop/tmq). Note that the default value of auto.offset.reset in data subscription on the TDengine server has changed since version 3.2.0.0. + +#### Subscribe to consume data + +```javascript +{{#include docs/examples/node/websocketexample/tmq_example.js:subscribe}} +``` + +#### Assignment subscription Offset + +```javascript +{{#include docs/examples/node/websocketexample/tmq_example.js:assignment}} +``` + +#### Close subscriptions + +```java +// Unsubscribe +consumer.unsubscribe(); +// Close consumer +consumer.close() +// free connector resource +taos.destroy(); +``` + +For more information, see [Data Subscription](../../develop/tmq). + +#### Full Sample Code + +```javascript +{{#include docs/examples/node/websocketexample/tmq_example.js}} +``` ## More sample programs | Sample Programs | Sample Program Description | -| --------------------------------------------------------------------------------------------------------------------------------- --------- | -------------------------------------- | -| [basicUse](https://github.com/taosdata/taos-connector-node/blob/3.0/nodejs/examples/queryExample.js) | Basic operations such as establishing connections and running SQl commands. | -| [stmtBindBatch](https://github.com/taosdata/taos-connector-node/blob/3.0/nodejs/examples/bindParamBatch.js) | Binding multi-line parameter insertion. | | -| [stmtBindSingleParamBatch](https://github.com/taosdata/taos-connector-node/blob/3.0/nodejs/examples/bindSingleParamBatch.js) | Columnar binding parameter insertion | -| [stmtQuery](https://github.com/taosdata/taos-connector-node/blob/3.0/nodejs/examples/stmtQuery.js) | Binding parameter query | -| [schemless insert](https://github.com/taosdata/taos-connector-node/blob/3.0/nodejs/examples/schemaless.js) | Schemaless insert | -| [TMQ](https://github.com/taosdata/taos-connector-node/blob/3.0/nodejs/examples/tmq.js) | Using data subscription | -| [asyncQuery](https://github.com/taosdata/taos-connector-node/blob/3.0/nodejs/examples/asyncQueryExample.js) | Using asynchronous queries | -| [REST](https://github.com/taosdata/taos-connector-node/blob/3.0/typescript-rest/example/example.ts) | Using TypeScript with the REST client library | +| ---------------------------------------------------------------------------------------------------------------------------------| -------------------------------------- | +| [sql_example](https://github.com/taosdata/TDengine/tree/main/docs/examples/node/websocketexample/sql_example.js) | Basic operations such as establishing connections and running SQl commands.| +| [stmt_example](https://github.com/taosdata/TDengine/tree/main/docs/examples/node/websocketexample/stmt_example.js) | Binding multi-line parameter insertion. | | +| [line_example](https://github.com/taosdata/TDengine/tree/main/docs/examples/node/websocketexample/line_example.js) | Schemaless insert | +| [telnet_line_example](https://github.com/taosdata/TDengine/tree/main/docs/examples/node/websocketexample/telnet_line_example.js) | OpenTSDB Telnet insert | +| [json_line_example](https://github.com/taosdata/TDengine/tree/main/docs/examples/node/websocketexample/json_line_example.js) | OpenTSDB Json insert | +| [tmq_example](https://github.com/taosdata/TDengine/tree/main/docs/examples/node/websocketexample/tmq_example.js) | Using data subscription | ## Usage limitations -`@tdengine/client` 3.0.0 supports Node.js LTS v12.8.0 to 12.9.1 and 10.9.0 to 10.20.0. - - - - +- Node.js client library (`@tdengine/websocket`) supports Node.js 14 or higher. +- It supports only WebSocket connection, so taosAdapter needs to be started in advance. +- After using the connect, you need to call taos.destroy(); Release connector resources. ## Frequently Asked Questions -1. Using REST connections requires starting taosadapter. - - ```bash - sudo systemctl start taosadapter - ``` - -2. Node.js versions - - `@tdengine/client` supports Node.js v10.9.0 to 10.20.0 and 12.8.0 to 12.9.1. - -3. "Unable to establish connection", "Unable to resolve FQDN" - - Usually, the root cause is an incorrect FQDN configuration. You can refer to this section in the [FAQ](https://docs.tdengine.com/2.4/train-faq/faq/#2-how-to-handle-unable-to-establish-connection) to troubleshoot. - -## Important update records - -### Native client library - -| package name | version | TDengine version | Description | -|------------------|---------|---------------------|------------------------------------------------------------------| -| @tdengine/client | 3.0.0 | 3.0.0 | Supports TDengine 3.0. Not compatible with TDengine 2.x. | -| td2.0-connector | 2.0.12 | 2.4.x; 2.5.x; 2.6.x | Fixed cursor.close() bug. | -| td2.0-connector | 2.0.11 | 2.4.x; 2.5.x; 2.6.x | Supports parameter binding, JSON tags and schemaless interface | -| td2.0-connector | 2.0.10 | 2.4.x; 2.5.x; 2.6.x | Supports connection management, standard queries, connection queries, system information, and data subscription | -### REST client library - -| package name | version | TDengine version | Description | -|----------------------|---------|---------------------|---------------------------------------------------------------------------| -| @tdengine/rest | 3.0.0 | 3.0.0 | Supports TDengine 3.0. Not compatible with TDengine 2.x. | -| td2.0-rest-connector | 1.0.7 | 2.4.x; 2.5.x; 2.6.x | Removed default port 6041 | -| td2.0-rest-connector | 1.0.6 | 2.4.x; 2.5.x; 2.6.x | Fixed affectRows bug with create, insert, update, and alter. | -| td2.0-rest-connector | 1.0.5 | 2.4.x; 2.5.x; 2.6.x | Support cloud token | -| td2.0-rest-connector | 1.0.3 | 2.4.x; 2.5.x; 2.6.x | Supports connection management, standard queries, system information, error information, and continuous queries | +1. "Unable to establish connection" or "Unable to resolve FQDN" + **Solution**: Usually, the root cause is an incorrect FQDN configuration. You can refer to this section in the [FAQ](../../train-faq/faq/#2-how-can-i-resolve-the-unable-to-establish-connection-error) to troubleshoot. diff --git a/docs/en/08-client-libraries/09-csharp.mdx b/docs/en/08-client-libraries/09-csharp.mdx index 71cbacb515..58e5196323 100644 --- a/docs/en/08-client-libraries/09-csharp.mdx +++ b/docs/en/08-client-libraries/09-csharp.mdx @@ -7,19 +7,23 @@ toc_max_heading_level: 4 import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; +import RequestId from "./_request_id.mdx"; `TDengine.Connector` is the C# language connector provided by TDengine. C# developers can use it to develop C# application software that accesses TDengine cluster data. -The `TDengine.Connector` connector supports establishing a connection with the TDengine running instance through the TDengine client driver (taosc), and provides functions such as data writing, query, data subscription, schemaless data writing, and parameter binding interface data writing. `TDengine.Connector` also supports WebSocket since v3.0.1, establishes WebSocket connection, and provides functions such as data writing, query, and parameter binding interface data writing. +## Connection types -This article introduces how to install `TDengine.Connector` in a Linux or Windows environment, and connect to the TDengine cluster through `TDengine.Connector` to perform basic operations such as data writing and querying. +`TDengine.Connector` provides 2 connection types. + +* **Native connection**, which connects to TDengine instances natively through the TDengine client driver (taosc), supporting data writing, querying, subscriptions, schemaless writing, and bind interface. +* **WebSocket connection** which is implemented through taosAdapter. The set of features implemented by the WebSocket connection differs slightly from those implemented by the native connection.(since v3.0.1) + +For a detailed introduction of the connection types, please refer to: [Establish Connection](../../develop/connect/#establish-connection) + +## Compatibility -:::warning * `TDengine.Connector` version 3.1.0 has been completely refactored and is no longer compatible with 3.0.2 and previous versions. For 3.0.2 documents, please refer to [nuget](https://www.nuget.org/packages/TDengine.Connector/3.0.2) * `TDengine.Connector` 3.x is not compatible with TDengine 2.x. If you need to use the C# connector in an environment running TDengine 2.x version, please use the 1.x version of TDengine.Connector. -::: - -The source code of `TDengine.Connector` is hosted on [GitHub](https://github.com/taosdata/taos-connector-dotnet/tree/3.0). ## Supported platforms @@ -31,9 +35,12 @@ TDengine no longer supports 32-bit Windows platforms. ## Version support -| **Connector version** | **TDengine version** | -|-----------------------|----------------------| -| 3.1.0 | 3.2.1.0/3.1.1.18 | +| **Connector version** | **TDengine version** | **major features** | +|-----------------------|----------------------|--------------------------------------| +| 3.1.3 | 3.2.1.0/3.1.1.18 | support WebSocket reconnect | +| 3.1.2 | 3.2.1.0/3.1.1.18 | fix schemaless result release | +| 3.1.1 | 3.2.1.0/3.1.1.18 | support varbinary and geometry | +| 3.1.0 | 3.2.1.0/3.1.1.18 | WebSocket uses native implementation | ## Handling exceptions @@ -58,6 +65,8 @@ TDengine no longer supports 32-bit Windows platforms. | BINARY | byte[] | | NCHAR | string (utf-8 encoding) | | JSON | byte[] | +| VARBINARY | byte[] | +| GEOMETRY | byte[] | **Note**: JSON type is only supported in tag. @@ -67,7 +76,7 @@ TDengine no longer supports 32-bit Windows platforms. * Install [.NET SDK](https://dotnet.microsoft.com/download) * [Nuget Client](https://docs.microsoft.com/en-us/nuget/install-nuget-client-tools) (optional installation) -* Install the TDengine client driver. For specific steps, please refer to [Installing the client driver](../#install-client-driver) +* Only for Native connections, you need to install the TDengine client driver. For specific steps, please refer to [Installing the client driver](../#install-client-driver) ### Install the connectors @@ -127,6 +136,12 @@ The parameters supported by `ConnectionStringBuilder` are as follows: * connTimeout: WebSocket connection timeout, only valid when the protocol is WebSocket, the default is 1 minute, use the `TimeSpan.Parse` method to parse the string into a `TimeSpan` object. * readTimeout: WebSocket read timeout, only valid when the protocol is WebSocket, the default is 5 minutes, use the `TimeSpan.Parse` method to parse the string into a `TimeSpan` object. * writeTimeout: WebSocket write timeout, only valid when the protocol is WebSocket, the default is 10 seconds, use the `TimeSpan.Parse` method to parse the string into a `TimeSpan` object. +* enableCompression: Whether to enable WebSocket compression (effective for dotnet version 6 and above, connector version 3.1.1 and above). The default is false. +* autoReconnect: Whether to enable WebSocket reconnect (connector version 3.1.3 and above). The default is false. +> **Note**:Enabling automatic reconnection is only effective for simple SQL statement execution, schemaless writing, and data subscription. It is not effective for parameter binding. Automatic reconnection is only effective for the database specified by parameters when the connection is established, and it is not effective for the `use db` statement to switch databases later. + +* reconnectRetryCount: The number of reconnection retries (connector version 3.1.3 and above). The default is 3. +* reconnectIntervalMs: The interval between reconnection retries (connector version 3.1.3 and above). The default is 2000. ### Specify the URL and Properties to get the connection @@ -407,6 +422,8 @@ namespace WSQuery ### execute SQL with reqId + + @@ -800,6 +817,10 @@ The configuration parameters supported by consumer are as follows: * auto.commit.interval.ms: The interval for automatically submitting offsets, the default is 5000 milliseconds * auto.offset.reset: When offset does not exist, where to start consumption, the optional value is earliest or latest, the default is latest * msg.with.table.name: Whether the message contains the table name +* ws.message.enableCompression: Whether to enable WebSocket compression (effective for dotnet version 6 and above, connector version 3.1.1 and above). The default is false. +* ws.autoReconnect: Whether to enable WebSocket reconnect (connector version 3.1.3 and above). The default is false. +* ws.reconnect.retry.count: The number of reconnection retries (connector version 3.1.3 and above). The default is 3. +* ws.reconnect.interval.ms: The interval between reconnection retries (connector version 3.1.3 and above). The default is 2000. Supports subscribing to the result set `Dictionary` where the key is the column name and the value is the column value. @@ -1185,4 +1206,4 @@ namespace WSADO ### More sample programs -[sample program](https://github.com/taosdata/taos-connector-dotnet/tree/3.0/examples) \ No newline at end of file +[sample program](https://github.com/taosdata/taos-connector-dotnet/tree/3.0/examples) diff --git a/docs/en/08-client-libraries/50-odbc.mdx b/docs/en/08-client-libraries/50-odbc.mdx index 4d9161473a..9ca60d4f59 100644 --- a/docs/en/08-client-libraries/50-odbc.mdx +++ b/docs/en/08-client-libraries/50-odbc.mdx @@ -6,32 +6,35 @@ title: TDengine ODBC ## Introduction -TDengine ODBC driver is a driver specifically designed for TDengine based on the ODBC standard. It can be used by ODBC based applications on Windows to access a local or remote TDengine cluster or TDengine cloud service, like [PowerBI](https://powerbi.microsoft.com). +The TDengine ODBC driver is a driver specifically designed for TDengine based on the ODBC standard. It can be used by ODBC based applications,like [PowerBI](https://powerbi.microsoft.com), on Windows, to access a local or remote TDengine cluster or an instance in the TDengine Cloud service. -TDengine ODBC provides two kinds of connections, native connection and WebSocket connection. You can choose to use either one for your convenience, WebSocket is recommded choice and you must use WebSocket if you are trying to access TDengine cloud service. +TDengine ODBC provides two kinds of connections, native connection and WebSocket connection. You can choose to use either one for your convenience. WebSocket is the recommended choice and you must use WebSocket if you are trying to access an instance in the TDengine Cloud service. -Note: TDengine ODBC driver can only be run on 64-bit system, and can only be invoked by 64-bit applications. +Note: TDengine ODBC driver can only be run on 64-bit systems, and can only be invoked by 64-bit applications. +## Compatibility with ODBC Versions + +- TDengine ODBC driver compatible with ODBC 3.8 and all earlier versions. ## Install -1. TDengine ODBC driver supports only Windows platform. To run on Windows, VisualStudio C Runtime library is required. If VisualStudio C Runtime Library is missing on your platform, you can download and install it from [VC Runtime Library](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170). +1. The TDengine ODBC driver only supports the Windows platform. To run on Windows, the Microsoft Visual C++ Runtime library is required. If the Microsoft Visual C++ Runtime Library is missing on your platform, you can download and install it from [VC Runtime Library](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170). -2. Install TDengine client package for Windows, the version should be above 3.2.1.0, the client package includes both TDengine ODBC driver and some other necessary libraries that will be used in either native connection or WebSocket connection. +2. Install TDengine Client package for Windows. The TDengine Client version should be above 3.2.1.0. The client package includes both the TDengine ODBC driver and some other necessary libraries that will be used in either native connection or WebSocket connection. ## Configure Data Source ### Connection Types -TDengine ODBC driver supports two kinds of connections to TDengine cluster, native connection and WebSocket connection, here is the major differences between them. +TDengine ODBC driver supports two kinds of connections to TDengine cluster: native connection and WebSocket connection. The major differences between them are listed below. -1. Only WebSocket can connect to TDengine cloud service. +1. Only a WebSocket connection can be used to connect to TDengine Cloud service. -2. Websocket connection is more compatible with different TDengine server versions, normally you don't need to uupgrade client package with the server side. +2. A Websocket connection is more compatible with different TDengine server versions. Usually, you don't need to upgrade the TDengine Client package along with the server side. -3. Native connection normally has better performance, but you need to keep the version aligned with the server side. +3. Native connections usually have better performance, but the TDengine Client version must be compatible with the TDengine server version. -4. For most users, it's recommended to use **WebSocket** connection, which has much better compatibility and almost same performance as native connection. +4. For most users, it's recommended to use **WebSocket** connection, which has much better compatibility and almost the same performance as native connection. ### WebSocket Connection @@ -57,9 +60,9 @@ TDengine ODBC driver supports two kinds of connections to TDengine cluster, nati 4.6 [Password]: optional field, only used for connection testing in step 5; -5. Click "Test Connecting" to test whether the data source can be connectted; if successful, it will prompt "connecting success" +5. Click "Test Connection" to test whether the connection to the data source is successful; if successful, it will prompt "Successfully connected to URL" -6. Click "OK" to sae the configuration and exit. +6. Click "OK" to set the configuration and exit. 7. You can also select an already configured data source name in step 2 to change existing configuration. @@ -72,4 +75,4 @@ The steps are exactly same as "WebSocket" connection, except for you choose "Nat ## PowerBI -As an example, you can use PowerBI, which inovkes TDengine ODBC driver, to access TDengine, please refer to[Power BI](../../third-party/powerbi) for more details. +As an example, you can use PowerBI, which invokes TDengine ODBC driver, to access TDengine, please refer to [Power BI](../../third-party/powerbi) for more details. diff --git a/docs/en/08-client-libraries/_request_id.mdx b/docs/en/08-client-libraries/_request_id.mdx new file mode 100644 index 0000000000..3dfd9b395b --- /dev/null +++ b/docs/en/08-client-libraries/_request_id.mdx @@ -0,0 +1,7 @@ +The reqId is very similar to TraceID in distributed tracing systems. In a distributed system, a request may need to pass through multiple services or modules to be completed. The reqId is used to identify and associate all related operations of this request, allowing us to track and understand the complete execution path of the request. +Here are some primary usage of reqId: +- **Request Tracing**: By associating the same reqId with all related operations of a request, we can trace the complete path of the request within the system. +- **Performance Analysis**: By analyzing a request's reqId, we can understand the processing time of the request across various services or modules, thereby identifying performance bottlenecks. +- **Fault Diagnosis**: When a request fails, we can identify the location of the issue by examining the reqId associated with that request. + +If the user does not set a reqId, the client library will generate one randomly internally, but it is still recommended for the user to set it, as it can better associate with the user's request. diff --git a/docs/en/08-client-libraries/assets/odbc-ws-config-en.webp b/docs/en/08-client-libraries/assets/odbc-ws-config-en.webp index aaca2e99b4f203fdffe68eb585260d7ec11d4a3d..e47f3bf97ffe17667a0db5e7f46ff5e3cd2c6d96 100644 GIT binary patch literal 18240 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 KD}f%xfB*p3Y3Y;z diff --git a/docs/en/08-client-libraries/index.mdx b/docs/en/08-client-libraries/index.mdx index 7cf2839609..c06103ce90 100644 --- a/docs/en/08-client-libraries/index.mdx +++ b/docs/en/08-client-libraries/index.mdx @@ -15,9 +15,9 @@ Currently, TDengine's native interface client libraries can support platforms su | -------------- | --------- | -------- | ---------- | ------ | ----------- | ------ | -------- | ----- | | **X86 64bit** | **Linux** | ● | ● | ● | ● | ● | ● | ● | | **X86 64bit** | **Win64** | ● | ● | ● | ● | ● | ● | ● | -| **X86 64bit** | **macOS** | ○ | ● | ● | ○ | ○ | ● | ● | +| **X86 64bit** | **macOS** | ● | ● | ● | ○ | ○ | ● | ● | | **ARM64** | **Linux** | ● | ● | ● | ● | ○ | ○ | ● | -| **ARM64** | **macOS** | ○ | ● | ● | ○ | ○ | ● | ● | +| **ARM64** | **macOS** | ● | ● | ● | ○ | ○ | ● | ● | Where ● means the official test verification passed, ○ means the unofficial test verification passed, -- means no assurance. @@ -59,9 +59,9 @@ The different database framework specifications for various programming language | -------------------------------------- | ------------- | --------------- | ------------- | ------------- | ------------- | ------------- | | **Connection Management** | Support | Support | Support | Support | Support | Support | | **Regular Query** | Support | Support | Support | Support | Support | Support | -| **Parameter Binding** | Supported | Supported | Support | Support | Not Supported | Support | -| **Subscription (TMQ) ** | Supported | Support | Support | Not Supported | Not Supported | Support | -| **Schemaless** | Supported | Supported | Supported | Not Supported | Not Supported | Not Supported | +| **Parameter Binding** | Support | Support | Support | Support | Not Supported | Support | +| **Subscription (TMQ) ** | Support | Support | Support | Support | Not Supported | Support | +| **Schemaless** | Support | Support | Support | Support | Not Supported | Not Supported | | **Bulk Pulling (based on WebSocket) ** | Support | Support | Support | Support | Support | Support | :::warning diff --git a/docs/en/10-deployment/01-deploy.md b/docs/en/10-deployment/01-deploy.md index 6e7e0d1b7c..c6f0f5a3a3 100644 --- a/docs/en/10-deployment/01-deploy.md +++ b/docs/en/10-deployment/01-deploy.md @@ -173,12 +173,6 @@ Query OK, 8 row(s) in set (0.001154s) Before running the TDengine CLI, ensure that the taosd process has been stopped on the dnode that you want to delete. -```sql -DROP DNODE "fqdn:port"; -``` - -or - ```sql DROP DNODE dnodeId; ``` diff --git a/docs/en/12-taos-sql/02-database.md b/docs/en/12-taos-sql/02-database.md index f49a9c6881..3e28a1567c 100644 --- a/docs/en/12-taos-sql/02-database.md +++ b/docs/en/12-taos-sql/02-database.md @@ -60,7 +60,7 @@ database_option: { - PAGES: specifies the number of pages in the metadata storage engine cache on each vnode. Enter a value greater than or equal to 64. The default value is 256. The space occupied by metadata storage on each vnode is equal to the product of the values of the PAGESIZE and PAGES parameters. The space occupied by default is 1 MB. - PAGESIZE: specifies the size (in KB) of each page in the metadata storage engine cache on each vnode. The default value is 4. Enter a value between 1 and 16384. - PRECISION: specifies the precision at which a database records timestamps. Enter ms for milliseconds, us for microseconds, or ns for nanoseconds. The default value is ms. -- REPLICA: specifies the number of replicas that are made of the database. Enter 1 or 3. The default value is 1. The value of the REPLICA parameter cannot exceed the number of dnodes in the cluster. +- REPLICA: specifies the number of replicas that are made of the database. Enter 1, 2 or 3. The default value is 1. 2 is only available in TDengine Enterprise since version 3.3.0.0. The value of the REPLICA parameter cannot exceed the number of dnodes in the cluster. - WAL_LEVEL: specifies whether fsync is enabled. The default value is 1. - 1: WAL is enabled but fsync is disabled. - 2: WAL and fsync are both enabled. diff --git a/docs/en/12-taos-sql/03-table.md b/docs/en/12-taos-sql/03-table.md index 487ce92e03..b45064ce08 100644 --- a/docs/en/12-taos-sql/03-table.md +++ b/docs/en/12-taos-sql/03-table.md @@ -207,6 +207,8 @@ The following SQL statement deletes one or more tables. DROP TABLE [IF EXISTS] [db_name.]tb_name [, [IF EXISTS] [db_name.]tb_name] ... ``` +**Note**:Dropping a table doesn't release the disk space occupied by the table, instead all the rows in the table are marked as deleted, so these data will not occur when querying. The disk space will be released when the system automatically performs `compact` operation or the user performs `compact` manually. + ## View Tables ### View All Tables diff --git a/docs/en/12-taos-sql/04-stable.md b/docs/en/12-taos-sql/04-stable.md index e0e21f4e82..ad179a5fe3 100644 --- a/docs/en/12-taos-sql/04-stable.md +++ b/docs/en/12-taos-sql/04-stable.md @@ -111,6 +111,8 @@ DROP STABLE [IF EXISTS] [db_name.]stb_name Note: Deleting a supertable will delete all subtables created from the supertable, including all data within those subtables. +**Note**:Dropping a supertable doesn't release the disk space occupied by the table, instead all the rows in the table are marked as deleted, so these data will not occur when querying. The disk space will be released when the system automatically performs `compact` operation or the user performs `compact` manually. + ## Modify a Supertable ```sql diff --git a/docs/en/12-taos-sql/06-select.md b/docs/en/12-taos-sql/06-select.md index e6ee64ddac..2d6c1469b6 100755 --- a/docs/en/12-taos-sql/06-select.md +++ b/docs/en/12-taos-sql/06-select.md @@ -148,6 +148,11 @@ You can query tag columns in supertables and subtables and receive results in th SELECT location, groupid, current FROM d1001 LIMIT 2; ``` +### Alias Name + +The naming rules for aliases are the same as those for columns, and it supports directly specifying Chinese aliases in UTF-8 encoding format. + + ### Distinct Values The DISTINCT keyword returns only values that are different over one or more columns. You can use the DISTINCT keyword with tag columns and data columns. @@ -278,7 +283,7 @@ The GROUP BY clause does not guarantee that the results are ordered. If you want The PARTITION BY clause is a TDengine-specific extension to standard SQL introduced in TDengine 3.0. This clause partitions data based on the part_list and performs computations per partition. -PARTITION BY and GROUP BY have similar meanings. They both group data according to a specified list and then perform calculations. The difference is that PARTITION BY does not have various restrictions on the SELECT list of the GROUP BY clause. Any operation can be performed within the group (constants, aggregations, scalars, expressions, etc.). Therefore, PARTITION BY is fully compatible with GROUP BY in terms of usage. All places that use the GROUP BY clause can be replaced with PARTITION BY. +PARTITION BY and GROUP BY have similar meanings. They both group data according to a specified list and then perform calculations. The difference is that PARTITION BY does not have various restrictions on the SELECT list of the GROUP BY clause. Any operation can be performed within the group (constants, aggregations, scalars, expressions, etc.). Therefore, PARTITION BY is fully compatible with GROUP BY in terms of usage. All places that use the GROUP BY clause can be replaced with PARTITION BY, there may be differences in the query results while no aggregation function in the query. Because PARTITION BY does not require returning a row of aggregated data, it can also support various window operations after grouping slices. All window operations that need to be grouped can only use the PARTITION BY clause. @@ -454,6 +459,7 @@ SELECT ... FROM (SELECT ... FROM ...) ...; :::info - The result of a nested query is returned as a virtual table used by the outer query. It's recommended to give an alias to this table for the convenience of using it in the outer query. +- Outer queries support directly referencing columns or pseudo-columns of inner queries in the form of column names or `column names`. - JOIN operation is allowed between tables/STables inside both inner and outer queries. Join operation can be performed on the result set of the inner query. - The features that can be used in the inner query are the same as those that can be used in a non-nested query. - `ORDER BY` inside the inner query is unnecessary and will slow down the query performance significantly. It is best to avoid the use of `ORDER BY` inside the inner query. diff --git a/docs/en/12-taos-sql/08-delete-data.mdx b/docs/en/12-taos-sql/08-delete-data.mdx index f91a89a7eb..33c2da1742 100644 --- a/docs/en/12-taos-sql/08-delete-data.mdx +++ b/docs/en/12-taos-sql/08-delete-data.mdx @@ -6,6 +6,8 @@ description: This document describes how to delete data from TDengine. TDengine provides the functionality of deleting data from a table or STable according to specified time range, it can be used to cleanup abnormal data generated due to device failure. +**Note**:Deletting some data doesn't release the disk space occupied by the table, instead all the rows in the table are marked as deleted, so these data will not occur when querying. The disk space will be released when the system automatically performs `compact` operation or the user performs `compact` manually. + **Syntax:** ```sql diff --git a/docs/en/12-taos-sql/10-function.md b/docs/en/12-taos-sql/10-function.md index 4bd701f713..afcebcb818 100644 --- a/docs/en/12-taos-sql/10-function.md +++ b/docs/en/12-taos-sql/10-function.md @@ -1167,7 +1167,7 @@ TDengine includes extensions to standard SQL that are intended specifically for CSUM(expr) ``` -**Description**: The cumulative sum of each row for a specific column. The number of output rows is same as that of the input rows. +**Description**: The cumulative sum of each row for a specific column, NULL value will be discard. **Return value type**: Long integer for integers; Double for floating points. uint64_t for unsigned integers diff --git a/docs/en/12-taos-sql/12-distinguished.md b/docs/en/12-taos-sql/12-distinguished.md index 4639a13bcc..5dca92a35c 100644 --- a/docs/en/12-taos-sql/12-distinguished.md +++ b/docs/en/12-taos-sql/12-distinguished.md @@ -31,7 +31,7 @@ A PARTITION BY clause is processed as follows: select _wstart, location, max(current) from meters partition by location interval(10m) ``` -The most common usage of PARTITION BY is partitioning the data in subtables by tags then perform computation when querying data in a supertable. More specifically, `PARTITION BY TBNAME` partitions the data of each subtable into a single timeline, and this method facilitates the statistical analysis in many use cases of processing timeseries data. For example, calculate the average voltage of each meter every 10 minutes£º +The most common usage of PARTITION BY is partitioning the data in subtables by tags then perform computation when querying data in a supertable. More specifically, `PARTITION BY TBNAME` partitions the data of each subtable into a single timeline, and this method facilitates the statistical analysis in many use cases of processing timeseries data. For example, calculate the average voltage of each meter every 10 minutes: ```sql select _wstart, tbname, avg(voltage) from meters partition by tbname interval(10m) ``` diff --git a/docs/en/12-taos-sql/14-stream.md b/docs/en/12-taos-sql/14-stream.md index fcd782b429..a6759da858 100644 --- a/docs/en/12-taos-sql/14-stream.md +++ b/docs/en/12-taos-sql/14-stream.md @@ -78,6 +78,10 @@ If a stream is created with PARTITION BY clause and SUBTABLE clause, the name of ```sql CREATE STREAM avg_vol_s INTO avg_vol SUBTABLE(CONCAT('new-', tname)) AS SELECT _wstart, count(*), avg(voltage) FROM meters PARTITION BY tbname tname INTERVAL(1m); + +CREATE STREAM streams0 INTO streamt0 AS SELECT _wstart, count(*), avg(voltage) from meters PARTITION BY tbname EVENT_WINDOW START WITH voltage < 0 END WITH voltage > 9; + +CREATE STREAM streams1 IGNORE EXPIRED 1 WATERMARK 100s INTO streamt1 AS SELECT _wstart, count(*), avg(voltage) from meters PARTITION BY tbname COUNT_WINDOW(10); ``` IN PARTITION clause, 'tbname', representing each subtable name of source supertable, is given alias 'tname'. And 'tname' is used in SUBTABLE clause. In SUBTABLE clause, each auto created subtable will concat 'new-' and source subtable name as their name(Starting from 3.2.3.0, in order to avoid the expression in subtable being unable to distinguish between different subtables, add '_stableName_groupId' to the end of subtable name). diff --git a/docs/en/12-taos-sql/21-node.md b/docs/en/12-taos-sql/21-node.md index 8a5069e66f..2ebccb76f7 100644 --- a/docs/en/12-taos-sql/21-node.md +++ b/docs/en/12-taos-sql/21-node.md @@ -27,10 +27,10 @@ The preceding SQL command shows all dnodes in the cluster with the ID, endpoint, ## Delete a DNODE ```sql -DROP DNODE {dnode_id | dnode_endpoint} +DROP DNODE dnode_id ``` -You can delete a dnode by its ID or by its endpoint. Note that deleting a dnode does not stop its process. You must stop the process after the dnode is deleted. +Note that deleting a dnode does not stop its process. You must stop the process after the dnode is deleted. ## Modify Dnode Configuration diff --git a/docs/en/12-taos-sql/22-meta.md b/docs/en/12-taos-sql/22-meta.md index fad479d9d3..f3d1017788 100644 --- a/docs/en/12-taos-sql/22-meta.md +++ b/docs/en/12-taos-sql/22-meta.md @@ -210,9 +210,13 @@ Provides information about TDengine users. Users whose SYSINFO attribute is 0 ca | # | **Column** | **Data Type** | **Description** | | --- | :---------: | ------------- | ---------------- | -| 1 | user_name | VARCHAR(23) | User name | -| 2 | privilege | VARCHAR(256) | User permissions | -| 3 | create_time | TIMESTAMP | Creation time | +| 1 | name | VARCHAR(24) | User name | +| 2 | super | TINYINT | Wether user is super user. 1 means yes; 0 means no. | +| 3 | enable | TINYINT | Wether user is enabled. 1 means yes; 0 means no. | +| 4 | sysinfo | TINYINT | Wether user can query system info. 1 means yes; 0 means no. | +| 5 | create_time | TIMESTAMP | Create time | +| 6 | allowed_host | VARCHAR(49152)| IP whitelist | + ## INS_GRANTS diff --git a/docs/en/12-taos-sql/25-grant.md b/docs/en/12-taos-sql/25-grant.md index 6575d2d7f4..6b5fa93c61 100644 --- a/docs/en/12-taos-sql/25-grant.md +++ b/docs/en/12-taos-sql/25-grant.md @@ -91,53 +91,4 @@ Query OK, 0 of 0 rows affected (0.001160s) ## Grant Permissions -```sql -GRANT privileges ON priv_level TO user_name - -privileges : { - ALL - | priv_type [, priv_type] ... -} - -priv_type : { - READ - | WRITE -} - -priv_level : { - dbname.* - | *.* -} -``` - -Grant permissions to a user, this feature is only available in enterprise edition. - -Permissions are granted on the database level. You can grant read or write permissions. - -TDengine has superusers and standard users. The default superuser name is root. This account has all permissions. You can use the superuser account to create standard users. With no permissions, standard users can create databases and have permissions on the databases that they create. These include deleting, modifying, querying, and writing to their own databases. Superusers can grant users permission to read and write other databases. However, standard users cannot delete or modify databases created by other users. - -For non-database objects such as users, dnodes, and user-defined functions, standard users have read permissions only, generally by means of the SHOW statement. Standard users cannot create or modify these objects. - -## Revoke Permissions - -```sql -REVOKE privileges ON priv_level FROM user_name - -privileges : { - ALL - | priv_type [, priv_type] ... -} - -priv_type : { - READ - | WRITE -} - -priv_level : { - dbname.* - | *.* -} - -``` - -Revoke permissions from a user, this feature is only available in enterprise edition. +Permission control is only available in TDengine Enterprise, please contact TDengine sales team. \ No newline at end of file diff --git a/docs/en/12-taos-sql/26-udf.md b/docs/en/12-taos-sql/26-udf.md index dec9ca217d..0e562bdc7b 100644 --- a/docs/en/12-taos-sql/26-udf.md +++ b/docs/en/12-taos-sql/26-udf.md @@ -53,7 +53,7 @@ CREATE AGGREGATE FUNCTION function_name AS library_path OUTPUTTYPE output_type [ CREATE AGGREGATE FUNCTION l2norm AS "/home/taos/udf_example/libl2norm.so" OUTPUTTYPE DOUBLE bufsize 64; ``` -For more information about user-defined functions, see [User-Defined Functions](../../develop/udf). +For more information about user-defined functions, see [User-Defined Functions](https://docs.tdengine.com/develop/udf/). ## Manage UDF diff --git a/docs/en/13-operation/10-monitor.md b/docs/en/13-operation/10-monitor.md index 009db425a4..ce530be1e4 100644 --- a/docs/en/13-operation/10-monitor.md +++ b/docs/en/13-operation/10-monitor.md @@ -43,201 +43,204 @@ Launch `TDinsight.sh` with the command above and restart Grafana, then open Dash The data of tdinsight dashboard is stored in `log` database (default. You can change it in taoskeeper's config file. For more infrmation, please reference to [taoskeeper document](../../reference/taosKeeper)). The taoskeeper will create log database on taoskeeper startup. -### cluster\_info table +### taosd\_cluster\_basic table -`cluster_info` table contains cluster information records. +`taosd_cluster_basic` table contains cluster basic information. |field|type|is\_tag|comment| |:----|:---|:-----|:------| |ts|TIMESTAMP||timestamp| |first\_ep|VARCHAR||first ep of cluster| |first\_ep\_dnode\_id|INT||dnode id or first\_ep| -|version|VARCHAR||tdengine version. such as: 3.0.4.0| -|master\_uptime|FLOAT||days of master's uptime| -|monitor\_interval|INT||monitor interval in second| -|dbs\_total|INT||total number of databases in cluster| -|tbs\_total|BIGINT||total number of tables in cluster| -|stbs\_total|INT||total number of stables in cluster| -|dnodes\_total|INT||total number of dnodes in cluster| -|dnodes\_alive|INT||total number of dnodes in ready state| -|mnodes\_total|INT||total number of mnodes in cluster| -|mnodes\_alive|INT||total number of mnodes in ready state| -|vgroups\_total|INT||total number of vgroups in cluster| -|vgroups\_alive|INT||total number of vgroups in ready state| -|vnodes\_total|INT||total number of vnode in cluster| -|vnodes\_alive|INT||total number of vnode in ready state| -|connections\_total|INT||total number of connections to cluster| -|topics\_total|INT||total number of topics in cluster| -|streams\_total|INT||total number of streams in cluster| -|protocol|INT||protocol version| -|cluster\_id|NCHAR|TAG|cluster id| +|cluster_version|VARCHAR||tdengine version. such as: 3.0.4.0| +|cluster\_id|VARCHAR|TAG|cluster id| -### d\_info table +### taosd\_cluster\_info table -`d_info` table contains dnodes information records. +`taosd_cluster_info` table contains cluster information records. |field|type|is\_tag|comment| |:----|:---|:-----|:------| |ts|TIMESTAMP||timestamp| -|status|VARCHAR||dnode status| -|dnode\_ep|NCHAR|TAG|dnode endpoint| -|cluster\_id|NCHAR|TAG|cluster id| +|cluster\_uptime|DOUBLE||seconds of master's uptime| +|dbs\_total|DOUBLE||total number of databases in cluster| +|tbs\_total|DOUBLE||total number of tables in cluster| +|stbs\_total|DOUBLE||total number of stables in cluster| +|dnodes\_total|DOUBLE||total number of dnodes in cluster| +|dnodes\_alive|DOUBLE||total number of dnodes in ready state| +|mnodes\_total|DOUBLE||total number of mnodes in cluster| +|mnodes\_alive|DOUBLE||total number of mnodes in ready state| +|vgroups\_total|DOUBLE||total number of vgroups in cluster| +|vgroups\_alive|DOUBLE||total number of vgroups in ready state| +|vnodes\_total|DOUBLE||total number of vnode in cluster| +|vnodes\_alive|DOUBLE||total number of vnode in ready state| +|connections\_total|DOUBLE||total number of connections to cluster| +|topics\_total|DOUBLE||total number of topics in cluster| +|streams\_total|DOUBLE||total number of streams in cluster| +|grants_expire\_time|DOUBLE||time until grants expire in seconds| +|grants_timeseries\_used|DOUBLE||timeseries used| +|grants_timeseries\_total|DOUBLE||total timeseries| +|cluster\_id|VARCHAR|TAG|cluster id| -### m\_info table +### taosd\_vgroups\_info table -`m_info` table contains mnode information records. +`taosd_vgroups_info` table contains vgroups information records. |field|type|is\_tag|comment| |:----|:---|:-----|:------| |ts|TIMESTAMP||timestamp| -|role|VARCHAR||the role of mnode. leader or follower| -|mnode\_id|INT|TAG|master node id| -|mnode\_ep|NCHAR|TAG|master node endpoint| -|cluster\_id|NCHAR|TAG|cluster id| +|tables\_num|DOUBLE||number of tables per vgroup| +|status|DOUBLE||status, value range:unsynced = 0, ready = 1| +|vgroup\_id|VARCHAR|TAG|vgroup id| +|database\_name|VARCHAR|TAG|database for the vgroup| +|cluster\_id|VARCHAR|TAG|cluster id| -### dnodes\_info table +### taosd\_dnodes\_info table -`dnodes_info` table contains dnodes information records. +`taosd_dnodes_info` table contains dnodes information records. |field|type|is\_tag|comment| |:----|:---|:-----|:------| |ts|TIMESTAMP||timestamp| -|uptime|FLOAT||dnode uptime in `days`| -|cpu\_engine|FLOAT||cpu usage of tdengine. read from `/proc//stat`| -|cpu\_system|FLOAT||cpu usage of server. read from `/proc/stat`| -|cpu\_cores|FLOAT||cpu cores of server| -|mem\_engine|INT||memory usage of tdengine. read from `/proc//status`| -|mem\_system|INT||available memory on the server in `KB`| -|mem\_total|INT||total memory of server in `KB`| -|disk\_engine|INT||| -|disk\_used|BIGINT||usage of data dir in `bytes`| -|disk\_total|BIGINT||the capacity of data dir in `bytes`| -|net\_in|FLOAT||network throughput rate in byte/s. read from `/proc/net/dev`| -|net\_out|FLOAT||network throughput rate in byte/s. read from `/proc/net/dev`| -|io\_read|FLOAT||io throughput rate in byte/s. read from `/proc//io`| -|io\_write|FLOAT||io throughput rate in byte/s. read from `/proc//io`| -|io\_read\_disk|FLOAT||io throughput rate of disk in byte/s. read from `/proc//io`| -|io\_write\_disk|FLOAT||io throughput rate of disk in byte/s. read from `/proc//io`| -|req\_select|INT||number of select queries received per dnode| -|req\_select\_rate|FLOAT||number of select queries received per dnode divided by monitor interval.| -|req\_insert|INT||number of insert queries received per dnode| -|req\_insert\_success|INT||number of successfully insert queries received per dnode| -|req\_insert\_rate|FLOAT||number of insert queries received per dnode divided by monitor interval| -|req\_insert\_batch|INT||number of batch insertions| -|req\_insert\_batch\_success|INT||number of successful batch insertions| -|req\_insert\_batch\_rate|FLOAT||number of batch insertions divided by monitor interval| -|errors|INT||dnode errors| -|vnodes\_num|INT||number of vnodes per dnode| -|masters|INT||number of master vnodes| -|has\_mnode|INT||if the dnode has mnode| -|has\_qnode|INT||if the dnode has qnode| -|has\_snode|INT||if the dnode has snode| -|has\_bnode|INT||if the dnode has bnode| -|dnode\_id|INT|TAG|dnode id| -|dnode\_ep|NCHAR|TAG|dnode endpoint| -|cluster\_id|NCHAR|TAG|cluster id| +|uptime|DOUBLE||dnode uptime in `seconds`| +|cpu\_engine|DOUBLE||cpu usage of tdengine. read from `/proc//stat`| +|cpu\_system|DOUBLE||cpu usage of server. read from `/proc/stat`| +|cpu\_cores|DOUBLE||cpu cores of server| +|mem\_engine|DOUBLE||memory usage of tdengine. read from `/proc//status`| +|mem\_free|DOUBLE||available memory on the server in `KB`| +|mem\_total|DOUBLE||total memory of server in `KB`| +|disk\_used|DOUBLE||usage of data dir in `bytes`| +|disk\_total|DOUBLE||the capacity of data dir in `bytes`| +|system\_net\_in|DOUBLE||network throughput rate in byte/s. read from `/proc/net/dev`| +|system\_net\_out|DOUBLE||network throughput rate in byte/s. read from `/proc/net/dev`| +|io\_read|DOUBLE||io throughput rate in byte/s. read from `/proc//io`| +|io\_write|DOUBLE||io throughput rate in byte/s. read from `/proc//io`| +|io\_read\_disk|DOUBLE||io throughput rate of disk in byte/s. read from `/proc//io`| +|io\_write\_disk|DOUBLE||io throughput rate of disk in byte/s. read from `/proc//io`| +|vnodes\_num|DOUBLE||number of vnodes per dnode| +|masters|DOUBLE||number of master vnodes| +|has\_mnode|DOUBLE||if the dnode has mnode, value range:include=1, not_include=0| +|has\_qnode|DOUBLE||if the dnode has qnode, value range:include=1, not_include=0| +|has\_snode|DOUBLE||if the dnode has snode, value range:include=1, not_include=0| +|has\_bnode|DOUBLE||if the dnode has bnode, value range:include=1, not_include=0| +|error\_log\_count|DOUBLE||error count| +|info\_log\_count|DOUBLE||info count| +|debug\_log\_count|DOUBLE||debug count| +|trace\_log\_count|DOUBLE||trace count| +|dnode\_id|VARCHAR|TAG|dnode id| +|dnode\_ep|VARCHAR|TAG|dnode endpoint| +|cluster\_id|VARCHAR|TAG|cluster id| -### data\_dir table +### taosd\_dnodes\_status table -`data_dir` table contains data directory information records. +`taosd_dnodes_status` table contains dnodes information records. |field|type|is\_tag|comment| |:----|:---|:-----|:------| |ts|TIMESTAMP||timestamp| -|name|NCHAR||data directory. default is `/var/lib/taos`| -|level|INT||level for multi-level storage| -|avail|BIGINT||available space for data directory in `bytes`| -|used|BIGINT||used space for data directory in `bytes`| -|total|BIGINT||total space for data directory in `bytes`| -|dnode\_id|INT|TAG|dnode id| -|dnode\_ep|NCHAR|TAG|dnode endpoint| -|cluster\_id|NCHAR|TAG|cluster id| +|status|DOUBLE||dnode status, value range:ready=1,offline =0| +|dnode\_id|VARCHAR|TAG|dnode id| +|dnode\_ep|VARCHAR|TAG|dnode endpoint| +|cluster\_id|VARCHAR|TAG|cluster id| -### log\_dir table +### taosd\_dnodes\_log\_dir table `log_dir` table contains log directory information records. |field|type|is\_tag|comment| |:----|:---|:-----|:------| |ts|TIMESTAMP||timestamp| -|name|NCHAR||log directory. default is `/var/log/taos/`| -|avail|BIGINT||available space for log directory in `bytes`| -|used|BIGINT||used space for data directory in `bytes`| -|total|BIGINT||total space for data directory in `bytes`| -|dnode\_id|INT|TAG|dnode id| -|dnode\_ep|NCHAR|TAG|dnode endpoint| -|cluster\_id|NCHAR|TAG|cluster id| +|avail|DOUBLE||available space for log directory in `bytes`| +|used|DOUBLE||used space for data directory in `bytes`| +|total|DOUBLE||total space for data directory in `bytes`| +|name|VARCHAR|TAG|log directory. default is `/var/log/taos/`| +|dnode\_id|VARCHAR|TAG|dnode id| +|dnode\_ep|VARCHAR|TAG|dnode endpoint| +|cluster\_id|VARCHAR|TAG|cluster id| -### temp\_dir table +### taosd\_dnodes\_data\_dir table -`temp_dir` table contains temp dir information records. +`taosd_dnodes_data_dir` table contains data directory information records. |field|type|is\_tag|comment| |:----|:---|:-----|:------| |ts|TIMESTAMP||timestamp| -|name|NCHAR||temp directory. default is `/tmp/`| -|avail|BIGINT||available space for temp directory in `bytes`| -|used|BIGINT||used space for temp directory in `bytes`| -|total|BIGINT||total space for temp directory in `bytes`| -|dnode\_id|INT|TAG|dnode id| -|dnode\_ep|NCHAR|TAG|dnode endpoint| -|cluster\_id|NCHAR|TAG|cluster id| +|avail|DOUBLE||available space for data directory in `bytes`| +|used|DOUBLE||used space for data directory in `bytes`| +|total|DOUBLE||total space for data directory in `bytes`| +|level|VARCHAR|TAG|level for multi-level storage| +|name|VARCHAR|TAG|data directory. default is `/var/lib/taos`| +|dnode\_id|VARCHAR|TAG|dnode id| +|dnode\_ep|VARCHAR|TAG|dnode endpoint| +|cluster\_id|VARCHAR|TAG|cluster id| -### vgroups\_info table +### taosd\_mnodes\_info table -`vgroups_info` table contains vgroups information records. +`taosd_mnodes_info` table contains mnode information records. |field|type|is\_tag|comment| |:----|:---|:-----|:------| |ts|TIMESTAMP||timestamp| -|vgroup\_id|INT||vgroup id| -|database\_name|VARCHAR||database for the vgroup| -|tables\_num|BIGINT||number of tables per vgroup| -|status|VARCHAR||status| -|dnode\_id|INT|TAG|dnode id| -|dnode\_ep|NCHAR|TAG|dnode endpoint| -|cluster\_id|NCHAR|TAG|cluster id| +|role|DOUBLE||the role of mnode. value range: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| -### vnodes\_role table +### taosd\_vnodes\_role table -`vnodes_role` table contains vnode role information records. +`taosd_vnodes_role` table contains vnode role information records. |field|type|is\_tag|comment| |:----|:---|:-----|:------| |ts|TIMESTAMP||timestamp| -|vnode\_role|VARCHAR||role. leader or follower| -|dnode\_id|INT|TAG|dnode id| -|dnode\_ep|NCHAR|TAG|dnode endpoint| -|cluster\_id|NCHAR|TAG|cluster id| +|role|DOUBLE||role. value range:offline = 0,follower = 100,candidate = 101,leader = 102,error = 103,learner = 104| +|vgroup\_id|VARCHAR|TAG|vgroup id| +|database\_name|VARCHAR|TAG|database for the vgroup| +|dnode\_id|VARCHAR|TAG|dnode id| +|cluster\_id|VARCHAR|TAG|cluster id| -### log\_summary table +### taosd\_sql\_req table -`log_summary` table contains log summary information records. +`taosd_sql_req` tables contains taosd sql records. |field|type|is\_tag|comment| |:----|:---|:-----|:------| |ts|TIMESTAMP||timestamp| -|error|INT||error count| -|info|INT||info count| -|debug|INT||debug count| -|trace|INT||trace count| -|dnode\_id|INT|TAG|dnode id| -|dnode\_ep|NCHAR|TAG|dnode endpoint| -|cluster\_id|NCHAR|TAG|cluster id| +|count|DOUBLE||sql count| +|result|VARCHAR|TAG|sql execution result,value range: Success, Failed| +|username|VARCHAR|TAG|user name who executed the sql| +|sql\_type|VARCHAR|TAG|sql type,value range: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| -### grants\_info table +### taos\_sql\_req 表 -`grants_info` table contains grants information records. +`taos_sql_req` tables contains taos sql records. |field|type|is\_tag|comment| |:----|:---|:-----|:------| |ts|TIMESTAMP||timestamp| -|expire\_time|BIGINT||time until grants expire in seconds| -|timeseries\_used|BIGINT||timeseries used| -|timeseries\_total|BIGINT||total timeseries| -|dnode\_id|INT|TAG|dnode id| -|dnode\_ep|NCHAR|TAG|dnode endpoint| -|cluster\_id|NCHAR|TAG|cluster id| +|count|DOUBLE||sql count| +|result|VARCHAR|TAG|sql execution result,value range: Success, Failed| +|username|VARCHAR|TAG|user name who executed the sql| +|sql\_type|VARCHAR|TAG|sql type,value range:select, insert,delete| +|cluster\_id|VARCHAR|TAG|cluster id| + +### taos\_slow\_sql 表 + +`taos_slow_sql` ables contains taos slow sql records. + +|field|type|is\_tag|comment| +|:----|:---|:-----|:------| +|ts|TIMESTAMP||timestamp| +|count|DOUBLE||sql count| +|result|VARCHAR|TAG|sql execution result,value range: Success, Failed| +|username|VARCHAR|TAG|user name who executed the sql| +|duration|VARCHAR|TAG|sql execution duration,value range:3-10s,10-100s,100-1000s,1000s-| +|cluster\_id|VARCHAR|TAG|cluster id| + ### keeper\_monitor table diff --git a/docs/en/14-reference/02-rest-api/02-rest-api.mdx b/docs/en/14-reference/02-rest-api/02-rest-api.mdx index 405b154d1d..783c5a64dc 100644 --- a/docs/en/14-reference/02-rest-api/02-rest-api.mdx +++ b/docs/en/14-reference/02-rest-api/02-rest-api.mdx @@ -68,7 +68,7 @@ The following return value results indicate that the verification passed. ## HTTP request URL format ```text -http://:/rest/sql/[db_name][?tz=timezone[&req_id=req_id]] +http://:/rest/sql/[db_name][?tz=timezone[&req_id=req_id][&row_with_meta=true]] ``` Parameter Description: @@ -78,6 +78,7 @@ Parameter Description: - db_name: Optional parameter that specifies the default database name for the executed SQL command. - tz: Optional parameter that specifies the timezone of the returned time, following the IANA Time Zone rules, e.g. `America/New_York`. - req_id: Optional parameter that specifies the request id for tracing. +- row_with_meta: Optional parameter that specifies whether each row of data carries the column name. The default value is `false`.(Supported starting from version 3.3.2.0) :::note @@ -336,6 +337,82 @@ Description: - code: (`int`) Error code. - desc: (`string`): Error code description. +#### Return key-value pair + +When the parameter `row_with_meta=true` is specified, the data returned in `data` field will change from array format to object format, where the key of the object is the column name and the value is the data. + +insert response example: + +```json +{ + "code": 0, + "column_meta": [ + [ + "affected_rows", + "INT", + 4 + ] + ], + "data": [ + { + "affected_rows": 1 + } + ], + "rows": 1 +} +``` + +query response example: + +```json +{ + "code": 0, + "column_meta": [ + [ + "ts", + "TIMESTAMP", + 8 + ], + [ + "current", + "FLOAT", + 4 + ], + [ + "voltage", + "INT", + 4 + ], + [ + "phase", + "FLOAT", + 4 + ], + [ + "groupid", + "INT", + 4 + ], + [ + "location", + "VARCHAR", + 24 + ] + ], + "data": [ + { + "ts": "2017-07-14T02:40:00.000Z", + "current": -2.498076, + "voltage": 0, + "phase": -0.846025, + "groupid": 8, + "location": "California.Sunnyvale" + } + ], + "rows": 1 +} +``` + ## Custom Authorization Code HTTP requests require an authorization code `` for identification purposes. The administrator usually provides the authorization code, and it can be obtained simply by sending an ``HTTP GET`` request as follows: diff --git a/docs/en/14-reference/08-taos-shell.md b/docs/en/14-reference/08-taos-shell.md index d69f3876d4..f14800a843 100644 --- a/docs/en/14-reference/08-taos-shell.md +++ b/docs/en/14-reference/08-taos-shell.md @@ -58,6 +58,7 @@ And many more parameters. - -a AUTHSTR: Authorization information to connect to the server. - -A: Obtain authorization information from username and password. +- -B: Set BI mode , all outputs follow the format of BI tools for output if setting - -c CONFIGDIR: Specify the directory where configuration file exists. The default is `/etc/taos`, and the default name of the configuration file in this directory is `taos.cfg` - -C: Print the configuration parameters of `taos.cfg` in the default directory or specified by -c - -d DATABASE: Specify the database to use when connecting to the server diff --git a/docs/en/14-reference/14-taosKeeper.md b/docs/en/14-reference/14-taosKeeper.md index f8148af869..0642aca261 100644 --- a/docs/en/14-reference/14-taosKeeper.md +++ b/docs/en/14-reference/14-taosKeeper.md @@ -137,17 +137,10 @@ port = 6041 username = "root" password = "taosdata" -# set taosAdapter to monitor -[taosAdapter] -address = ["127.0.0.1:6041","192.168.1.95:6041"] - [metrics] # monitoring metric prefix prefix = "taos" -# cluster data identifier -cluster = "production" - # database to store monitoring data database = "log" @@ -157,6 +150,19 @@ tables = ["normal_table"] # database options for db storing metrics data [metrics.databaseoptions] cachemodel = "none" + +[environment] +# Whether running in cgroup. +incgroup = false + +[log] +# rotation file num +rotationCount = 5 +# rotation on time +rotationTime = "24h" +# rotation on file size (bytes) +rotationSize = 100000000 + ``` ### Obtain Monitoring Metrics @@ -169,16 +175,16 @@ taosKeeper records monitoring metrics generated by TDengine in a specified datab $ taos # the log database is used in this example > use log; -> select * from cluster_info limit 1; +> select * from taosd_cluster_info limit 1; ``` Example result set: ```shell - ts | first_ep | first_ep_dnode_id | version | master_uptime | monitor_interval | dbs_total | tbs_total | stbs_total | dnodes_total | dnodes_alive | mnodes_total | mnodes_alive | vgroups_total | vgroups_alive | vnodes_total | vnodes_alive | connections_total | protocol | cluster_id | -=============================================================================================================================================================================================================================================================================================================================================================================== - 2022-08-16 17:37:01.629 | hlb:6030 | 1 | 3.0.0.0 | 0.27250 | 15 | 2 | 27 | 38 | 1 | 1 | 1 | 1 | 4 | 4 | 4 | 4 | 14 | 1 | 5981392874047724755 | -Query OK, 1 rows in database (0.036162s) + _ts | cluster_uptime | dbs_total | tbs_total | stbs_total | vgroups_total | vgroups_alive | vnodes_total | vnodes_alive | mnodes_total | mnodes_alive | connections_total | topics_total | streams_total | dnodes_total | dnodes_alive | grants_expire_time | grants_timeseries_used | grants_timeseries_total | cluster_id | +=================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================== + 2024-06-04 03:03:34.341 | 0.000000000000000 | 2.000000000000000 | 1.000000000000000 | 4.000000000000000 | 4.000000000000000 | 4.000000000000000 | 4.000000000000000 | 4.000000000000000 | 1.000000000000000 | 1.000000000000000 | 2.000000000000000 | 0.000000000000000 | 0.000000000000000 | 1.000000000000000 | 1.000000000000000 | 0.000000000000000 | 3.000000000000000 | 0.000000000000000 | 554014120921134497 | +Query OK, 1 row(s) in set (0.001652s) ``` #### Export Monitoring Metrics @@ -190,21 +196,24 @@ $ curl http://127.0.0.1:6043/metrics Sample result set (excerpt): ```shell -# HELP taos_cluster_info_connections_total +# HELP taos_cluster_info_connections_total # TYPE taos_cluster_info_connections_total counter -taos_cluster_info_connections_total{cluster_id="5981392874047724755"} 16 -# HELP taos_cluster_info_dbs_total +taos_cluster_info_connections_total{cluster_id="554014120921134497"} 8 +# HELP taos_cluster_info_dbs_total # TYPE taos_cluster_info_dbs_total counter -taos_cluster_info_dbs_total{cluster_id="5981392874047724755"} 2 -# HELP taos_cluster_info_dnodes_alive +taos_cluster_info_dbs_total{cluster_id="554014120921134497"} 2 +# HELP taos_cluster_info_dnodes_alive # TYPE taos_cluster_info_dnodes_alive counter -taos_cluster_info_dnodes_alive{cluster_id="5981392874047724755"} 1 -# HELP taos_cluster_info_dnodes_total +taos_cluster_info_dnodes_alive{cluster_id="554014120921134497"} 1 +# HELP taos_cluster_info_dnodes_total # TYPE taos_cluster_info_dnodes_total counter -taos_cluster_info_dnodes_total{cluster_id="5981392874047724755"} 1 -# HELP taos_cluster_info_first_ep +taos_cluster_info_dnodes_total{cluster_id="554014120921134497"} 1 +# HELP taos_cluster_info_first_ep # TYPE taos_cluster_info_first_ep gauge -taos_cluster_info_first_ep{cluster_id="5981392874047724755",value="hlb:6030"} 1 +taos_cluster_info_first_ep{cluster_id="554014120921134497",value="tdengine:6030"} 1 +# HELP taos_cluster_info_first_ep_dnode_id +# TYPE taos_cluster_info_first_ep_dnode_id counter +taos_cluster_info_first_ep_dnode_id{cluster_id="554014120921134497"} 1 ``` ### check\_health diff --git a/docs/en/14-reference/15-explorer.md b/docs/en/14-reference/15-explorer.md new file mode 100644 index 0000000000..2a824c6d8f --- /dev/null +++ b/docs/en/14-reference/15-explorer.md @@ -0,0 +1,53 @@ +--- +title: Graphic User Interface +sidebar_label: Taos-Explorer +description: User guide about taosExplorer +--- + +taos-explorer is a web service which provides GUI based interactive database management tool. + +## Install + +taos-explorer is delivered in the TDengine server package since version 3.3.0.0. After installing TDengine server, you will get the `taos-explorer` service. + +## Configure + +The configuration file of `taos-explorer` service is `/etc/taos/explorer.toml` on Linux platform, the key items in the configuration are like below: + +``` toml +port = 6060 +cluster = "http://localhost:6041" +``` + +The description of these two parameters: + +- port:taos-explorer service port +- cluster:The end point of the TDengine cluster for the taos-explorer to manage. It supports only websocket connection, so this address is actually the end point of `taosAdapter` service in the TDengine cluster. + +## Start & Stop + +Before starting the service, please first make sure the configuration is correct, and the TDengine cluster (majorly including `taosd` and `taosAdapter` services) are already alive and working well. + +### Linux + +On Linux system you can use `systemctl` to manage the service as below: + +- Start the service: `systemctl start taos-explorer` + +- Stop the service: `systemctl stop taos-explorer` + +- Restart the service: `systemctl restart taos-explorer` + +- Check service status: `systemctl status taos-explorer` + +## Register & Logon + +### Register + +After installing, configuring and starting, you can use your browser to access taos-explorer using address `http://ip:6060`. At this time, if you have not registered before, the registering page will first show up. You need to enter your valid enterprise email, receive the activation code, then input the code. Congratulations, you have registered successfully. + +### Logon + +After registering, you can use your user name and corresponding password in the database system to logon. The default username is `root`, but you can change it to use another one. After loggin into the system, you can view or manage databases, create super tables, create child tables, or view the data in the database. + +There are some functionalities only available to enterprise users, you can view and experience but can't really use them. diff --git a/docs/en/20-third-party/75-powerbi.md b/docs/en/20-third-party/75-powerbi.md index 03268a5610..0f7a1de0ca 100644 --- a/docs/en/20-third-party/75-powerbi.md +++ b/docs/en/20-third-party/75-powerbi.md @@ -4,33 +4,43 @@ title: Power BI description: Use PowerBI and TDengine to analyze time series data --- -## Introduction +# Tools - Power BI -With TDengine ODBC driver, PowerBI can access time series data stored in TDengine. You can import tag data, original time series data, or aggregated data into PowerBI from TDengine, to create reports or dashboard without any coding effort. +![Power BI use step](./powerbi-step-en.png) -## Steps -![Power BI use step](./powerbi-step-en.webp) +[Power BI](https://powerbi.microsoft.com/) is a business analytics tool provided by Microsoft. With TDengine ODBC driver, PowerBI can access time series data stored in the TDengine. You can import tag data, original time series data, or aggregated data into Power BI from a TDengine, to create reports or dashboard without any coding effort. -### Prerequisites +### Prerequisite +1. TDengine server software is installed and running. +2. Power BI Desktop has been installed and running (If not, please download and install the latest Windows X64 version from [PowerBI](https://www.microsoft.com/zh-cn/download/details.aspx?id=58494) ). -1. TDengine server has been installed and running well. -2. Power BI Desktop has been installed and running. (If not, please download and install latest Windows X64 version from [PowerBI](https://www.microsoft.com/download/details.aspx?id=58494). +### Install ODBC connector +1. Only support Windows operation system. And you need to install [VC Runtime Library](https://learn.microsoft.com/zh-cn/cpp/windows/latest-supported-vc-redist?view=msvc-170) first. If already installed, please ignore this step. +2. Install [TDengine Windows client installation package](https://docs.taosdata.com/get-started/package/). +### Configure ODBC DataSource +1. Click the "Start" Menu, and Search for "ODBC", and choose "ODBC Data Source (64-bit)" (Note: Don't choose 32-bit). +2. Select the "User DSN" tab, and click "Add" button to enter the page for "Create Data Source". +3. Choose the data source to be added, here we choose "TDengine" and click "Finish", and enter the configuration page for "TDengine ODBC Data Source", fill in required fields as the following: -## Install Driver +  [DSN]:       Data Source Name, required field, such as "MyTDengine" Depending on your TDengine server version, download appropriate version of TDengine client package from TDengine website [Download Link](../../get-started/package/), or TDengine explorer if you are using a local TDengine cluster. Install the TDengine client package on same Windows machine where PowerBI is running. -### Configure Data Source -Please refer to [ODBC](../../client-libraries/odbc) to configure TDengine ODBC Driver with WebSocket connection. +  [URL]:        taos://localhost:6041 + +  [Database]:     optional field, the default database to access, such as "test" + +  [UserID]:      Enter the user name. If this parameter is not specified, the user name is root by default + +  [Password]:     Enter the user password. If not, the default is taosdata + +4. Click "Test Connection" to test whether the data source can be connectted; if successful, it will prompt "Successfully connected to taos://root:taosdata@localhost:6041". ### Import Data from TDengine to Power BI - -1. Open Power BI and logon, add data source following steps "Home Page" -> "Get Data" -> "Others" -> "ODBC" -> "Connect" - -2. Choose data source name, connect to configured data source, go to the nativator, browse tables of the selected database and load data - +1. Open Power BI and logon, add data source following steps "Home" -> "Get data" -> "Other" -> "ODBC" -> "Connect". +2. Choose the created data source name, such as "MyTDengine", then click "OK" button to open the "ODBC Driver" dialog. In the dialog, select "Default or Custom" left menu and then click "Connect" button to connect to the configured data source. After go to the "Nativator", browse tables of the selected database and load data. 3. If you want to input some specific SQL, click "Advanced Options", and input your SQL in the open dialogue box and load the data. @@ -49,17 +59,9 @@ To better use Power BI to analyze the data stored in TDengine, you need to under 4. Correlation: Indicates how to correlate data. Dimentions and Metrics can be correlated by tbname, dates and metrics can be correlated by date. All these can cooperate to form visual reports. ### Example - Meters - -TDengine has its own specific data model, which uses supertable as template and creates a specific table for each device. Each table can have maximum 4,096 data columns and 128 tags. In the example of meters, assume each meter generates one record per second, then there will be 86,400 records each day and 31,536,000 records every year, then only 1,000 meters will occupy 500GB disk space. So, the common usage of Power BI should be mapping tags to dimention columns, mapping the aggregation of data columns to metric columns, to provide indicators for decision makers. - -1. Import Dimentions - -Import the tags of tables in PowerBI, and name as "tags", the SQL is like `select distinct tbname, groupid, location from test.meters;`. - -2. Import Metrics - -In Power BI, import the average current, average voltage, average phase with 1 hour window, and name it as "data", the SQL is like `select tbname, _wstart ws, avg(current), avg(voltage), avg(phase) from test.meters PARTITION by tbname interval(1h)` . - -3. Correlate Dimentions and Metrics - -In Power BI, open model view, correlate "tags" and "data", and set "tabname" as the correlation column, then you can use the data in histogram, pie chart, etc. For more information about building visual reports in PowerBI, please refer to [Power BI](https://learn.microsoft.com/power-bi/)。 \ No newline at end of file +TDengine has its own specific data model, which uses supertable as template and creates a specific table for each device. Each table can have maximum 4,096 data columns and 128 tags. In [the example of meters](https://docs.taosdata.com/concept/) , assume each meter generates one record per second, then there will be 86,400 records each day and 31,536,000 records every year, then only 1,000 meters will occupy 500GB disk space. So, the common usage of Power BI should be mapping tags to dimension columns, mapping the aggregation of data columns to metric columns, to provide indicators for decision makers. +1. Import Dimensions: Import the tags of tables in PowerBI, and name as "tags", the SQL is as the following: +`select distinct tbname, groupid, location from test.meters;` +2. Import Metrics: In Power BI, import the average current, average voltage, average phase with 1 hour window, and name it as "data", the SQL is as the following: +`select tbname, _wstart ws, avg(current), avg(voltage), avg(phase) from test.meters PARTITION by tbname interval(1h)` ; +3. Correlate Dimensions and Metrics: In Power BI, open model view, correlate "tags" and "data", and set "tabname" as the correlation column, then you can use the data in histogram, pie chart, etc. For more information about building visual reports in PowerBI, please refer to [Power BI](https://learn.microsoft.com/zh-cn/power-bi/). \ No newline at end of file diff --git a/docs/en/20-third-party/76-yonghongbi.mdx b/docs/en/20-third-party/76-yonghongbi.mdx new file mode 100644 index 0000000000..163e947832 --- /dev/null +++ b/docs/en/20-third-party/76-yonghongbi.mdx @@ -0,0 +1,64 @@ +--- +sidebar_label: Yonghong BI +title: Yonghong BI +description: Use YonghongBI and TDengine to analyze time series data +--- + +# Tools - Yonghong BI + +![Yonghong BI use step](./yonghongbi-step-en.png) + +[Yonghong one-stop big data BI platform](https://www.yonghongtech.com/)to provide enterprises of all sizes with flexible and easy-to-use whole-business chain big data analysis solutions, so that every user can use this platform to easily discover the value of big data and obtain deep insight. TDengine can be added to Yonghong BI as a data source via a JDBC connector. Once the data source is configured, Yonghong BI can read data from TDengine and provide functions such as data presentation, analysis and prediction. + +### Prerequisite + +1. Yonghong Desktop Basic is installed and running (if not,please go to [official download page of Yonghong Technology](https://www.yonghongtech.com/cp/desktop/) download). +2. The TDengine is installed and running, and ensure that the taosadapter service is started on the TDengine server side. + +### Install JDBC Connector + +Go to [maven.org](https://central.sonatype.com/artifact/com.taosdata.jdbc/taos-jdbcdriver/versions) download the latest TDengine JDBC connector (current version [3.2.7](https://repo1.maven.org/maven2/com/taosdata/jdbc/taos-jdbcdriver/3.2.7/taos-jdbcdriver-3.2.7-dist.jar)) and install it on the machine where the BI tool is running. + +### Configure JDBC DataSource + +1. In the Yonghong Desktop BI tool, click "Add data source" and select the "GENERIC" type in the SQL data source. +2. Click "Select Custom Driver", in the "Driver Management" dialog box, click "+" next to "Driver List", enter the name "MyTDengine". Then click the "upload file" button to upload just download TDengine JDBC connector file "taos-jdbcdriver-3.2.7-dist.jar", and select "com.taosdata.jdbc.rs.RestfulDriver" drive, Finally, click the "OK" button to complete the driver addition. +3. Then copy the following into the "URL" field: +``` +jdbc:TAOS-RS://localhost:6041?user=root&password=taosdata +``` +4. Then select "No identity Authentication" under "Authentication Mode". +5. In the advanced Settings of the data source, change the value of the Quote symbol to the backquote "`". +6. Click "Test connection" and the dialog box "Test success" will pop up. Click the "Save" button and enter "tdengine" to save the TDengine data source. + +### Create TDengine datasets + +1. Click "Add Data Set" in the BI tool, expand the data source you just created, and browse the super table in TDengine. +2. You can load all the data of the super table into the BI tool, or you can import some data through custom SQL statements. +3. When "Computation in Database" is selected, the BI tool will no longer cache TDengine timing data and will send SQL requests to TDengine for direct processing when processing queries. + +When data is imported, the BI tool automatically sets the numeric type to the "metric" column and the text type to the "dimension" column. In TDengine super tables, ordinary columns are used as data metrics and label columns are used as data dimensions, so you may need to change the properties of some columns when you create a dataset. On the basis of supporting standard SQL, TDengine also provides a series of special query syntax to meet the requirements of time series business scenarios, such as data segmentation query, window segmentation query, etc., for [TDengine Specialized Queries](https://docs.taosdata.com/taos-sql/distinguished/) .By using these featured queries, BI tools can greatly improve data access speed and reduce network transmission bandwidth when they send SQL queries to TDengine databases. + +In BI tools, you can create "parameters" and use them in SQL statements, which can be dynamically executed manually and periodically to achieve a visual report refresh effect.The following SQL statement: + +```sql +select _wstart ws, count(*) cnt from supertable where tbname=?{metric} and ts >= ?{from} and ts < ?{to} interval(?{interval}) +``` + +Data can be read in real time from TDengine, where: + +- `_wstart`: Indicates the start time of the time window. +- `count(*)`: Indicates the aggregate value in the time window. +- `?{interval}`: Indicates that the parameter interval is introduced into the SQL statement. When the BI tool queries data, it assigns a value to the parameter interval. If the value is 1m, the sampling data is reduced based on a 1-minute time window. +- `?{metric}`: This parameter is used to specify the name of the data table to be queried. When the ID of a drop-down parameter component is set as metric in the BI tool, the selected items of the drop-down parameter component are bound to this parameter to achieve dynamic selection. +- `?{from}` `?{to}`:These two parameters are used to represent the time range of the query data set and can be bound with the Text Parameter Component. + +You can modify the data type, data range, and default values of parameters in the "Edit Parameters" dialog box of the BI tool, and dynamically set the values of these parameters in the "Visual Report". + +### Create a visual report + +1. Click "Make Report" in Yonghong BI tool to create a canvas. +2. Drag visual components, such as Table Components, onto the canvas. +3. Select the data set to be bound in the Data Set sidebar, and bind Dimensions and Measures in the data column to Table Components as needed. +4. Click "Save" to view the report. +5. For more information about Yonghong BI tools, please consult them [Help document](https://www.yonghongtech.com/help/Z-Suite/10.0/ch/). \ No newline at end of file diff --git a/docs/en/20-third-party/powerbi-step-en.png b/docs/en/20-third-party/powerbi-step-en.png new file mode 100644 index 0000000000000000000000000000000000000000..90e68eee86318677fa527569a9b1a577124bd4b5 GIT binary patch literal 13485 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�*54|5;vz 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 N6mDTuY2beC{{i(vpk4p~ literal 0 HcmV?d00001 diff --git a/docs/en/20-third-party/powerbi-step-en.webp b/docs/en/20-third-party/powerbi-step-en.webp deleted file mode 100644 index 5d6eff1ac2b98e0e18387ba679ef851ac613b375..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37206 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 U000000000000000000000PO^SjsO4v diff --git a/docs/en/20-third-party/yonghongbi-step-en.png b/docs/en/20-third-party/yonghongbi-step-en.png new file mode 100644 index 0000000000000000000000000000000000000000..6448a9550b42dd7207b8d429c390ab2c13ebe77e GIT binary patch literal 13775 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 literal 0 HcmV?d00001 diff --git a/docs/en/21-tdinternal/01-arch.md b/docs/en/21-tdinternal/01-arch.md index 5cf9b90d0a..2269ecda5a 100644 --- a/docs/en/21-tdinternal/01-arch.md +++ b/docs/en/21-tdinternal/01-arch.md @@ -90,7 +90,7 @@ Through TAOSC caching mechanism, mnode needs to be accessed only when a table is ### Storage Model -The data stored by TDengine includes collected time-series data, metadata and tag data related to database and tablesetc. All of the data is specifically divided into three parts: +The data stored by TDengine includes collected time-series data, metadata and tag data related to database and tables, etc. All of the data is specifically divided into three parts: - Time-series data: stored in vnode and composed of data, head and last files. Normally the amount of time series data is very huge and query amount depends on the application scenario. Out-of-order writing is allowed. By adopting the model with **one table for each data collection point**, the data of a given time period is continuously stored, and the writing against one single table is a simple appending operation. Multiple records can be read at one time, thus ensuring the best performance for both insert and query operations of a single data collection point. - Table Metadata: table meta data includes tags and table schema and is stored in meta file in each vnode. CRUD can be operated on table metadata. There is a specific record for each table, so the amount of table meta data depends on the number of tables. Table meta data is stored in LRU model and supports index for tag data. TDengine can support multiple queries in parallel. As long as the memory resource is enough, meta data is all stored in memory for quick access. The filtering on tens of millions of tags can be finished in a few milliseconds. Even though when the memory resource is not sufficient, TDengine can still perform high speed query on tens of millions of tables. @@ -200,7 +200,7 @@ dataDir format is as follows: dataDir data_path [tier_level] [primary] [disable_create_new_file] ``` -Where `data_path` is the folder path of mount point, and `tier_level` is the media storage-tier. The higher the media storage-tier, means the older the data file. Multiple hard disks can be mounted at the same storage-tier, and data files on the same storage-tier are distributed on all hard disks within the tier. TDengine supports up to 3 tiers of storage, so tier_level values are 0, 1, and 2. When configuring dataDir, there must be only one mount path without specifying tier_level, which is called special mount disk (path). The mount path defaults to level 0 storage media and contains special file links, which cannot be removed, otherwise it will have a devastating impact on the written data. And `primary` means whether the data dir is the primary mount point. Enter 0 for false or 1 for true. The default value is 1. A TDengine cluster can have only one `primary` mount point, which must be on tier 0. And `disable_create_new_file` means whether to prohibit the creation of new file sets on the specified mount point. Enter 0 for false and 1 for true. The default value is 0. Tier 0 storage must have at least one mount point with disable_create_new_file set to 0. Tier 1 and tier 2 storage do not have this restriction. +Where `data_path` is the folder path of mount point, and `tier_level` is the media storage-tier. The higher the media storage-tier, means the older the data file. Multiple hard disks can be mounted at the same storage-tier, and data files on the same storage-tier are distributed on all hard disks within the tier. TDengine supports up to 3 tiers of storage, so tier_level values are 0, 1, and 2. When configuring dataDir, there must be only one mount path without specifying tier_level, which is called special mount disk (path). The mount path defaults to level 0 storage media and contains special file links, which cannot be removed, otherwise it will have a devastating impact on the written data. And `primary` means whether the data dir is the primary mount point. Enter 0 for false or 1 for true. The default value is 1. A TDengine cluster can have only one `primary` mount point, which must be on tier 0. And `disable_create_new_file` means whether to prohibit the creation of new file sets on the specified mount point. Enter 0 for false and 1 for true. The default value is 0. Tier 0 storage must have at least one mount point with disable_create_new_file set to 0. Tier 1 and tier 2 storage do not have this restriction. Suppose there is a physical node with six mountable hard disks/mnt/disk1,/mnt/disk2, ..., /mnt/disk6, where disk1 and disk2 need to be designated as level 0 storage media, disk3 and disk4 are level 1 storage media, and disk5 and disk6 are level 2 storage media. Disk1 is a special mount disk, you can configure it in/etc/taos/taos.cfg as follows: diff --git a/docs/en/28-releases/01-tdengine.md b/docs/en/28-releases/01-tdengine.md index 119f22d96b..a6e157cf74 100644 --- a/docs/en/28-releases/01-tdengine.md +++ b/docs/en/28-releases/01-tdengine.md @@ -4,12 +4,26 @@ sidebar_label: TDengine description: This document provides download links for all released versions of TDengine 3.0. --- +## TDengine Version Rules + +TDengine version number consists of 4 numbers separated by `.`, defined as below: +- `[Major+].[Major].[Feature].[Maintenance]` +- `Major+`: Significant rearchitecture release, can't be upgraded from an old version with different `Major+` number. If you have such a need, please contact TDengine support team. +- `Major`: Important new feature release, can't be rolling upgraded from old version with different `Major` number, and can't be rolled back after upgrading. For example, after upgrading from `3.2.3.0` to `3.3.0.0`, you can't roll back to `3.2.3.0`. +- `Feature`:New feature release, can't be rolling upgraded from an old version with different `Feature` number, but can be rolled back after upgrading. For example, after upgrading from `3.3.0.0` to `3.3.1.0`, you can roll back to `3.3.0.0`. The client driver (libtaos.so) must be upgraded to same version as the server side (taosd). +- `Maintenance`: Maintenance release, no new features but only bug fixings, can be rolling upgraded from an old version with only `Maintenance` number different, and can be rolled back after upgrading. +- `Rolling Upgrade`: For a cluster consisting of three or more dnodes with three replica enabled, you can upgrade one dnode each time by stopping it, upgrading it, and then restarting it, repeat this process to upgrade the whole cluster. During this period, the cluster is still in service. If rolling upgrade is not supported based on the above version number rules, you need to first stop the whole cluster, upgrade all dndoes, and restart all dnodes after upgrading. During this period, the cluster is out of service. + TDengine 3.x installation packages can be downloaded at the following links: For TDengine 2.x installation packages by version, please visit [here](https://tdengine.com/downloads/historical/). import Release from "/components/ReleaseV3"; +## 3.3.2.0 + + + ## 3.3.1.0 diff --git a/docs/examples/go/demo/consumer/main.go b/docs/examples/go/demo/consumer/main.go new file mode 100644 index 0000000000..a8a1d82274 --- /dev/null +++ b/docs/examples/go/demo/consumer/main.go @@ -0,0 +1,113 @@ +package main + +import ( + "fmt" + "os" + "time" + + "github.com/taosdata/driver-go/v3/af" + "github.com/taosdata/driver-go/v3/af/tmq" + tmqcommon "github.com/taosdata/driver-go/v3/common/tmq" +) + +func main() { + db, err := af.Open("", "root", "taosdata", "", 0) + if err != nil { + panic(err) + } + defer db.Close() + _, err = db.Exec("create database if not exists power WAL_RETENTION_PERIOD 86400") + if err != nil { + panic(err) + } + _, err = db.Exec("CREATE STABLE IF NOT EXISTS power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))") + if err != nil { + panic(err) + } + _, err = db.Exec("create table if not exists power.d001 using power.meters tags(1,'location')") + if err != nil { + panic(err) + } + // ANCHOR: create_topic + _, err = db.Exec("CREATE TOPIC IF NOT EXISTS topic_meters AS SELECT ts, current, voltage, phase, groupid, location FROM power.meters") + if err != nil { + panic(err) + } + // ANCHOR_END: create_topic + // ANCHOR: create_consumer + consumer, err := tmq.NewConsumer(&tmqcommon.ConfigMap{ + "group.id": "test", + "auto.offset.reset": "latest", + "td.connect.ip": "127.0.0.1", + "td.connect.user": "root", + "td.connect.pass": "taosdata", + "td.connect.port": "6030", + "client.id": "test_tmq_client", + "enable.auto.commit": "false", + "msg.with.table.name": "true", + }) + if err != nil { + panic(err) + } + // ANCHOR_END: create_consumer + // ANCHOR: poll_data + go func() { + for { + _, err = db.Exec("insert into power.d001 values (now, 1.1, 220, 0.1)") + if err != nil { + panic(err) + } + time.Sleep(time.Millisecond * 100) + } + }() + + err = consumer.Subscribe("topic_meters", nil) + if err != nil { + panic(err) + } + + for i := 0; i < 5; i++ { + ev := consumer.Poll(500) + if ev != nil { + switch e := ev.(type) { + case *tmqcommon.DataMessage: + fmt.Printf("get message:%v\n", e) + case tmqcommon.Error: + fmt.Fprintf(os.Stderr, "%% Error: %v: %v\n", e.Code(), e) + panic(e) + } + consumer.Commit() + } + } + // ANCHOR_END: poll_data + // ANCHOR: consumer_seek + partitions, err := consumer.Assignment() + if err != nil { + panic(err) + } + for i := 0; i < len(partitions); i++ { + fmt.Println(partitions[i]) + err = consumer.Seek(tmqcommon.TopicPartition{ + Topic: partitions[i].Topic, + Partition: partitions[i].Partition, + Offset: 0, + }, 0) + if err != nil { + panic(err) + } + } + partitions, err = consumer.Assignment() + if err != nil { + panic(err) + } + // ANCHOR_END: consumer_seek + for i := 0; i < len(partitions); i++ { + fmt.Println(partitions[i]) + } + // ANCHOR: consumer_close + err = consumer.Close() + if err != nil { + panic(err) + } + // ANCHOR_END: consumer_close +} diff --git a/docs/examples/go/demo/consumerws/main.go b/docs/examples/go/demo/consumerws/main.go new file mode 100644 index 0000000000..9f1105111a --- /dev/null +++ b/docs/examples/go/demo/consumerws/main.go @@ -0,0 +1,115 @@ +package main + +import ( + "database/sql" + "fmt" + "os" + "time" + + "github.com/taosdata/driver-go/v3/common" + tmqcommon "github.com/taosdata/driver-go/v3/common/tmq" + _ "github.com/taosdata/driver-go/v3/taosRestful" + "github.com/taosdata/driver-go/v3/ws/tmq" +) + +func main() { + db, err := sql.Open("taosRestful", "root:taosdata@http(localhost:6041)/") + if err != nil { + panic(err) + } + defer db.Close() + _, err = db.Exec("create database if not exists power WAL_RETENTION_PERIOD 86400") + if err != nil { + panic(err) + } + _, err = db.Exec("CREATE STABLE IF NOT EXISTS power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))") + if err != nil { + panic(err) + } + _, err = db.Exec("create table if not exists power.d001 using power.meters tags(1,'location')") + if err != nil { + panic(err) + } + // ANCHOR: create_topic + _, err = db.Exec("CREATE TOPIC IF NOT EXISTS topic_meters AS SELECT ts, current, voltage, phase, groupid, location FROM power.meters") + if err != nil { + panic(err) + } + // ANCHOR_END: create_topic + // ANCHOR: create_consumer + consumer, err := tmq.NewConsumer(&tmqcommon.ConfigMap{ + "ws.url": "ws://127.0.0.1:6041", + "ws.message.channelLen": uint(0), + "ws.message.timeout": common.DefaultMessageTimeout, + "ws.message.writeWait": common.DefaultWriteWait, + "td.connect.user": "root", + "td.connect.pass": "taosdata", + "group.id": "example", + "client.id": "example_consumer", + "auto.offset.reset": "latest", + }) + if err != nil { + panic(err) + } + // ANCHOR_END: create_consumer + // ANCHOR: poll_data + go func() { + for { + _, err = db.Exec("insert into power.d001 values (now, 1.1, 220, 0.1)") + if err != nil { + panic(err) + } + time.Sleep(time.Millisecond * 100) + } + }() + + err = consumer.Subscribe("topic_meters", nil) + if err != nil { + panic(err) + } + + for i := 0; i < 5; i++ { + ev := consumer.Poll(500) + if ev != nil { + switch e := ev.(type) { + case *tmqcommon.DataMessage: + fmt.Printf("get message:%v\n", e) + case tmqcommon.Error: + fmt.Fprintf(os.Stderr, "%% Error: %v: %v\n", e.Code(), e) + panic(e) + } + consumer.Commit() + } + } + // ANCHOR_END: poll_data + // ANCHOR: consumer_seek + partitions, err := consumer.Assignment() + if err != nil { + panic(err) + } + for i := 0; i < len(partitions); i++ { + fmt.Println(partitions[i]) + err = consumer.Seek(tmqcommon.TopicPartition{ + Topic: partitions[i].Topic, + Partition: partitions[i].Partition, + Offset: 0, + }, 0) + if err != nil { + panic(err) + } + } + partitions, err = consumer.Assignment() + if err != nil { + panic(err) + } + // ANCHOR_END: consumer_seek + for i := 0; i < len(partitions); i++ { + fmt.Println(partitions[i]) + } + // ANCHOR: consumer_close + err = consumer.Close() + if err != nil { + panic(err) + } + // ANCHOR_END: consumer_close +} diff --git a/docs/examples/go/demo/query/main.go b/docs/examples/go/demo/query/main.go new file mode 100644 index 0000000000..7ceae06f04 --- /dev/null +++ b/docs/examples/go/demo/query/main.go @@ -0,0 +1,76 @@ +package main + +import ( + "context" + "database/sql" + "log" + "time" + + "github.com/taosdata/driver-go/v3/common" + _ "github.com/taosdata/driver-go/v3/taosSql" +) + +func main() { + var taosDSN = "root:taosdata@tcp(localhost:6030)/" + taos, err := sql.Open("taosSql", taosDSN) + if err != nil { + log.Fatalln("failed to connect TDengine, err:", err) + } + defer taos.Close() + // ANCHOR: create_db_and_table + _, err = taos.Exec("CREATE DATABASE if not exists power") + if err != nil { + log.Fatalln("failed to create database, err:", err) + } + _, err = taos.Exec("CREATE STABLE IF NOT EXISTS power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))") + if err != nil { + log.Fatalln("failed to create stable, err:", err) + } + // ANCHOR_END: create_db_and_table + // ANCHOR: insert_data + affected, err := taos.Exec("INSERT INTO " + + "power.d1001 USING power.meters TAGS(2,'California.SanFrancisco') " + + "VALUES " + + "(NOW + 1a, 10.30000, 219, 0.31000) " + + "(NOW + 2a, 12.60000, 218, 0.33000) " + + "(NOW + 3a, 12.30000, 221, 0.31000) " + + "power.d1002 USING power.meters TAGS(3, 'California.SanFrancisco') " + + "VALUES " + + "(NOW + 1a, 10.30000, 218, 0.25000) ") + if err != nil { + log.Fatalln("failed to insert data, err:", err) + } + log.Println("affected rows:", affected) + // ANCHOR_END: insert_data + // ANCHOR: query_data + rows, err := taos.Query("SELECT * FROM power.meters") + if err != nil { + log.Fatalln("failed to select from table, err:", err) + } + + defer rows.Close() + for rows.Next() { + var ( + ts time.Time + current float32 + voltage int + phase float32 + groupId int + location string + ) + err := rows.Scan(&ts, ¤t, &voltage, &phase, &groupId, &location) + if err != nil { + log.Fatalln("scan error:\n", err) + return + } + log.Println(ts, current, voltage, phase, groupId, location) + } + // ANCHOR_END: query_data + // ANCHOR: with_reqid + ctx := context.WithValue(context.Background(), common.ReqIDKey, common.GetReqID()) + _, err = taos.ExecContext(ctx, "CREATE DATABASE IF NOT EXISTS power") + if err != nil { + log.Fatalln("failed to create database, err:", err) + } + // ANCHOR_END: with_reqid +} diff --git a/docs/examples/go/demo/sml/main.go b/docs/examples/go/demo/sml/main.go new file mode 100644 index 0000000000..b9e4bb5145 --- /dev/null +++ b/docs/examples/go/demo/sml/main.go @@ -0,0 +1,41 @@ +package main + +import ( + "fmt" + + "github.com/taosdata/driver-go/v3/af" +) + +const LineDemo = "meters,groupid=2,location=California.SanFrancisco current=10.3000002f64,voltage=219i32,phase=0.31f64 1626006833639000000" + +const TelnetDemo = "stb0_0 1707095283260 4 host=host0 interface=eth0" + +const JsonDemo = "{\"metric\": \"meter_current\",\"timestamp\": 1626846400,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"California.SanFrancisco\", \"id\": \"d1001\"}}" + +func main() { + conn, err := af.Open("localhost", "root", "taosdata", "", 6030) + if err != nil { + fmt.Println("fail to connect, err:", err) + } + defer conn.Close() + _, err = conn.Exec("CREATE DATABASE IF NOT EXISTS power") + if err != nil { + panic(err) + } + _, err = conn.Exec("use power") + if err != nil { + panic(err) + } + err = conn.InfluxDBInsertLines([]string{LineDemo}, "ns") + if err != nil { + panic(err) + } + err = conn.OpenTSDBInsertTelnetLines([]string{TelnetDemo}) + if err != nil { + panic(err) + } + err = conn.OpenTSDBInsertJsonPayload(JsonDemo) + if err != nil { + panic(err) + } +} diff --git a/docs/examples/go/demo/smlws/main.go b/docs/examples/go/demo/smlws/main.go new file mode 100644 index 0000000000..07f81d01a7 --- /dev/null +++ b/docs/examples/go/demo/smlws/main.go @@ -0,0 +1,54 @@ +package main + +import ( + "database/sql" + "log" + "time" + + "github.com/taosdata/driver-go/v3/common" + _ "github.com/taosdata/driver-go/v3/taosWS" + "github.com/taosdata/driver-go/v3/ws/schemaless" +) + +const LineDemo = "meters,groupid=2,location=California.SanFrancisco current=10.3000002f64,voltage=219i32,phase=0.31f64 1626006833639000000" + +const TelnetDemo = "stb0_0 1707095283260 4 host=host0 interface=eth0" + +const JsonDemo = "{\"metric\": \"meter_current\",\"timestamp\": 1626846400,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"California.SanFrancisco\", \"id\": \"d1001\"}}" + +func main() { + db, err := sql.Open("taosWS", "root:taosdata@ws(localhost:6041)/") + if err != nil { + log.Fatal(err) + } + defer db.Close() + _, err = db.Exec("create database if not exists power") + if err != nil { + log.Fatal(err) + } + s, err := schemaless.NewSchemaless(schemaless.NewConfig("ws://localhost:6041", 1, + schemaless.SetDb("power"), + schemaless.SetReadTimeout(10*time.Second), + schemaless.SetWriteTimeout(10*time.Second), + schemaless.SetUser("root"), + schemaless.SetPassword("taosdata"), + schemaless.SetErrorHandler(func(err error) { + log.Fatal(err) + }), + )) + if err != nil { + panic(err) + } + err = s.Insert(LineDemo, schemaless.InfluxDBLineProtocol, "ns", 0, common.GetReqID()) + if err != nil { + panic(err) + } + err = s.Insert(TelnetDemo, schemaless.OpenTSDBTelnetLineProtocol, "ms", 0, common.GetReqID()) + if err != nil { + panic(err) + } + err = s.Insert(JsonDemo, schemaless.OpenTSDBJsonFormatProtocol, "ms", 0, common.GetReqID()) + if err != nil { + panic(err) + } +} diff --git a/docs/examples/go/demo/stmt/main.go b/docs/examples/go/demo/stmt/main.go new file mode 100644 index 0000000000..d65dadcae6 --- /dev/null +++ b/docs/examples/go/demo/stmt/main.go @@ -0,0 +1,81 @@ +package main + +import ( + "fmt" + "strconv" + "time" + + "github.com/taosdata/driver-go/v3/af" + "github.com/taosdata/driver-go/v3/common" + "github.com/taosdata/driver-go/v3/common/param" +) + +const ( + NumOfSubTable = 10 + NumOfRow = 10 +) + +func main() { + prepare() + db, err := af.Open("", "root", "taosdata", "power", 0) + if err != nil { + panic(err) + } + defer db.Close() + stmt := db.InsertStmt() + defer stmt.Close() + err = stmt.Prepare("INSERT INTO ? USING meters TAGS(?,?) VALUES (?,?,?,?)") + if err != nil { + panic(err) + } + for i := 1; i <= NumOfSubTable; i++ { + tags := param.NewParam(2).AddInt(i).AddBinary([]byte("location")) + err = stmt.SetTableNameWithTags("d_bind_"+strconv.Itoa(i), tags) + if err != nil { + panic(err) + } + now := time.Now() + params := make([]*param.Param, 4) + params[0] = param.NewParam(NumOfRow) + params[1] = param.NewParam(NumOfRow) + params[2] = param.NewParam(NumOfRow) + params[3] = param.NewParam(NumOfRow) + for i := 0; i < NumOfRow; i++ { + params[0].SetTimestamp(i, now.Add(time.Duration(i)*time.Second), common.PrecisionMilliSecond) + params[1].SetFloat(i, float32(i)) + params[2].SetInt(i, i) + params[3].SetFloat(i, float32(i)) + } + paramTypes := param.NewColumnType(4).AddTimestamp().AddFloat().AddInt().AddFloat() + err = stmt.BindParam(params, paramTypes) + if err != nil { + panic(err) + } + err = stmt.AddBatch() + if err != nil { + panic(err) + } + err = stmt.Execute() + if err != nil { + panic(err) + } + affected := stmt.GetAffectedRows() + fmt.Println("affected rows:", affected) + } +} + +func prepare() { + db, err := af.Open("", "root", "taosdata", "", 0) + if err != nil { + panic(err) + } + defer db.Close() + _, err = db.Exec("CREATE DATABASE IF NOT EXISTS power") + if err != nil { + panic(err) + } + _, err = db.Exec("CREATE STABLE IF NOT EXISTS power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))") + if err != nil { + panic(err) + } +} diff --git a/docs/examples/go/demo/stmtws/main.go b/docs/examples/go/demo/stmtws/main.go new file mode 100644 index 0000000000..71d978a88d --- /dev/null +++ b/docs/examples/go/demo/stmtws/main.go @@ -0,0 +1,95 @@ +package main + +import ( + "database/sql" + "fmt" + "strconv" + "time" + + "github.com/taosdata/driver-go/v3/common" + "github.com/taosdata/driver-go/v3/common/param" + _ "github.com/taosdata/driver-go/v3/taosRestful" + "github.com/taosdata/driver-go/v3/ws/stmt" +) + +const ( + NumOfSubTable = 10 + NumOfRow = 10 +) + +func main() { + prepare() + config := stmt.NewConfig("ws://127.0.0.1:6041", 0) + config.SetConnectUser("root") + config.SetConnectPass("taosdata") + config.SetConnectDB("power") + config.SetMessageTimeout(common.DefaultMessageTimeout) + config.SetWriteWait(common.DefaultWriteWait) + config.SetErrorHandler(func(connector *stmt.Connector, err error) { + panic(err) + }) + config.SetCloseHandler(func() { + fmt.Println("stmt connector closed") + }) + + connector, err := stmt.NewConnector(config) + if err != nil { + panic(err) + } + stmt, err := connector.Init() + err = stmt.Prepare("INSERT INTO ? USING meters TAGS(?,?) VALUES (?,?,?,?)") + if err != nil { + panic(err) + } + for i := 1; i <= NumOfSubTable; i++ { + tags := param.NewParam(2).AddInt(i).AddBinary([]byte("location")) + err = stmt.SetTableName("d_bind_" + strconv.Itoa(i)) + if err != nil { + panic(err) + } + err = stmt.SetTags(tags, param.NewColumnType(2).AddInt().AddBinary(8)) + now := time.Now() + params := make([]*param.Param, 4) + params[0] = param.NewParam(NumOfRow) + params[1] = param.NewParam(NumOfRow) + params[2] = param.NewParam(NumOfRow) + params[3] = param.NewParam(NumOfRow) + for i := 0; i < NumOfRow; i++ { + params[0].SetTimestamp(i, now.Add(time.Duration(i)*time.Second), common.PrecisionMilliSecond) + params[1].SetFloat(i, float32(i)) + params[2].SetInt(i, i) + params[3].SetFloat(i, float32(i)) + } + paramTypes := param.NewColumnType(4).AddTimestamp().AddFloat().AddInt().AddFloat() + err = stmt.BindParam(params, paramTypes) + if err != nil { + panic(err) + } + err = stmt.AddBatch() + if err != nil { + panic(err) + } + err = stmt.Exec() + if err != nil { + panic(err) + } + affected := stmt.GetAffectedRows() + fmt.Println("affected rows:", affected) + } +} + +func prepare() { + db, err := sql.Open("taosRestful", "root:taosdata@http(localhost:6041)/") + if err != nil { + panic(err) + } + defer db.Close() + _, err = db.Exec("CREATE DATABASE IF NOT EXISTS power") + if err != nil { + panic(err) + } + _, err = db.Exec("CREATE STABLE IF NOT EXISTS power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))") + if err != nil { + panic(err) + } +} diff --git a/docs/examples/node/websocketexample/json_line_example.js b/docs/examples/node/websocketexample/json_line_example.js new file mode 100644 index 0000000000..e6587eaa45 --- /dev/null +++ b/docs/examples/node/websocketexample/json_line_example.js @@ -0,0 +1,53 @@ +const taos = require("@tdengine/websocket"); + +var host = null; +for(var i = 2; i < global.process.argv.length; i++){ + var key = global.process.argv[i].split("=")[0]; + var value = global.process.argv[i].split("=")[1]; + if("host" == key){ + host = value; + } +} + +if(host == null){ + console.log("Usage: node nodejsChecker.js host= port="); + process.exit(0); + } + +let dbData = ["{\"metric\": \"meter_current\",\"timestamp\": 1626846402,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"California.SanFrancisco\", \"id\": \"d1001\"}}", + "{\"metric\": \"meter_current\",\"timestamp\": 1626846403,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"California.SanFrancisco\", \"id\": \"d1002\"}}", + "{\"metric\": \"meter_current\",\"timestamp\": 1626846404,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"California.SanFrancisco\", \"id\": \"d1003\"}}"] + +async function createConnect() { + let dsn = 'ws://' + host + ':6041' + let conf = new taos.WSConfig(dsn); + conf.setUser('root'); + conf.setPwd('taosdata'); + conf.setDb('power'); + return await taos.sqlConnect(conf); +} + +async function test() { + let wsSql = null; + let wsRows = null; + let reqId = 0; + try { + wsSql = await createConnect() + await wsSql.exec('CREATE DATABASE IF NOT EXISTS power KEEP 3650 DURATION 10 BUFFER 16 WAL_LEVEL 1;', reqId++); + await wsSql.schemalessInsert([dbData], taos.SchemalessProto.OpenTSDBJsonFormatProtocol, taos.Precision.SECONDS, 0); + } + catch (err) { + console.error(err.code, err.message); + } + finally { + if (wsRows) { + await wsRows.close(); + } + if (wsSql) { + await wsSql.close(); + } + taos.destroy(); + } +} + +test() \ No newline at end of file diff --git a/docs/examples/node/websocketexample/line_example.js b/docs/examples/node/websocketexample/line_example.js new file mode 100644 index 0000000000..a08bdb21e4 --- /dev/null +++ b/docs/examples/node/websocketexample/line_example.js @@ -0,0 +1,49 @@ +const taos = require("@tdengine/websocket"); + +let influxdbData = ["meters,location=California.LosAngeles,groupId=2 current=11.8,voltage=221,phase=0.28 1648432611249", + "meters,location=California.LosAngeles,groupId=2 current=13.4,voltage=223,phase=0.29 1648432611250", + "meters,location=California.LosAngeles,groupId=3 current=10.8,voltage=223,phase=0.29 1648432611249"]; + +let jsonData = ["{\"metric\": \"meter_current\",\"timestamp\": 1626846402,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"California.SanFrancisco\", \"id\": \"d1001\"}}", + "{\"metric\": \"meter_current\",\"timestamp\": 1626846403,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"California.SanFrancisco\", \"id\": \"d1002\"}}", + "{\"metric\": \"meter_current\",\"timestamp\": 1626846404,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"California.SanFrancisco\", \"id\": \"d1003\"}}"] + +let telnetData = ["meters.current 1648432611249 10.3 location=California.SanFrancisco groupid=2", + "meters.current 1648432611250 12.6 location=California.SanFrancisco groupid=2", + "meters.current 1648432611249 10.8 location=California.LosAngeles groupid=3"]; + +async function createConnect() { + let dsn = 'ws://localhost:6041' + let conf = new taos.WSConfig(dsn); + conf.setUser('root'); + conf.setPwd('taosdata'); + let wsSql = await taos.sqlConnect(conf); + await wsSql.exec('CREATE DATABASE IF NOT EXISTS power KEEP 3650 DURATION 10 BUFFER 16 WAL_LEVEL 1;'); + await wsSql.exec('USE power'); + return wsSql; +} + +async function test() { + let wsSql = null; + let wsRows = null; + let ttl = 0; + try { + wsSql = await createConnect() + await wsSql.schemalessInsert(influxdbData, taos.SchemalessProto.InfluxDBLineProtocol, taos.Precision.MILLI_SECONDS, ttl); + await wsSql.schemalessInsert(jsonData, taos.SchemalessProto.OpenTSDBJsonFormatProtocol, taos.Precision.SECONDS, ttl); + await wsSql.schemalessInsert(telnetData, taos.SchemalessProto.OpenTSDBTelnetLineProtocol, taos.Precision.MILLI_SECONDS, ttl); + } + catch (err) { + console.error(err.code, err.message); + } + finally { + if (wsRows) { + await wsRows.close(); + } + if (wsSql) { + await wsSql.close(); + } + taos.destroy(); + } +} +test() \ No newline at end of file diff --git a/docs/examples/node/websocketexample/nodejsChecker.js b/docs/examples/node/websocketexample/nodejsChecker.js new file mode 100644 index 0000000000..d81aeb585f --- /dev/null +++ b/docs/examples/node/websocketexample/nodejsChecker.js @@ -0,0 +1,77 @@ +const taos = require("@tdengine/websocket"); + +var host = null; +for(var i = 2; i < global.process.argv.length; i++){ + var key = global.process.argv[i].split("=")[0]; + var value = global.process.argv[i].split("=")[1]; + if("host" == key){ + host = value; + } +} + +if(host == null){ + console.log("Usage: node nodejsChecker.js host= port="); + process.exit(0); +} + + +async function createConnect() { + let dsn = 'ws://' + host + ':6041' + console.log(dsn) + let conf = new taos.WSConfig(dsn); + conf.setUser('root') + conf.setPwd('taosdata') + return await taos.sqlConnect(conf); +} + +async function test() { + let wsSql = null; + let wsRows = null; + let reqId = 0; + try { + wsSql = await createConnect() + let version = await wsSql.version(); + console.log(version); + let taosResult = await wsSql.exec('SHOW DATABASES', reqId++); + console.log(taosResult); + + taosResult = await wsSql.exec('CREATE DATABASE IF NOT EXISTS power KEEP 3650 DURATION 10 BUFFER 16 WAL_LEVEL 1;', reqId++); + console.log(taosResult); + + taosResult = await wsSql.exec('USE power', reqId++); + console.log(taosResult); + + taosResult = await wsSql.exec('CREATE STABLE IF NOT EXISTS meters (_ts timestamp, current float, voltage int, phase float) TAGS (location binary(64), groupId int);', reqId++); + console.log(taosResult); + + taosResult = await wsSql.exec('DESCRIBE meters', reqId++); + console.log(taosResult); + + taosResult = await wsSql.exec('INSERT INTO d1001 USING meters (location, groupId) TAGS ("California.SanFrancisco", 3) VALUES (NOW, 10.2, 219, 0.32)', reqId++); + console.log(taosResult); + + wsRows = await wsSql.query('SELECT * FROM meters', reqId++); + let meta = wsRows.getMeta(); + console.log("wsRow:meta:=>", meta); + + while (await wsRows.next()) { + let result = wsRows.getData(); + console.log('queryRes.Scan().then=>', result); + } + + } + catch (err) { + console.error(err.code, err.message); + } + finally { + if (wsRows) { + await wsRows.close(); + } + if (wsSql) { + await wsSql.close(); + } + taos.destroy(); + } +} + +test() \ No newline at end of file diff --git a/docs/examples/node/websocketexample/sql_example.js b/docs/examples/node/websocketexample/sql_example.js new file mode 100644 index 0000000000..484559e2b3 --- /dev/null +++ b/docs/examples/node/websocketexample/sql_example.js @@ -0,0 +1,143 @@ +const taos = require("@tdengine/websocket"); + +// ANCHOR: createConnect +async function createConnect() { + let dsn = 'ws://localhost:6041'; + let conf = new taos.WSConfig(dsn); + conf.setUser('root'); + conf.setPwd('taosdata'); + conf.setDb('power'); + return await taos.sqlConnect(conf); +} +// ANCHOR_END: createConnect + +// ANCHOR: create_db_and_table +async function createDbAndTable(wsSql) { + let wsSql = null; + try { + wsSql = await createConnect(); + await wsSql.exec('CREATE DATABASE IF NOT EXISTS POWER ' + + 'KEEP 3650 DURATION 10 BUFFER 16 WAL_LEVEL 1;'); + + await wsSql.exec('USE power'); + + await wsSql.exec('CREATE STABLE IF NOT EXISTS meters ' + + '(_ts timestamp, current float, voltage int, phase float) ' + + 'TAGS (location binary(64), groupId int);'); + + taosResult = await wsSql.exec('describe meters'); + console.log(taosResult); + } catch (err) { + + console.error(err.code, err.message); + } finally { + if (wsSql) { + await wsSql.close(); + } + } + +} +// ANCHOR_END: create_db_and_table + +// ANCHOR: insertData +async function insertData(wsSql) { + let wsSql = null; + try { + wsSql = await createConnect(); + let insertQuery = "INSERT INTO " + + "power.d1001 USING power.meters (location, groupId) TAGS('California.SanFrancisco', 2) " + + "VALUES " + + "(NOW + 1a, 10.30000, 219, 0.31000) " + + "(NOW + 2a, 12.60000, 218, 0.33000) " + + "(NOW + 3a, 12.30000, 221, 0.31000) " + + "power.d1002 USING power.meters TAGS('California.SanFrancisco', 3) " + + "VALUES " + + "(NOW + 1a, 10.30000, 218, 0.25000) "; + taosResult = await wsSql.exec(insertQuery); + console.log(taosResult); + } catch (err) { + console.error(err.code, err.message); + } finally { + if (wsSql) { + await wsSql.close(); + } + } +} +// ANCHOR_END: insertData + +// ANCHOR: queryData +async function queryData() { + let wsRows = null; + let wsSql = null; + try { + wsSql = await createConnect(); + wsRows = await wsSql.query('select * from meters'); + let meta = wsRows.getMeta(); + console.log("wsRow:meta:=>", meta); + while (await wsRows.next()) { + let result = wsRows.getData(); + console.log('queryRes.Scan().then=>', result); + } + } + catch (err) { + console.error(err.code, err.message); + } + finally { + if (wsRows) { + await wsRows.close(); + } + if (wsSql) { + await wsSql.close(); + } + } +} +// ANCHOR_END: queryData + +// ANCHOR: sqlWithReqid +async function sqlWithReqid(wsSql) { + let insertQuery = "INSERT INTO " + + "power.d1001 USING power.meters (location, groupId) TAGS('California.SanFrancisco', 2) " + + "VALUES " + + "(NOW + 1a, 10.30000, 219, 0.31000) " + + "(NOW + 2a, 12.60000, 218, 0.33000) " + + "(NOW + 3a, 12.30000, 221, 0.31000) " + + "power.d1002 USING power.meters TAGS('California.SanFrancisco', 3) " + + "VALUES " + + "(NOW + 1a, 10.30000, 218, 0.25000) "; + + let wsRows = null; + let wsSql = null; + try { + wsSql = await createConnect(); + taosResult = await wsSql.exec(insertQuery, 1); + wsRows = await wsSql.query('select * from meters', 2); + let meta = wsRows.getMeta(); + console.log("wsRow:meta:=>", meta); + while (await wsRows.next()) { + let result = wsRows.getData(); + console.log('queryRes.Scan().then=>', result); + } + } + catch (err) { + console.error(err.code, err.message); + } + finally { + if (wsRows) { + await wsRows.close(); + } + if (wsSql) { + await wsSql.close(); + } + } +} +// ANCHOR_END: sqlWithReqid + +async function test() { + await createDbAndTable(); + await insertData(); + await queryData(); + await sqlWithReqid(); + taos.destroy(); +} + +test() \ No newline at end of file diff --git a/docs/examples/node/websocketexample/stmt_example.js b/docs/examples/node/websocketexample/stmt_example.js new file mode 100644 index 0000000000..842a8a4c3d --- /dev/null +++ b/docs/examples/node/websocketexample/stmt_example.js @@ -0,0 +1,60 @@ +const taos = require("@tdengine/websocket"); + +let db = 'power'; +let stable = 'meters'; +let tags = ['California.SanFrancisco', 3]; +let values = [ + [1706786044994, 1706786044995, 1706786044996], + [10.2, 10.3, 10.4], + [292, 293, 294], + [0.32, 0.33, 0.34], +]; + +async function prepare() { + let dsn = 'ws://localhost:6041' + let conf = new taos.WSConfig(dsn); + conf.setUser('root') + conf.setPwd('taosdata') + conf.setDb(db) + let wsSql = await taos.sqlConnect(conf); + await wsSql.exec(`CREATE DATABASE IF NOT EXISTS ${db} KEEP 3650 DURATION 10 BUFFER 16 WAL_LEVEL 1;`); + await wsSql.exec(`CREATE STABLE IF NOT EXISTS ${db}.${stable} (ts timestamp, current float, voltage int, phase float) TAGS (location binary(64), groupId int);`); + return wsSql +} + +(async () => { + let stmt = null; + let connector = null; + try { + connector = await prepare(); + stmt = await connector.stmtInit(); + await stmt.prepare(`INSERT INTO ? USING ${db}.${stable} (location, groupId) TAGS (?, ?) VALUES (?, ?, ?, ?)`); + await stmt.setTableName('d1001'); + let tagParams = stmt.newStmtParam(); + tagParams.setVarchar([tags[0]]); + tagParams.setInt([tags[1]]); + await stmt.setTags(tagParams); + + let bindParams = stmt.newStmtParam(); + bindParams.setTimestamp(values[0]); + bindParams.setFloat(values[1]); + bindParams.setInt(values[2]); + bindParams.setFloat(values[3]); + await stmt.bind(bindParams); + await stmt.batch(); + await stmt.exec(); + console.log(stmt.getLastAffected()); + } + catch (err) { + console.error(err.code, err.message); + } + finally { + if (stmt) { + await stmt.close(); + } + if (connector) { + await connector.close(); + } + taos.destroy(); + } +})(); diff --git a/docs/examples/node/websocketexample/telnet_line_example.js b/docs/examples/node/websocketexample/telnet_line_example.js new file mode 100644 index 0000000000..924137e162 --- /dev/null +++ b/docs/examples/node/websocketexample/telnet_line_example.js @@ -0,0 +1,58 @@ +const taos = require("@tdengine/websocket"); + +var host = null; +for(var i = 2; i < global.process.argv.length; i++){ + var key = global.process.argv[i].split("=")[0]; + var value = global.process.argv[i].split("=")[1]; + if("host" == key){ + host = value; + } +} + +if(host == null){ + console.log("Usage: node nodejsChecker.js host= port="); + process.exit(0); + } + + let dbData = ["meters.current 1648432611249 10.3 location=California.SanFrancisco groupid=2", + "meters.current 1648432611250 12.6 location=California.SanFrancisco groupid=2", + "meters.current 1648432611249 10.8 location=California.LosAngeles groupid=3", + "meters.current 1648432611250 11.3 location=California.LosAngeles groupid=3", + "meters.voltage 1648432611249 219 location=California.SanFrancisco groupid=2", + "meters.voltage 1648432611250 218 location=California.SanFrancisco groupid=2", + "meters.voltage 1648432611249 221 location=California.LosAngeles groupid=3", + "meters.voltage 1648432611250 217 location=California.LosAngeles groupid=3",]; + +async function createConnect() { + let dsn = 'ws://' + host + ':6041' + let conf = new taos.WSConfig(dsn); + conf.setUser('root'); + conf.setPwd('taosdata'); + + return await taos.sqlConnect(conf); +} + +async function test() { + let wsSql = null; + let wsRows = null; + let reqId = 0; + try { + wsSql = await createConnect() + await wsSql.exec('create database if not exists power KEEP 3650 DURATION 10 BUFFER 16 WAL_LEVEL 1;', reqId++); + await wsSql.exec('use power', reqId++); + await wsSql.schemalessInsert(dbData, taos.SchemalessProto.OpenTSDBTelnetLineProtocol, taos.Precision.MILLI_SECONDS, 0); + } + catch (err) { + console.error(err.code, err.message); + } + finally { + if (wsRows) { + await wsRows.close(); + } + if (wsSql) { + await wsSql.close(); + } + taos.destroy(); + } +} +test() \ No newline at end of file diff --git a/docs/examples/node/websocketexample/tmq_example.js b/docs/examples/node/websocketexample/tmq_example.js new file mode 100644 index 0000000000..9b77abe4d0 --- /dev/null +++ b/docs/examples/node/websocketexample/tmq_example.js @@ -0,0 +1,90 @@ +const taos = require("@tdengine/websocket"); + +const db = 'power'; +const stable = 'meters'; +const topics = ['power_meters_topic']; + +// ANCHOR: create_consumer +async function createConsumer() { + let configMap = new Map([ + [taos.TMQConstants.GROUP_ID, "gId"], + [taos.TMQConstants.CONNECT_USER, "root"], + [taos.TMQConstants.CONNECT_PASS, "taosdata"], + [taos.TMQConstants.AUTO_OFFSET_RESET, "latest"], + [taos.TMQConstants.CLIENT_ID, 'test_tmq_client'], + [taos.TMQConstants.WS_URL, 'ws://localhost:6041'], + [taos.TMQConstants.ENABLE_AUTO_COMMIT, 'true'], + [taos.TMQConstants.AUTO_COMMIT_INTERVAL_MS, '1000'] + ]); + return await taos.tmqConnect(configMap); +} +// ANCHOR_END: create_consumer + +async function prepare() { + let conf = new taos.WSConfig('ws://localhost:6041'); + conf.setUser('root') + conf.setPwd('taosdata') + conf.setDb('power') + const createDB = `CREATE DATABASE IF NOT EXISTS POWER ${db} KEEP 3650 DURATION 10 BUFFER 16 WAL_LEVEL 1;`; + const createStable = `CREATE STABLE IF NOT EXISTS ${db}.${stable} (ts timestamp, current float, voltage int, phase float) TAGS (location binary(64), groupId int);`; + + let wsSql = await taos.sqlConnect(conf); + await wsSql.exec(createDB); + await wsSql.exec(createStable); + +// ANCHOR: create_topic +let createTopic = `CREATE TOPIC IF NOT EXISTS ${topics[0]} AS SELECT * FROM ${db}.${stable}`; +await wsSql.exec(createTopic); +// ANCHOR_END: create_topic + + for (let i = 0; i < 10; i++) { + await wsSql.exec(`INSERT INTO d1001 USING ${stable} (location, groupId) TAGS ("California.SanFrancisco", 3) VALUES (NOW, ${10 + i}, ${200 + i}, ${0.32 + i})`); + } + wsSql.Close(); +} + +// ANCHOR: subscribe +async function subscribe(consumer) { + await consumer.subscribe(topics); + for (let i = 0; i < 5; i++) { + let res = await consumer.poll(500); + for (let [key, value] of res) { + console.log(key, value); + } + if (res.size == 0) { + break; + } + await consumer.commit(); + } +} +// ANCHOR_END: subscribe + +async function test() { + let consumer = null; + try { + await prepare(); + let consumer = await createConsumer() + await subscribe(consumer) + // ANCHOR: assignment + let assignment = await consumer.assignment(); + console.log(assignment); + + assignment = await consumer.seekToBeginning(assignment); + for(let i in assignment) { + console.log("seek after:", assignment[i]) + } + // ANCHOR_END: assignment + await consumer.unsubscribe(); + } + catch (err) { + console.error(err.code, err.message); + } + finally { + if (consumer) { + await consumer.close(); + } + taos.destroy(); + } +} + +test() diff --git a/docs/examples/python/connect_native_reference.py b/docs/examples/python/connect_native_reference.py index 09b0685ace..37f98ab472 100644 --- a/docs/examples/python/connect_native_reference.py +++ b/docs/examples/python/connect_native_reference.py @@ -1,12 +1,14 @@ import taos -conn: taos.TaosConnection = taos.connect(host="localhost", - user="root", - password="taosdata", - database="test", - port=6030, - config="/etc/taos", # for windows the default value is C:\TDengine\cfg - timezone="Asia/Shanghai") # default your host's timezone +conn = taos.connect( + host="localhost", + user="root", + password="taosdata", + database="test", + port=6030, + config="/etc/taos", # for windows the default value is C:\TDengine\cfg + timezone="Asia/Shanghai", +) # default your host's timezone server_version = conn.server_info print("server_version", server_version) diff --git a/docs/examples/python/create_db_native.py b/docs/examples/python/create_db_native.py new file mode 100644 index 0000000000..6eeb94b879 --- /dev/null +++ b/docs/examples/python/create_db_native.py @@ -0,0 +1,26 @@ +import taos + +conn = taos.connect( + host="localhost", + user="root", + password="taosdata", + port=6030, +) + +db = "power" + +conn.execute(f"DROP DATABASE IF EXISTS {db}") +conn.execute(f"CREATE DATABASE {db}") + +# change database. same as execute "USE db" +conn.select_db(db) + +# create super table +conn.execute( + "CREATE TABLE `meters` (`ts` TIMESTAMP, `current` FLOAT, `voltage` INT, `phase` FLOAT) TAGS (`groupid` INT, `location` BINARY(16))" +) + +# create table +conn.execute("CREATE TABLE `d0` USING `meters` TAGS(0, 'Los Angles')") + +conn.close() diff --git a/docs/examples/python/create_db_rest.py b/docs/examples/python/create_db_rest.py new file mode 100644 index 0000000000..fc262b30f8 --- /dev/null +++ b/docs/examples/python/create_db_rest.py @@ -0,0 +1,18 @@ +import taosrest + +conn = taosrest.connect(url="http://localhost:6041") + +db = "power" + +conn.execute(f"DROP DATABASE IF EXISTS {db}") +conn.execute(f"CREATE DATABASE {db}") + +# create super table +conn.execute( + f"CREATE TABLE `{db}`.`meters` (`ts` TIMESTAMP, `current` FLOAT, `voltage` INT, `phase` FLOAT) TAGS (`groupid` INT, `location` BINARY(16))" +) + +# create table +conn.execute(f"CREATE TABLE `{db}`.`d0` USING `{db}`.`meters` TAGS(0, 'Los Angles')") + +conn.close() diff --git a/docs/examples/python/create_db_ws.py b/docs/examples/python/create_db_ws.py new file mode 100644 index 0000000000..844f7a56e3 --- /dev/null +++ b/docs/examples/python/create_db_ws.py @@ -0,0 +1,22 @@ +import taosws + +dsn = "taosws://root:taosdata@localhost:6041" +conn = taosws.connect(dsn) + +db = "power" + +conn.execute(f"DROP DATABASE IF EXISTS {db}") +conn.execute(f"CREATE DATABASE {db}") + +# change database. +conn.execute(f"USE {db}") + +# create super table +conn.execute( + "CREATE TABLE `meters` (`ts` TIMESTAMP, `current` FLOAT, `voltage` INT, `phase` FLOAT) TAGS (`groupid` INT, `location` BINARY(16))" +) + +# create table +conn.execute("CREATE TABLE `d0` USING `meters` TAGS(0, 'Los Angles')") + +conn.close() diff --git a/docs/examples/python/insert_native.py b/docs/examples/python/insert_native.py new file mode 100644 index 0000000000..37bbb807ec --- /dev/null +++ b/docs/examples/python/insert_native.py @@ -0,0 +1,76 @@ +import taos + +conn = taos.connect( + host="localhost", + user="root", + password="taosdata", + port=6030, +) + +db = "power" + +conn.execute(f"DROP DATABASE IF EXISTS {db}") +conn.execute(f"CREATE DATABASE {db}") + +# change database. same as execute "USE db" +conn.select_db(db) + +# create super table +conn.execute( + "CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)" +) + +# ANCHOR: insert +# insert data +sql = """ +INSERT INTO +power.d1001 USING power.meters TAGS('California.SanFrancisco', 2) + VALUES ('2018-10-03 14:38:05.000', 10.30000, 219, 0.31000) + ('2018-10-03 14:38:15.000', 12.60000, 218, 0.33000) ('2018-10-03 14:38:16.800', 12.30000, 221, 0.31000) +power.d1002 USING power.meters TAGS('California.SanFrancisco', 3) + VALUES ('2018-10-03 14:38:16.650', 10.30000, 218, 0.25000) +power.d1003 USING power.meters TAGS('California.LosAngeles', 2) + VALUES ('2018-10-03 14:38:05.500', 11.80000, 221, 0.28000) ('2018-10-03 14:38:16.600', 13.40000, 223, 0.29000) +power.d1004 USING power.meters TAGS('California.LosAngeles', 3) + VALUES ('2018-10-03 14:38:05.000', 10.80000, 223, 0.29000) ('2018-10-03 14:38:06.500', 11.50000, 221, 0.35000) +""" + +inserted = conn.execute(sql) +assert inserted == 8 +# ANCHOR_END: insert + +# ANCHOR: query +# Execute a sql and get its result set. It's useful for SELECT statement +result = conn.query("SELECT * from meters") + +# Get fields from result +fields = result.fields +for field in fields: + print(field) + +""" +output: +{name: ts, type: 9, bytes: 8} +{name: current, type: 6, bytes: 4} +{name: voltage, type: 4, bytes: 4} +{name: phase, type: 6, bytes: 4} +{name: location, type: 8, bytes: 64} +{name: groupid, type: 4, bytes: 4} +""" + +# Get data from result as list of tuple +data = result.fetch_all() +for row in data: + print(row) + +""" +output: +(datetime.datetime(2018, 10, 3, 14, 38, 16, 650000), 10.300000190734863, 218, 0.25, 'California.SanFrancisco', 3) +... +""" +# ANCHOR_END: query + +# ANCHOR: req_id +result = conn.query("SELECT * from meters", req_id=1) +# ANCHOR_END: req_id +conn.close() diff --git a/docs/examples/python/insert_rest.py b/docs/examples/python/insert_rest.py new file mode 100644 index 0000000000..16b8fe669c --- /dev/null +++ b/docs/examples/python/insert_rest.py @@ -0,0 +1,48 @@ +import taosrest + +conn = taosrest.connect(url="http://localhost:6041") + +db = "power" + +conn.execute(f"DROP DATABASE IF EXISTS {db}") +conn.execute(f"CREATE DATABASE {db}") + +# create super table +conn.execute( + "CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)" +) + +# ANCHOR: insert +# rest insert data +sql = """ +INSERT INTO +power.d1001 USING power.meters TAGS('California.SanFrancisco', 2) + VALUES ('2018-10-03 14:38:05.000', 10.30000, 219, 0.31000) + ('2018-10-03 14:38:15.000', 12.60000, 218, 0.33000) ('2018-10-03 14:38:16.800', 12.30000, 221, 0.31000) +power.d1002 USING power.meters TAGS('California.SanFrancisco', 3) + VALUES ('2018-10-03 14:38:16.650', 10.30000, 218, 0.25000) +power.d1003 USING power.meters TAGS('California.LosAngeles', 2) + VALUES ('2018-10-03 14:38:05.500', 11.80000, 221, 0.28000) ('2018-10-03 14:38:16.600', 13.40000, 223, 0.29000) +power.d1004 USING power.meters TAGS('California.LosAngeles', 3) + VALUES ('2018-10-03 14:38:05.000', 10.80000, 223, 0.29000) ('2018-10-03 14:38:06.500', 11.50000, 221, 0.35000) +""" + +inserted = conn.execute(sql) +assert inserted == 8 +# ANCHOR_END: insert + +# ANCHOR: query +client = taosrest.RestClient("http://localhost:6041") +result = client.sql(f"SELECT * from {db}.meters") +print(result) + +""" +output: +{'code': 0, 'column_meta': [['ts', 'TIMESTAMP', 8], ['current', 'FLOAT', 4], ['voltage', 'INT', 4], ['phase', 'FLOAT', 4], ['location', 'VARCHAR', 64], ['groupid', 'INT', 4]], 'data': [[datetime.datetime(2018, 10, 3, 14, 38, 5), 10.3, 219, 0.31, 'California.SanFrancisco', 2], [datetime.datetime(2018, 10, 3, 14, 38, 15), 12.6, 218, 0.33, 'California.SanFrancisco', 2], [datetime.datetime(2018, 10, 3, 14, 38, 16, 800000), 12.3, 221, 0.31, 'California.SanFrancisco', 2], [datetime.datetime(2018, 10, 3, 14, 38, 16, 650000), 10.3, 218, 0.25, 'California.SanFrancisco', 3], [datetime.datetime(2018, 10, 3, 14, 38, 5, 500000), 11.8, 221, 0.28, 'California.LosAngeles', 2], [datetime.datetime(2018, 10, 3, 14, 38, 16, 600000), 13.4, 223, 0.29, 'California.LosAngeles', 2], [datetime.datetime(2018, 10, 3, 14, 38, 5), 10.8, 223, 0.29, 'California.LosAngeles', 3], [datetime.datetime(2018, 10, 3, 14, 38, 6, 500000), 11.5, 221, 0.35, 'California.LosAngeles', 3]], 'rows': 8} +""" +# ANCHOR_END: query + +# ANCHOR: req_id +result = client.sql(f"SELECT * from {db}.meters", req_id=1) +# ANCHOR_END: req_id +conn.close() diff --git a/docs/examples/python/insert_ws.py b/docs/examples/python/insert_ws.py new file mode 100644 index 0000000000..ef3ae3ea99 --- /dev/null +++ b/docs/examples/python/insert_ws.py @@ -0,0 +1,71 @@ +import taosws + +dsn = "taosws://root:taosdata@localhost:6041" +conn = taosws.connect(dsn) + +db = "power" + +conn.execute(f"DROP DATABASE IF EXISTS {db}") +conn.execute(f"CREATE DATABASE {db}") + +# change database. +conn.execute(f"USE {db}") + +# create super table +conn.execute( + "CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)" +) + +# ANCHOR: insert +# ws insert data +sql = """ +INSERT INTO +power.d1001 USING power.meters TAGS('California.SanFrancisco', 2) + VALUES ('2018-10-03 14:38:05.000', 10.30000, 219, 0.31000) + ('2018-10-03 14:38:15.000', 12.60000, 218, 0.33000) ('2018-10-03 14:38:16.800', 12.30000, 221, 0.31000) +power.d1002 USING power.meters TAGS('California.SanFrancisco', 3) + VALUES ('2018-10-03 14:38:16.650', 10.30000, 218, 0.25000) +power.d1003 USING power.meters TAGS('California.LosAngeles', 2) + VALUES ('2018-10-03 14:38:05.500', 11.80000, 221, 0.28000) ('2018-10-03 14:38:16.600', 13.40000, 223, 0.29000) +power.d1004 USING power.meters TAGS('California.LosAngeles', 3) + VALUES ('2018-10-03 14:38:05.000', 10.80000, 223, 0.29000) ('2018-10-03 14:38:06.500', 11.50000, 221, 0.35000) +""" + +inserted = conn.execute(sql) +assert inserted == 8 +# ANCHOR_END: insert + +# ANCHOR: query +# Execute a sql and get its result set. It's useful for SELECT statement +result = conn.query("SELECT * from meters") + +# Get fields from result +fields = result.fields +for field in fields: + print(field) + +""" +output: +{name: ts, type: TIMESTAMP, bytes: 8} +{name: current, type: FLOAT, bytes: 4} +{name: voltage, type: INT, bytes: 4} +{name: phase, type: FLOAT, bytes: 4} +{name: location, type: BINARY, bytes: 64} +{name: groupid, type: INT, bytes: 4} +""" + +# Get rows from result +for row in result: + print(row) + +""" +output: +('2018-10-03 14:38:05 +08:00', 10.300000190734863, 219, 0.3100000023841858, 'California.SanFrancisco', 2) +... +""" +# ANCHOR_END: query + +# ANCHOR: req_id +result = conn.query_with_req_id("SELECT * from meters", req_id=1) +# ANCHOR_END: req_id +conn.close() diff --git a/docs/examples/python/schemaless_native.py b/docs/examples/python/schemaless_native.py new file mode 100644 index 0000000000..e166cf5af5 --- /dev/null +++ b/docs/examples/python/schemaless_native.py @@ -0,0 +1,36 @@ +import taos + +conn = taos.connect( + host="localhost", + user="root", + password="taosdata", + port=6030, +) + +db = "power" + +conn.execute(f"DROP DATABASE IF EXISTS {db}") +conn.execute(f"CREATE DATABASE {db}") + +# change database. same as execute "USE db" +conn.select_db(db) + +lineDemo = [ + "meters,groupid=2,location=California.SanFrancisco current=10.3000002f64,voltage=219i32,phase=0.31f64 1626006833639000000" +] +telnetDemo = ["stb0_0 1707095283260 4 host=host0 interface=eth0"] +jsonDemo = [ + '{"metric": "meter_current","timestamp": 1626846400,"value": 10.3, "tags": {"groupid": 2, "location": "California.SanFrancisco", "id": "d1001"}}' +] + +conn.schemaless_insert( + lineDemo, taos.SmlProtocol.LINE_PROTOCOL, taos.SmlPrecision.MILLI_SECONDS +) +conn.schemaless_insert( + telnetDemo, taos.SmlProtocol.TELNET_PROTOCOL, taos.SmlPrecision.MICRO_SECONDS +) +conn.schemaless_insert( + jsonDemo, taos.SmlProtocol.JSON_PROTOCOL, taos.SmlPrecision.MILLI_SECONDS +) + +conn.close() diff --git a/docs/examples/python/schemaless_ws.py b/docs/examples/python/schemaless_ws.py new file mode 100644 index 0000000000..9e091f02c9 --- /dev/null +++ b/docs/examples/python/schemaless_ws.py @@ -0,0 +1,46 @@ +import taosws + +dsn = "taosws://root:taosdata@localhost:6041" +conn = taosws.connect(dsn) + +db = "power" + +conn.execute(f"DROP DATABASE IF EXISTS {db}") +conn.execute(f"CREATE DATABASE {db}") + +# change database. +conn = taosws.connect(f"{dsn}/{db}") + +lineDemo = [ + "meters,groupid=2,location=California.SanFrancisco current=10.3000002f64,voltage=219i32,phase=0.31f64 1626006833639000000" +] +telnetDemo = ["stb0_0 1707095283260 4 host=host0 interface=eth0"] +jsonDemo = [ + '{"metric": "meter_current","timestamp": 1626846400,"value": 10.3, "tags": {"groupid": 2, "location": "California.SanFrancisco", "id": "d1001"}}' +] + +conn.schemaless_insert( + lines=lineDemo, + protocol=taosws.PySchemalessProtocol.Line, + precision=taosws.PySchemalessPrecision.Millisecond, + ttl=1, + req_id=1, +) + +conn.schemaless_insert( + lines=telnetDemo, + protocol=taosws.PySchemalessProtocol.Telnet, + precision=taosws.PySchemalessPrecision.Microsecond, + ttl=1, + req_id=2, +) + +conn.schemaless_insert( + lines=jsonDemo, + protocol=taosws.PySchemalessProtocol.Json, + precision=taosws.PySchemalessPrecision.Millisecond, + ttl=1, + req_id=3, +) + +conn.close() diff --git a/docs/examples/python/stmt_native.py b/docs/examples/python/stmt_native.py new file mode 100644 index 0000000000..0af9d15fca --- /dev/null +++ b/docs/examples/python/stmt_native.py @@ -0,0 +1,53 @@ +import taos + +conn = taos.connect( + host="localhost", + user="root", + password="taosdata", + port=6030, +) + +db = "power" + +conn.execute(f"DROP DATABASE IF EXISTS {db}") +conn.execute(f"CREATE DATABASE {db}") + +# change database. same as execute "USE db" +conn.select_db(db) + +# create super table +conn.execute( + "CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)" +) + +# ANCHOR: stmt +sql = "INSERT INTO ? USING meters TAGS(?,?) VALUES (?,?,?,?)" +stmt = conn.statement(sql) + +tbname = "power.d1001" + +tags = taos.new_bind_params(2) +tags[0].binary(["California.SanFrancisco"]) +tags[1].int([2]) + +stmt.set_tbname_tags(tbname, tags) + +params = taos.new_bind_params(4) +params[0].timestamp((1626861392589, 1626861392591, 1626861392592)) +params[1].float((10.3, 12.6, 12.3)) +params[2].int([194, 200, 201]) +params[3].float([0.31, 0.33, 0.31]) + +stmt.bind_param_batch(params) + +stmt.execute() + +stmt.close() +# ANCHOR_END: stmt + +result = conn.query("SELECT * from meters") + +for row in result.fetch_all(): + print(row) + +conn.close() diff --git a/docs/examples/python/stmt_ws.py b/docs/examples/python/stmt_ws.py new file mode 100644 index 0000000000..3900f71bfb --- /dev/null +++ b/docs/examples/python/stmt_ws.py @@ -0,0 +1,52 @@ +import taosws + +dsn = "taosws://root:taosdata@localhost:6041" +conn = taosws.connect(dsn) + +db = "power" + +conn.execute(f"DROP DATABASE IF EXISTS {db}") +conn.execute(f"CREATE DATABASE {db}") + +# change database. +conn.execute(f"USE {db}") + +# create super table +conn.execute( + "CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)" +) + +# ANCHOR: stmt +sql = "INSERT INTO ? USING meters TAGS(?,?) VALUES (?,?,?,?)" +stmt = conn.statement() +stmt.prepare(sql) + +tbname = "power.d1001" + +tags = [ + taosws.varchar_to_tag("California.SanFrancisco"), + taosws.int_to_tag(2), +] + +stmt.set_tbname_tags(tbname, tags) + +stmt.bind_param( + [ + taosws.millis_timestamps_to_column( + [1626861392589, 1626861392591, 1626861392592] + ), + taosws.floats_to_column([10.3, 12.6, 12.3]), + taosws.ints_to_column([194, 200, 201]), + taosws.floats_to_column([0.31, 0.33, 0.31]), + ] +) + +stmt.add_batch() +rows = stmt.execute() + +assert rows == 3 + +stmt.close() +# ANCHOR_END: stmt + +conn.close() diff --git a/docs/examples/python/tmq_native.py b/docs/examples/python/tmq_native.py new file mode 100644 index 0000000000..7759c7b8e9 --- /dev/null +++ b/docs/examples/python/tmq_native.py @@ -0,0 +1,84 @@ +import taos + +conn = taos.connect( + host="localhost", + user="root", + password="taosdata", + port=6030, +) + +db = "power" +topic = "topic_meters" + +conn.execute(f"DROP TOPIC IF EXISTS {topic}") +conn.execute(f"DROP DATABASE IF EXISTS {db}") +conn.execute(f"CREATE DATABASE {db}") + +# change database. same as execute "USE db" +conn.select_db(db) + +# create super table +conn.execute( + "CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)" +) + +# ANCHOR: create_topic +# create topic +conn.execute( + f"CREATE TOPIC IF NOT EXISTS {topic} AS SELECT ts, current, voltage, phase, groupid, location FROM meters" +) +# ANCHOR_END: create_topic + +# ANCHOR: create_consumer +from taos.tmq import Consumer + +consumer = Consumer( + { + "group.id": "1", + "td.connect.user": "root", + "td.connect.pass": "taosdata", + "enable.auto.commit": "true", + } +) +# ANCHOR_END: create_consumer + +# ANCHOR: subscribe +consumer.subscribe([topic]) +# ANCHOR_END: subscribe + +try: + # ANCHOR: consume + while True: + res = consumer.poll(1) + if not res: + break + err = res.error() + if err is not None: + raise err + val = res.value() + + for block in val: + print(block.fetchall()) + # ANCHOR_END: consume + + # ANCHOR: assignment + assignments = consumer.assignment() + for assignment in assignments: + print(assignment) + # ANCHOR_END: assignment + + # ANCHOR: seek + offset = taos.tmq.TopicPartition( + topic=topic, + partition=assignment.partition, + offset=0, + ) + consumer.seek(offset) + # ANCHOR_END: seek +finally: + # ANCHOR: unsubscribe + consumer.unsubscribe() + consumer.close() + # ANCHOR_END: unsubscribe + +conn.close() diff --git a/docs/examples/rust/nativeexample/Cargo.toml b/docs/examples/rust/nativeexample/Cargo.toml index 5ecc407854..98c90eb7af 100644 --- a/docs/examples/rust/nativeexample/Cargo.toml +++ b/docs/examples/rust/nativeexample/Cargo.toml @@ -9,5 +9,7 @@ anyhow = "1" chrono = "0.4" serde = { version = "1", features = ["derive"] } tokio = { version = "1", features = ["rt", "macros", "rt-multi-thread"] } +log = "0.4" +pretty_env_logger = "0.5.0" -taos = { version = "0.4.8" } +taos = { version = "0.11.8" } \ No newline at end of file diff --git a/docs/zh/05-get-started/01-docker.md b/docs/zh/05-get-started/01-docker.md index efc21d3296..5219912d80 100644 --- a/docs/zh/05-get-started/01-docker.md +++ b/docs/zh/05-get-started/01-docker.md @@ -23,7 +23,7 @@ docker pull tdengine/tdengine:3.0.1.4 然后只需执行下面的命令: ```shell -docker run -d -p 6030:6030 -p 6041:6041 -p 6043-6049:6043-6049 -p 6043-6049:6043-6049/udp tdengine/tdengine +docker run -d -p 6030:6030 -p 6041:6041 -p 6043-6060:6043-6060 -p 6043-6060:6043-6060/udp tdengine/tdengine ``` 注意:TDengine 3.0 服务端仅使用 6030 TCP 端口。6041 为 taosAdapter 所使用提供 REST 服务端口。6043-6049 为 taosAdapter 提供第三方应用接入所使用端口,可根据需要选择是否打开。 @@ -33,7 +33,7 @@ docker run -d -p 6030:6030 -p 6041:6041 -p 6043-6049:6043-6049 -p 6043-6049:6043 ```shell docker run -d -v ~/data/taos/dnode/data:/var/lib/taos \ -v ~/data/taos/dnode/log:/var/log/taos \ - -p 6030:6030 -p 6041:6041 -p 6043-6049:6043-6049 -p 6043-6049:6043-6049/udp tdengine/tdengine + -p 6030:6030 -p 6041:6041 -p 6043-6060:6043-6060 -p 6043-6060:6043-6060/udp tdengine/tdengine ``` :::note @@ -59,7 +59,7 @@ docker exec -it bash 注:Docker 工具自身的下载和使用请参考 [Docker 官网文档](https://docs.docker.com/get-docker/)。 -## 运行 TDengine CLI +## TDengine 命令行界面 进入容器,执行 `taos`: @@ -69,7 +69,13 @@ $ taos taos> ``` -## 使用 taosBenchmark 体验写入速度 +## TDengine 图形化界面 + +从 TDengine 3.3.0.0 开始,TDengine docker image 中增加了一个新的 web 组件:taos-explorer, 可以使用它进行数据库、超级表、子表、数据的查看和管理。其上也有一些只在企业版中才提供的高级特性,如需要可联系 TDengine 销售团队。 + +使用 taos-explorer,需要从浏览器访问其映射在主机上的端口,假定主机名为 abc.com,映射到主机的端口为 6060,则需从浏览器访问 http://abc.com:6060. taos-explorer 默认在容器内使用 6060 端口。初次使用需要使用企业邮箱进行注册,注册后即可使用数据库中的用户名和密码登录。 + +## 体验写入速度 可以使用 TDengine 的自带工具 taosBenchmark 快速体验 TDengine 的写入速度。 @@ -85,7 +91,7 @@ $ taosBenchmark taosBenchmark 命令本身带有很多选项,配置表的数目、记录条数等等,您可以设置不同参数进行体验,请执行 `taosBenchmark --help` 详细列出。taosBenchmark 详细使用方法请参照[如何使用 taosBenchmark 对 TDengine 进行性能测试](https://www.taosdata.com/2021/10/09/3111.html)和 [taosBenchmark 参考手册](../../reference/taosbenchmark)。 -## 使用 TDengine CLI 体验查询速度 +## 体验查询速度 使用上述 `taosBenchmark` 插入数据后,可以在 TDengine CLI(taos)输入查询命令,体验查询速度。 diff --git a/docs/zh/05-get-started/03-package.md b/docs/zh/05-get-started/03-package.md index 621effa6fd..b25e148444 100644 --- a/docs/zh/05-get-started/03-package.md +++ b/docs/zh/05-get-started/03-package.md @@ -32,6 +32,10 @@ gcc 版本 - 9.3.1或以上; ## 安装 +**注意** + +从TDengine 3.0.6.0 开始,不再提供单独的 taosTools 安装包,原 taosTools 安装包中包含的工具都在 TDengine-server 安装包中,如果需要请直接下载 TDengine -server 安装包。 + @@ -119,11 +123,17 @@ apt-get 方式只适用于 Debian 或 Ubuntu 系统。 -注意:目前 TDengine 在 Windows 平台上只支持 Windows Server 2016/2019 和 Windows 10/11。 +**注意** +- 目前 TDengine 在 Windows 平台上只支持 Windows Server 2016/2019 和 Windows 10/11。 +- 从 TDengine 3.1.0.0 开始,只提供 Windows 客户端安装包。如果需要 Windows 服务端安装包,请联系 TDengine 销售团队升级为企业版。 +- Windows 上需要安装 VC 运行时库,可在此下载安装 [VC运行时库](https://learn.microsoft.com/zh-cn/cpp/windows/latest-supported-vc-redist?view=msvc-170), 如果已经安装此运行库可忽略。 + +按照以下步骤安装: 1. 从列表中下载获得 exe 安装程序; 2. 运行可执行程序来安装 TDengine。 +Note: 从 3.0.1.7 开始,只提供 TDengine 客户端的 Windows 客户端的下载。想要使用TDengine 服务端的 Windows 版本,请联系销售升级为企业版本。 @@ -153,38 +163,25 @@ apt-get 方式只适用于 Debian 或 Ubuntu 系统。 ```bash systemctl start taosd +systemctl start taosadapter +systemctl start taoskeeper +systemctl start taos-explorer ``` -检查服务是否正常工作: +你也可以直接运行 start-all.sh 脚本来启动上面的所有服务 +```bash +start-all.sh +``` + +可以使用 systemctl 来单独管理上面的每一个服务 ```bash +systemctl start taosd +systemctl stop taosd +systemctl restart taosd systemctl status taosd ``` -如果服务进程处于活动状态,则 status 指令会显示如下的相关信息: - -``` -Active: active (running) -``` - -如果后台服务进程处于停止状态,则 status 指令会显示如下的相关信息: - -``` -Active: inactive (dead) -``` - -如果 TDengine 服务正常工作,那么您可以通过 TDengine 的命令行程序 `taos` 来访问并体验 TDengine。 - -如下 `systemctl` 命令可以帮助你管理 TDengine 服务: - -- 启动服务进程:`systemctl start taosd` - -- 停止服务进程:`systemctl stop taosd` - -- 重启服务进程:`systemctl restart taosd` - -- 查看服务状态:`systemctl status taosd` - :::info - `systemctl` 命令需要 _root_ 权限来运行,如果您非 _root_ 用户,请在命令前添加 `sudo`。 @@ -193,35 +190,39 @@ Active: inactive (dead) ::: -**TDengine 命令行(CLI)** - -为便于检查 TDengine 的状态,执行数据库(Database)的各种即席(Ad Hoc)查询,TDengine 提供一命令行应用程序(以下简称为 TDengine CLI)taos。要进入 TDengine 命令行,您只要在终端执行 `taos` 即可。 - 安装后,可以在拥有管理员权限的 cmd 窗口执行 `sc start taosd` 或在 `C:\TDengine` 目录下,运行 `taosd.exe` 来启动 TDengine 服务进程。如需使用 http/REST 服务,请执行 `sc start taosadapter` 或运行 `taosadapter.exe` 来启动 taosAdapter 服务进程。 -**TDengine 命令行(CLI)** - -为便于检查 TDengine 的状态,执行数据库(Database)的各种即席(Ad Hoc)查询,TDengine 提供一命令行应用程序(以下简称为 TDengine CLI)taos。要进入 TDengine 命令行,您只要在终端执行 `taos` 即可。 - -安装后,在应用程序目录下,双击 TDengine 图标来启动程序,也可以运行 `sudo launchctl start com.tdengine.taosd` 来启动 TDengine 服务进程。 +安装后,在应用程序目录下,双击 TDengine 图标来启动程序,也可以运行 `sudo launchctl start ` 来启动 TDengine 服务进程。 -如下 `launchctl` 命令用于管理 TDengine 服务: -- 启动服务进程:`sudo launchctl start com.tdengine.taosd` +```bash +sudo launchctl start com.tdengine.taosd +sudo launchctl start com.tdengine.taosadapter +sudo launchctl start com.tdengine.taoskeeper +sudo launchctl start com.tdengine.taos-explorer +``` -- 停止服务进程:`sudo launchctl stop com.tdengine.taosd` +你也可以直接运行 start-all.sh 脚本来启动上面的所有服务 +```bash +start-all.sh +``` -- 查看服务状态:`sudo launchctl list | grep taosd` +可以使用 `launchctl` 命令管理上面提到的每个 TDengine 服务,以下示例使用 `taosd` : -- 查看服务详细信息:`launchctl print system/com.tdengine.taosd` +```bash +sudo launchctl start com.tdengine.taosd +sudo launchctl stop com.tdengine.taosd +sudo launchctl list | grep taosd +sudo launchctl print system/com.tdengine.taosd +``` :::info @@ -232,18 +233,13 @@ Active: inactive (dead) ::: -**TDengine 命令行(CLI)** - -为便于检查 TDengine 的状态,执行数据库(Database)的各种即席(Ad Hoc)查询,TDengine 提供一命令行应用程序(以下简称为 TDengine CLI)taos。要进入 TDengine 命令行,您只要在 Windows 终端的 C:\TDengine 目录下,运行 taos.exe 来启动 TDengine 命令行。 - -```bash -taos -``` -如果连接服务成功,将会打印出欢迎消息和版本信息。如果失败,则会打印错误消息出来(请参考 [FAQ](/train-faq/faq) 来解决终端连接服务端失败的问题)。TDengine CLI 的提示符号如下: +## TDengine 命令行(CLI) + +为便于检查 TDengine 的状态,执行数据库(Database)的各种即席(Ad Hoc)查询,TDengine 提供一命令行应用程序(以下简称为 TDengine CLI)taos。要进入 TDengine 命令行,您只要在终端执行 `taos` (Linux/Mac) 或 `taos.exe` (Windows) 即可。 TDengine CLI 的提示符号如下: ```cmd taos> @@ -269,6 +265,12 @@ Query OK, 2 row(s) in set (0.003128s) 除执行 SQL 语句外,系统管理员还可以从 TDengine CLI 进行检查系统运行状态、添加删除用户账号等操作。TDengine CLI 连同应用驱动也可以独立安装在机器上运行,更多细节请参考 [TDengine 命令行](../../reference/taos-shell/)。 +## TDengine 图形化界面 + +从 TDengine 3.3.0.0 开始,TDengine 发布包中增加了一个新的 web 组件:taos-explorer, 可以使用它进行数据库、超级表、子表、数据的查看和管理。其上也有一些只在企业版中才提供的高级特性,如需要可联系 TDengine 销售团队。 + +使用 taos-explorer,需要从浏览器访问其映射在主机上的端口,假定主机名为 abc.com,映射到主机的端口为 6060,则需从浏览器访问 http://abc.com:6060. taos-explorer 默认在容器内使用 6060 端口。初次使用需要使用企业邮箱进行注册,注册后即可使用数据库中的用户名和密码登录 + ## 使用 taosBenchmark 体验写入速度 可以使用 TDengine 的自带工具 taosBenchmark 快速体验 TDengine 的写入速度。 diff --git a/docs/zh/07-develop/01-connect/_connect_rust.mdx b/docs/zh/07-develop/01-connect/_connect_rust.mdx index 25f178a285..0e65e8f920 100644 --- a/docs/zh/07-develop/01-connect/_connect_rust.mdx +++ b/docs/zh/07-develop/01-connect/_connect_rust.mdx @@ -1,8 +1,8 @@ -```rust title="原生连接/REST 连接" +```rust title="原生连接" {{#include docs/examples/rust/nativeexample/examples/connect.rs}} ``` :::note -对于 Rust 连接器, 连接方式的不同只体现在使用的特性不同。如果启用了 "rest" 特性,那么只有 RESTful 的实现会被编译进来。 +对于 Rust 连接器, 连接方式的不同只体现在使用的特性不同。如果启用了 "ws" 特性,那么只有 Websocket 的实现会被编译进来。 ::: diff --git a/docs/zh/07-develop/01-connect/connection-type-zh.webp b/docs/zh/07-develop/01-connect/connection-type-zh.webp new file mode 100644 index 0000000000000000000000000000000000000000..1821748d59acd886fc1b58b4a0110f60ad2f58b8 GIT binary patch literal 13774 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 -编辑 `Cargo.toml` 添加 `libtaos` 依赖即可。 +编辑 `Cargo.toml` 添加 `taos` 依赖即可。 ```toml title=Cargo.toml [dependencies] -libtaos = { version = "0.4.2"} +taos = { version = "*"} ``` :::info -Rust 连接器通过不同的特性区分不同的连接方式。如果要建立 REST 连接,需要开启 `rest` 特性: +Rust 连接器通过不同的特性区分不同的连接方式。默认同时支持原生连接和 Websocket 连接,如果仅需要建立 Websocket 连接,可设置 `ws` 特性: ```toml -libtaos = { version = "*", features = ["rest"] } +taos = { version = "*", default-features = false, features = ["ws"] } ``` ::: diff --git a/docs/zh/07-develop/02-model/index.mdx b/docs/zh/07-develop/02-model/index.mdx index ca2afdf664..cc070e2e5f 100644 --- a/docs/zh/07-develop/02-model/index.mdx +++ b/docs/zh/07-develop/02-model/index.mdx @@ -16,7 +16,7 @@ TDengine 采用类关系型数据模型,需要建库、建表。因此对于 CREATE DATABASE power KEEP 365 DURATION 10 BUFFER 16 WAL_LEVEL 1; ``` -上述语句将创建一个名为 power 的库,这个库的数据将保留 365 天(超过 365 天将被自动删除),每 10 天一个数据文件,每个 VNode 的写入内存池的大小为 16 MB,对该数据库入会写 WAL 但不执行 FSYNC。详细的语法及参数请见 [数据库管理](/taos-sql/database) 章节。 +上述语句将创建一个名为 power 的库,这个库的数据将保留 365 天(超过 365 天将被自动删除),每 10 天一个数据文件,每个 VNode 的写入内存池的大小为 16 MB,对该数据库入会写 WAL 但不执行 FSYNC。详细的语法及参数请见 [数据库管理](../../taos-sql/database) 章节。 创建库之后,需要使用 SQL 命令 `USE` 将当前库切换过来,例如: @@ -35,13 +35,13 @@ USE power; ## 创建超级表 -一个物联网系统,往往存在多种类型的设备,比如对于电网,存在智能电表、变压器、母线、开关等等。为便于多表之间的聚合,使用 TDengine, 需要对每个类型的数据采集点创建一个超级表。以 [表 1](/concept) 中的智能电表为例,可以使用如下的 SQL 命令创建超级表: +一个物联网系统,往往存在多种类型的设备,比如对于电网,存在智能电表、变压器、母线、开关等等。为便于多表之间的聚合,使用 TDengine, 需要对每个类型的数据采集点创建一个超级表。以 [表 1](../../concept) 中的智能电表为例,可以使用如下的 SQL 命令创建超级表: ```sql CREATE STABLE meters (ts timestamp, current float, voltage int, phase float) TAGS (location binary(64), groupId int); ``` -与创建普通表一样,创建超级表时,需要提供表名(示例中为 meters),表结构 Schema,即数据列的定义。第一列必须为时间戳(示例中为 ts),其他列为采集的物理量(示例中为 current, voltage, phase),数据类型可以为整型、浮点型、字符串等。除此之外,还需要提供标签的 Schema (示例中为 location, groupId),标签的数据类型可以为整型、浮点型、字符串等。采集点的静态属性往往可以作为标签,比如采集点的地理位置、设备型号、设备组 ID、管理员 ID 等等。标签的 Schema 可以事后增加、删除、修改。具体定义以及细节请见 [TDengine SQL 的超级表管理](/taos-sql/stable) 章节。 +与创建普通表一样,创建超级表时,需要提供表名(示例中为 meters),表结构 Schema,即数据列的定义。第一列必须为时间戳(示例中为 ts),其他列为采集的物理量(示例中为 current, voltage, phase),数据类型可以为整型、浮点型、字符串等。除此之外,还需要提供标签的 Schema (示例中为 location, groupId),标签的数据类型可以为整型、浮点型、字符串等。采集点的静态属性往往可以作为标签,比如采集点的地理位置、设备型号、设备组 ID、管理员 ID 等等。标签的 Schema 可以事后增加、删除、修改。具体定义以及细节请见 [TDengine SQL 的超级表管理](../../taos-sql/stable) 章节。 每一种类型的数据采集点需要建立一个超级表,因此一个物联网系统,往往会有多个超级表。对于电网,我们就需要对智能电表、变压器、母线、开关等都建立一个超级表。在物联网中,一个设备就可能有多个数据采集点(比如一台风力发电的风机,有的采集点采集电流、电压等电参数,有的采集点采集温度、湿度、风向等环境参数),这个时候,对这一类型的设备,需要建立多张超级表。 @@ -49,13 +49,13 @@ CREATE STABLE meters (ts timestamp, current float, voltage int, phase float) TAG ## 创建表 -TDengine 对每个数据采集点需要独立建表。与标准的关系型数据库一样,一张表有表名,Schema,但除此之外,还可以带有一到多个标签。创建时,需要使用超级表做模板,同时指定标签的具体值。以 [表 1](/concept) 中的智能电表为例,可以使用如下的 SQL 命令建表: +TDengine 对每个数据采集点需要独立建表。与标准的关系型数据库一样,一张表有表名,Schema,但除此之外,还可以带有一到多个标签。创建时,需要使用超级表做模板,同时指定标签的具体值。以 [表 1](../../concept) 中的智能电表为例,可以使用如下的 SQL 命令建表: ```sql CREATE TABLE d1001 USING meters TAGS ("California.SanFrancisco", 2); ``` -其中 d1001 是表名,meters 是超级表的表名,后面紧跟标签 Location 的具体标签值为 "California.SanFrancisco",标签 groupId 的具体标签值为 2。虽然在创建表时,需要指定标签值,但可以事后修改。详细细则请见 [TDengine SQL 的表管理](/taos-sql/table) 章节。 +其中 d1001 是表名,meters 是超级表的表名,后面紧跟标签 Location 的具体标签值为 "California.SanFrancisco",标签 groupId 的具体标签值为 2。虽然在创建表时,需要指定标签值,但可以事后修改。详细细则请见 [TDengine SQL 的表管理](../../taos-sql/table) 章节。 TDengine 建议将数据采集点的全局唯一 ID 作为表名(比如设备序列号)。但对于有的场景,并没有唯一的 ID,可以将多个 ID 组合成一个唯一的 ID。不建议将具有唯一性的 ID 作为标签值。 @@ -69,7 +69,7 @@ INSERT INTO d1001 USING meters TAGS ("California.SanFrancisco", 2) VALUES (NOW, 上述 SQL 语句将记录`(NOW, 10.2, 219, 0.32)`插入表 d1001。如果表 d1001 还未创建,则使用超级表 meters 做模板自动创建,同时打上标签值 `"California.SanFrancisco", 2`。 -关于自动建表的详细语法请参见 [插入记录时自动建表](/taos-sql/insert#插入记录时自动建表) 章节。 +关于自动建表的详细语法请参见 [插入记录时自动建表](../../taos-sql/insert#插入记录时自动建表) 章节。 ## 多列模型 vs 单列模型 diff --git a/docs/zh/07-develop/03-insert-data/01-sql-writing.mdx b/docs/zh/07-develop/03-insert-data/01-sql-writing.mdx index 89974ceb7b..49043495a8 100644 --- a/docs/zh/07-develop/03-insert-data/01-sql-writing.mdx +++ b/docs/zh/07-develop/03-insert-data/01-sql-writing.mdx @@ -33,7 +33,7 @@ import PhpStmt from "./_php_stmt.mdx"; INSERT INTO d1001 VALUES (ts1, 10.3, 219, 0.31); ``` -这里的`ts1`为Unix时间戳(Unix timestamp),允许插入的最老记录的时间戳,是相对于当前服务器时间,减去配置的 KEEP 值。时间戳详情规则参考 [TDengine SQL数据写入 关于时间戳一节](/taos-sql/insert) +这里的`ts1`为Unix时间戳(Unix timestamp),允许插入的最老记录的时间戳,是相对于当前服务器时间,减去配置的 KEEP 值。时间戳详情规则参考 [TDengine SQL数据写入 关于时间戳一节](../../../taos-sql/insert) ### 一次写入多条 @@ -43,7 +43,7 @@ TDengine 支持一次写入多条记录,比如下面这条命令就将两条 INSERT INTO d1001 VALUES (ts1, 10.2, 220, 0.23) (ts2, 10.3, 218, 0.25); ``` -这里的`ts1`和`ts2`为Unix时间戳(Unix timestamp),允许插入的最老记录的时间戳,是相对于当前服务器时间,减去配置的 KEEP 值。时间戳详情规则参考 [TDengine SQL数据写入 关于时间戳一节](/taos-sql/insert) +这里的`ts1`和`ts2`为Unix时间戳(Unix timestamp),允许插入的最老记录的时间戳,是相对于当前服务器时间,减去配置的 KEEP 值。时间戳详情规则参考 [TDengine SQL数据写入 关于时间戳一节](../../../taos-sql/insert) ### 一次写入多表 @@ -53,9 +53,9 @@ TDengine 也支持一次向多个表写入数据,比如下面这条命令就 INSERT INTO d1001 VALUES (ts1, 10.3, 219, 0.31) (ts2, 12.6, 218, 0.33) d1002 VALUES (ts3, 12.3, 221, 0.31); ``` -这里的`ts1`、`ts2`和`ts3`为Unix时间戳(Unix timestamp),允许插入的最老记录的时间戳,是相对于当前服务器时间,减去配置的 KEEP 值。时间戳详情规则参考 [TDengine SQL数据写入 关于时间戳一节](/taos-sql/insert) +这里的`ts1`、`ts2`和`ts3`为Unix时间戳(Unix timestamp),允许插入的最老记录的时间戳,是相对于当前服务器时间,减去配置的 KEEP 值。时间戳详情规则参考 [TDengine SQL数据写入 关于时间戳一节](../../../taos-sql/insert) -详细的 SQL INSERT 语法规则参考 [TDengine SQL 的数据写入](/taos-sql/insert)。 +详细的 SQL INSERT 语法规则参考 [TDengine SQL 的数据写入](../../../taos-sql/insert)。 :::info diff --git a/docs/zh/07-develop/03-insert-data/20-kafka-writting.mdx b/docs/zh/07-develop/03-insert-data/20-kafka-writting.mdx index 32d3c2e5cb..4bd68b7faa 100644 --- a/docs/zh/07-develop/03-insert-data/20-kafka-writting.mdx +++ b/docs/zh/07-develop/03-insert-data/20-kafka-writting.mdx @@ -35,7 +35,7 @@ bin/kafka-topics.sh --bootstrap-server=localhost:9092 --describe ## 写入 TDengine -TDengine 支持 Sql 方式和 Schemaless 方式的数据写入,Sql 方式数据写入可以参考 [TDengine SQL 写入](/develop/insert-data/sql-writing/) 和 [TDengine 高效写入](/develop/insert-data/high-volume/)。Schemaless 方式数据写入可以参考 [TDengine Schemaless 写入](/reference/schemaless/) 文档。 +TDengine 支持 Sql 方式和 Schemaless 方式的数据写入,Sql 方式数据写入可以参考 [TDengine SQL 写入](../sql-writing/) 和 [TDengine 高效写入](../high-volume/)。Schemaless 方式数据写入可以参考 [TDengine Schemaless 写入](../../../reference/schemaless/) 文档。 ## 示例代码 diff --git a/docs/zh/07-develop/03-insert-data/30-influxdb-line.mdx b/docs/zh/07-develop/03-insert-data/30-influxdb-line.mdx index ed84f05fef..4208cacb5d 100644 --- a/docs/zh/07-develop/03-insert-data/30-influxdb-line.mdx +++ b/docs/zh/07-develop/03-insert-data/30-influxdb-line.mdx @@ -37,15 +37,15 @@ meters,location=California.LosAngeles,groupid=2 current=13.4,voltage=223,phase=0 - tag_set 中的所有的数据自动转化为 NCHAR 数据类型 - field_set 中的每个数据项都需要对自身的数据类型进行描述, 比如 1.2f32 代表 FLOAT 类型的数值 1.2, 如果不带类型后缀会被当作 DOUBLE 处理 - timestamp 支持多种时间精度。写入数据的时候需要用参数指定时间精度,支持从小时到纳秒的 6 种时间精度 -- 为了提高写入的效率,默认假设同一个超级表中 field_set 的顺序是一样的(第一条数据包含所有的 field,后面的数据按照这个顺序),如果顺序不一样,需要配置参数 smlDataFormat 为 false,否则,数据写入按照相同顺序写入,库中数据会异常。(3.0.1.3 之后的版本 smlDataFormat 默认为 false,从3.0.3.0开始,该配置废弃) [TDengine 无模式写入参考指南](/reference/schemaless/#无模式写入行协议) +- 为了提高写入的效率,默认假设同一个超级表中 field_set 的顺序是一样的(第一条数据包含所有的 field,后面的数据按照这个顺序),如果顺序不一样,需要配置参数 smlDataFormat 为 false,否则,数据写入按照相同顺序写入,库中数据会异常。(3.0.1.3 之后的版本 smlDataFormat 默认为 false,从3.0.3.0开始,该配置废弃) [TDengine 无模式写入参考指南](../../../reference/schemaless/#无模式写入行协议) - 子表名生成规则 - 默认产生的子表名是根据规则生成的唯一 ID 值。 - 用户也可以通过在client端的 taos.cfg 里配置 smlAutoChildTableNameDelimiter 参数来指定连接标签之间的分隔符,连接起来后作为子表名。举例如下:配置 smlAutoChildTableNameDelimiter=-, 插入数据为 st,t0=cpu1,t1=4 c1=3 1626006833639000000 则创建的子表名为 cpu1-4。 - - 用户也可以通过在client端的 taos.cfg 里配置 smlChildTableName 参数来指定某个标签值作为子表名。该标签值应该具有全局唯一性。举例如下:假设有个标签名为tname, 配置 smlChildTableName=tname, 插入数据为 st,tname=cpu1,t1=4 c1=3 1626006833639000000 则创建的子表名为 cpu1。注意如果多行数据 tname 相同,但是后面的 tag_set 不同,则使用第一行自动建表时指定的 tag_set,其他的行会忽略)。[TDengine 无模式写入参考指南](/reference/schemaless/#无模式写入行协议) + - 用户也可以通过在client端的 taos.cfg 里配置 smlChildTableName 参数来指定某个标签值作为子表名。该标签值应该具有全局唯一性。举例如下:假设有个标签名为tname, 配置 smlChildTableName=tname, 插入数据为 st,tname=cpu1,t1=4 c1=3 1626006833639000000 则创建的子表名为 cpu1。注意如果多行数据 tname 相同,但是后面的 tag_set 不同,则使用第一行自动建表时指定的 tag_set,其他的行会忽略)。[TDengine 无模式写入参考指南](../../../reference/schemaless/#无模式写入行协议) ::: -要了解更多可参考:[InfluxDB Line 协议官方文档](https://docs.influxdata.com/influxdb/v2.0/reference/syntax/line-protocol/) 和 [TDengine 无模式写入参考指南](/reference/schemaless/#无模式写入行协议) +要了解更多可参考:[InfluxDB Line 协议官方文档](https://docs.influxdata.com/influxdb/v2.0/reference/syntax/line-protocol/) 和 [TDengine 无模式写入参考指南](../../../reference/schemaless/#无模式写入行协议) ## 示例代码 diff --git a/docs/zh/07-develop/03-insert-data/40-opentsdb-telnet.mdx b/docs/zh/07-develop/03-insert-data/40-opentsdb-telnet.mdx index 195f3a31f4..41b4069424 100644 --- a/docs/zh/07-develop/03-insert-data/40-opentsdb-telnet.mdx +++ b/docs/zh/07-develop/03-insert-data/40-opentsdb-telnet.mdx @@ -34,7 +34,7 @@ meters.current 1648432611250 11.3 location=California.LosAngeles groupid=3 - 子表名生成规则 - 默认产生的子表名是根据规则生成的唯一 ID 值。 - 用户也可以通过在client端的 taos.cfg 里配置 smlAutoChildTableNameDelimiter 参数来指定连接标签之间的分隔符,连接起来后作为子表名。举例如下:配置 smlAutoChildTableNameDelimiter=-, 插入数据为 st,t0=cpu1,t1=4 c1=3 1626006833639000000 则创建的子表名为 cpu1-4。 - - 用户也可以通过在client端的 taos.cfg 里配置 smlChildTableName 参数来指定某个标签值作为子表名。该标签值应该具有全局唯一性。举例如下:假设有个标签名为tname, 配置 smlChildTableName=tname, 插入数据为 st,tname=cpu1,t1=4 c1=3 1626006833639000000 则创建的子表名为 cpu1。注意如果多行数据 tname 相同,但是后面的 tag_set 不同,则使用第一行自动建表时指定的 tag_set,其他的行会忽略)。[TDengine 无模式写入参考指南](/reference/schemaless/#无模式写入行协议) + - 用户也可以通过在client端的 taos.cfg 里配置 smlChildTableName 参数来指定某个标签值作为子表名。该标签值应该具有全局唯一性。举例如下:假设有个标签名为tname, 配置 smlChildTableName=tname, 插入数据为 st,tname=cpu1,t1=4 c1=3 1626006833639000000 则创建的子表名为 cpu1。注意如果多行数据 tname 相同,但是后面的 tag_set 不同,则使用第一行自动建表时指定的 tag_set,其他的行会忽略)。[TDengine 无模式写入参考指南](../../../reference/schemaless/#无模式写入行协议) 参考 [OpenTSDB Telnet API 文档](http://opentsdb.net/docs/build/html/api_telnet/put.html)。 diff --git a/docs/zh/07-develop/03-insert-data/50-opentsdb-json.mdx b/docs/zh/07-develop/03-insert-data/50-opentsdb-json.mdx index ce8d5b0caa..f6375dc3af 100644 --- a/docs/zh/07-develop/03-insert-data/50-opentsdb-json.mdx +++ b/docs/zh/07-develop/03-insert-data/50-opentsdb-json.mdx @@ -50,7 +50,7 @@ OpenTSDB JSON 格式协议采用一个 JSON 字符串表示一行或多行数据 - 子表名生成规则 - 默认产生的子表名是根据规则生成的唯一 ID 值。 - 用户也可以通过在client端的 taos.cfg 里配置 smlAutoChildTableNameDelimiter 参数来指定连接标签之间的分隔符,连接起来后作为子表名。举例如下:配置 smlAutoChildTableNameDelimiter=-, 插入数据为 st,t0=cpu1,t1=4 c1=3 1626006833639000000 则创建的子表名为 cpu1-4。 - - 用户也可以通过在client端的 taos.cfg 里配置 smlChildTableName 参数来指定某个标签值作为子表名。该标签值应该具有全局唯一性。举例如下:假设有个标签名为tname, 配置 smlChildTableName=tname, 插入数据为 st,tname=cpu1,t1=4 c1=3 1626006833639000000 则创建的子表名为 cpu1。注意如果多行数据 tname 相同,但是后面的 tag_set 不同,则使用第一行自动建表时指定的 tag_set,其他的行会忽略)。[TDengine 无模式写入参考指南](/reference/schemaless/#无模式写入行协议) + - 用户也可以通过在client端的 taos.cfg 里配置 smlChildTableName 参数来指定某个标签值作为子表名。该标签值应该具有全局唯一性。举例如下:假设有个标签名为tname, 配置 smlChildTableName=tname, 插入数据为 st,tname=cpu1,t1=4 c1=3 1626006833639000000 则创建的子表名为 cpu1。注意如果多行数据 tname 相同,但是后面的 tag_set 不同,则使用第一行自动建表时指定的 tag_set,其他的行会忽略)。[TDengine 无模式写入参考指南](../../../reference/schemaless/#无模式写入行协议) ::: diff --git a/docs/zh/07-develop/04-query-data/index.mdx b/docs/zh/07-develop/04-query-data/index.mdx index e892f9fc8d..52fd70653e 100644 --- a/docs/zh/07-develop/04-query-data/index.mdx +++ b/docs/zh/07-develop/04-query-data/index.mdx @@ -23,7 +23,7 @@ import CAsync from "./_c_async.mdx"; TDengine 采用 SQL 作为查询语言。应用程序可以通过 REST API 或连接器发送 SQL 语句,用户还可以通过 TDengine 命令行工具 taos 手动执行 SQL 即席查询(Ad-Hoc Query)。TDengine 支持如下查询功能: - 单列、多列数据查询 -- 标签和数值的多种过滤条件:>, <, =, <\>, like 等 +- 标签和数值的多种过滤条件:>, \<, =, \<>, like 等 - 聚合结果的分组(Group by)、排序(Order by)、约束输出(Limit/Offset) - 时间窗口(Interval)、会话窗口(Session)和状态窗口(State_window)等窗口切分聚合查询 - 数值列及聚合结果的四则运算 @@ -128,7 +128,7 @@ Query OK, 6 rows in database (0.005515s) ### 查询数据 -在 [SQL 写入](/develop/insert-data/sql-writing) 一章,我们创建了 power 数据库,并向 meters 表写入了一些数据,以下示例代码展示如何查询这个表的数据。 +在 [SQL 写入](../insert-data/sql-writing) 一章,我们创建了 power 数据库,并向 meters 表写入了一些数据,以下示例代码展示如何查询这个表的数据。 @@ -160,7 +160,7 @@ Query OK, 6 rows in database (0.005515s) :::note 1. 无论是使用 REST 连接还是原生连接的连接器,以上示例代码都能正常工作。 -2. 唯一需要注意的是:由于 REST 接口无状态, 不能使用 `use db` 语句来切换数据库。除了在 REST 参数中指定数据库以外也可以在 SQL 语句中使用 . 来指定数据库。 +2. 唯一需要注意的是:由于 REST 接口无状态, 不能使用 `use db` 语句来切换数据库。除了在 REST 参数中指定数据库以外也可以在 SQL 语句中使用 \.\ 来指定数据库。 ::: diff --git a/docs/zh/07-develop/07-tmq.mdx b/docs/zh/07-develop/07-tmq.mdx index 881838f8f0..cf0d22050f 100644 --- a/docs/zh/07-develop/07-tmq.mdx +++ b/docs/zh/07-develop/07-tmq.mdx @@ -261,7 +261,7 @@ impl AsAsyncConsumer for Consumer async fn unsubscribe(self); ``` -可在 上查看详细 API 说明。 +可在 \ 上查看详细 API 说明。 @@ -848,7 +848,7 @@ consumer.Close(); 2023/09/22 00:00:00.000 2023/09/22 00:00:05.000 2023/09/22 00:00:08.000 - ``` + ``` 则订阅出第一条数据 5s 后返回第二条数据,获取第二条数据 3s 后返回第三条数据。 - 仅查询订阅支持数据回放 - 回放需要保证独立时间线 diff --git a/docs/zh/07-develop/09-udf.md b/docs/zh/07-develop/09-udf.md index f9d69130d7..aa2e3b3100 100644 --- a/docs/zh/07-develop/09-udf.md +++ b/docs/zh/07-develop/09-udf.md @@ -195,7 +195,7 @@ typedef struct SUdfInterBuf { ``` 数据结构说明如下: -- SUdfDataBlock 数据块包含行数 numOfRows 和列数 numCols。udfCols[i] (0 <= i <= numCols-1)表示每一列数据,类型为SUdfColumn*。 +- SUdfDataBlock 数据块包含行数 numOfRows 和列数 numCols。udfCols[i] (0 \<= i \<= numCols-1)表示每一列数据,类型为SUdfColumn*。 - SUdfColumn 包含列的数据类型定义 colMeta 和列的数据 colData。 - SUdfColumnMeta 成员定义同 taos.h 数据类型定义。 - SUdfColumnData 数据可以变长,varLenCol 定义变长数据,fixLenCol 定义定长数据。 @@ -396,7 +396,7 @@ def finish(buf: bytes) -> output_type: 1. 定义一个只接收一个整数的标量函数: 输入 n, 输出 ln(n^2 + 1)。 2. 定义一个接收 n 个整数的标量函数, 输入 (x1, x2, ..., xn), 输出每个值和它们的序号的乘积的和: x1 + 2 * x2 + ... + n * xn。 3. 定义一个标量函数,输入一个时间戳,输出距离这个时间最近的下一个周日。完成这个函数要用到第三方库 moment。我们在这个示例中讲解使用第三方库的注意事项。 -4. 定义一个聚合函数,计算某一列最大值和最小值的差, 也就是实现 TDengien 内置的 spread 函数。 +4. 定义一个聚合函数,计算某一列最大值和最小值的差, 也就是实现 TDengine 内置的 spread 函数。 同时也包含大量实用的 debug 技巧。 本文假设你用的是 Linux 系统,且已安装好了 TDengine 3.0.4.0+ 和 Python 3.7+。 diff --git a/docs/zh/07-develop/index.md b/docs/zh/07-develop/index.md index a3d1410ab6..e7578232c0 100644 --- a/docs/zh/07-develop/index.md +++ b/docs/zh/07-develop/index.md @@ -15,7 +15,7 @@ description: 让开发者能够快速上手的指南 7. 在很多场景下(如车辆管理),应用需要获取每个数据采集点的最新状态,那么建议你采用 TDengine 的 Cache 功能,而不用单独部署 Redis 等缓存软件。 8. 如果你发现 TDengine 的函数无法满足你的要求,那么你可以使用用户自定义函数(UDF)来解决问题。 -本部分内容就是按照上述顺序组织的。为便于理解,TDengine 为每个功能和每个支持的编程语言都提供了示例代码。如果你希望深入了解 SQL 的使用,需要查看[SQL 手册](/taos-sql/)。如果想更深入地了解各连接器的使用,请阅读[连接器参考指南](../connector/)。如果还希望想将 TDengine 与第三方系统集成起来,比如 Grafana, 请参考[第三方工具](../third-party/)。 +本部分内容就是按照上述顺序组织的。为便于理解,TDengine 为每个功能和每个支持的编程语言都提供了示例代码。如果你希望深入了解 SQL 的使用,需要查看[SQL 手册](../taos-sql/)。如果想更深入地了解各连接器的使用,请阅读[连接器参考指南](../connector/)。如果还希望想将 TDengine 与第三方系统集成起来,比如 Grafana, 请参考[第三方工具](../third-party/)。 如果在开发过程中遇到任何问题,请点击每个页面下方的["反馈问题"](https://github.com/taosdata/TDengine/issues/new/choose), 在 GitHub 上直接递交 Issue。 diff --git a/docs/zh/08-connector/02-rest-api.mdx b/docs/zh/08-connector/02-rest-api.mdx index 904b06cfe1..f97e1a836f 100644 --- a/docs/zh/08-connector/02-rest-api.mdx +++ b/docs/zh/08-connector/02-rest-api.mdx @@ -69,7 +69,7 @@ curl -L -H "Authorization: Basic cm9vdDp0YW9zZGF0YQ==" \ ## HTTP 请求格式 ```text -http://:/rest/sql/[db_name][?tz=timezone[&req_id=req_id]] +http://:/rest/sql/[db_name][?tz=timezone[&req_id=req_id][&row_with_meta=true]] ``` 参数说明: @@ -79,6 +79,7 @@ http://:/rest/sql/[db_name][?tz=timezone[&req_id=req_id]] - db_name: 可选参数,指定本次所执行的 SQL 语句的默认数据库库名。 - tz: 可选参数,指定返回时间的时区,遵照 IANA Time Zone 规则,如 `America/New_York`。 - req_id: 可选参数,指定请求 id,可以用于 tracing。 +- row_with_meta: 可选参数,指定是否每行数据都携带列名,缺省为 false。(3.3.2.0 版本开始支持) 例如:`http://h1.taos.com:6041/rest/sql/test` 是指向地址为 `h1.taos.com:6041` 的 URL,并将默认使用的数据库库名设置为 `test`。 @@ -101,13 +102,13 @@ HTTP 请求的 BODY 里就是一个完整的 SQL 语句,SQL 语句中的数据 使用 `curl` 通过自定义身份认证方式来发起一个 HTTP Request,语法如下: ```bash -curl -L -H "Authorization: Basic " -d "" :/rest/sql/[db_name][?tz=timezone[&req_id=req_id]] +curl -L -H "Authorization: Basic " -d "" :/rest/sql/[db_name][?tz=timezone[&req_id=req_id][&row_with_meta=true]] ``` 或者, ```bash -curl -L -u username:password -d "" :/rest/sql/[db_name][?tz=timezone[&req_id=req_id]] +curl -L -u username:password -d "" :/rest/sql/[db_name][?tz=timezone[&req_id=req_id][&row_with_meta=true]] ``` 其中,`TOKEN` 为 `{username}:{password}` 经过 Base64 编码之后的字符串,例如 `root:taosdata` 编码后为 `cm9vdDp0YW9zZGF0YQ==`。 @@ -331,6 +332,82 @@ curl --location 'http://:/rest/sql' \ - code:(`int`)错误码。 - desc:(`string`)错误描述。 +#### 返回key-value形式数据 + +当指定 url 参数 `row_with_meta=true` 时,返回的 data 中的数据会从数组的形式变成对象的形式,对象的 key 为列名,value 为数据,如下所示: + +插入数据返回示例 + +```json +{ + "code": 0, + "column_meta": [ + [ + "affected_rows", + "INT", + 4 + ] + ], + "data": [ + { + "affected_rows": 1 + } + ], + "rows": 1 +} +``` + +查询数据返回示例 + +```json +{ + "code": 0, + "column_meta": [ + [ + "ts", + "TIMESTAMP", + 8 + ], + [ + "current", + "FLOAT", + 4 + ], + [ + "voltage", + "INT", + 4 + ], + [ + "phase", + "FLOAT", + 4 + ], + [ + "groupid", + "INT", + 4 + ], + [ + "location", + "VARCHAR", + 24 + ] + ], + "data": [ + { + "ts": "2017-07-14T02:40:00.000Z", + "current": -2.498076, + "voltage": 0, + "phase": -0.846025, + "groupid": 8, + "location": "California.Sunnyvale" + } + ], + "rows": 1 +} +``` + ## 自定义授权码 HTTP 请求中需要带有授权码 ``,用于身份识别。授权码通常由管理员提供,可简单的通过发送 `HTTP GET` 请求来获取授权码,操作如下: @@ -569,4 +646,4 @@ curl http://192.168.0.1:6041/rest/login/root/taosdata ## 参考 -[taosAdapter](/reference/taosadapter/) +[taosAdapter](../../reference/taosadapter/) diff --git a/docs/zh/08-connector/10-cpp.mdx b/docs/zh/08-connector/10-cpp.mdx index f03777ac4b..d4761ee5c4 100644 --- a/docs/zh/08-connector/10-cpp.mdx +++ b/docs/zh/08-connector/10-cpp.mdx @@ -274,7 +274,7 @@ int taos_print_row(char *str, TAOS_ROW row, TAOS_FIELD *fields, int num_fields) - database,len为用户在外面申请的空间,内部会把当前db赋值到database里。 - 只要是没有正常把db名赋值到database中(包括截断),返回错误,返回值为-1,然后用户可以通过 taos_errstr(NULL) 来获取错误提示。 - - 如果,database == NULL 或者 len<=0 返回错误,required里保存存储db需要的空间(包含最后的'\0') + - 如果,database == NULL 或者 len\<=0 返回错误,required里保存存储db需要的空间(包含最后的'\0') - 如果,len 小于 存储db需要的空间(包含最后的'\0'),返回错误,database里赋值截断的数据,以'\0'结尾。 - 如果,len 大于等于 存储db需要的空间(包含最后的'\0'),返回正常0,database里赋值以'\0‘结尾的db名。 @@ -472,7 +472,7 @@ TDengine 的异步 API 均采用非阻塞调用模式。应用程序可以用多 ### 无模式(schemaless)写入 API -除了使用 SQL 方式或者使用参数绑定 API 写入数据外,还可以使用 Schemaless 的方式完成写入。Schemaless 可以免于预先创建超级表/数据子表的数据结构,而是可以直接写入数据,TDengine 系统会根据写入的数据内容自动创建和维护所需要的表结构。Schemaless 的使用方式详见 [Schemaless 写入](/reference/schemaless/) 章节,这里介绍与之配套使用的 C/C++ API。 +除了使用 SQL 方式或者使用参数绑定 API 写入数据外,还可以使用 Schemaless 的方式完成写入。Schemaless 可以免于预先创建超级表/数据子表的数据结构,而是可以直接写入数据,TDengine 系统会根据写入的数据内容自动创建和维护所需要的表结构。Schemaless 的使用方式详见 [Schemaless 写入](../../reference/schemaless/) 章节,这里介绍与之配套使用的 C/C++ API。 - `TAOS_RES* taos_schemaless_insert(TAOS* taos, const char* lines[], int numLines, int protocol, int precision)` diff --git a/docs/zh/08-connector/14-java.mdx b/docs/zh/08-connector/14-java.mdx index 9f7313b996..117ec5e5d1 100644 --- a/docs/zh/08-connector/14-java.mdx +++ b/docs/zh/08-connector/14-java.mdx @@ -7,17 +7,9 @@ description: TDengine Java 连接器基于标准 JDBC API 实现, 并提供原 import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; +import RequestId from "./_request_id.mdx"; -`taos-jdbcdriver` 是 TDengine 的官方 Java 语言连接器,Java 开发人员可以通过它开发存取 TDengine 数据库的应用软件。`taos-jdbcdriver` 实现了 JDBC driver 标准的接口,并提供两种形式的连接器。一种是通过 TDengine 客户端驱动程序(taosc)原生连接 TDengine 实例,支持数据写入、查询、订阅、schemaless 接口和参数绑定接口等功能,一种是通过 taosAdapter 提供的 REST 接口连接 TDengine 实例。REST 连接实现的功能集合和原生连接有少量不同。 - -![TDengine Database Connector Java](tdengine-jdbc-connector.webp) - -上图显示了两种 Java 应用使用连接器访问 TDengine 的两种方式: - -- JDBC 原生连接:Java 应用在物理节点 1(pnode1)上使用 TSDBDriver 直接调用客户端驱动(libtaos.so 或 taos.dll)的 API 将写入和查询请求发送到位于物理节点 2(pnode2)上的 taosd 实例。 -- JDBC REST 连接:Java 应用通过 RestfulDriver 将 SQL 封装成一个 REST 请求,发送给物理节点 2 的 REST 服务器(taosAdapter),通过 REST 服务器请求 taosd 并返回结果。 - -使用 REST 连接,不依赖 TDengine 客户端驱动,可以跨平台,更加方便灵活。 +`taos-jdbcdriver` 是 TDengine 的官方 Java 语言连接器,Java 开发人员可以通过它开发存取 TDengine 数据库的应用软件。`taos-jdbcdriver` 实现了 JDBC driver 标准的接口. :::info TDengine 的 JDBC 驱动实现尽可能与关系型数据库驱动保持一致,但 TDengine 与关系对象型数据库的使用场景和技术特征存在差异,所以`taos-jdbcdriver` 与传统的 JDBC driver 也存在一定差异。在使用时需要注意以下几点: @@ -27,6 +19,19 @@ TDengine 的 JDBC 驱动实现尽可能与关系型数据库驱动保持一致 ::: +## 连接方式 +`taos-jdbcdriver`主要提供三种形式的连接器。一般我们推荐使用 **Websocket 连接**。 +- **原生连接**,通过 TDengine 客户端驱动程序(taosc)原生连接 TDengine 实例,支持数据写入、查询、数据订阅、schemaless 接口和参数绑定接口等功能。 +- **REST 连接**,通过 taosAdapter 提供的 HTTP 接口连接 TDengine 实例,不支持 schemaless 和数据订阅等特性。 +- **Websocket 连接**,通过 taosAdapter 提供的 Websocket 接口连接 TDengine 实例,WebSocket 连接实现的功能集合和原生连接有少量不同。 + +连接方式的详细介绍请参考:[连接器建立连接的方式](../../develop/connect/#连接器建立连接的方式) + +## JDBC 和 JRE 兼容性 + +- JDBC: 支持 JDBC 4.2 版本,部分功能如无模式写入和数据订阅单独提供 +- JRE: 支持 JRE 8 及以上版本 + ## 支持的平台 原生连接支持的平台和 TDengine 客户端驱动支持的平台一致。 @@ -58,23 +63,12 @@ REST 连接支持所有能运行 Java 的平台。 | 2.0.37 | 增加对 json tag 支持 | - | | 2.0.36 | 增加对 schemaless 写入支持 | - | -**注**:REST 连接中增加 `batchfetch` 参数并设置为 true,将开启 WebSocket 连接。 - ## 处理异常 在报错后,通过 SQLException 可以获取到错误的信息和错误码: ```java -try (Statement statement = connection.createStatement()) { - // executeQuery - ResultSet resultSet = statement.executeQuery(sql); - // print result - printResult(resultSet); -} catch (SQLException e) { - System.out.println("ERROR Message: " + e.getMessage()); - System.out.println("ERROR Code: " + e.getErrorCode()); - e.printStackTrace(); -} +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/JdbcBasicDemo.java:jdbc_exception}} ``` JDBC 连接器可能报错的错误码包括 4 种: @@ -128,7 +122,7 @@ JDBC 连接器可能报错的错误码包括 4 种: | 0x2379 | seek offset must not be a negative number | seek 接口参数不能为负值,请使用正确的参数 | | 0x237a | vGroup not found in result set | VGroup 没有分配给当前 consumer,由于 Rebalance 机制导致 Consumer 与 VGroup 不是绑定的关系 | -- [TDengine Java Connector](https://github.com/taosdata/taos-connector-jdbc/blob/main/src/main/java/com/taosdata/jdbc/TSDBErrorNumbers.java) +- [TDengine Java Connector Error Code](https://github.com/taosdata/taos-connector-jdbc/blob/main/src/main/java/com/taosdata/jdbc/TSDBErrorNumbers.java) ## TDengine DataType 和 Java DataType @@ -153,7 +147,7 @@ TDengine 目前支持时间戳、数字、字符、布尔类型,与 Java 对 **注意**:JSON 类型仅在 tag 中支持。 由于历史原因,TDengine中的BINARY底层不是真正的二进制数据,已不建议使用。请用VARBINARY类型代替。 -GEOMETRY类型是little endian字节序的二进制数据,符合WKB规范。详细信息请参考 [数据类型](/taos-sql/data-type/#数据类型) +GEOMETRY类型是little endian字节序的二进制数据,符合WKB规范。详细信息请参考 [数据类型](../../taos-sql/data-type/#数据类型) WKB规范请参考[Well-Known Binary (WKB)](https://libgeos.org/specifications/wkb/) 对于java连接器,可以使用jts库来方便的创建GEOMETRY类型对象,序列化后写入TDengine,这里有一个样例[Geometry示例](https://github.com/taosdata/TDengine/blob/3.0/examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/GeometryDemo.java) @@ -210,16 +204,18 @@ TDengine 的 JDBC URL 规范格式为: 对于建立连接,原生连接与 REST 连接有细微不同。 +**注**:REST 连接中增加 `batchfetch` 参数并设置为 true,将开启 WebSocket 连接。 + ```java Class.forName("com.taosdata.jdbc.TSDBDriver"); -String jdbcUrl = "jdbc:TAOS://taosdemo.com:6030/test?user=root&password=taosdata"; +String jdbcUrl = "jdbc:TAOS://taosdemo.com:6030/power?user=root&password=taosdata"; Connection conn = DriverManager.getConnection(jdbcUrl); ``` -以上示例,使用了 JDBC 原生连接的 TSDBDriver,建立了到 hostname 为 taosdemo.com,端口为 6030(TDengine 的默认端口),数据库名为 test 的连接。这个 URL +以上示例,使用了 JDBC 原生连接的 TSDBDriver,建立了到 hostname 为 taosdemo.com,端口为 6030(TDengine 的默认端口),数据库名为 power 的连接。这个 URL 中指定用户名(user)为 root,密码(password)为 taosdata。 **注意**:使用 JDBC 原生连接,taos-jdbcdriver 需要依赖客户端驱动(Linux 下是 libtaos.so;Windows 下是 taos.dll;macOS 下是 libtaos.dylib)。 @@ -246,7 +242,7 @@ JDBC 原生连接的使用请参见[视频教程](https://www.taosdata.com/blog/ ```java public Connection getConn() throws Exception{ Class.forName("com.taosdata.jdbc.TSDBDriver"); - String jdbcUrl = "jdbc:TAOS://:/test?user=root&password=taosdata"; + String jdbcUrl = "jdbc:TAOS://:/power?user=root&password=taosdata"; Properties connProps = new Properties(); connProps.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8"); connProps.setProperty(TSDBDriver.PROPERTY_KEY_LOCALE, "en_US.UTF-8"); @@ -272,7 +268,7 @@ secondEp cluster_node2:6030 # locale en_US.UTF-8 ``` -以上示例,jdbc 会使用客户端的配置文件,建立到 hostname 为 cluster_node1、端口为 6030、数据库名为 test 的连接。当集群中 firstEp 节点失效时,JDBC 会尝试使用 secondEp 连接集群。 +以上示例,jdbc 会使用客户端的配置文件,建立到 hostname 为 cluster_node1、端口为 6030、数据库名为 power 的连接。当集群中 firstEp 节点失效时,JDBC 会尝试使用 secondEp 连接集群。 TDengine 中,只要保证 firstEp 和 secondEp 中一个节点有效,就可以正常建立到集群的连接。 @@ -283,11 +279,11 @@ TDengine 中,只要保证 firstEp 和 secondEp 中一个节点有效,就可 ```java Class.forName("com.taosdata.jdbc.rs.RestfulDriver"); -String jdbcUrl = "jdbc:TAOS-RS://taosdemo.com:6041/test?user=root&password=taosdata"; +String jdbcUrl = "jdbc:TAOS-RS://taosdemo.com:6041/power?user=root&password=taosdata"; Connection conn = DriverManager.getConnection(jdbcUrl); ``` -以上示例,使用了 JDBC REST 连接的 RestfulDriver,建立了到 hostname 为 taosdemo.com,端口为 6041,数据库名为 test 的连接。这个 URL 中指定用户名(user)为 root,密码(password)为 taosdata。 +以上示例,使用了 JDBC REST 连接的 RestfulDriver,建立了到 hostname 为 taosdemo.com,端口为 6041,数据库名为 power 的连接。这个 URL 中指定用户名(user)为 root,密码(password)为 taosdata。 使用 JDBC REST 连接,不需要依赖客户端驱动。与 JDBC 原生连接相比,仅需要: @@ -315,10 +311,10 @@ url 中的配置参数如下: - 与原生连接方式不同,REST 接口是无状态的。在使用 JDBC REST 连接时,需要在 SQL 中指定表、超级表的数据库名称。例如: ```sql -INSERT INTO test.t1 USING test.weather (ts, temperature) TAGS('California.SanFrancisco') VALUES(now, 24.6); +INSERT INTO power.d1001 USING power.meters TAGS(2,'California.SanFrancisco') VALUES (NOW, 10.30000, 219, 0.31000); ``` -- 如果在 url 中指定了 dbname,那么,JDBC REST 连接会默认使用/rest/sql/dbname 作为 restful 请求的 url,在 SQL 中不需要指定 dbname。例如:url 为 jdbc:TAOS-RS://127.0.0.1:6041/test,那么,可以执行 sql:insert into t1 using weather(ts, temperature) tags('California.SanFrancisco') values(now, 24.6); +- 如果在 url 中指定了 dbname,那么,JDBC REST 连接会默认使用/rest/sql/dbname 作为 restful 请求的 url,在 SQL 中不需要指定 dbname。例如:url 为 jdbc:TAOS-RS://127.0.0.1:6041/power,那么,可以执行 sql:INSERT INTO d1001 USING meters TAGS(2,'California.SanFrancisco') VALUES (NOW, 10.30000, 219, 0.31000); ::: @@ -332,25 +328,24 @@ INSERT INTO test.t1 USING test.weather (ts, temperature) TAGS('California.SanFra **注意**: - 应用中设置的 client parameter 为进程级别的,即如果要更新 client 的参数,需要重启应用。这是因为 client parameter 是全局参数,仅在应用程序的第一次设置生效。 -- 以下示例代码基于 taos-jdbcdriver-3.1.0。 +- 以下示例代码基于 taos-jdbcdriver-3.1.0 或以上版本。 ```java public Connection getConn() throws Exception{ Class.forName("com.taosdata.jdbc.TSDBDriver"); - String jdbcUrl = "jdbc:TAOS://taosdemo.com:6030/test?user=root&password=taosdata"; + String jdbcUrl = "jdbc:TAOS://taosdemo.com:6030/power?user=root&password=taosdata"; Properties connProps = new Properties(); connProps.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8"); connProps.setProperty(TSDBDriver.PROPERTY_KEY_LOCALE, "en_US.UTF-8"); connProps.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC-8"); connProps.setProperty("debugFlag", "135"); - connProps.setProperty("maxSQLLength", "1048576"); Connection conn = DriverManager.getConnection(jdbcUrl, connProps); return conn; } public Connection getRestConn() throws Exception{ Class.forName("com.taosdata.jdbc.rs.RestfulDriver"); - String jdbcUrl = "jdbc:TAOS-RS://taosdemo.com:6041/test?user=root&password=taosdata"; + String jdbcUrl = "jdbc:TAOS-RS://taosdemo.com:6041/power?user=root&password=taosdata"; Properties connProps = new Properties(); connProps.setProperty(TSDBDriver.PROPERTY_KEY_BATCH_LOAD, "true"); Connection conn = DriverManager.getConnection(jdbcUrl, connProps); @@ -358,7 +353,7 @@ public Connection getRestConn() throws Exception{ } ``` -以上示例,建立一个到 hostname 为 taosdemo.com,端口为 6030/6041,数据库名为 test 的连接。这个连接在 url 中指定了用户名(user)为 root,密码(password)为 taosdata,并在 connProps 中指定了使用的字符集、语言环境、时区、是否开启批量拉取等信息。 +以上示例,建立一个到 hostname 为 taosdemo.com,端口为 6030/6041,数据库名为 power 的连接。这个连接在 url 中指定了用户名(user)为 root,密码(password)为 taosdata,并在 connProps 中指定了使用的字符集、语言环境、时区、是否开启批量拉取等信息。 properties 中的配置参数如下: @@ -401,64 +396,34 @@ properties 中的配置参数如下: ### 创建数据库和表 ```java -Statement stmt = conn.createStatement(); - -// create database -stmt.executeUpdate("create database if not exists db"); - -// use database -stmt.executeUpdate("use db"); - -// create table -stmt.executeUpdate("create table if not exists tb (ts timestamp, temperature int, humidity float)"); +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/JdbcBasicDemo.java:create_db_and_table}} ``` -> **注意**:如果不使用 `use db` 指定数据库,则后续对表的操作都需要增加数据库名称作为前缀,如 db.tb。 +> **注意**:如果不使用 `USE power` 指定数据库,则后续对表的操作都需要增加数据库名称作为前缀,如 power.meters。 ### 插入数据 ```java -// insert data -int affectedRows = stmt.executeUpdate("insert into tb values(now, 23, 10.3) (now + 1s, 20, 9.3)"); - -System.out.println("insert " + affectedRows + " rows."); +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/JdbcBasicDemo.java:insert_data}} ``` -> now 为系统内部函数,默认为客户端所在计算机当前时间。 -> `now + 1s` 代表客户端当前时间往后加 1 秒,数字后面代表时间单位:a(毫秒),s(秒),m(分),h(小时),d(天),w(周),n(月),y(年)。 +> NOW 为系统内部函数,默认为客户端所在计算机当前时间。 +> `NOW + 1s` 代表客户端当前时间往后加 1 秒,数字后面代表时间单位:a(毫秒),s(秒),m(分),h(小时),d(天),w(周),n(月),y(年)。 ### 查询数据 ```java -// query data -ResultSet resultSet = stmt.executeQuery("select * from tb"); - -Timestamp ts = null; -int temperature = 0; -float humidity = 0; -while(resultSet.next()){ - - ts = resultSet.getTimestamp(1); - temperature = resultSet.getInt(2); - humidity = resultSet.getFloat("humidity"); - - System.out.printf("%s, %d, %s\n", ts, temperature, humidity); -} +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/JdbcBasicDemo.java:query_data}} ``` > 查询和操作关系型数据库一致,使用下标获取返回字段内容时从 1 开始,建议使用字段名称获取。 ### 执行带有 reqId 的 SQL -此 reqId 可用于请求链路追踪。 + ```java -AbstractStatement aStmt = (AbstractStatement) connection.createStatement(); -aStmt.execute("create database if not exists db", 1L); -aStmt.executeUpdate("use db", 2L); -try (ResultSet rs = aStmt.executeQuery("select * from tb", 3L)) { - Timestamp ts = rs.getTimestamp(1); -} +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/JdbcBasicDemo.java:with_reqid}} ``` ### 通过参数绑定写入数据 @@ -468,7 +433,7 @@ TDengine 的 JDBC 原生连接实现大幅改进了参数绑定方式对数据 **注意**: - JDBC REST 连接目前不支持参数绑定 -- 以下示例代码基于 taos-jdbcdriver-3.2.4 +- 以下示例代码基于 taos-jdbcdriver-3.2.1 或以上版本 - binary 类型数据需要调用 setString 方法,nchar 类型数据需要调用 setNString 方法 - 预处理语句中指定数据库与子表名称不要使用 `db.?`,应直接使用 `?`,然后在 setTableName 中指定数据库,如:`prepareStatement.setTableName("db.t1")`。 @@ -476,312 +441,23 @@ TDengine 的 JDBC 原生连接实现大幅改进了参数绑定方式对数据 ```java -public class ParameterBindingDemo { - - private static final String host = "127.0.0.1"; - private static final Random random = new Random(System.currentTimeMillis()); - private static final int BINARY_COLUMN_SIZE = 50; - private static final String[] schemaList = { - "create table stable1(ts timestamp, f1 tinyint, f2 smallint, f3 int, f4 bigint) tags(t1 tinyint, t2 smallint, t3 int, t4 bigint)", - "create table stable2(ts timestamp, f1 float, f2 double) tags(t1 float, t2 double)", - "create table stable3(ts timestamp, f1 bool) tags(t1 bool)", - "create table stable4(ts timestamp, f1 binary(" + BINARY_COLUMN_SIZE + ")) tags(t1 binary(" + BINARY_COLUMN_SIZE + "))", - "create table stable5(ts timestamp, f1 nchar(" + BINARY_COLUMN_SIZE + ")) tags(t1 nchar(" + BINARY_COLUMN_SIZE + "))", - "create table stable6(ts timestamp, f1 varbinary(" + BINARY_COLUMN_SIZE + ")) tags(t1 varbinary(" + BINARY_COLUMN_SIZE + "))", - "create table stable7(ts timestamp, f1 geometry(" + BINARY_COLUMN_SIZE + ")) tags(t1 geometry(" + BINARY_COLUMN_SIZE + "))", - }; - private static final int numOfSubTable = 10, numOfRow = 10; - - public static void main(String[] args) throws SQLException { - - String jdbcUrl = "jdbc:TAOS://" + host + ":6030/"; - Connection conn = DriverManager.getConnection(jdbcUrl, "root", "taosdata"); - - init(conn); - - bindInteger(conn); - bindFloat(conn); - bindBoolean(conn); - bindBytes(conn); - bindString(conn); - bindVarbinary(conn); - bindGeometry(conn); - - clean(conn); - conn.close(); - } - - private static void init(Connection conn) throws SQLException { - clean(conn); - try (Statement stmt = conn.createStatement()) { - stmt.execute("create database if not exists test_parabind"); - stmt.execute("use test_parabind"); - for (int i = 0; i < schemaList.length; i++) { - stmt.execute(schemaList[i]); - } - } - } - private static void clean(Connection conn) throws SQLException { - try (Statement stmt = conn.createStatement()) { - stmt.execute("drop database if exists test_parabind"); - } - } - - private static void bindInteger(Connection conn) throws SQLException { - String sql = "insert into ? using stable1 tags(?,?,?,?) values(?,?,?,?,?)"; - - try (TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class)) { - - for (int i = 1; i <= numOfSubTable; i++) { - // set table name - pstmt.setTableName("t1_" + i); - // set tags - pstmt.setTagByte(0, Byte.parseByte(Integer.toString(random.nextInt(Byte.MAX_VALUE)))); - pstmt.setTagShort(1, Short.parseShort(Integer.toString(random.nextInt(Short.MAX_VALUE)))); - pstmt.setTagInt(2, random.nextInt(Integer.MAX_VALUE)); - pstmt.setTagLong(3, random.nextLong()); - // set columns - ArrayList tsList = new ArrayList<>(); - long current = System.currentTimeMillis(); - for (int j = 0; j < numOfRow; j++) - tsList.add(current + j); - pstmt.setTimestamp(0, tsList); - - ArrayList f1List = new ArrayList<>(); - for (int j = 0; j < numOfRow; j++) - f1List.add(Byte.parseByte(Integer.toString(random.nextInt(Byte.MAX_VALUE)))); - pstmt.setByte(1, f1List); - - ArrayList f2List = new ArrayList<>(); - for (int j = 0; j < numOfRow; j++) - f2List.add(Short.parseShort(Integer.toString(random.nextInt(Short.MAX_VALUE)))); - pstmt.setShort(2, f2List); - - ArrayList f3List = new ArrayList<>(); - for (int j = 0; j < numOfRow; j++) - f3List.add(random.nextInt(Integer.MAX_VALUE)); - pstmt.setInt(3, f3List); - - ArrayList f4List = new ArrayList<>(); - for (int j = 0; j < numOfRow; j++) - f4List.add(random.nextLong()); - pstmt.setLong(4, f4List); - - // add column - pstmt.columnDataAddBatch(); - } - // execute column - pstmt.columnDataExecuteBatch(); - } - } - - private static void bindFloat(Connection conn) throws SQLException { - String sql = "insert into ? using stable2 tags(?,?) values(?,?,?)"; - - TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class); - - for (int i = 1; i <= numOfSubTable; i++) { - // set table name - pstmt.setTableName("t2_" + i); - // set tags - pstmt.setTagFloat(0, random.nextFloat()); - pstmt.setTagDouble(1, random.nextDouble()); - // set columns - ArrayList tsList = new ArrayList<>(); - long current = System.currentTimeMillis(); - for (int j = 0; j < numOfRow; j++) - tsList.add(current + j); - pstmt.setTimestamp(0, tsList); - - ArrayList f1List = new ArrayList<>(); - for (int j = 0; j < numOfRow; j++) - f1List.add(random.nextFloat()); - pstmt.setFloat(1, f1List); - - ArrayList f2List = new ArrayList<>(); - for (int j = 0; j < numOfRow; j++) - f2List.add(random.nextDouble()); - pstmt.setDouble(2, f2List); - - // add column - pstmt.columnDataAddBatch(); - } - // execute - pstmt.columnDataExecuteBatch(); - // close if no try-with-catch statement is used - pstmt.close(); - } - - private static void bindBoolean(Connection conn) throws SQLException { - String sql = "insert into ? using stable3 tags(?) values(?,?)"; - - try (TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class)) { - for (int i = 1; i <= numOfSubTable; i++) { - // set table name - pstmt.setTableName("t3_" + i); - // set tags - pstmt.setTagBoolean(0, random.nextBoolean()); - // set columns - ArrayList tsList = new ArrayList<>(); - long current = System.currentTimeMillis(); - for (int j = 0; j < numOfRow; j++) - tsList.add(current + j); - pstmt.setTimestamp(0, tsList); - - ArrayList f1List = new ArrayList<>(); - for (int j = 0; j < numOfRow; j++) - f1List.add(random.nextBoolean()); - pstmt.setBoolean(1, f1List); - - // add column - pstmt.columnDataAddBatch(); - } - // execute - pstmt.columnDataExecuteBatch(); - } - } - - private static void bindBytes(Connection conn) throws SQLException { - String sql = "insert into ? using stable4 tags(?) values(?,?)"; - - try (TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class)) { - - for (int i = 1; i <= numOfSubTable; i++) { - // set table name - pstmt.setTableName("t4_" + i); - // set tags - pstmt.setTagString(0, new String("abc")); - - // set columns - ArrayList tsList = new ArrayList<>(); - long current = System.currentTimeMillis(); - for (int j = 0; j < numOfRow; j++) - tsList.add(current + j); - pstmt.setTimestamp(0, tsList); - - ArrayList f1List = new ArrayList<>(); - for (int j = 0; j < numOfRow; j++) { - f1List.add(new String("abc")); - } - pstmt.setString(1, f1List, BINARY_COLUMN_SIZE); - - // add column - pstmt.columnDataAddBatch(); - } - // execute - pstmt.columnDataExecuteBatch(); - } - } - - private static void bindString(Connection conn) throws SQLException { - String sql = "insert into ? using stable5 tags(?) values(?,?)"; - - try (TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class)) { - - for (int i = 1; i <= numOfSubTable; i++) { - // set table name - pstmt.setTableName("t5_" + i); - // set tags - pstmt.setTagNString(0, "California.SanFrancisco"); - - // set columns - ArrayList tsList = new ArrayList<>(); - long current = System.currentTimeMillis(); - for (int j = 0; j < numOfRow; j++) - tsList.add(current + j); - pstmt.setTimestamp(0, tsList); - - ArrayList f1List = new ArrayList<>(); - for (int j = 0; j < numOfRow; j++) { - f1List.add("California.LosAngeles"); - } - pstmt.setNString(1, f1List, BINARY_COLUMN_SIZE); - - // add column - pstmt.columnDataAddBatch(); - } - // execute - pstmt.columnDataExecuteBatch(); - } - } - - private static void bindVarbinary(Connection conn) throws SQLException { - String sql = "insert into ? using stable6 tags(?) values(?,?)"; - - try (TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class)) { - - for (int i = 1; i <= numOfSubTable; i++) { - // set table name - pstmt.setTableName("t6_" + i); - // set tags - byte[] bTag = new byte[]{0,2,3,4,5}; - bTag[0] = (byte) i; - pstmt.setTagVarbinary(0, bTag); - - // set columns - ArrayList tsList = new ArrayList<>(); - long current = System.currentTimeMillis(); - for (int j = 0; j < numOfRow; j++) - tsList.add(current + j); - pstmt.setTimestamp(0, tsList); - - ArrayList f1List = new ArrayList<>(); - for (int j = 0; j < numOfRow; j++) { - byte[] v = new byte[]{0,2,3,4,5,6}; - v[0] = (byte)j; - f1List.add(v); - } - pstmt.setVarbinary(1, f1List, BINARY_COLUMN_SIZE); - - // add column - pstmt.columnDataAddBatch(); - } - // execute - pstmt.columnDataExecuteBatch(); - } - } - - private static void bindGeometry(Connection conn) throws SQLException { - String sql = "insert into ? using stable7 tags(?) values(?,?)"; - - try (TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class)) { - - byte[] g1 = StringUtils.hexToBytes("0101000000000000000000F03F0000000000000040"); - byte[] g2 = StringUtils.hexToBytes("0102000020E610000002000000000000000000F03F000000000000004000000000000008400000000000001040"); - List listGeo = new ArrayList<>(); - listGeo.add(g1); - listGeo.add(g2); - - for (int i = 1; i <= 2; i++) { - // set table name - pstmt.setTableName("t7_" + i); - // set tags - pstmt.setTagGeometry(0, listGeo.get(i - 1)); - - // set columns - ArrayList tsList = new ArrayList<>(); - long current = System.currentTimeMillis(); - for (int j = 0; j < numOfRow; j++) - tsList.add(current + j); - pstmt.setTimestamp(0, tsList); - - ArrayList f1List = new ArrayList<>(); - for (int j = 0; j < numOfRow; j++) { - f1List.add(listGeo.get(i - 1)); - } - pstmt.setGeometry(1, f1List, BINARY_COLUMN_SIZE); - - // add column - pstmt.columnDataAddBatch(); - } - // execute - pstmt.columnDataExecuteBatch(); - } - } -} +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/ParameterBindingBasicDemo.java:para_bind}} ``` -**注**:字符串和数组类型都要求用户在 size 参数里声明表定义中对应列的列宽。 +这是一个[更详细的参数绑定示例](https://github.com/taosdata/TDengine/blob/main/examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/ParameterBindingFullDemo.java) + + + + +```java +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/WSParameterBindingBasicDemo.java:para_bind}} +``` + + +这是一个[更详细的参数绑定示例](https://github.com/taosdata/TDengine/blob/main/examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/WSParameterBindingFullDemo.java) + + + 用于设定 VALUES 数据列的取值的方法总共有: @@ -799,178 +475,7 @@ public void setNString(int columnIndex, ArrayList list, int size) throws public void setVarbinary(int columnIndex, ArrayList list, int size) throws SQLException public void setGeometry(int columnIndex, ArrayList list, int size) throws SQLException ``` - - - - -```java -public class ParameterBindingDemo { - private static final String host = "127.0.0.1"; - private static final Random random = new Random(System.currentTimeMillis()); - private static final int BINARY_COLUMN_SIZE = 30; - private static final String[] schemaList = { - "create table stable1(ts timestamp, f1 tinyint, f2 smallint, f3 int, f4 bigint) tags(t1 tinyint, t2 smallint, t3 int, t4 bigint)", - "create table stable2(ts timestamp, f1 float, f2 double) tags(t1 float, t2 double)", - "create table stable3(ts timestamp, f1 bool) tags(t1 bool)", - "create table stable4(ts timestamp, f1 binary(" + BINARY_COLUMN_SIZE + ")) tags(t1 binary(" + BINARY_COLUMN_SIZE + "))", - "create table stable5(ts timestamp, f1 nchar(" + BINARY_COLUMN_SIZE + ")) tags(t1 nchar(" + BINARY_COLUMN_SIZE + "))" - }; - private static final int numOfSubTable = 10, numOfRow = 10; - - public static void main(String[] args) throws SQLException { - - String jdbcUrl = "jdbc:TAOS-RS://" + host + ":6041/?batchfetch=true"; - Connection conn = DriverManager.getConnection(jdbcUrl, "root", "taosdata"); - - init(conn); - - bindInteger(conn); - - bindFloat(conn); - - bindBoolean(conn); - - bindBytes(conn); - - bindString(conn); - - conn.close(); - } - - private static void init(Connection conn) throws SQLException { - try (Statement stmt = conn.createStatement()) { - stmt.execute("drop database if exists test_ws_parabind"); - stmt.execute("create database if not exists test_ws_parabind"); - stmt.execute("use test_ws_parabind"); - for (int i = 0; i < schemaList.length; i++) { - stmt.execute(schemaList[i]); - } - } - } - - private static void bindInteger(Connection conn) throws SQLException { - String sql = "insert into ? using stable1 tags(?,?,?,?) values(?,?,?,?,?)"; - - try (TSWSPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSWSPreparedStatement.class)) { - - for (int i = 1; i <= numOfSubTable; i++) { - // set table name - pstmt.setTableName("t1_" + i); - // set tags - pstmt.setTagByte(1, Byte.parseByte(Integer.toString(random.nextInt(Byte.MAX_VALUE)))); - pstmt.setTagShort(2, Short.parseShort(Integer.toString(random.nextInt(Short.MAX_VALUE)))); - pstmt.setTagInt(3, random.nextInt(Integer.MAX_VALUE)); - pstmt.setTagLong(4, random.nextLong()); - // set columns - long current = System.currentTimeMillis(); - for (int j = 0; j < numOfRow; j++) { - pstmt.setTimestamp(1, new Timestamp(current + j)); - pstmt.setByte(2, Byte.parseByte(Integer.toString(random.nextInt(Byte.MAX_VALUE)))); - pstmt.setShort(3, Short.parseShort(Integer.toString(random.nextInt(Short.MAX_VALUE)))); - pstmt.setInt(4, random.nextInt(Integer.MAX_VALUE)); - pstmt.setLong(5, random.nextLong()); - pstmt.addBatch(); - } - pstmt.executeBatch(); - } - } - } - - private static void bindFloat(Connection conn) throws SQLException { - String sql = "insert into ? using stable2 tags(?,?) values(?,?,?)"; - - try(TSWSPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSWSPreparedStatement.class)) { - - for (int i = 1; i <= numOfSubTable; i++) { - // set table name - pstmt.setTableName("t2_" + i); - // set tags - pstmt.setTagFloat(1, random.nextFloat()); - pstmt.setTagDouble(2, random.nextDouble()); - // set columns - long current = System.currentTimeMillis(); - for (int j = 0; j < numOfRow; j++) { - pstmt.setTimestamp(1, new Timestamp(current + j)); - pstmt.setFloat(2, random.nextFloat()); - pstmt.setDouble(3, random.nextDouble()); - pstmt.addBatch(); - } - pstmt.executeBatch(); - } - } - } - - private static void bindBoolean(Connection conn) throws SQLException { - String sql = "insert into ? using stable3 tags(?) values(?,?)"; - - try (TSWSPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSWSPreparedStatement.class)) { - for (int i = 1; i <= numOfSubTable; i++) { - // set table name - pstmt.setTableName("t3_" + i); - // set tags - pstmt.setTagBoolean(1, random.nextBoolean()); - // set columns - long current = System.currentTimeMillis(); - for (int j = 0; j < numOfRow; j++) { - pstmt.setTimestamp(1, new Timestamp(current + j)); - pstmt.setBoolean(2, random.nextBoolean()); - pstmt.addBatch(); - } - pstmt.executeBatch(); - } - } - } - - private static void bindBytes(Connection conn) throws SQLException { - String sql = "insert into ? using stable4 tags(?) values(?,?)"; - - try (TSWSPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSWSPreparedStatement.class)) { - - for (int i = 1; i <= numOfSubTable; i++) { - // set table name - pstmt.setTableName("t4_" + i); - // set tags - pstmt.setTagString(1, new String("abc")); - - // set columns - long current = System.currentTimeMillis(); - for (int j = 0; j < numOfRow; j++) { - pstmt.setTimestamp(1, new Timestamp(current + j)); - pstmt.setString(2, "abc"); - pstmt.addBatch(); - } - pstmt.executeBatch(); - } - } - } - - private static void bindString(Connection conn) throws SQLException { - String sql = "insert into ? using stable5 tags(?) values(?,?)"; - - try (TSWSPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSWSPreparedStatement.class)) { - - for (int i = 1; i <= numOfSubTable; i++) { - // set table name - pstmt.setTableName("t5_" + i); - // set tags - pstmt.setTagNString(1, "California.SanFrancisco"); - - // set columns - long current = System.currentTimeMillis(); - for (int j = 0; j < numOfRow; j++) { - pstmt.setTimestamp(0, new Timestamp(current + j)); - pstmt.setNString(1, "California.SanFrancisco"); - pstmt.addBatch(); - } - pstmt.executeBatch(); - } - } - } -} -``` - - - +**注**:字符串和数组类型都要求用户在 size 参数里声明表定义中对应列的列宽。 用于设定 TAGS 取值的方法总共有: @@ -999,65 +504,14 @@ TDengine 支持无模式写入功能。无模式写入兼容 InfluxDB 的 行协 ```java -public class SchemalessJniTest { - private static final String host = "127.0.0.1"; - private static final String lineDemo = "st,t1=3i64,t2=4f64,t3=\"t3\" c1=3i64,c3=L\"passit\",c2=false,c4=4f64 1626006833639000000"; - private static final String telnetDemo = "stb0_0 1626006833 4 host=host0 interface=eth0"; - private static final String jsonDemo = "{\"metric\": \"meter_current\",\"timestamp\": 1346846400,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"California.SanFrancisco\", \"id\": \"d1001\"}}"; - - public static void main(String[] args) throws SQLException { - final String url = "jdbc:TAOS://" + host + ":6030/?user=root&password=taosdata"; - try (Connection connection = DriverManager.getConnection(url)) { - init(connection); - - SchemalessWriter writer = new SchemalessWriter(connection); - writer.write(lineDemo, SchemalessProtocolType.LINE, SchemalessTimestampType.NANO_SECONDS); - writer.write(telnetDemo, SchemalessProtocolType.TELNET, SchemalessTimestampType.MILLI_SECONDS); - writer.write(jsonDemo, SchemalessProtocolType.JSON, SchemalessTimestampType.NOT_CONFIGURED); - } - } - - private static void init(Connection connection) throws SQLException { - try (Statement stmt = connection.createStatement()) { - stmt.executeUpdate("drop database if exists test_schemaless"); - stmt.executeUpdate("create database if not exists test_schemaless"); - stmt.executeUpdate("use test_schemaless"); - } - } -} +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/SchemalessJniTest.java:schemaless}} ``` ```java -public class SchemalessWsTest { - private static final String host = "127.0.0.1"; - private static final String lineDemo = "st,t1=3i64,t2=4f64,t3=\"t3\" c1=3i64,c3=L\"passit\",c2=false,c4=4f64 1626006833639000000"; - private static final String telnetDemo = "stb0_0 1626006833 4 host=host0 interface=eth0"; - private static final String jsonDemo = "{\"metric\": \"meter_current\",\"timestamp\": 1626846400,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"California.SanFrancisco\", \"id\": \"d1001\"}}"; - - public static void main(String[] args) throws SQLException { - final String url = "jdbc:TAOS-RS://" + host + ":6041/?user=root&password=taosdata&batchfetch=true"; - try(Connection connection = DriverManager.getConnection(url)){ - init(connection); - - try(SchemalessWriter writer = new SchemalessWriter(connection, "test_ws_schemaless")){ - writer.write(lineDemo, SchemalessProtocolType.LINE, SchemalessTimestampType.NANO_SECONDS); - writer.write(telnetDemo, SchemalessProtocolType.TELNET, SchemalessTimestampType.MILLI_SECONDS); - writer.write(jsonDemo, SchemalessProtocolType.JSON, SchemalessTimestampType.SECONDS); - } - } - } - - private static void init(Connection connection) throws SQLException { - try (Statement stmt = connection.createStatement()) { - stmt.executeUpdate("drop database if exists test_ws_schemaless"); - stmt.executeUpdate("create database if not exists test_ws_schemaless keep 36500"); - stmt.executeUpdate("use test_ws_schemaless"); - } - } -} +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/SchemalessWsTest.java:schemaless}} ``` @@ -1078,53 +532,41 @@ TDengine Java 连接器支持订阅功能,应用 API 如下: #### 创建 Topic ```java -Connection connection = DriverManager.getConnection(url, properties); -Statement statement = connection.createStatement(); -statement.executeUpdate("create topic if not exists topic_speed as select ts, speed from speed_table"); +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/ConsumerLoopImp.java:create_topic}} ``` -`subscribe` 方法的三个参数含义如下: - -- topic_speed:订阅的主题(即名称),此参数是订阅的唯一标识。 -- sql:订阅的查询语句,此语句只能是 `select` 语句,只应查询原始数据,只能按时间正序查询数据。 - -如上面的例子将使用 SQL 语句 `select ts, speed from speed_table` 创建一个名为 `topic_speed` 的订阅。 +如上面的例子将使用 SQL 语句 `SELECT ts, current, voltage, phase, groupid, location FROM meters` 创建一个名为 `topic_meters` 的主题。 +> **注意**:订阅主题的查询语句只能是 `SELECT` 语句,只应查询原始数据,只能按时间正序查询数据。 #### 创建 Consumer ```java -Properties config = new Properties(); -config.setProperty("bootstrap.servers", "localhost:6030"); -config.setProperty("enable.auto.commit", "true"); -config.setProperty("group.id", "group1"); -config.setProperty("value.deserializer", "com.taosdata.jdbc.tmq.ConsumerTest.ResultDeserializer"); - -TaosConsumer consumer = new TaosConsumer<>(config); +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/AbsConsumerLoop.java:create_consumer}} ``` +- td.connect.type: 连接方式。jni:表示使用动态库连接的方式,ws/WebSocket:表示使用 WebSocket 进行数据通信。默认为 jni 方式。 - bootstrap.servers: TDengine 服务端所在的`ip:port`,如果使用 WebSocket 连接,则为 taosAdapter 所在的`ip:port`。 - enable.auto.commit: 是否允许自动提交。 - group.id: consumer: 所在的 group。 - value.deserializer: 结果集反序列化方法,可以继承 `com.taosdata.jdbc.tmq.ReferenceDeserializer`,并指定结果集 bean,实现反序列化。也可以继承 `com.taosdata.jdbc.tmq.Deserializer`,根据 SQL 的 resultSet 自定义反序列化方式。 -- td.connect.type: 连接方式。jni:表示使用动态库连接的方式,ws/WebSocket:表示使用 WebSocket 进行数据通信。默认为 jni 方式。 - httpConnectTimeout: 创建连接超时参数,单位 ms,默认为 5000 ms。仅在 WebSocket 连接下有效。 - messageWaitTimeout: 数据传输超时参数,单位 ms,默认为 10000 ms。仅在 WebSocket 连接下有效。 -- httpPoolSize: 同一个连接下最大并行请求数。仅在 WebSocket 连接下有效。 - 其他参数请参考:[Consumer 参数列表](../../develop/tmq#创建-consumer-以及consumer-group), 注意TDengine服务端自3.2.0.0版本开始消息订阅中的auto.offset.reset默认值发生变化。 +- httpPoolSize: 同一个连接下最大并行请求数。仅在 WebSocket 连接下有效。 +- TSDBDriver.PROPERTY_KEY_ENABLE_COMPRESSION: 传输过程是否启用压缩。仅在使用 Websocket 连接时生效。true: 启用,false: 不启用。默认为 false。 +- TSDBDriver.PROPERTY_KEY_ENABLE_AUTO_RECONNECT: 是否启用自动重连。仅在使用 Websocket 连接时生效。true: 启用,false: 不启用。默认为 false。 +- TSDBDriver.PROPERTY_KEY_RECONNECT_INTERVAL_MS: 自动重连重试间隔,单位毫秒,默认值 2000。仅在 PROPERTY_KEY_ENABLE_AUTO_RECONNECT 为 true 时生效。 +- TSDBDriver.PROPERTY_KEY_RECONNECT_RETRY_COUNT: 自动重连重试次数,默认值 3,仅在 PROPERTY_KEY_ENABLE_AUTO_RECONNECT 为 true 时生效。 + +其他参数请参考:[Consumer 参数列表](../../develop/tmq/#数据订阅相关参数), 注意TDengine服务端自 3.2.0.0 版本开始消息订阅中的 auto.offset.reset 默认值发生变化。 #### 订阅消费数据 ```java -while(true) { - ConsumerRecords records = consumer.poll(Duration.ofMillis(100)); - for (ConsumerRecord record : records) { - ResultBean bean = record.value(); - process(bean); - } -} +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/AbsConsumerLoop.java:poll_data}} ``` -`poll` 每次调用获取一个消息。 +`subscribe` 方法的参数含义如为:订阅的主题列表(即名称),支持同时订阅多个主题。 +`poll` 每次调用获取一个消息,一个消息中可能包含多个记录。 #### 指定订阅 Offset @@ -1147,24 +589,7 @@ void seekToEnd(Collection partitions) throws SQLException; 示例代码: ```java -String topic = "offset_seek_test"; -Map offset = null; -try (TaosConsumer consumer = new TaosConsumer<>(properties)) { - consumer.subscribe(Collections.singletonList(topic)); - for (int i = 0; i < 10; i++) { - if (i == 3) { - // Saving consumption position - offset = consumer.position(topic); - } - if (i == 5) { - // reset consumption to the previously saved position - for (Map.Entry entry : offset.entrySet()) { - consumer.seek(entry.getKey(), entry.getValue()); - } - } - ConsumerRecords records = consumer.poll(Duration.ofMillis(500)); - } -} +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/ConsumerOffsetSeek.java:consumer_seek}} ``` #### 提交 Offset @@ -1196,83 +621,7 @@ consumer.close() ```java -public abstract class ConsumerLoop { - private final TaosConsumer consumer; - private final List topics; - private final AtomicBoolean shutdown; - private final CountDownLatch shutdownLatch; - - public ConsumerLoop() throws SQLException { - Properties config = new Properties(); - config.setProperty("td.connect.type", "jni"); - config.setProperty("bootstrap.servers", "localhost:6030"); - config.setProperty("td.connect.user", "root"); - config.setProperty("td.connect.pass", "taosdata"); - config.setProperty("auto.offset.reset", "latest"); - config.setProperty("msg.with.table.name", "true"); - config.setProperty("enable.auto.commit", "true"); - config.setProperty("auto.commit.interval.ms", "1000"); - config.setProperty("group.id", "group1"); - config.setProperty("client.id", "1"); - config.setProperty("value.deserializer", "com.taosdata.jdbc.tmq.ConsumerTest.ConsumerLoop$ResultDeserializer"); - config.setProperty("value.deserializer.encoding", "UTF-8"); - - this.consumer = new TaosConsumer<>(config); - this.topics = Collections.singletonList("topic_speed"); - this.shutdown = new AtomicBoolean(false); - this.shutdownLatch = new CountDownLatch(1); - } - - public abstract void process(ResultBean result); - - public void pollData() throws SQLException { - try { - consumer.subscribe(topics); - - while (!shutdown.get()) { - ConsumerRecords records = consumer.poll(Duration.ofMillis(100)); - for (ConsumerRecord record : records) { - ResultBean bean = record.value(); - process(bean); - } - } - consumer.unsubscribe(); - } finally { - consumer.close(); - shutdownLatch.countDown(); - } - } - - public void shutdown() throws InterruptedException { - shutdown.set(true); - shutdownLatch.await(); - } - - public static class ResultDeserializer extends ReferenceDeserializer { - - } - - public static class ResultBean { - private Timestamp ts; - private int speed; - - public Timestamp getTs() { - return ts; - } - - public void setTs(Timestamp ts) { - this.ts = ts; - } - - public int getSpeed() { - return speed; - } - - public void setSpeed(int speed) { - this.speed = speed; - } - } -} +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/AbsConsumerLoopFull.java:consumer_demo}} ``` @@ -1281,83 +630,7 @@ public abstract class ConsumerLoop { 除了原生的连接方式,Java 连接器还支持通过 WebSocket 订阅数据。 ```java -public abstract class ConsumerLoop { - private final TaosConsumer consumer; - private final List topics; - private final AtomicBoolean shutdown; - private final CountDownLatch shutdownLatch; - - public ConsumerLoop() throws SQLException { - Properties config = new Properties(); - config.setProperty("td.connect.type", "ws"); - config.setProperty("bootstrap.servers", "localhost:6041"); - config.setProperty("td.connect.user", "root"); - config.setProperty("td.connect.pass", "taosdata"); - config.setProperty("auto.offset.reset", "latest"); - config.setProperty("msg.with.table.name", "true"); - config.setProperty("enable.auto.commit", "true"); - config.setProperty("auto.commit.interval.ms", "1000"); - config.setProperty("group.id", "group2"); - config.setProperty("client.id", "1"); - config.setProperty("value.deserializer", "com.taosdata.jdbc.tmq.ConsumerTest.ConsumerLoop$ResultDeserializer"); - config.setProperty("value.deserializer.encoding", "UTF-8"); - - this.consumer = new TaosConsumer<>(config); - this.topics = Collections.singletonList("topic_speed"); - this.shutdown = new AtomicBoolean(false); - this.shutdownLatch = new CountDownLatch(1); - } - - public abstract void process(ResultBean result); - - public void pollData() throws SQLException { - try { - consumer.subscribe(topics); - - while (!shutdown.get()) { - ConsumerRecords records = consumer.poll(Duration.ofMillis(100)); - for (ConsumerRecord record : records) { - ResultBean bean = record.value(); - process(bean); - } - } - consumer.unsubscribe(); - } finally { - consumer.close(); - shutdownLatch.countDown(); - } - } - - public void shutdown() throws InterruptedException { - shutdown.set(true); - shutdownLatch.await(); - } - - public static class ResultDeserializer extends ReferenceDeserializer { - - } - - public static class ResultBean { - private Timestamp ts; - private int speed; - - public Timestamp getTs() { - return ts; - } - - public void setTs(Timestamp ts) { - this.ts = ts; - } - - public int getSpeed() { - return speed; - } - - public void setSpeed(int speed) { - this.speed = speed; - } - } -} +{{#include examples/JDBC/JDBCDemo/src/main/java/com/taosdata/example/AbsWsConsumerLoop.java:consumer_demo}} ``` @@ -1372,30 +645,7 @@ public abstract class ConsumerLoop { 使用示例如下: ```java - public static void main(String[] args) throws SQLException { - HikariConfig config = new HikariConfig(); - // jdbc properties - config.setJdbcUrl("jdbc:TAOS://127.0.0.1:6030/log"); - config.setUsername("root"); - config.setPassword("taosdata"); - // connection pool configurations - config.setMinimumIdle(10); //minimum number of idle connection - config.setMaximumPoolSize(10); //maximum number of connection in the pool - config.setConnectionTimeout(30000); //maximum wait milliseconds for get connection from pool - config.setMaxLifetime(0); // maximum life time for each connection - config.setIdleTimeout(0); // max idle time for recycle idle connection - config.setConnectionTestQuery("select server_status()"); //validation query - - HikariDataSource ds = new HikariDataSource(config); //create datasource - - Connection connection = ds.getConnection(); // get connection - Statement statement = connection.createStatement(); // get statement - - //query or insert - // ... - - connection.close(); // put back to connection pool -} +{{#include examples/JDBC/connectionPools/src/main/java/com/taosdata/example/HikariDemo.java:connection_pool}} ``` > 通过 HikariDataSource.getConnection() 获取连接后,使用完成后需要调用 close() 方法,实际上它并不会关闭连接,只是放回连接池中。 @@ -1406,28 +656,7 @@ public abstract class ConsumerLoop { 使用示例如下: ```java -public static void main(String[] args) throws Exception { - - DruidDataSource dataSource = new DruidDataSource(); - // jdbc properties - dataSource.setDriverClassName("com.taosdata.jdbc.TSDBDriver"); - dataSource.setUrl(url); - dataSource.setUsername("root"); - dataSource.setPassword("taosdata"); - // pool configurations - dataSource.setInitialSize(10); - dataSource.setMinIdle(10); - dataSource.setMaxActive(10); - dataSource.setMaxWait(30000); - dataSource.setValidationQuery("select server_status()"); - - Connection connection = dataSource.getConnection(); // get connection - Statement statement = connection.createStatement(); // get statement - //query or insert - // ... - - connection.close(); // put back to connection pool -} +{{#include examples/JDBC/connectionPools/src/main/java/com/taosdata/example/DruidDemo.java:connection_pool}} ``` > 更多 druid 使用问题请查看[官方说明](https://github.com/alibaba/druid)。 @@ -1437,10 +666,10 @@ public static void main(String[] args) throws Exception { 示例程序源码位于 `TDengine/examples/JDBC` 下: - JDBCDemo:JDBC 示例源程序。 -- JDBCConnectorChecker:JDBC 安装校验源程序及 jar 包。 - connectionPools:HikariCP, Druid, dbcp, c3p0 等连接池中使用 taos-jdbcdriver。 - SpringJdbcTemplate:Spring JdbcTemplate 中使用 taos-jdbcdriver。 - mybatisplus-demo:Springboot + Mybatis 中使用 taos-jdbcdriver。 +- springbootdemo: Springboot 中使用 taos-jdbcdriver。 - consumer-demo:Consumer 消费 TDengine 数据示例,可通过参数控制消费速度。 请参考:[JDBC example](https://github.com/taosdata/TDengine/tree/3.0/examples/JDBC) diff --git a/docs/zh/08-connector/20-go.mdx b/docs/zh/08-connector/20-go.mdx index 3994278eef..97fc474e44 100644 --- a/docs/zh/08-connector/20-go.mdx +++ b/docs/zh/08-connector/20-go.mdx @@ -8,20 +8,23 @@ title: TDengine Go Connector import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -import Preparation from "./_preparation.mdx" -import GoInsert from "../07-develop/03-insert-data/_go_sql.mdx" -import GoInfluxLine from "../07-develop/03-insert-data/_go_line.mdx" -import GoOpenTSDBTelnet from "../07-develop/03-insert-data/_go_opts_telnet.mdx" -import GoOpenTSDBJson from "../07-develop/03-insert-data/_go_opts_json.mdx" -import GoQuery from "../07-develop/04-query-data/_go.mdx" +import RequestId from "./_request_id.mdx"; `driver-go` 是 TDengine 的官方 Go 语言连接器,实现了 Go 语言 [database/sql](https://golang.org/pkg/database/sql/) 包的接口。Go 开发人员可以通过它开发存取 TDengine 集群数据的应用软件。 -`driver-go` 提供两种建立连接的方式。一种是**原生连接**,它通过 TDengine 客户端驱动程序(taosc)原生连接 TDengine 运行实例,支持数据写入、查询、订阅、schemaless 接口和参数绑定接口等功能。另外一种是 **REST 连接**,它通过 taosAdapter 提供的 REST 接口连接 TDengine 运行实例。REST 连接实现的功能特性集合和原生连接有少量不同。 +## 连接方式 -本文介绍如何安装 `driver-go`,并通过 `driver-go` 连接 TDengine 集群、进行数据查询、数据写入等基本操作。 +`driver-go` 提供三种建立连接的方式。 -`driver-go` 的源码托管在 [GitHub](https://github.com/taosdata/driver-go)。 +* **原生连接**,通过 TDengine 客户端驱动程序(taosc)原生连接 TDengine 实例,支持数据写入、查询、数据订阅、schemaless 接口和参数绑定接口等功能。 +* **REST 连接**,通过 taosAdapter 提供的 HTTP 接口连接 TDengine 实例,不支持 schemaless 和数据订阅等特性。 +* **Websocket 连接**,通过 taosAdapter 提供的 Websocket 接口连接 TDengine 实例,WebSocket 连接实现的功能集合和原生连接有少量不同。 + +连接方式的详细介绍请参考:[连接器建立连接的方式](../../develop/connect/#连接器建立连接的方式) + +## 兼容性 + +支持最低 Go 版本 1.14,建议使用最新 Go 版本 ## 支持的平台 @@ -238,45 +241,27 @@ Go 连接器不支持此功能 ### 创建数据库和表 ```go -var taosDSN = "root:taosdata@tcp(localhost:6030)/" -taos, err := sql.Open("taosSql", taosDSN) -if err != nil { - log.Fatalln("failed to connect TDengine, err:", err) -} -defer taos.Close() -_, err := taos.Exec("CREATE DATABASE power") -if err != nil { - log.Fatalln("failed to create database, err:", err) -} -_, err = taos.Exec("CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)") -if err != nil { - log.Fatalln("failed to create stable, err:", err) -} +{{#include docs/examples/go/demo/query/main.go:create_db_and_table}} ``` ### 插入数据 - +```go +{{#include docs/examples/go/demo/query/main.go:insert_data}} +``` ### 查询数据 - +```go +{{#include docs/examples/go/demo/query/main.go:query_data}} +``` ### 执行带有 reqId 的 SQL -此 reqId 可用于请求链路追踪。 + ```go -db, err := sql.Open("taosSql", "root:taosdata@tcp(localhost:6030)/") -if err != nil { - panic(err) -} -defer db.Close() -ctx := context.WithValue(context.Background(), common.ReqIDKey, common.GetReqID()) -_, err = db.ExecContext(ctx, "create database if not exists example_taos_sql") -if err != nil { - panic(err) -} +{{#include docs/examples/go/demo/query/main.go:with_reqid}} ``` ### 通过参数绑定写入数据 @@ -285,375 +270,14 @@ if err != nil { ```go -package main - -import ( - "time" - - "github.com/taosdata/driver-go/v3/af" - "github.com/taosdata/driver-go/v3/common" - "github.com/taosdata/driver-go/v3/common/param" -) - -func main() { - db, err := af.Open("", "root", "taosdata", "", 0) - if err != nil { - panic(err) - } - defer db.Close() - _, err = db.Exec("create database if not exists example_stmt") - if err != nil { - panic(err) - } - _, err = db.Exec("create table if not exists example_stmt.tb1(ts timestamp," + - "c1 bool," + - "c2 tinyint," + - "c3 smallint," + - "c4 int," + - "c5 bigint," + - "c6 tinyint unsigned," + - "c7 smallint unsigned," + - "c8 int unsigned," + - "c9 bigint unsigned," + - "c10 float," + - "c11 double," + - "c12 binary(20)," + - "c13 nchar(20)" + - ")") - if err != nil { - panic(err) - } - stmt := db.InsertStmt() - err = stmt.Prepare("insert into example_stmt.tb1 values(?,?,?,?,?,?,?,?,?,?,?,?,?,?)") - if err != nil { - panic(err) - } - now := time.Now() - params := make([]*param.Param, 14) - params[0] = param.NewParam(2). - AddTimestamp(now, common.PrecisionMilliSecond). - AddTimestamp(now.Add(time.Second), common.PrecisionMilliSecond) - params[1] = param.NewParam(2).AddBool(true).AddNull() - params[2] = param.NewParam(2).AddTinyint(2).AddNull() - params[3] = param.NewParam(2).AddSmallint(3).AddNull() - params[4] = param.NewParam(2).AddInt(4).AddNull() - params[5] = param.NewParam(2).AddBigint(5).AddNull() - params[6] = param.NewParam(2).AddUTinyint(6).AddNull() - params[7] = param.NewParam(2).AddUSmallint(7).AddNull() - params[8] = param.NewParam(2).AddUInt(8).AddNull() - params[9] = param.NewParam(2).AddUBigint(9).AddNull() - params[10] = param.NewParam(2).AddFloat(10).AddNull() - params[11] = param.NewParam(2).AddDouble(11).AddNull() - params[12] = param.NewParam(2).AddBinary([]byte("binary")).AddNull() - params[13] = param.NewParam(2).AddNchar("nchar").AddNull() - - paramTypes := param.NewColumnType(14). - AddTimestamp(). - AddBool(). - AddTinyint(). - AddSmallint(). - AddInt(). - AddBigint(). - AddUTinyint(). - AddUSmallint(). - AddUInt(). - AddUBigint(). - AddFloat(). - AddDouble(). - AddBinary(6). - AddNchar(5) - err = stmt.BindParam(params, paramTypes) - if err != nil { - panic(err) - } - err = stmt.AddBatch() - if err != nil { - panic(err) - } - err = stmt.Execute() - if err != nil { - panic(err) - } - err = stmt.Close() - if err != nil { - panic(err) - } - // select * from example_stmt.tb1 -} +{{#include docs/examples/go/demo/stmt/main.go}} ``` ```go -package main - -import ( - "database/sql" - "fmt" - "time" - - "github.com/taosdata/driver-go/v3/common" - "github.com/taosdata/driver-go/v3/common/param" - _ "github.com/taosdata/driver-go/v3/taosRestful" - "github.com/taosdata/driver-go/v3/ws/stmt" -) - -func main() { - db, err := sql.Open("taosRestful", "root:taosdata@http(localhost:6041)/") - if err != nil { - panic(err) - } - defer db.Close() - prepareEnv(db) - - config := stmt.NewConfig("ws://127.0.0.1:6041/rest/stmt", 0) - config.SetConnectUser("root") - config.SetConnectPass("taosdata") - config.SetConnectDB("example_ws_stmt") - config.SetMessageTimeout(common.DefaultMessageTimeout) - config.SetWriteWait(common.DefaultWriteWait) - config.SetErrorHandler(func(connector *stmt.Connector, err error) { - panic(err) - }) - config.SetCloseHandler(func() { - fmt.Println("stmt connector closed") - }) - - connector, err := stmt.NewConnector(config) - if err != nil { - panic(err) - } - now := time.Now() - { - stmt, err := connector.Init() - if err != nil { - panic(err) - } - err = stmt.Prepare("insert into ? using all_json tags(?) values(?,?,?,?,?,?,?,?,?,?,?,?,?,?)") - if err != nil { - panic(err) - } - err = stmt.SetTableName("tb1") - if err != nil { - panic(err) - } - err = stmt.SetTags(param.NewParam(1).AddJson([]byte(`{"tb":1}`)), param.NewColumnType(1).AddJson(0)) - if err != nil { - panic(err) - } - params := []*param.Param{ - param.NewParam(3).AddTimestamp(now, 0).AddTimestamp(now.Add(time.Second), 0).AddTimestamp(now.Add(time.Second*2), 0), - param.NewParam(3).AddBool(true).AddNull().AddBool(true), - param.NewParam(3).AddTinyint(1).AddNull().AddTinyint(1), - param.NewParam(3).AddSmallint(1).AddNull().AddSmallint(1), - param.NewParam(3).AddInt(1).AddNull().AddInt(1), - param.NewParam(3).AddBigint(1).AddNull().AddBigint(1), - param.NewParam(3).AddUTinyint(1).AddNull().AddUTinyint(1), - param.NewParam(3).AddUSmallint(1).AddNull().AddUSmallint(1), - param.NewParam(3).AddUInt(1).AddNull().AddUInt(1), - param.NewParam(3).AddUBigint(1).AddNull().AddUBigint(1), - param.NewParam(3).AddFloat(1).AddNull().AddFloat(1), - param.NewParam(3).AddDouble(1).AddNull().AddDouble(1), - param.NewParam(3).AddBinary([]byte("test_binary")).AddNull().AddBinary([]byte("test_binary")), - param.NewParam(3).AddNchar("test_nchar").AddNull().AddNchar("test_nchar"), - } - paramTypes := param.NewColumnType(14). - AddTimestamp(). - AddBool(). - AddTinyint(). - AddSmallint(). - AddInt(). - AddBigint(). - AddUTinyint(). - AddUSmallint(). - AddUInt(). - AddUBigint(). - AddFloat(). - AddDouble(). - AddBinary(0). - AddNchar(0) - err = stmt.BindParam(params, paramTypes) - if err != nil { - panic(err) - } - err = stmt.AddBatch() - if err != nil { - panic(err) - } - err = stmt.Exec() - if err != nil { - panic(err) - } - affected := stmt.GetAffectedRows() - fmt.Println("all_json affected rows:", affected) - err = stmt.Close() - if err != nil { - panic(err) - } - } - { - stmt, err := connector.Init() - if err != nil { - panic(err) - } - err = stmt.Prepare("insert into ? using all_all tags(?,?,?,?,?,?,?,?,?,?,?,?,?,?) values(?,?,?,?,?,?,?,?,?,?,?,?,?,?)") - err = stmt.SetTableName("tb1") - if err != nil { - panic(err) - } - - err = stmt.SetTableName("tb2") - if err != nil { - panic(err) - } - err = stmt.SetTags( - param.NewParam(14). - AddTimestamp(now, 0). - AddBool(true). - AddTinyint(2). - AddSmallint(2). - AddInt(2). - AddBigint(2). - AddUTinyint(2). - AddUSmallint(2). - AddUInt(2). - AddUBigint(2). - AddFloat(2). - AddDouble(2). - AddBinary([]byte("tb2")). - AddNchar("tb2"), - param.NewColumnType(14). - AddTimestamp(). - AddBool(). - AddTinyint(). - AddSmallint(). - AddInt(). - AddBigint(). - AddUTinyint(). - AddUSmallint(). - AddUInt(). - AddUBigint(). - AddFloat(). - AddDouble(). - AddBinary(0). - AddNchar(0), - ) - if err != nil { - panic(err) - } - params := []*param.Param{ - param.NewParam(3).AddTimestamp(now, 0).AddTimestamp(now.Add(time.Second), 0).AddTimestamp(now.Add(time.Second*2), 0), - param.NewParam(3).AddBool(true).AddNull().AddBool(true), - param.NewParam(3).AddTinyint(1).AddNull().AddTinyint(1), - param.NewParam(3).AddSmallint(1).AddNull().AddSmallint(1), - param.NewParam(3).AddInt(1).AddNull().AddInt(1), - param.NewParam(3).AddBigint(1).AddNull().AddBigint(1), - param.NewParam(3).AddUTinyint(1).AddNull().AddUTinyint(1), - param.NewParam(3).AddUSmallint(1).AddNull().AddUSmallint(1), - param.NewParam(3).AddUInt(1).AddNull().AddUInt(1), - param.NewParam(3).AddUBigint(1).AddNull().AddUBigint(1), - param.NewParam(3).AddFloat(1).AddNull().AddFloat(1), - param.NewParam(3).AddDouble(1).AddNull().AddDouble(1), - param.NewParam(3).AddBinary([]byte("test_binary")).AddNull().AddBinary([]byte("test_binary")), - param.NewParam(3).AddNchar("test_nchar").AddNull().AddNchar("test_nchar"), - } - paramTypes := param.NewColumnType(14). - AddTimestamp(). - AddBool(). - AddTinyint(). - AddSmallint(). - AddInt(). - AddBigint(). - AddUTinyint(). - AddUSmallint(). - AddUInt(). - AddUBigint(). - AddFloat(). - AddDouble(). - AddBinary(0). - AddNchar(0) - err = stmt.BindParam(params, paramTypes) - if err != nil { - panic(err) - } - err = stmt.AddBatch() - if err != nil { - panic(err) - } - err = stmt.Exec() - if err != nil { - panic(err) - } - affected := stmt.GetAffectedRows() - fmt.Println("all_all affected rows:", affected) - err = stmt.Close() - if err != nil { - panic(err) - } - - } -} - -func prepareEnv(db *sql.DB) { - steps := []string{ - "create database example_ws_stmt", - "create table example_ws_stmt.all_json(ts timestamp," + - "c1 bool," + - "c2 tinyint," + - "c3 smallint," + - "c4 int," + - "c5 bigint," + - "c6 tinyint unsigned," + - "c7 smallint unsigned," + - "c8 int unsigned," + - "c9 bigint unsigned," + - "c10 float," + - "c11 double," + - "c12 binary(20)," + - "c13 nchar(20)" + - ")" + - "tags(t json)", - "create table example_ws_stmt.all_all(" + - "ts timestamp," + - "c1 bool," + - "c2 tinyint," + - "c3 smallint," + - "c4 int," + - "c5 bigint," + - "c6 tinyint unsigned," + - "c7 smallint unsigned," + - "c8 int unsigned," + - "c9 bigint unsigned," + - "c10 float," + - "c11 double," + - "c12 binary(20)," + - "c13 nchar(20)" + - ")" + - "tags(" + - "tts timestamp," + - "tc1 bool," + - "tc2 tinyint," + - "tc3 smallint," + - "tc4 int," + - "tc5 bigint," + - "tc6 tinyint unsigned," + - "tc7 smallint unsigned," + - "tc8 int unsigned," + - "tc9 bigint unsigned," + - "tc10 float," + - "tc11 double," + - "tc12 binary(20)," + - "tc13 nchar(20))", - } - for _, step := range steps { - _, err := db.Exec(step) - if err != nil { - panic(err) - } - } -} - +{{#include docs/examples/go/demo/stmtws/main.go}} ``` @@ -665,98 +289,14 @@ func prepareEnv(db *sql.DB) { ```go -import ( - "fmt" - - "github.com/taosdata/driver-go/v3/af" -) - -func main() { - conn, err := af.Open("localhost", "root", "taosdata", "", 6030) - if err != nil { - fmt.Println("fail to connect, err:", err) - } - defer conn.Close() - _, err = conn.Exec("create database if not exists example") - if err != nil { - panic(err) - } - _, err = conn.Exec("use example") - if err != nil { - panic(err) - } - influxdbData := "st,t1=3i64,t2=4f64,t3=\"t3\" c1=3i64,c3=L\"passit\",c2=false,c4=4f64 1626006833639000000" - err = conn.InfluxDBInsertLines([]string{influxdbData}, "ns") - if err != nil { - panic(err) - } - telnetData := "stb0_0 1626006833 4 host=host0 interface=eth0" - err = conn.OpenTSDBInsertTelnetLines([]string{telnetData}) - if err != nil { - panic(err) - } - jsonData := "{\"metric\": \"meter_current\",\"timestamp\": 1626846400,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"California.SanFrancisco\", \"id\": \"d1001\"}}" - err = conn.OpenTSDBInsertJsonPayload(jsonData) - if err != nil { - panic(err) - } -} +{{#include docs/examples/go/demo/sml/main.go}} ``` ```go -import ( - "database/sql" - "log" - "time" - - "github.com/taosdata/driver-go/v3/common" - _ "github.com/taosdata/driver-go/v3/taosWS" - "github.com/taosdata/driver-go/v3/ws/schemaless" -) - -func main() { - db, err := sql.Open("taosWS", "root:taosdata@ws(localhost:6041)/") - if err != nil { - log.Fatal(err) - } - defer db.Close() - _, err = db.Exec("create database if not exists schemaless_ws") - if err != nil { - log.Fatal(err) - } - s, err := schemaless.NewSchemaless(schemaless.NewConfig("ws://localhost:6041/rest/schemaless", 1, - schemaless.SetDb("schemaless_ws"), - schemaless.SetReadTimeout(10*time.Second), - schemaless.SetWriteTimeout(10*time.Second), - schemaless.SetUser("root"), - schemaless.SetPassword("taosdata"), - schemaless.SetErrorHandler(func(err error) { - log.Fatal(err) - }), - )) - if err != nil { - panic(err) - } - influxdbData := "st,t1=3i64,t2=4f64,t3=\"t3\" c1=3i64,c3=L\"passit\",c2=false,c4=4f64 1626006833639000000" - telnetData := "stb0_0 1626006833 4 host=host0 interface=eth0" - jsonData := "{\"metric\": \"meter_current\",\"timestamp\": 1626846400,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"California.SanFrancisco\", \"id\": \"d1001\"}}" - - err = s.Insert(influxdbData, schemaless.InfluxDBLineProtocol, "ns", 0, common.GetReqID()) - if err != nil { - panic(err) - } - err = s.Insert(telnetData, schemaless.OpenTSDBTelnetLineProtocol, "ms", 0, common.GetReqID()) - if err != nil { - panic(err) - } - err = s.Insert(jsonData, schemaless.OpenTSDBJsonFormatProtocol, "ms", 0, common.GetReqID()) - if err != nil { - panic(err) - } -} +{{#include docs/examples/go/demo/smlws/main.go}} ``` @@ -777,89 +317,31 @@ TDengine Go 连接器支持订阅功能,应用 API 如下: #### 创建 Topic ```go - db, err := af.Open("", "root", "taosdata", "", 0) - if err != nil { - panic(err) - } - defer db.Close() - _, err = db.Exec("create database if not exists example_tmq WAL_RETENTION_PERIOD 86400") - if err != nil { - panic(err) - } - _, err = db.Exec("create topic if not exists example_tmq_topic as DATABASE example_tmq") - if err != nil { - panic(err) - } +{{#include docs/examples/go/demo/consumer/main.go:create_topic}} ``` #### 创建 Consumer ```go - consumer, err := tmq.NewConsumer(&tmqcommon.ConfigMap{ - "group.id": "test", - "auto.offset.reset": "latest", - "td.connect.ip": "127.0.0.1", - "td.connect.user": "root", - "td.connect.pass": "taosdata", - "td.connect.port": "6030", - "client.id": "test_tmq_client", - "enable.auto.commit": "false", - "msg.with.table.name": "true", - }) - if err != nil { - panic(err) - } +{{#include docs/examples/go/demo/consumer/main.go:create_consumer}} ``` #### 订阅消费数据 ```go - err = consumer.Subscribe("example_tmq_topic", nil) - if err != nil { - panic(err) - } - for i := 0; i < 5; i++ { - ev := consumer.Poll(500) - if ev != nil { - switch e := ev.(type) { - case *tmqcommon.DataMessage: - fmt.Printf("get message:%v\n", e) - case tmqcommon.Error: - fmt.Fprintf(os.Stderr, "%% Error: %v: %v\n", e.Code(), e) - panic(e) - } - consumer.Commit() - } - } +{{#include docs/examples/go/demo/consumer/main.go:poll_data}} ``` #### 指定订阅 Offset ```go - partitions, err := consumer.Assignment() - if err != nil { - panic(err) - } - for i := 0; i < len(partitions); i++ { - fmt.Println(partitions[i]) - err = consumer.Seek(tmqcommon.TopicPartition{ - Topic: partitions[i].Topic, - Partition: partitions[i].Partition, - Offset: 0, - }, 0) - if err != nil { - panic(err) - } - } +{{#include docs/examples/go/demo/consumer/main.go:consumer_seek}} ``` #### 关闭订阅 ```go - err = consumer.Close() - if err != nil { - panic(err) - } +{{#include docs/examples/go/demo/consumer/main.go:consumer_close}} ``` #### 完整示例 @@ -868,232 +350,14 @@ TDengine Go 连接器支持订阅功能,应用 API 如下: ```go -package main - -import ( - "fmt" - "os" - "time" - - "github.com/taosdata/driver-go/v3/af" - "github.com/taosdata/driver-go/v3/af/tmq" - tmqcommon "github.com/taosdata/driver-go/v3/common/tmq" -) - -func main() { - db, err := af.Open("", "root", "taosdata", "", 0) - if err != nil { - panic(err) - } - defer db.Close() - _, err = db.Exec("create database if not exists example_tmq WAL_RETENTION_PERIOD 86400") - if err != nil { - panic(err) - } - _, err = db.Exec("create topic if not exists example_tmq_topic as DATABASE example_tmq") - if err != nil { - panic(err) - } - consumer, err := tmq.NewConsumer(&tmqcommon.ConfigMap{ - "group.id": "test", - "auto.offset.reset": "latest", - "td.connect.ip": "127.0.0.1", - "td.connect.user": "root", - "td.connect.pass": "taosdata", - "td.connect.port": "6030", - "client.id": "test_tmq_client", - "enable.auto.commit": "false", - "msg.with.table.name": "true", - }) - if err != nil { - panic(err) - } - err = consumer.Subscribe("example_tmq_topic", nil) - if err != nil { - panic(err) - } - _, err = db.Exec("create table example_tmq.t1 (ts timestamp,v int)") - if err != nil { - panic(err) - } - go func() { - for { - _, err = db.Exec("insert into example_tmq.t1 values(now,1)") - if err != nil { - panic(err) - } - time.Sleep(time.Millisecond * 100) - } - }() - - for i := 0; i < 5; i++ { - ev := consumer.Poll(500) - if ev != nil { - switch e := ev.(type) { - case *tmqcommon.DataMessage: - fmt.Printf("get message:%v\n", e) - case tmqcommon.Error: - fmt.Fprintf(os.Stderr, "%% Error: %v: %v\n", e.Code(), e) - panic(e) - } - consumer.Commit() - } - } - partitions, err := consumer.Assignment() - if err != nil { - panic(err) - } - for i := 0; i < len(partitions); i++ { - fmt.Println(partitions[i]) - err = consumer.Seek(tmqcommon.TopicPartition{ - Topic: partitions[i].Topic, - Partition: partitions[i].Partition, - Offset: 0, - }, 0) - if err != nil { - panic(err) - } - } - - partitions, err = consumer.Assignment() - if err != nil { - panic(err) - } - for i := 0; i < len(partitions); i++ { - fmt.Println(partitions[i]) - } - - err = consumer.Close() - if err != nil { - panic(err) - } -} +{{#include docs/examples/go/demo/consumer/main.go}} ``` ```go -package main - -import ( - "database/sql" - "fmt" - "time" - - "github.com/taosdata/driver-go/v3/common" - tmqcommon "github.com/taosdata/driver-go/v3/common/tmq" - _ "github.com/taosdata/driver-go/v3/taosRestful" - "github.com/taosdata/driver-go/v3/ws/tmq" -) - -func main() { - db, err := sql.Open("taosRestful", "root:taosdata@http(localhost:6041)/") - if err != nil { - panic(err) - } - defer db.Close() - prepareEnv(db) - consumer, err := tmq.NewConsumer(&tmqcommon.ConfigMap{ - "ws.url": "ws://127.0.0.1:6041/rest/tmq", - "ws.message.channelLen": uint(0), - "ws.message.timeout": common.DefaultMessageTimeout, - "ws.message.writeWait": common.DefaultWriteWait, - "td.connect.user": "root", - "td.connect.pass": "taosdata", - "group.id": "example", - "client.id": "example_consumer", - "auto.offset.reset": "latest", - }) - if err != nil { - panic(err) - } - err = consumer.Subscribe("example_ws_tmq_topic", nil) - if err != nil { - panic(err) - } - - _, err = db.Exec("create table example_ws_tmq.t_all(ts timestamp," + - "c1 bool," + - "c2 tinyint," + - "c3 smallint," + - "c4 int," + - "c5 bigint," + - "c6 tinyint unsigned," + - "c7 smallint unsigned," + - "c8 int unsigned," + - "c9 bigint unsigned," + - "c10 float," + - "c11 double," + - "c12 binary(20)," + - "c13 nchar(20)" + - ")") - if err != nil { - panic(err) - } - go func() { - for { - _, err = db.Exec("insert into example_ws_tmq.t_all values(now,true,2,3,4,5,6,7,8,9,10.123,11.123,'binary','nchar')") - if err != nil { - panic(err) - } - time.Sleep(time.Millisecond * 100) - } - - }() - for i := 0; i < 5; i++ { - ev := consumer.Poll(500) - if ev != nil { - switch e := ev.(type) { - case *tmqcommon.DataMessage: - fmt.Printf("get message:%v\n", e) - case tmqcommon.Error: - fmt.Printf("%% Error: %v: %v\n", e.Code(), e) - panic(e) - } - consumer.Commit() - } - } - partitions, err := consumer.Assignment() - if err != nil { - panic(err) - } - for i := 0; i < len(partitions); i++ { - fmt.Println(partitions[i]) - err = consumer.Seek(tmqcommon.TopicPartition{ - Topic: partitions[i].Topic, - Partition: partitions[i].Partition, - Offset: 0, - }, 0) - if err != nil { - panic(err) - } - } - - partitions, err = consumer.Assignment() - if err != nil { - panic(err) - } - for i := 0; i < len(partitions); i++ { - fmt.Println(partitions[i]) - } - - err = consumer.Close() - if err != nil { - panic(err) - } -} - -func prepareEnv(db *sql.DB) { - _, err := db.Exec("create database example_ws_tmq WAL_RETENTION_PERIOD 86400") - if err != nil { - panic(err) - } - _, err = db.Exec("create topic example_ws_tmq_topic as database example_ws_tmq") - if err != nil { - panic(err) - } -} +{{#include docs/examples/go/demo/consumerws/main.go}} ``` diff --git a/docs/zh/08-connector/26-rust.mdx b/docs/zh/08-connector/26-rust.mdx index 37b1787707..7f8019377d 100644 --- a/docs/zh/08-connector/26-rust.mdx +++ b/docs/zh/08-connector/26-rust.mdx @@ -12,15 +12,24 @@ import RustInsert from "../07-develop/03-insert-data/_rust_sql.mdx" import RustBind from "../07-develop/03-insert-data/_rust_stmt.mdx" import RustSml from "../07-develop/03-insert-data/_rust_schemaless.mdx" import RustQuery from "../07-develop/04-query-data/_rust.mdx" +import RequestId from "./_request_id.mdx"; [![Crates.io](https://img.shields.io/crates/v/taos)](https://crates.io/crates/taos) ![Crates.io](https://img.shields.io/crates/d/taos) [![docs.rs](https://img.shields.io/docsrs/taos)](https://docs.rs/taos) `taos` 是 TDengine 的官方 Rust 语言连接器。Rust 开发人员可以通过它开发存取 TDengine 数据库的应用软件。 -`taos` 提供两种建立连接的方式。一种是**原生连接**,它通过 TDengine 客户端驱动程序(taosc)连接 TDengine 运行实例。另外一种是 **Websocket 连接**,它通过 taosAdapter 的 Websocket 接口连接 TDengine 运行实例。你可以通过不同的 “特性(即 Cargo 关键字 `features`)” 来指定使用哪种连接器(默认同时支持)。Websocket 连接支持任何平台,原生连接支持所有 TDengine 客户端能运行的平台。 - 该 Rust 连接器的源码托管在 [GitHub](https://github.com/taosdata/taos-connector-rust)。 +## 连接方式 + +`taos` 提供两种建立连接的方式。一般我们推荐使用 **Websocket 连接**。 +- **原生连接**,它通过 TDengine 客户端驱动程序(taosc)连接 TDengine 运行实例。 +- **Websocket 连接**,它通过 taosAdapter 的 Websocket 接口连接 TDengine 运行实例。 + +你可以通过不同的 “特性(即 Cargo 关键字 `features`)” 来指定使用哪种连接器(默认同时支持)。 + +连接方式的详细介绍请参考:[连接器建立连接的方式](../../develop/connect/#连接器建立连接的方式) + ## 支持的平台 原生连接支持的平台和 TDengine 客户端驱动支持的平台一致。 @@ -30,8 +39,11 @@ Websocket 连接支持所有能运行 Rust 的平台。 | Rust 连接器版本 | TDengine 版本 | 主要功能 | | :----------------: | :--------------: | :--------------------------------------------------: | -| v0.9.2 | 3.0.7.0 or later | STMT:ws 下获取 tag_fields、col_fields。 | -| v0.8.12 | 3.0.5.0 | 消息订阅:获取消费进度及按照指定进度开始消费。 | +| v0.12.0 | 3.2.3.0 or later | WS 支持压缩。 | +| v0.11.0 | 3.2.0.0 | TMQ 功能优化。 | +| v0.10.0 | 3.1.0.0 | WS endpoint 变更。 | +| v0.9.2 | 3.0.7.0 | STMT:ws 下获取 tag_fields、col_fields。 | +| v0.8.12 | 3.0.5.0 | 消息订阅:获取消费进度及按照指定进度开始消费。 | | v0.8.0 | 3.0.4.0 | 支持无模式写入。 | | v0.7.6 | 3.0.3.0 | 支持在请求中使用 req_id。 | | v0.6.0 | 3.0.0.0 | 基础功能。 | @@ -68,7 +80,7 @@ TDengine 目前支持时间戳、数字、字符、布尔类型,与 Rust 对 | SMALLINT | i16 | | TINYINT | i8 | | BOOL | bool | -| BINARY | Vec | +| BINARY | Vec\ | | NCHAR | String | | JSON | serde_json::Value | @@ -274,52 +286,29 @@ async fn demo(taos: &Taos, db: &str) -> Result<(), Error> { ### 创建数据库和表 ```rust -use taos::*; - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - let dsn = "taos://localhost:6030"; - let builder = TaosBuilder::from_dsn(dsn)?; - - let taos = builder.build()?; - - let db = "query"; - - // create database - taos.exec_many([ - format!("DROP DATABASE IF EXISTS `{db}`"), - format!("CREATE DATABASE `{db}`"), - format!("USE `{db}`"), - ]) - .await?; - - // create table - taos.exec_many([ - // create super table - "CREATE TABLE `meters` (`ts` TIMESTAMP, `current` FLOAT, `voltage` INT, `phase` FLOAT) \ - TAGS (`groupid` INT, `location` BINARY(16))", - // create child table - "CREATE TABLE `d0` USING `meters` TAGS(0, 'Los Angles')", - ]).await?; -} +{{#include docs/examples/rust/nativeexample/examples/query.rs:create_db_and_table}} ``` > **注意**:如果不使用 `use db` 指定数据库,则后续对表的操作都需要增加数据库名称作为前缀,如 db.tb。 ### 插入数据 - +```rust +{{#include docs/examples/rust/nativeexample/examples/query.rs:insert_data}} +``` ### 查询数据 - +```rust +{{#include docs/examples/rust/nativeexample/examples/query.rs:query_data}} +``` ### 执行带有 req_id 的 SQL -此 req_id 可用于请求链路追踪。 + ```rust -let rs = taos.query_with_req_id("select * from stable where tag1 is null", 1)?; +{{#include docs/examples/rust/nativeexample/examples/query.rs:query_with_req_id}} ``` ### 通过参数绑定写入数据 @@ -328,13 +317,17 @@ TDengine 的 Rust 连接器实现了参数绑定方式对数据写入(INSERT 参数绑定接口详见[API参考](#stmt-api) - +```rust +{{#include docs/examples/rust/nativeexample/examples/stmt.rs}} +``` ### 无模式写入 TDengine 支持无模式写入功能。无模式写入兼容 InfluxDB 的 行协议(Line Protocol)、OpenTSDB 的 telnet 行协议和 OpenTSDB 的 JSON 格式协议。详情请参见[无模式写入](../../reference/schemaless/)。 - +```rust +{{#include docs/examples/rust/nativeexample/examples/schemaless.rs}} +``` ### 执行带有 req_id 的无模式写入 @@ -357,25 +350,15 @@ TDengine 通过消息队列 [TMQ](../../taos-sql/tmq/) 启动一个订阅。 #### 创建 Topic ```rust -taos.exec_many([ - // create topic for subscription - format!("CREATE TOPIC tmq_meters with META AS DATABASE {db}") -]) -.await?; +{{#include docs/examples/rust/nativeexample/examples/tmq.rs:create_topic}} ``` #### 创建 Consumer -从 DSN 开始,构建一个 TMQ 连接器。 - -```rust -let tmq = TmqBuilder::from_dsn("taos://localhost:6030/?group.id=test")?; -``` - 创建消费者: ```rust -let mut consumer = tmq.build()?; +{{#include docs/examples/rust/nativeexample/examples/tmq.rs:create_consumer}} ``` #### 订阅消费数据 @@ -383,40 +366,13 @@ let mut consumer = tmq.build()?; 消费者可订阅一个或多个 `TOPIC`。 ```rust -consumer.subscribe(["tmq_meters"]).await?; +{{#include docs/examples/rust/nativeexample/examples/tmq.rs:subscribe}} ``` TMQ 消息队列是一个 [futures::Stream](https://docs.rs/futures/latest/futures/stream/index.html) 类型,可以使用相应 API 对每个消息进行消费,并通过 `.commit` 进行已消费标记。 ```rust -{ - let mut stream = consumer.stream(); - - while let Some((offset, message)) = stream.try_next().await? { - // get information from offset - - // the topic - let topic = offset.topic(); - // the vgroup id, like partition id in kafka. - let vgroup_id = offset.vgroup_id(); - println!("* in vgroup id {vgroup_id} of topic {topic}\n"); - - if let Some(data) = message.into_data() { - while let Some(block) = data.fetch_raw_block().await? { - // one block for one table, get table name if needed - let name = block.table_name(); - let records: Vec = block.deserialize().try_collect()?; - println!( - "** table: {}, got {} records: {:#?}\n", - name.unwrap(), - records.len(), - records - ); - } - } - consumer.commit(offset).await?; - } -} +{{#include docs/examples/rust/nativeexample/examples/tmq.rs:consume}} ``` 获取消费进度: @@ -424,7 +380,7 @@ TMQ 消息队列是一个 [futures::Stream](https://docs.rs/futures/latest/futur 版本要求 connector-rust >= v0.8.8, TDengine >= 3.0.5.0 ```rust -let assignments = consumer.assignments().await.unwrap(); +{{#include docs/examples/rust/nativeexample/examples/tmq.rs:assignments}} ``` #### 指定订阅 Offset @@ -434,13 +390,13 @@ let assignments = consumer.assignments().await.unwrap(); 版本要求 connector-rust >= v0.8.8, TDengine >= 3.0.5.0 ```rust -consumer.offset_seek(topic, vgroup_id, offset).await; +{{#include docs/examples/rust/nativeexample/examples/tmq.rs:seek_offset}} ``` #### 关闭订阅 ```rust -consumer.unsubscribe().await; +{{#include docs/examples/rust/nativeexample/examples/tmq.rs:unsubscribe}} ``` 对于 TMQ DSN, 有以下配置项可以进行设置,需要注意的是,`group.id` 是必须的。 @@ -453,7 +409,7 @@ consumer.unsubscribe().await; #### 完整示例 -完整订阅示例参见 [GitHub 示例文件](https://github.com/taosdata/TDengine/blob/3.0/docs/examples/rust/nativeexample/examples/subscribe_demo.rs). +完整订阅示例参见 [GitHub 示例文件](https://github.com/taosdata/TDengine/blob/3.0/docs/examples/rust/nativeexample/examples/tmq.rs). ### 与连接池使用 @@ -655,7 +611,7 @@ stmt.execute()?; 一个可运行的示例请见 [GitHub 上的示例](https://github.com/taosdata/taos-connector-rust/blob/main/examples/bind.rs)。 -其他相关结构体 API 使用说明请移步 Rust 文档托管网页:。 +其他相关结构体 API 使用说明请移步 Rust 文档托管网页:\。 [taos]: https://github.com/taosdata/rust-connector-taos [deadpool]: https://crates.io/crates/deadpool diff --git a/docs/zh/08-connector/30-python.mdx b/docs/zh/08-connector/30-python.mdx index 71dc82316e..9f2be23067 100644 --- a/docs/zh/08-connector/30-python.mdx +++ b/docs/zh/08-connector/30-python.mdx @@ -7,16 +7,24 @@ description: "taospy 是 TDengine 的官方 Python 连接器。taospy 提供了 import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem"; +import RequestId from "./_request_id.mdx"; -`taospy` 是 TDengine 的官方 Python 连接器。`taospy` 提供了丰富的 API, 使得 Python 应用可以很方便地使用 TDengine。`taospy` 对 TDengine 的[原生接口](../cpp)和 [REST 接口](../rest-api)都进行了封装, 分别对应 `taospy` 包的 `taos` 模块 和 `taosrest` 模块。 -除了对原生接口和 REST 接口的封装,`taospy` 还提供了符合 [Python 数据访问规范(PEP 249)](https://peps.python.org/pep-0249/) 的编程接口。这使得 `taospy` 和很多第三方工具集成变得简单,比如 [SQLAlchemy](https://www.sqlalchemy.org/) 和 [pandas](https://pandas.pydata.org/)。 - -`taos-ws-py` 是使用 WebSocket 方式连接 TDengine 的 Python 连接器包。可以选装。 - -使用客户端驱动提供的原生接口直接与服务端建立的连接的方式下文中称为“原生连接”;使用 taosAdapter 提供的 REST 接口或 WebSocket 接口与服务端建立的连接的方式下文中称为“REST 连接”或“WebSocket 连接”。 +`taospy` 是 TDengine 的官方 Python 连接器。`taospy` 提供了丰富的 API, 使得 Python 应用可以很方便地使用 TDengine。 Python 连接器的源码托管在 [GitHub](https://github.com/taosdata/taos-connector-python)。 +## 连接方式 +`taospy`主要提供三种形式的连接器。一般我们推荐使用 **Websocket 连接**。 +- **原生连接**,对应 `taospy` 包的 `taos` 模块。通过 TDengine 客户端驱动程序(taosc)原生连接 TDengine 实例,支持数据写入、查询、数据订阅、schemaless 接口和参数绑定接口等功能。 +- **REST 连接**,对应 `taospy` 包的 `taosrest` 模块。通过 taosAdapter 提供的 HTTP 接口连接 TDengine 实例,不支持 schemaless 和数据订阅等特性。 +- **Websocket 连接**,对应 `taos-ws-py` 包,可以选装。通过 taosAdapter 提供的 Websocket 接口连接 TDengine 实例,WebSocket 连接实现的功能集合和原生连接有少量不同。 + +连接方式的详细介绍请参考:[连接器建立连接的方式](../../develop/connect/#连接器建立连接的方式) + +除了对原生接口和 REST 接口的封装,`taospy` 还提供了符合 [Python 数据访问规范(PEP 249)](https://peps.python.org/pep-0249/) 的编程接口。这使得 `taospy` 和很多第三方工具集成变得简单,比如 [SQLAlchemy](https://www.sqlalchemy.org/) 和 [pandas](https://pandas.pydata.org/)。 + +使用客户端驱动提供的原生接口直接与服务端建立的连接的方式下文中称为“原生连接”;使用 taosAdapter 提供的 REST 接口或 WebSocket 接口与服务端建立的连接的方式下文中称为“REST 连接”或“WebSocket 连接”。 + ## 支持的平台 - 原生连接[支持的平台](../#支持的平台)和 TDengine 客户端支持的平台一致。 @@ -317,7 +325,7 @@ Transfer-Encoding: chunked `connect()` 函数的所有参数都是可选的关键字参数。下面是连接参数的具体说明: -- `url`: taosAdapter REST 服务的 URL。默认是 。 +- `url`: taosAdapter REST 服务的 URL。默认是 \。 - `user`: TDengine 用户名。默认是 root。 - `password`: TDengine 用户密码。默认是 taosdata。 - `timeout`: HTTP 请求超时时间。单位为秒。默认为 `socket._GLOBAL_DEFAULT_TIMEOUT`。 一般无需配置。 @@ -350,13 +358,7 @@ Transfer-Encoding: chunked ```python -conn = taos.connect() -# Execute a sql, ignore the result set, just get affected rows. It's useful for DDL and DML statement. -conn.execute("DROP DATABASE IF EXISTS test") -conn.execute("CREATE DATABASE test") -# change database. same as execute "USE db" -conn.select_db("test") -conn.execute("CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT) TAGS (location INT)") +{{#include docs/examples/python/create_db_native.py}} ``` @@ -364,12 +366,7 @@ conn.execute("CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT) TAGS (locat ```python -conn = taosrest.connect(url="http://localhost:6041") -# Execute a sql, ignore the result set, just get affected rows. It's useful for DDL and DML statement. -conn.execute("DROP DATABASE IF EXISTS test") -conn.execute("CREATE DATABASE test") -conn.execute("USE test") -conn.execute("CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT) TAGS (location INT)") +{{#include docs/examples/python/create_db_rest.py}} ``` @@ -377,12 +374,7 @@ conn.execute("CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT) TAGS (locat ```python -conn = taosws.connect("taosws://localhost:6041") -# Execute a sql, ignore the result set, just get affected rows. It's useful for DDL and DML statement. -conn.execute("DROP DATABASE IF EXISTS test") -conn.execute("CREATE DATABASE test") -conn.execute("USE test") -conn.execute("CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT) TAGS (location INT)") +{{#include docs/examples/python/create_db_ws.py}} ``` @@ -390,101 +382,35 @@ conn.execute("CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT) TAGS (locat ### 插入数据 -```python -conn.execute("INSERT INTO t1 USING weather TAGS(1) VALUES (now, 23.5) (now+1m, 23.5) (now+2m, 24.4)") -``` - -::: -now 为系统内部函数,默认为客户端所在计算机当前时间。 now + 1s 代表客户端当前时间往后加 1 秒,数字后面代表时间单位:a(毫秒),s(秒),m(分),h(小时),d(天),w(周),n(月),y(年)。 -::: - -### 基本使用 - -##### TaosConnection 类的使用 - -`TaosConnection` 类既包含对 PEP249 Connection 接口的实现(如:`cursor`方法和 `close` 方法),也包含很多扩展功能(如: `execute`、 `query`、`schemaless_insert` 和 `subscribe` 方法。 - -```python title="execute 方法" -{{#include docs/examples/python/connection_usage_native_reference.py:insert}} +```python +{{#include docs/examples/python/insert_native.py:insert}} ``` -```python title="query 方法" -{{#include docs/examples/python/connection_usage_native_reference.py:query}} -``` - -:::tip -查询结果只能获取一次。比如上面的示例中 `fetch_all()` 和 `fetch_all_into_dict()` 只能用一个。重复获取得到的结果为空列表。 -::: - -##### TaosResult 类的使用 - -上面 `TaosConnection` 类的使用示例中,我们已经展示了两种获取查询结果的方法: `fetch_all()` 和 `fetch_all_into_dict()`。除此之外 `TaosResult` 还提供了按行迭代(`rows_iter`)或按数据块迭代(`blocks_iter`)结果集的方法。在查询数据量较大的场景,使用这两个方法会更高效。 - -```python title="blocks_iter 方法" -{{#include docs/examples/python/result_set_examples.py}} -``` -##### TaosCursor 类的使用 - -`TaosConnection` 类和 `TaosResult` 类已经实现了原生接口的所有功能。如果你对 PEP249 规范中的接口比较熟悉也可以使用 `TaosCursor` 类提供的方法。 - -```python title="TaosCursor 的使用" -{{#include docs/examples/python/cursor_usage_native_reference.py}} -``` - -:::note -TaosCursor 类使用原生连接进行写入、查询操作。在客户端多线程的场景下,这个游标实例必须保持线程独享,不能跨线程共享使用,否则会导致返回结果出现错误。 - -TaosCursor 的最佳实践是,查询开始时创建 cursor,用完之后就关闭,请避免复用同一个 cursor 多次执行。 -::: - + -##### RestClient 类的使用 - -`RestClient` 类是对于 [REST API](../rest-api) 的直接封装。它只包含一个 `sql()` 方法用于执行任意 SQL 语句, 并返回执行结果。 - -```python title="RestClient 的使用" -{{#include docs/examples/python/rest_client_example.py}} +```python +{{#include docs/examples/python/insert_rest.py:insert}} ``` -对于 `sql()` 方法更详细的介绍, 请参考 [RestClient](https://docs.taosdata.com/api/taospy/taosrest/restclient.html)。 - -##### TaosRestCursor 类的使用 - -`TaosRestCursor` 类是对 PEP249 Cursor 接口的实现。 - -```python title="TaosRestCursor 的使用" -{{#include docs/examples/python/connect_rest_examples.py:basic}} -``` -- `cursor.execute` : 用来执行任意 SQL 语句。 -- `cursor.rowcount`: 对于写入操作返回写入成功记录数。对于查询操作,返回结果集行数。 -- `cursor.description` : 返回字段的描述信息。关于描述信息的具体格式请参考[TaosRestCursor](https://docs.taosdata.com/api/taospy/taosrest/cursor.html)。 - -:::note -TaosRestCursor 的最佳实践是,查询开始时创建 cursor,用完之后就关闭,请避免复用同一个 cursor 多次执行。 -::: - + -##### Connection 类的使用 - -`Connection` 类既包含对 PEP249 Connection 接口的实现(如:cursor方法和 close 方法),也包含很多扩展功能(如: execute、 query、schemaless_insert 和 subscribe 方法。 - ```python -{{#include docs/examples/python/connect_websocket_examples.py:basic}} +{{#include docs/examples/python/insert_ws.py:insert}} ``` -- `conn.execute`: 用来执行任意 SQL 语句,返回影响的行数 -- `conn.query`: 用来执行查询 SQL 语句,返回查询结果 - +> NOW 为系统内部函数,默认为客户端所在计算机当前时间。 +> `NOW + 1s` 代表客户端当前时间往后加 1 秒,数字后面代表时间单位:a(毫秒),s(秒),m(分),h(小时),d(天),w(周),n(月),y(年)。 + ### 查询数据 @@ -493,7 +419,7 @@ TaosRestCursor 的最佳实践是,查询开始时创建 cursor,用完之后 `TaosConnection` 类的 `query` 方法可以用来查询数据,返回 `TaosResult` 类型的结果数据。 ```python -{{#include docs/examples/python/connection_usage_native_reference.py:query}} +{{#include docs/examples/python/insert_native.py:query}} ``` :::tip @@ -507,7 +433,7 @@ TaosRestCursor 的最佳实践是,查询开始时创建 cursor,用完之后 RestClient 类是对于 REST API 的直接封装。它只包含一个 sql() 方法用于执行任意 SQL 语句, 并返回执行结果。 ```python -{{#include docs/examples/python/rest_client_example.py}} +{{#include docs/examples/python/insert_rest.py:query}} ``` 对于 `sql()` 方法更详细的介绍, 请参考 [RestClient](https://docs.taosdata.com/api/taospy/taosrest/restclient.html)。 @@ -519,7 +445,7 @@ RestClient 类是对于 REST API 的直接封装。它只包含一个 sql() 方 `TaosConnection` 类的 `query` 方法可以用来查询数据,返回 `TaosResult` 类型的结果数据。 ```python -{{#include docs/examples/python/connect_websocket_examples.py:basic}} +{{#include docs/examples/python/insert_ws.py:query}} ``` @@ -527,36 +453,15 @@ RestClient 类是对于 REST API 的直接封装。它只包含一个 sql() 方 ### 执行带有 reqId 的 SQL -使用可选的 req_id 参数,指定请求 id,可以用于 tracing + -##### TaosConnection 类的使用 - 类似上文介绍的使用方法,增加 `req_id` 参数。 -```python title="execute 方法" -{{#include docs/examples/python/connection_usage_native_reference_with_req_id.py:insert}} -``` - -```python title="query 方法" -{{#include docs/examples/python/connection_usage_native_reference_with_req_id.py:query}} -``` - -##### TaosResult 类的使用 - -类似上文介绍的使用方法,增加 `req_id` 参数。 - -```python title="blocks_iter 方法" -{{#include docs/examples/python/result_set_with_req_id_examples.py}} -``` -##### TaosCursor 类的使用 - -`TaosConnection` 类和 `TaosResult` 类已经实现了原生接口的所有功能。如果你对 PEP249 规范中的接口比较熟悉也可以使用 `TaosCursor` 类提供的方法。 - -```python title="TaosCursor 的使用" -{{#include docs/examples/python/cursor_usage_native_reference_with_req_id.py}} +```python +{{#include docs/examples/python/insert_native.py:req_id}} ``` @@ -564,27 +469,10 @@ RestClient 类是对于 REST API 的直接封装。它只包含一个 sql() 方 类似上文介绍的使用方法,增加 `req_id` 参数。 -##### RestClient 类的使用 - -`RestClient` 类是对于 [REST API](../rest-api) 的直接封装。它只包含一个 `sql()` 方法用于执行任意 SQL 语句, 并返回执行结果。 - -```python title="RestClient 的使用" -{{#include docs/examples/python/rest_client_with_req_id_example.py}} +```python +{{#include docs/examples/python/insert_rest.py:req_id}} ``` -对于 `sql()` 方法更详细的介绍, 请参考 [RestClient](https://docs.taosdata.com/api/taospy/taosrest/restclient.html)。 - -##### TaosRestCursor 类的使用 - -`TaosRestCursor` 类是对 PEP249 Cursor 接口的实现。 - -```python title="TaosRestCursor 的使用" -{{#include docs/examples/python/connect_rest_with_req_id_examples.py:basic}} -``` -- `cursor.execute` : 用来执行任意 SQL 语句。 -- `cursor.rowcount`: 对于写入操作返回写入成功记录数。对于查询操作,返回结果集行数。 -- `cursor.description` : 返回字段的描述信息。关于描述信息的具体格式请参考[TaosRestCursor](https://docs.taosdata.com/api/taospy/taosrest/cursor.html)。 - @@ -592,36 +480,7 @@ RestClient 类是对于 REST API 的直接封装。它只包含一个 sql() 方 类似上文介绍的使用方法,增加 `req_id` 参数。 ```python -{{#include docs/examples/python/connect_websocket_with_req_id_examples.py:basic}} -``` - -- `conn.execute`: 用来执行任意 SQL 语句,返回影响的行数 -- `conn.query`: 用来执行查询 SQL 语句,返回查询结果 - - - - -### 与 pandas 一起使用 - - - - -```python -{{#include docs/examples/python/conn_native_pandas.py}} -``` - - - - -```python -{{#include docs/examples/python/conn_rest_pandas.py}} -``` - - - - -```python -{{#include docs/examples/python/conn_websocket_pandas.py}} +{{#include docs/examples/python/insert_ws.py:req_id}} ``` @@ -634,130 +493,15 @@ TDengine 的 Python 连接器支持参数绑定风格的 Prepare API 方式写 -##### 创建 stmt - -Python 连接器的 `Connection` 提供了 `statement` 方法用于创建参数绑定对象 stmt,该方法接收 sql 字符串作为参数,sql 字符串目前仅支持用 `?` 来代表绑定的参数。 - -``` -import taos - -conn = taos.connect() -stmt = conn.statement("insert into log values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)") -``` - -##### 参数绑定 - -调用 `new_multi_binds` 函数创建 params 列表,用于参数绑定。 - -``` -params = new_multi_binds(16) -params[0].timestamp((1626861392589, 1626861392590, 1626861392591)) -params[1].bool((True, None, False)) -params[2].tinyint([-128, -128, None]) # -128 is tinyint null -params[3].tinyint([0, 127, None]) -params[4].smallint([3, None, 2]) -params[5].int([3, 4, None]) -params[6].bigint([3, 4, None]) -params[7].tinyint_unsigned([3, 4, None]) -params[8].smallint_unsigned([3, 4, None]) -params[9].int_unsigned([3, 4, None]) -params[10].bigint_unsigned([3, 4, None]) -params[11].float([3, None, 1]) -params[12].double([3, None, 1.2]) -params[13].binary(["abc", "dddafadfadfadfadfa", None]) -params[14].nchar(["涛思数据", None, "a long string with 中文字符"]) -params[15].timestamp([None, None, 1626861392591]) -``` - -调用 stmt 的 `bind_param` 以单行的方式设置 values 或 `bind_param_batch` 以多行的方式设置 values 方法绑定参数。 - -``` -stmt.bind_param_batch(params) -``` - -##### 执行 sql - -调用 stmt 的 `execute` 方法执行 sql - -``` -stmt.execute() -``` - -##### 关闭 stmt - -最后需要关闭 stmt。 - -``` -stmt.close() -``` - -##### 示例代码 - ```python -{{#include docs/examples/python/stmt_example.py}} +{{#include docs/examples/python/stmt_native.py:stmt}} ``` -##### 创建 stmt - -Python WebSocket 连接器的 `Connection` 提供了 `statement` 方法用于创建参数绑定对象 stmt,该方法接收 sql 字符串作为参数,sql 字符串目前仅支持用 `?` 来代表绑定的参数。 - -``` -import taosws - -conn = taosws.connect('taosws://localhost:6041/test') -stmt = conn.statement() -``` - -##### 解析 sql - -调用 stmt 的 `prepare` 方法来解析 insert 语句。 - -``` -stmt.prepare("insert into t1 values (?, ?, ?, ?)") -``` - -##### 参数绑定 - -调用 stmt 的 `bind_param` 方法绑定参数。 - -``` -stmt.bind_param([ - taosws.millis_timestamps_to_column([1686844800000, 1686844801000, 1686844802000, 1686844803000]), - taosws.ints_to_column([1, 2, 3, 4]), - taosws.floats_to_column([1.1, 2.2, 3.3, 4.4]), - taosws.varchar_to_column(['a', 'b', 'c', 'd']), -]) -``` - -调用 stmt 的 `add_batch` 方法,将参数加入批处理。 - -``` -stmt.add_batch() -``` - -##### 执行 sql - -调用 stmt 的 `execute` 方法执行 sql - -``` -stmt.execute() -``` - -##### 关闭 stmt - -最后需要关闭 stmt。 - -``` -stmt.close() -``` - -##### 示例代码 - ```python -{{#include docs/examples/python/stmt_websocket_example.py}} +{{#include docs/examples/python/stmt_ws.py:stmt}} ``` @@ -767,46 +511,18 @@ stmt.close() 连接器支持无模式写入功能。 - - -##### 简单写入 + ```python -{{#include docs/examples/python/schemaless_insert.py}} -``` - -##### 带有 ttl 参数的写入 - -```python -{{#include docs/examples/python/schemaless_insert_ttl.py}} -``` - -##### 带有 req_id 参数的写入 - -```python -{{#include docs/examples/python/schemaless_insert_req_id.py}} +{{#include docs/examples/python/schemaless_native.py}} ``` - - -##### 简单写入 + ```python -{{#include docs/examples/python/schemaless_insert_raw.py}} -``` - -##### 带有 ttl 参数的写入 - -```python -{{#include docs/examples/python/schemaless_insert_raw_ttl.py}} -``` - -##### 带有 req_id 参数的写入 - -```python -{{#include docs/examples/python/schemaless_insert_raw_req_id.py}} +{{#include docs/examples/python/schemaless_ws.py}} ``` @@ -814,14 +530,15 @@ stmt.close() ### 执行带有 reqId 的无模式写入 -连接器的 `schemaless_insert` 和 `schemaless_insert_raw` 方法支持 `req_id` 可选参数,此 `req_Id` 可用于请求链路追踪。 +连接器的 `schemaless_insert` 和 `schemaless_insert_raw` 方法支持 `req_id` 可选参数,此 `req_id` 可用于请求链路追踪。 ```python -{{#include docs/examples/python/schemaless_insert_req_id.py}} -``` - -```python -{{#include docs/examples/python/schemaless_insert_raw_req_id.py}} +conn.schemaless_insert( + lines=lineDemo, + protocol=taos.SmlProtocol.LINE_PROTOCOL, + precision=taos.SmlPrecision.NANO_SECONDS, + req_id=1, +) ``` ### 数据订阅 @@ -830,194 +547,56 @@ stmt.close() #### 创建 Topic -创建 Topic 相关请参考 [数据订阅文档](../../develop/tmq/#创建-topic)。 +```python +{{#include docs/examples/python/tmq_native.py:create_topic}} +``` #### 创建 Consumer - - - - -`Consumer` 提供了 Python 连接器订阅 TMQ 数据的 API。创建 Consumer 语法为 `consumer = Consumer(configs)`,参数定义请参考 [数据订阅文档](../../develop/tmq/#创建消费者-consumer)。 - ```python -from taos.tmq import Consumer - -consumer = Consumer({"group.id": "local", "td.connect.ip": "127.0.0.1"}) +{{#include docs/examples/python/tmq_native.py:create_consumer}} ``` - - - - -除了原生的连接方式,Python 连接器还支持通过 websocket 订阅 TMQ 数据,使用 websocket 方式订阅 TMQ 数据需要安装 `taos-ws-py`。 - -taosws `Consumer` API 提供了基于 Websocket 订阅 TMQ 数据的 API。创建 Consumer 语法为 `consumer = Consumer(conf=configs)`,使用时需要指定 `td.connect.websocket.scheme` 参数值为 "ws",参数定义请参考 [数据订阅文档](../../develop/tmq/#%E5%88%9B%E5%BB%BA%E6%B6%88%E8%B4%B9%E8%80%85-consumer)。 - -```python -import taosws - -consumer = taosws.Consumer(conf={"group.id": "local", "td.connect.websocket.scheme": "ws"}) -``` - - - #### 订阅 topics - - - - -Consumer API 的 `subscribe` 方法用于订阅 topics,consumer 支持同时订阅多个 topic。 - ```python -consumer.subscribe(['topic1', 'topic2']) +{{#include docs/examples/python/tmq_native.py:subscribe}} ``` - - - -Consumer API 的 `subscribe` 方法用于订阅 topics,consumer 支持同时订阅多个 topic。 - -```python -consumer.subscribe(['topic1', 'topic2']) -``` - - - - #### 消费数据 - - - - -Consumer API 的 `poll` 方法用于消费数据,`poll` 方法接收一个 float 类型的超时时间,超时时间单位为秒(s),`poll` 方法在超时之前返回一条 Message 类型的数据或超时返回 `None`。消费者必须通过 Message 的 `error()` 方法校验返回数据的 error 信息。 - ```python -while True: - message = consumer.poll(1) - if not message: - continue - err = message.error() - if err is not None: - raise err - val = message.value() - - for block in val: - print(block.fetchall()) +{{#include docs/examples/python/tmq_native.py:consume}} ``` - - - -Consumer API 的 `poll` 方法用于消费数据,`poll` 方法接收一个 float 类型的超时时间,超时时间单位为秒(s),`poll` 方法在超时之前返回一条 Message 类型的数据或超时返回 `None`。 - -```python -while True: - message = consumer.poll(1) - if not message: - continue - - for block in message: - for row in block: - print(row) -``` - - - - #### 获取消费进度 - - - - Consumer API 的 `assignment` 方法用于获取 Consumer 订阅的所有 topic 的消费进度,返回结果类型为 TopicPartition 列表。 ```python -assignments = consumer.assignment() +{{#include docs/examples/python/tmq_native.py:assignment}} ``` Consumer API 的 `seek` 方法用于重置 Consumer 的消费进度到指定位置,方法参数类型为 TopicPartition。 ```python -tp = TopicPartition(topic='topic1', partition=0, offset=0) -consumer.seek(tp) +{{#include docs/examples/python/tmq_native.py:consume}} ``` - - - -Consumer API 的 `assignment` 方法用于获取 Consumer 订阅的所有 topic 的消费进度,返回结果类型为 TopicPartition 列表。 - -```python -assignments = consumer.assignment() -``` - -Consumer API 的 `seek` 方法用于重置 Consumer 的消费进度到指定位置。 - -```python -consumer.seek(topic='topic1', partition=0, offset=0) -``` - - - - #### 关闭订阅 - - - - 消费结束后,应当取消订阅,并关闭 Consumer。 ```python -consumer.unsubscribe() -consumer.close() +{{#include docs/examples/python/tmq_native.py:unsubscribe}} ``` - - - -消费结束后,应当取消订阅,并关闭 Consumer。 - -```python -consumer.unsubscribe() -consumer.close() -``` - - - - #### 完整示例 - - - - ```python -{{#include docs/examples/python/tmq_example.py}} +{{#include docs/examples/python/tmq_native.py}} ``` -```python -{{#include docs/examples/python/tmq_assignment_example.py:taos_get_assignment_and_seek_demo}} -``` - - - - -```python -{{#include docs/examples/python/tmq_websocket_example.py}} -``` - -```python -{{#include docs/examples/python/tmq_websocket_assgnment_example.py:taosws_get_assignment_and_seek_demo}} -``` - - - - ### 更多示例程序 | 示例程序链接 | 示例程序内容 | diff --git a/docs/zh/08-connector/35-node.mdx b/docs/zh/08-connector/35-node.mdx index 25f8bdf177..778da19b34 100644 --- a/docs/zh/08-connector/35-node.mdx +++ b/docs/zh/08-connector/35-node.mdx @@ -6,337 +6,328 @@ title: TDengine Node.js Connector import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem"; +import RequestId from "./_request_id.mdx"; -import Preparation from "./_preparation.mdx"; -import NodeInsert from "../07-develop/03-insert-data/_js_sql.mdx"; -import NodeInfluxLine from "../07-develop/03-insert-data/_js_line.mdx"; -import NodeOpenTSDBTelnet from "../07-develop/03-insert-data/_js_opts_telnet.mdx"; -import NodeOpenTSDBJson from "../07-develop/03-insert-data/_js_opts_json.mdx"; -import NodeQuery from "../07-develop/04-query-data/_js.mdx"; +`@tdengine/websocket` 是 TDengine 的官方 Node.js 语言连接器。Node.js 开发人员可以通过它开发存取 TDengine 数据库的的应用软件。 -`@tdengine/client` 和 `@tdengine/rest` 是 TDengine 的官方 Node.js 语言连接器。 Node.js 开发人员可以通过它开发可以存取 TDengine 集群数据的应用软件。注意:从 TDengine 3.0 开始 Node.js 原生连接器的包名由 `td2.0-connector` 改名为 `@tdengine/client` 而 rest 连接器的包名由 `td2.0-rest-connector` 改为 `@tdengine/rest`。并且不与 TDengine 2.x 兼容。 +Node.js 连接器源码托管在 [GitHub](https://github.com/taosdata/taos-connector-node/tree/main)。 -`@tdengine/client` 是**原生连接器**,它通过 TDengine 客户端驱动程序(taosc)连接 TDengine 运行实例,支持数据写入、查询、订阅、schemaless 接口和参数绑定接口等功能。`@tdengine/rest` 是 **REST 连接器**,它通过 taosAdapter 提供的 REST 接口连接 TDengine 的运行实例。REST 连接器可以在任何平台运行,但性能略为下降,接口实现的功能特性集合和原生接口有少量不同。 +## 连接方式 -Node.js 连接器源码托管在 [GitHub](https://github.com/taosdata/taos-connector-node/tree/3.0)。 +Node.js 连接器目前仅支持 Websocket 连接器, 其通过 taosAdapter 提供的 Websocket 接口连接 TDengine 实例。 + +连接方式的详细介绍请参考:[连接器建立连接的方式](../../develop/connect/#连接器建立连接的方式) ## 支持的平台 -原生连接器支持的平台和 TDengine 客户端驱动支持的平台一致。 -REST 连接器支持所有能运行 Node.js 的平台。 +支持 Node.js 14及以上版本。 -## 版本支持 +## 版本历史 -请参考[版本支持列表](../#版本支持) +| Node.js 连接器 版本 | 主要变化 | TDengine 版本 | +| :------------------: | :----------------------: | :----------------: | +| 3.1.0 | 新版本发布,支持 WebSocket 连接 | 3.2.0.0 及更高版本 | ## 支持的功能特性 - - +- 连接管理 +- SQL写入 +- SQL查询 +- 参数绑定 +- 数据订阅 +- 无模式写入 -1. 连接管理 -2. 普通查询 -3. 连续查询 -4. 参数绑定 -5. 订阅功能 -6. Schemaless +## 处理异常 - - +在调用连接器 api 报错后,通过 try catch 可以获取到错误的信息和错误码。 -1. 连接管理 -2. 普通查询 -3. 连续查询 +错误说明:Node.js 连接器错误码在 100 到 110 之间,之外的错误为 TDengine 其他功能模块的报错。 - - +具体的连接器错误码请参考: +| Error Code | Description | Suggested Actions | +| ---------- | -------------------------------------------------------------| ----------------------------------------------------------------------------------------- | +| 100 | invalid variables | 参数不合法,请检查相应接口规范,调整参数类型及大小。 | +| 101 | invalid url | url 错误,请检查 url 是否填写正确。 | +| 102 | received server data but did not find a callback for processing | 接收到服务端数据但没有找到上层回调 | +| 103 | invalid message type | 接收到的消息类型无法识别,请检查服务端是否正常。 | +| 104 | connection creation failed | 连接创建失败,请检查网络是否正常。 | +| 105 | websocket request timeout | 请求超时 | +| 106 | authentication fail | 认证失败,请检查用户名,密码是否正确。 | +| 107 | unknown sql type in tdengine | 请检查 TDengine 支持的 Data Type 类型。 | +| 108 | connection has been closed | 连接已经关闭,请检查 Connection 是否关闭后再次使用,或是连接是否正常。 | +| 109 | fetch block data parse fail | 获取到的查询数据,解析失败 | +| 110 | websocket connection has reached its maximum limit | Websocket 连接达到上限 | + +## 类型映射 + +下表为 TDengine DataType 和 Node.js DataType 之间的映射关系 + +| TDengine DataType | Node.js DataType| +|-------------------|-------------| +| TIMESTAMP | bigint | +| TINYINT | number | +| SMALLINT | number | +| INT | number | +| BIGINT | bigint | +| TINYINT UNSIGNED | number | +| SMALLINT UNSIGNED | number | +| INT UNSIGNED | number | +| BIGINT UNSIGNED | bigint | +| FLOAT | number | +| DOUBLE | number | +| BOOL | boolean | +| BINARY | string | +| NCHAR | string | +| JSON | string | +| VARBINARY | ArrayBuffer | +| GEOMETRY | ArrayBuffer | + +**注意**:JSON 类型仅在 tag 中支持。 ## 安装步骤 ### 安装前准备 -- 安装 Node.js 开发环境 -- 如果使用 REST 连接器,跳过此步。但如果使用原生连接器,请安装 TDengine 客户端驱动,具体步骤请参考[安装客户端驱动](../#安装客户端驱动)。我们使用 [node-gyp](https://github.com/nodejs/node-gyp) 和 TDengine 实例进行交互,还需要根据具体操作系统来安装下文提到的一些依赖工具。 +- 安装 Node.js 开发环境, 使用14以上版本。下载链接: https://nodejs.org/en/download/ - - - -- `python` (建议`v2.7` , `v3.x.x` 目前还不支持) -- `@tdengine/client` 3.0.0 支持 Node.js LTS v10.9.0 或更高版本, Node.js LTS v12.8.0 或更高版本;其他版本可能存在包兼容性的问题 -- `make` -- C 语言编译器,[GCC](https://gcc.gnu.org) v4.8.5 或更高版本 - - - - - -- `python` (建议`v2.7` , `v3.x.x` 目前还不支持) -- `@tdengine/client` 3.0.0 目前是只支持 Node.js v12.22.12 或 v12 的更高版本;其他版本可能存在包兼容性的问题 -- `make` -- C 语言编译器,[GCC](https://gcc.gnu.org) v4.8.5 或更高版本 - - - - - -- 安装方法 1 - -使用微软的[ windows-build-tools ](https://github.com/felixrieseberg/windows-build-tools)在`cmd` 命令行界面执行`npm install --global --production windows-build-tools` 即可安装所有的必备工具。 - -- 安装方法 2 - -手动安装以下工具: - -- 安装 Visual Studio 相关:[Visual Studio Build 工具](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools) 或者 [Visual Studio 2017 Community](https://visualstudio.microsoft.com/pl/thank-you-downloading-visual-studio/?sku=Community) -- 安装 [Python](https://www.python.org/downloads/) 2.7(`v3.x.x` 暂不支持) 并执行 `npm config set python python2.7` -- 进入`cmd`命令行界面,`npm config set msvs_version 2017` - -参考微软的 Node.js 用户手册[ Microsoft's Node.js Guidelines for Windows](https://github.com/Microsoft/nodejs-guidelines/blob/master/windows-environment.md#compiling-native-addon-modules)。 - -如果在 Windows 10 ARM 上使用 ARM64 Node.js,还需添加 "Visual C++ compilers and libraries for ARM64" 和 "Visual C++ ATL for ARM64"。 - - - - -### 使用 npm 安装 - - - +### 使用 npm 安装 Node.js 连接器 ```bash -npm install @tdengine/client +npm install @tdengine/websocket ``` - - - -```bash -npm install @tdengine/rest -``` - - - - ### 安装验证 - - - -在安装好 TDengine 客户端后,使用 nodejsChecker.js 程序能够验证当前环境是否支持 Node.js 方式访问 TDengine。 - 验证方法: -- 新建安装验证目录,例如:`~/tdengine-test`,下载 GitHub 上 [nodejsChecker.js 源代码](https://github.com/taosdata/taos-connector-node/blob/3.0/nodejs/examples/nodejsChecker.js)到本地。 +- 新建安装验证目录,例如:`~/tdengine-test`,下载 GitHub 上 [nodejsChecker.js 源代码](https://github.com/taosdata/TDengine/tree/main/docs/examples/node/websocketexample/nodejsChecker.js)到本地。 - 在命令行中执行以下命令。 ```bash -npm init -y -npm install @tdengine/client -node nodejsChecker.js host=localhost + npm init -y + npm install @tdengine/websocket + node nodejsChecker.js ``` -- 执行以上步骤后,在命令行会输出 nodejsChecker.js 连接 TDengine 实例,并执行简单插入和查询的结果。 - - - - -在安装好 TDengine 客户端后,使用 nodejsChecker.js 程序能够验证当前环境是否支持 Node.js 方式访问 TDengine。 - -验证方法: - -- 新建安装验证目录,例如:`~/tdengine-test`,下载 GitHub 上 [restChecker.js 源代码](https://github.com/taosdata/TDengine/tree/3.0/docs/examples/node/restexample/restChecker.js)到本地。 - -- 在命令行中执行以下命令。 - -```bash -npm init -y -npm install @tdengine/rest -node restChecker.js -``` - -- 执行以上步骤后,在命令行会输出 restChecker.js 连接 TDengine 实例,并执行简单插入和查询的结果。 - - - - - +- 执行以上步骤后,在命令行会输出 nodeChecker.js 连接 TDengine 实例,并执行简单插入和查询的结果。 ## 建立连接 -请选择使用一种连接器。 - - - - -安装并引用 `@tdengine/client` 包。 +安装并引用 `@tdengine/websocket` 包。 +**注意**: +- 链接器使用结束后,需要调用 taos.destroy() 释放连接器资源 ```javascript -//A cursor also needs to be initialized in order to interact with TDengine from Node.js. -const taos = require("@tdengine/client"); -var conn = taos.connect({ - host: "127.0.0.1", - user: "root", - password: "taosdata", - config: "/etc/taos", - port: 0, -}); -var cursor = conn.cursor(); // Initializing a new cursor +const taos = require("@tdengine/websocket"); -//Close a connection -conn.close(); +//数据库操作...... + +taos.destroy(); ``` - - - -安装并引用 `@tdengine/rest` 包。 - ```javascript -//A cursor also needs to be initialized in order to interact with TDengine from Node.js. -import { options, connect } from "@tdengine/rest"; -options.path = "/rest/sql"; -// set host -options.host = "localhost"; -// set other options like user/passwd - -let conn = connect(options); -let cursor = conn.cursor(); +WSConfig配置Websocket参数如下: + getToken(): string | undefined | null; + setToken(token: string): void; + getUser(): string | undefined | null; + setUser(user: string): void; + getPwd(): string | undefined | null; + setPwd(pws: string): void; + getDb(): string | undefined | null; + setDb(db: string): void; + getUrl(): string; + setUrl(url: string): void; + setTimeOut(ms: number): void; + getTimeOut(): number | undefined | null; ``` - - +```javascript +{{#include docs/examples/node/websocketexample/sql_example.js:createConnect}} +``` ## 使用示例 -### 写入数据 +### 创建数据库和表 -#### SQL 写入 +```javascript +{{#include docs/examples/node/websocketexample/sql_example.js:create_db_and_table}} +``` +> **注意**:如果不使用 `USE power` 指定数据库,则后续对表的操作都需要增加数据库名称作为前缀,如 power.meters。 - - +### 插入数据 - - - - - -```js -{{#include docs/examples/node/restexample/insert_example.js}} +```javascript +{{#include docs/examples/node/websocketexample/sql_example.js:insertData}} ``` - - - - - -#### InfluxDB 行协议写入 - - - - - - - - - -#### OpenTSDB Telnet 行协议写入 - - - - - - - - - -#### OpenTSDB JSON 行协议写入 - - - - - - - - +> NOW 为系统内部函数,默认为客户端所在计算机当前时间。 +> `NOW + 1s` 代表客户端当前时间往后加 1 秒,数字后面代表时间单位:a(毫秒),s(秒),m(分),h(小时),d(天),w(周),n(月),y(年)。 ### 查询数据 - - - - - - - - - -```js -{{#include docs/examples/node/restexample/query_example.js}} +```javascript +{{#include docs/examples/node/websocketexample/sql_example.js:queryData}} ``` - - +> 查询到的数据 +```javascript +wsRow:meta:=> [ + { name: 'ts', type: 'TIMESTAMP', length: 8 }, + { name: 'current', type: 'FLOAT', length: 4 }, + { name: 'voltage', type: 'INT', length: 4 }, + { name: 'phase', type: 'FLOAT', length: 4 }, + { name: 'location', type: 'VARCHAR', length: 64}, + { name: 'groupid', type: 'INT', length: 4 } +] +wsRow:data:=> [ + [ 1714013737536n, 12.3, 221, 0.31, 'California.SanFrancisco', 3 ] +] +``` + +### 执行带有 reqId 的 SQL + + + +```javascript +{{#include docs/examples/node/websocketexample/sql_example.js:sqlWithReqid}} +``` + +### 通过参数绑定写入数据 + +TDengine 的 NodeJs 连接器支持参数绑定风格的 Prepare API 方式写入数据,和大多数数据库类似,目前仅支持用 ? 来代表待绑定的参数。采用这种方式写入数据时,能避免 SQL 语法解析的资源消耗,从而在很多情况下显著提升写入性能。 + +**注意**: +- 预处理语句中指定数据库与子表名称不要使用 `db.?`,应直接使用 `?`,然后在 setTableName 中指定数据库,如:`stmt.setTableName("db.t1")`。 + +示例代码: +```javascript +{{#include docs/examples/node/websocketexample/stmt_example.js}} +``` + +用于设定 TAG/VALUES 数据列的取值的方法总共有: + +```javascript +setBoolean(params: any[]): void; +setTinyInt(params: any[]): void; +setUTinyInt(params: any[]): void; +setSmallInt(params: any[]): void; +setUSmallInt(params: any[]): void; +setInt(params: any[]): void; +setUInt(params: any[]): void; +setBigint(params: any[]): void; +setUBigint(params: any[]): void; +setFloat(params: any[]): void; +setDouble(params: any[]): void; +setVarchar(params: any[]): void; +setBinary(params: any[]): void; +setNchar(params: any[]): void; +setJson(params: any[]): void; +setVarBinary(params: any[]): void; +setGeometry(params: any[]): void; +setTimestamp(params: any[]): void; +``` + +**注意**:JSON 类型仅在 tag 中支持。 + +### 无模式写入 + +TDengine 支持无模式写入功能。无模式写入兼容 InfluxDB 的 行协议(Line Protocol)、OpenTSDB 的 telnet 行协议和 OpenTSDB 的 JSON 格式协议。详情请参见[无模式写入](../../reference/schemaless/)。 + +```javascript +{{#include docs/examples/node/websocketexample/line_example.js}} +``` + +### 执行带有 reqId 的无模式写入 + +此 reqId 可用于请求链路追踪。 + +```javascript +await wsSchemaless.schemalessInsert([influxdbData], SchemalessProto.InfluxDBLineProtocol, Precision.NANO_SECONDS, ttl, reqId); +await wsSchemaless.schemalessInsert([telnetData], SchemalessProto.OpenTSDBTelnetLineProtocol, Precision.NANO_SECONDS, ttl, reqId); +await wsSchemaless.schemalessInsert([jsonData], SchemalessProto.OpenTSDBJsonFormatProtocol, Precision.NANO_SECONDS, ttl, reqId); +``` + +### 数据订阅 + +TDengine NodeJs 连接器支持订阅功能,应用 API 如下: + +#### 创建 Topic +```javascript +{{#include docs/examples/node/websocketexample/tmq_example.js:create_topic}} +``` + +#### 创建 Consumer + +```javascript +{{#include docs/examples/node/websocketexample/tmq_example.js:create_consumer}} +``` +> 参数说明 +- taos.TMQConstants.CONNECT_USER: 用户名。 +- taos.TMQConstants.CONNECT_PASS: 密码。 +- taos.TMQConstants.GROUP_ID: 所在的 group。 +- taos.TMQConstants.CLIENT_ID: client id。 +- taos.TMQConstants.WS_URL: taosAdapter 的url地址。 +- taos.TMQConstants.AUTO_OFFSET_RESET: 来确定消费位置为最新数据(latest)还是包含旧数据(earliest)。 +- taos.TMQConstants.ENABLE_AUTO_COMMIT: 是否允许自动提交。 +- taos.TMQConstants.AUTO_COMMIT_INTERVAL_MS: 自动提交间隔。 +- taos.TMQConstants.CONNECT_MESSAGE_TIMEOUT: 数据传输超时参数,单位 ms,默认为 10000 ms。 + +其他参数请参考:[Consumer 参数列表](../../develop/tmq/#数据订阅相关参数), 注意 TDengine 服务端自3.2.0.0版本开始消息订阅中的 auto.offset.reset 默认值发生变化。 + +#### 订阅消费数据 +```javascript +{{#include docs/examples/node/websocketexample/tmq_example.js:subscribe}} +``` + +#### 指定订阅 Offset + +```javascript +{{#include docs/examples/node/websocketexample/tmq_example.js:assignment}} +``` + +#### 关闭订阅 +```javascript +// 取消订阅 +consumer.unsubscribe(); +// 关闭消费 +consumer.close() +// 释放连接器资源 +taos.destroy(); +``` + +详情请参考:[数据订阅](../../develop/tmq) + +#### 完整示例 + +```javascript +{{#include docs/examples/node/websocketexample/tmq_example.js}} +``` ## 更多示例程序 -| 示例程序 | 示例程序描述 | +| 示例程序 | 示例程序描述 | | ------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------- | -| [basicUse](https://github.com/taosdata/taos-connector-node/blob/3.0/nodejs/examples/queryExample.js) | 基本的使用如如建立连接,执行 SQL 等操作。 | -| [stmtBindBatch](https://github.com/taosdata/taos-connector-node/blob/3.0/nodejs/examples/bindParamBatch.js) | 绑定多行参数插入的示例。 | | -| [stmtBindSingleParamBatch](https://github.com/taosdata/taos-connector-node/blob/3.0/nodejs/examples/bindSingleParamBatch.js) | 按列绑定参数插入的示例。 | -| [stmtQuery](https://github.com/taosdata/taos-connector-node/blob/3.0/nodejs/examples/stmtQuery.js) | 绑定参数查询的示例。 | -| [schemless insert](https://github.com/taosdata/taos-connector-node/blob/3.0/nodejs/examples/schemaless.js) | schemless 插入的示例。 | -| [TMQ](https://github.com/taosdata/taos-connector-node/blob/3.0/nodejs/examples/tmq.js) | 订阅的使用示例。 | -| [asyncQuery](https://github.com/taosdata/taos-connector-node/blob/3.0/nodejs/examples/asyncQueryExample.js) | 异步查询的使用示例。 | -| [REST](https://github.com/taosdata/taos-connector-node/blob/3.0/typescript-rest/example/example.ts) | 使用 REST 连接的 TypeScript 使用示例。 | +| [sql_example](https://github.com/taosdata/TDengine/tree/main/docs/examples/node/websocketexample/sql_example.js) | 基本的使用如如建立连接,执行 SQL 等操作。 | +| [stmt_example](https://github.com/taosdata/TDengine/tree/main/docs/examples/node/websocketexample/stmt_example.js) | 绑定参数插入的示例。 | | +| [line_example](https://github.com/taosdata/TDengine/tree/main/docs/examples/node/websocketexample/line_example.js) | 行协议写入示例。 | +| [telnet_line_example](https://github.com/taosdata/TDengine/tree/main/docs/examples/node/websocketexample/telnet_line_example.js) | OpenTSDB Telnet 行协议写入示例。 | +| [json_line_example](https://github.com/taosdata/TDengine/tree/main/docs/examples/node/websocketexample/json_line_example.js) | OpenTSDB JSON 行协议写入示例。 | +| [tmq_example](https://github.com/taosdata/TDengine/tree/main/docs/examples/node/websocketexample/tmq_example.js) | 订阅的使用示例。 | ## 使用限制 -native 连接器(`@tdengine/client`) >= v3.0.0 目前支持 node 的版本为:支持 >=v12.8.0 <= v12.9.1 || >=v10.20.0 <= v10.9.0 ;2.0.5 及更早版本支持 v10.x 版本,其他版本可能存在包兼容性的问题。 +- Node.js 连接器(`@tdengine/websocket`)支持 Node.js 14 以上版本,低于 14 的版本可能存在包兼容性的问题。 +- 目前只支持 WebSocket 连接,需要提前启动 taosAdapter +- 使用连接器结束后,需要调用 taos.connectorDestroy(); 释放连接器资源。 -## 其他说明 - -Node.js 连接器的使用参见[视频教程](https://www.taosdata.com/blog/2020/11/11/1957.html)。 ## 常见问题 -1. 使用 REST 连接需要启动 taosadapter。 +1. "Unable to establish connection" 或 "Unable to resolve FQDN" - ```bash - sudo systemctl start taosadapter - ``` + **原因**:一般都是因为配置 FQDN 不正确。 可以参考[如何彻底搞懂 TDengine 的 FQDN](https://www.taosdata.com/blog/2021/07/29/2741.html) 。 -2. Node.js 版本 - 原生连接器 `@tdengine/client` 目前兼容的 Node.js 版本为:>=v10.20.0 <= v10.9.0 || >=v12.8.0 <= v12.9.1 - -3. "Unable to establish connection","Unable to resolve FQDN" - - 一般都是因为配置 FQDN 不正确。 可以参考[如何彻底搞懂 TDengine 的 FQDN](https://www.taosdata.com/blog/2021/07/29/2741.html) 。 - -## 重要更新记录 - -### 原生连接器 - -| package name | version | TDengine version | 说明 | -|------------------|---------|---------------------|------------------------------------------------------------------| -| @tdengine/client | 3.0.0 | 3.0.0 | 支持TDengine 3.0 且不与2.x 兼容。 | -| td2.0-connector | 2.0.12 | 2.4.x;2.5.x;2.6.x | 修复 cursor.close() 报错的 bug。 | -| td2.0-connector | 2.0.11 | 2.4.x;2.5.x;2.6.x | 支持绑定参数、json tag、schemaless 接口等功能。 | -| td2.0-connector | 2.0.10 | 2.4.x;2.5.x;2.6.x | 支持连接管理,普通查询、连续查询、获取系统信息、订阅功能等功能。 | -### REST 连接器 - -| package name | version | TDengine version | 说明 | -|----------------------|---------|---------------------|---------------------------------------------------------------------------| -| @tdengine/rest | 3.0.0 | 3.0.0 | 支持 TDegnine 3.0,且不与2.x 兼容。 | -| td2.0-rest-connector | 1.0.7 | 2.4.x;2.5.x;2.6.x | 移除默认端口 6041。 | -| td2.0-rest-connector | 1.0.6 | 2.4.x;2.5.x;2.6.x | 修复create,insert,update,alter 等SQL 执行返回的 affectRows 错误的bug。 | -| td2.0-rest-connector | 1.0.5 | 2.4.x;2.5.x;2.6.x | 支持云服务 cloud Token; | -| td2.0-rest-connector | 1.0.3 | 2.4.x;2.5.x;2.6.x | 支持连接管理、普通查询、获取系统信息、错误信息、连续查询等功能。 | - -## API 参考 - -[API 参考](https://docs.taosdata.com/api/td2.0-connector/) diff --git a/docs/zh/08-connector/40-csharp.mdx b/docs/zh/08-connector/40-csharp.mdx index f88002d3dc..a175e6bf05 100644 --- a/docs/zh/08-connector/40-csharp.mdx +++ b/docs/zh/08-connector/40-csharp.mdx @@ -6,19 +6,22 @@ title: TDengine C# Connector import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; +import RequestId from "./_request_id.mdx"; `TDengine.Connector` 是 TDengine 提供的 C# 语言连接器。C# 开发人员可以通过它开发存取 TDengine 集群数据的 C# 应用软件。 -`TDengine.Connector` 连接器支持通过 TDengine 客户端驱动(taosc)建立与 TDengine 运行实例的连接,提供数据写入、查询、数据订阅、schemaless 数据写入、参数绑定接口数据写入等功能。 `TDengine.Connector` 自 v3.0.1 起还支持 WebSocket 连接,提供数据写入、查询、参数绑定接口数据写入等功能。 +## 连接方式 -本文介绍如何在 Linux 或 Windows 环境中安装 `TDengine.Connector`,并通过 `TDengine.Connector` 连接 TDengine 集群,进行数据写入、查询等基本操作。 +`TDengine.Connector` 提供两种形式的连接器 +* **原生连接**,通过 TDengine 客户端驱动程序(taosc)原生连接 TDengine 实例,支持数据写入、查询、数据订阅、schemaless 接口和参数绑定接口等功能。 +* **Websocket 连接**,通过 taosAdapter 提供的 Websocket 接口连接 TDengine 实例,WebSocket 连接实现的功能集合和原生连接有少量不同。(自 v3.0.1 起) + +连接方式的详细介绍请参考:[连接器建立连接的方式](../../develop/connect/#连接器建立连接的方式) + +## 兼容性 -:::warning * `TDengine.Connector` 3.1.0 版本进行了完整的重构,不再兼容 3.0.2 及以前版本。3.0.2 文档请参考 [nuget](https://www.nuget.org/packages/TDengine.Connector/3.0.2) * `TDengine.Connector` 3.x 不兼容 TDengine 2.x,如果在运行 TDengine 2.x 版本的环境下需要使用 C# 连接器请使用 TDengine.Connector 的 1.x 版本。 -::: - -`TDengine.Connector` 的源码托管在 [GitHub](https://github.com/taosdata/taos-connector-dotnet/tree/3.0)。 ## 支持的平台 @@ -30,9 +33,12 @@ TDengine 不再支持 32 位 Windows 平台。 ## 版本支持 -| **Connector version** | **TDengine version** | -|-----------------------|----------------------| -| 3.1.0 | 3.2.1.0/3.1.1.18 | +| **Connector 版本** | **TDengine 版本** | **主要功能** | +|------------------|------------------|----------------------------| +| 3.1.3 | 3.2.1.0/3.1.1.18 | 支持 WebSocket 自动重连 | +| 3.1.2 | 3.2.1.0/3.1.1.18 | 修复 schemaless 资源释放 | +| 3.1.1 | 3.2.1.0/3.1.1.18 | 支持 varbinary 和 geometry 类型 | +| 3.1.0 | 3.2.1.0/3.1.1.18 | WebSocket 使用原生实现 | ## 处理异常 @@ -57,6 +63,8 @@ TDengine 不再支持 32 位 Windows 平台。 | BINARY | byte[] | | NCHAR | string (utf-8编码) | | JSON | byte[] | +| VARBINARY | byte[] | +| GEOMETRY | byte[] | :::note JSON 类型仅在 tag 中支持。 @@ -68,7 +76,7 @@ JSON 类型仅在 tag 中支持。 * 安装 [.NET SDK](https://dotnet.microsoft.com/download) * [Nuget 客户端](https://docs.microsoft.com/en-us/nuget/install-nuget-client-tools) (可选安装) -* 安装 TDengine 客户端驱动,具体步骤请参考[安装客户端驱动](../#安装客户端驱动) +* 对于 Native 连接方式,需要安装 TDengine 客户端驱动,具体步骤请参考[安装客户端驱动](../#安装客户端驱动),WebSocket 连接方式无需安装。 ### 安装连接器 @@ -127,6 +135,11 @@ ConnectionStringBuilder 支持的参数如下: * connTimeout: WebSocket 连接超时时间,仅当 protocol 为 WebSocket 时有效,默认为 1 分钟,使用 `TimeSpan.Parse` 方法解析字符串为 `TimeSpan` 对象。 * readTimeout: WebSocket 读超时时间,仅当 protocol 为 WebSocket 时有效,默认为 5 分钟,使用 `TimeSpan.Parse` 方法解析字符串为 `TimeSpan` 对象。 * writeTimeout: WebSocket 写超时时间,仅当 protocol 为 WebSocket 时有效,默认为 10 秒,使用 `TimeSpan.Parse` 方法解析字符串为 `TimeSpan` 对象。 +* autoReconnect: 是否自动重连(连接器版本 3.1.3 及以上生效),默认为 false +> **注意**:启用自动重连仅对简单执行 SQL 语句以及 无模式写入、数据订阅有效。对于参数绑定无效。自动重连仅对连接建立时通过参数指定数据库有效,对后面的 `use db` 语句切换数据库无效。 + +* reconnectRetryCount: 重连次数(连接器版本 3.1.3 及以上生效),默认为 3 +* reconnectIntervalMs: 重连间隔时间(连接器版本 3.1.3 及以上生效),默认为 2000 ### 指定 URL 和 Properties 获取连接 @@ -407,6 +420,8 @@ namespace WSQuery ### 执行带有 reqId 的 SQL + + @@ -801,6 +816,10 @@ consumer 支持的配置参数如下: * auto.commit.interval.ms: 自动提交 offset 的间隔时间,默认为 5000 毫秒 * auto.offset.reset: 当 offset 不存在时,从哪里开始消费,可选值为 earliest 或 latest,默认为 latest * msg.with.table.name: 消息是否包含表名 +* ws.message.enableCompression: 是否启用 WebSocket 压缩(dotnet 版本 6 及以上,连接器版本 3.1.1 及以上生效),默认为 false +* ws.autoReconnect: 是否自动重连(连接器版本 3.1.3 及以上生效),默认为 false +* ws.reconnect.retry.count: 重连次数(连接器版本 3.1.3 及以上生效),默认为 3 +* ws.reconnect.interval.ms: 重连间隔时间(连接器版本 3.1.3 及以上生效),默认为 2000 支持订阅结果集 `Dictionary` key 为列名,value 为列值。 @@ -1187,4 +1206,4 @@ namespace WSADO ### 更多示例程序 -[示例程序](https://github.com/taosdata/taos-connector-dotnet/tree/3.0/examples) \ No newline at end of file +[示例程序](https://github.com/taosdata/taos-connector-dotnet/tree/3.0/examples) diff --git a/docs/zh/08-connector/45-php.mdx b/docs/zh/08-connector/45-php.mdx index 0f32b29bf7..9bc9662a72 100644 --- a/docs/zh/08-connector/45-php.mdx +++ b/docs/zh/08-connector/45-php.mdx @@ -7,7 +7,7 @@ title: PHP Connector PHP 连接器依赖 TDengine 客户端驱动。 -项目地址: +项目地址:\ TDengine 服务端或客户端安装后,`taos.h` 位于: diff --git a/docs/zh/08-connector/50-odbc.mdx b/docs/zh/08-connector/50-odbc.mdx index 0668aed7df..b3081ed9a9 100644 --- a/docs/zh/08-connector/50-odbc.mdx +++ b/docs/zh/08-connector/50-odbc.mdx @@ -14,6 +14,9 @@ TDengine ODBC 提供基于 WebSocket(推荐)和 原生连接两种方式连 想更多了解 TDengine 时序时序数据库的使用,可访问 [TDengine官方文档](https://docs.taosdata.com/intro/)。 +## ODBC 版本兼容性 +- 支持 ODBC 3.8 及以前所有版本。 + ## 安装 1. 仅支持 Windows 平台。Windows 上需要安装过 VC 运行时库,可在此下载安装 [VC运行时库](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170) 如果已经安装VS开发工具可忽略。 @@ -48,17 +51,17 @@ TDengine ODBC 支持两种连接 TDengine 数据库方式:Websocket 连接与 4.1 【DSN】:Data Source Name 必填,为新添加的 ODBC 数据源命名 - 4.2【Connection Type】 : 必选,选择连接类型,这里选择 【Websocket】 + 4.2【连接类型】 : 必选,选择连接类型,这里选择 【Websocket】 - 4.3【URL】必填,ODBC 数据源 URL,example: `http://localhost:6041`, 云服务的 url 示例: `https://gw.cloud.taosdata.com?token=your_token` + 4.3【URL】必填,ODBC 数据源 URL,示例: `http://localhost:6041`, 云服务的 url 示例: `https://gw.cloud.taosdata.com?token=your_token` - 4.4【Database】选填,需要连接的默认数据库 + 4.4【数据库】选填,需要连接的默认数据库 - 4.5【User】仅供第5步测试连接使用,选填,数据库用户名,如果不填,TDengine 默认 root + 4.5【用户名】仅供第5步测试连接使用,选填,数据库用户名,如果不填,TDengine 默认 root - 4.6【Password】仅供第5步测试连接使用,选填,数据库用户密码,如果不填,TDengine 默认 taosdata + 4.6【密码】仅供第5步测试连接使用,选填,数据库用户密码,如果不填,TDengine 默认 taosdata -5. 点【Test Connecting...】测试连接情况,如果成功,提示"connecting success" +5. 点【测试连接】测试连接情况,如果成功,提示"成功连接到URL" 6. 点【确定】,即可保存配置并退出 @@ -78,17 +81,17 @@ TDengine ODBC 支持两种连接 TDengine 数据库方式:Websocket 连接与 4.1 【DSN】:Data Source Name 必填,为新添加的 ODBC 数据源命名 - 4.2 【Connection Type】 : 必选,选择连接类型,这里选择 【Native】 原生连接; + 4.2 【连接类型】 : 必选,选择连接类型,这里选择 【Native】 原生连接; - 4.3 【Server】必填,ODBC 数据源 Server 地址,example: `localhost:6030` + 4.3 【服务器】必填,ODBC 数据源 服务器 地址,示例: `localhost:6030` - 4.4 【Database】选填,需要连接的默认数据库 + 4.4 【数据库】选填,需要连接的默认数据库 - 4.5 【User】仅供第5步测试连接使用,选填,数据库用户名,如果不填,TDengine 默认 root + 4.5 【用户名】仅供第5步测试连接使用,选填,数据库用户名,如果不填,TDengine 默认 root - 4.6 【Password】仅供第5步测试连接使用,选填,数据库用户密码,如果不填,TDengine 默认 taosdata + 4.6 【密码】仅供第5步测试连接使用,选填,数据库用户密码,如果不填,TDengine 默认 taosdata -5. 点【Test Connecting...】测试连接情况,如果成功,提示"connecting success" +5. 点【测试连接】测试连接情况,如果成功,提示"连接成功" 6. 点【确定】,即可保存配置并退出 diff --git a/docs/zh/08-connector/_05-schemaless.mdx b/docs/zh/08-connector/_05-schemaless.mdx index 8f0a9273b9..1f293219b5 100644 --- a/docs/zh/08-connector/_05-schemaless.mdx +++ b/docs/zh/08-connector/_05-schemaless.mdx @@ -19,7 +19,7 @@ TDengine 提供了兼容 InfluxDB (v1) 和 OpenTSDB 行协议的 Schemaless API - `precision` TDengine 使用的时间精度 - `u` TDengine 用户名 - `p` TDengine 密码 -- `ttl` 自动创建的子表生命周期,以子表的第一条数据的 TTL 参数为准,不可更新。更多信息请参考[创建表文档](taos-sql/table/#创建表)的 TTL 参数 +- `ttl` 自动创建的子表生命周期,以子表的第一条数据的 TTL 参数为准,不可更新。更多信息请参考[创建表文档](/../../taos-sql/table/#创建表)的 TTL 参数 注意: 目前不支持 InfluxDB 的 token 验证方式,仅支持 Basic 验证和查询参数验证。 diff --git a/docs/zh/08-connector/_preparation.mdx b/docs/zh/08-connector/_preparation.mdx index a0140e15b3..b7177b022f 100644 --- a/docs/zh/08-connector/_preparation.mdx +++ b/docs/zh/08-connector/_preparation.mdx @@ -2,7 +2,7 @@ :::info -由于 TDengine 的客户端驱动使用 C 语言编写,使用原生连接时需要加载系统对应安装在本地的客户端驱动共享库文件,通常包含在 TDengine 安装包。TDengine Linux 服务端安装包附带了 TDengine 客户端,也可以单独安装 [Linux 客户端](/get-started/) 。在 Windows 环境开发时需要安装 TDengine 对应的 [Windows 客户端](https://www.taosdata.com/cn/all-downloads/#TDengine-Windows-Client) 。 +由于 TDengine 的客户端驱动使用 C 语言编写,使用原生连接时需要加载系统对应安装在本地的客户端驱动共享库文件,通常包含在 TDengine 安装包。TDengine Linux 服务端安装包附带了 TDengine 客户端,也可以单独安装 [Linux 客户端](../get-started/) 。在 Windows 环境开发时需要安装 TDengine 对应的 [Windows 客户端](https://www.taosdata.com/cn/all-downloads/#TDengine-Windows-Client) 。 - libtaos.so: 在 Linux 系统中成功安装 TDengine 后,依赖的 Linux 版客户端驱动 libtaos.so 文件会被自动拷贝至 /usr/lib/libtaos.so,该目录包含在 Linux 自动扫描路径上,无需单独指定。 - taos.dll: 在 Windows 系统中安装完客户端之后,依赖的 Windows 版客户端驱动 taos.dll 文件会自动拷贝到系统默认搜索路径 C:/Windows/System32 下,同样无需要单独指定。 diff --git a/docs/zh/08-connector/_request_id.mdx b/docs/zh/08-connector/_request_id.mdx new file mode 100644 index 0000000000..64da0a0b1b --- /dev/null +++ b/docs/zh/08-connector/_request_id.mdx @@ -0,0 +1,7 @@ +reqId 可用于请求链路追踪,reqId 就像分布式系统中的 traceId 作用一样。一个请求可能需要经过多个服务或者模块才能完成。reqId 用于标识和关联这个请求的所有相关操作,以便于我们可以追踪和分析请求的完整路径。 +使用 reqId 有下面好处: +- **请求追踪**:通过将同一个 reqId 关联到一个请求的所有相关操作,可以追踪请求在系统中的完整路径 +- **性能分析**:通过分析一个请求的 reqId,可以了解请求在各个服务和模块中的处理时间,从而找出性能瓶颈 +- **故障诊断**:当一个请求失败时,可以通过查看与该请求关联的 reqId 来找出问题发生的位置 + +如果用户不设置reqId,连接器也会内部随机生成一个,但是还是建议用户设置,可以更好的跟用户请求关联起来。 diff --git a/docs/zh/08-connector/assets/odbc-native-config-zh.webp b/docs/zh/08-connector/assets/odbc-native-config-zh.webp index ed9005f2a195d6ec3db2ee41c53862bac57e8298..5589bc6cf76ae728ac00c88c8147a203801b0483 100644 GIT binary patch literal 15080 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 literal 6306 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 Y4x5@XZ)+WFYEoy~@~pr+D>16$K!ov6>Hq)$ diff --git a/docs/zh/08-connector/assets/odbc-ws-config-zh.webp b/docs/zh/08-connector/assets/odbc-ws-config-zh.webp index c8a6e11011da8a04e9847139bbb2cd8fcfb2bbd8..6a9cece9d90f93d4efecb436e666a3bc59fa558e 100644 GIT binary patch literal 15954 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 BAQS)q literal 6234 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.vgroups; 可以查看 cacheload +通过 show \.vgroups; 可以查看 cacheload 3. 判断 cachesize 是否够用 diff --git a/docs/zh/12-taos-sql/03-table.md b/docs/zh/12-taos-sql/03-table.md index 09580fb788..f5ff1b83ef 100644 --- a/docs/zh/12-taos-sql/03-table.md +++ b/docs/zh/12-taos-sql/03-table.md @@ -42,7 +42,7 @@ table_option: { **使用说明** 1. 表的第一个字段必须是 TIMESTAMP,并且系统自动将其设为主键。 -2. 除时间戳主键列之外,还可以通过 PRIAMRY KEY 关键字指定第二列为额外的主键列。被指定为主键列的第二列必须为整型或字符串类型(varchar)。 +2. 除时间戳主键列之外,还可以通过 PRIMARY KEY 关键字指定第二列为额外的主键列。被指定为主键列的第二列必须为整型或字符串类型(varchar)。 3. 表名最大长度为 192。 4. 表的每行长度不能超过 48KB(从 3.0.5.0 版本开始为 64KB);(注意:每个 BINARY/NCHAR/GEOMETRY 类型的列还会额外占用 2 个字节的存储位置)。 5. 子表名只能由字母、数字和下划线组成,且不能以数字开头,不区分大小写。 @@ -207,6 +207,8 @@ ALTER TABLE tb_name COMMENT 'string_value' DROP TABLE [IF EXISTS] [db_name.]tb_name [, [IF EXISTS] [db_name.]tb_name] ... ``` +**注意**:删除表并不会立即释放该表所占用的磁盘空间,而是把该表的数据标记为已删除,在查询时这些数据将不会再出现,但释放磁盘空间会延迟到系统自动或用户手动进行数据重整时。 + ## 查看表的信息 ### 显示所有表 diff --git a/docs/zh/12-taos-sql/04-stable.md b/docs/zh/12-taos-sql/04-stable.md index 923d7acdc6..9c6de9e220 100644 --- a/docs/zh/12-taos-sql/04-stable.md +++ b/docs/zh/12-taos-sql/04-stable.md @@ -112,6 +112,8 @@ DROP STABLE [IF EXISTS] [db_name.]stb_name 删除 STable 会自动删除通过 STable 创建的子表以及子表中的所有数据。 +**注意**:删除超级表并不会立即释放该表所占用的磁盘空间,而是把该表的数据标记为已删除,在查询时这些数据将不会再出现,但释放磁盘空间会延迟到系统自动或用户手动进行数据重整时。 + ## 修改超级表 ```sql diff --git a/docs/zh/12-taos-sql/06-select.md b/docs/zh/12-taos-sql/06-select.md index d754821749..acf42a3b8f 100755 --- a/docs/zh/12-taos-sql/06-select.md +++ b/docs/zh/12-taos-sql/06-select.md @@ -148,6 +148,9 @@ SELECT d1001.* FROM d1001,d1003 WHERE d1001.ts = d1003.ts; SELECT location, groupid, current FROM d1001 LIMIT 2; ``` +### 别名 +别名的命名规则与列相同,支持直接指定 UTF-8 编码格式的中文别名。 + ### 结果去重 `DISTINCT` 关键字可以对结果集中的一列或多列进行去重,去除的列既可以是标签列也可以是数据列。 @@ -280,7 +283,7 @@ GROUP BY 子句中的表达式可以包含表或视图中的任何列,这些 PARTITION BY 子句是 TDengine 3.0版本引入的特色语法,用于根据 part_list 对数据进行切分,在每个切分的分片中可以进行各种计算。 -PARTITION BY 与 GROUP BY 基本含义相似,都是按照指定列表进行数据分组然后进行计算,不同点在于 PARTITION BY 没有 GROUP BY 子句的 SELECT 列表的各种限制,组内可以进行任意运算(常量、聚合、标量、表达式等),因此在使用上 PARTITION BY 完全兼容 GROUP BY,所有使用 GROUP BY 子句的地方都可以替换为 PARTITION BY。 +PARTITION BY 与 GROUP BY 基本含义相似,都是按照指定列表进行数据分组然后进行计算,不同点在于 PARTITION BY 没有 GROUP BY 子句的 SELECT 列表的各种限制,组内可以进行任意运算(常量、聚合、标量、表达式等),因此在使用上 PARTITION BY 完全兼容 GROUP BY,所有使用 GROUP BY 子句的地方都可以替换为 PARTITION BY, 需要注意的是在没有聚合查询时两者的查询结果可能存在差异。 因为 PARTITION BY 没有返回一行聚合数据的要求,因此还可以支持在分组切片后的各种窗口运算,所有需要分组进行的窗口运算都只能使用 PARTITION BY 子句。 @@ -456,6 +459,7 @@ SELECT ... FROM (SELECT ... FROM ...) ...; :::info - 内层查询的返回结果将作为“虚拟表”供外层查询使用,此虚拟表建议起别名,以便于外层查询中方便引用。 +- 外层查询支持直接通过列名或`列名`的形式引用内层查询的列或伪列。 - 在内层和外层查询中,都支持普通的表间/超级表间 JOIN。内层查询的计算结果也可以再参与数据子表的 JOIN 操作。 - 内层查询支持的功能特性与非嵌套的查询语句能力是一致的。 - 内层查询的 ORDER BY 子句一般没有意义,建议避免这样的写法以免无谓的资源消耗。 diff --git a/docs/zh/12-taos-sql/08-delete-data.mdx b/docs/zh/12-taos-sql/08-delete-data.mdx index 15becd2593..3f9c431338 100644 --- a/docs/zh/12-taos-sql/08-delete-data.mdx +++ b/docs/zh/12-taos-sql/08-delete-data.mdx @@ -6,6 +6,8 @@ title: "删除数据" 删除数据是 TDengine 提供的根据指定时间段删除指定表或超级表中数据记录的功能,方便用户清理由于设备故障等原因产生的异常数据。 +**注意**:删除数据并不会立即释放该表所占用的磁盘空间,而是把该表的数据标记为已删除,在查询时这些数据将不会再出现,但释放磁盘空间会延迟到系统自动或用户手动进行数据重整时。 + **语法:** ```sql diff --git a/docs/zh/12-taos-sql/10-function.md b/docs/zh/12-taos-sql/10-function.md index 26996a39fd..3e78ac0da0 100644 --- a/docs/zh/12-taos-sql/10-function.md +++ b/docs/zh/12-taos-sql/10-function.md @@ -448,7 +448,7 @@ TO_JSON(str_literal) **返回结果数据类型**: JSON。 -**适用数据类型**: JSON 字符串,形如 '{ "literal" : literal }'。'{}'表示空值。键必须为字符串字面量,值可以为数值字面量、字符串字面量、布尔字面量或空值字面量。str_literal中不支持转义符。 +**适用数据类型**: JSON 字符串,形如 '\{ "literal" : literal }'。'\{}'表示空值。键必须为字符串字面量,值可以为数值字面量、字符串字面量、布尔字面量或空值字面量。str_literal中不支持转义符。 **嵌套子查询支持**:适用于内层查询和外层查询。 @@ -883,11 +883,11 @@ HISTOGRAM(expr,bin_type, bin_description, normalized) - "user_input": "[1, 3, 5, 7]" 用户指定 bin 的具体数值。 - - "linear_bin": "{"start": 0.0, "width": 5.0, "count": 5, "infinity": true}" + - "linear_bin": "\{"start": 0.0, "width": 5.0, "count": 5, "infinity": true}" "start" 表示数据起始点,"width" 表示每次 bin 偏移量, "count" 为 bin 的总数,"infinity" 表示是否添加(-inf, inf)作为区间起点和终点, 生成区间为[-inf, 0.0, 5.0, 10.0, 15.0, 20.0, +inf]。 - - "log_bin": "{"start":1.0, "factor": 2.0, "count": 5, "infinity": true}" + - "log_bin": "\{"start":1.0, "factor": 2.0, "count": 5, "infinity": true}" "start" 表示数据起始点,"factor" 表示按指数递增的因子,"count" 为 bin 的总数,"infinity" 表示是否添加(-inf, inf)作为区间起点和终点, 生成区间为[-inf, 1.0, 2.0, 4.0, 8.0, 16.0, +inf]。 - normalized 是否将返回结果归一化到 0~1 之间 。有效输入为 0 和 1。 @@ -983,7 +983,7 @@ ignore_null_values: { - INTERP 用于在指定时间断面获取指定列的记录值,如果该时间断面不存在符合条件的行数据,那么会根据 FILL 参数的设定进行插值。 - INTERP 的输入数据为指定列的数据,可以通过条件语句(where 子句)来对原始列数据进行过滤,如果没有指定过滤条件则输入为全部数据。 - INTERP 需要同时与 RANGE,EVERY 和 FILL 关键字一起使用。 -- INTERP 的输出时间范围根据 RANGE(timestamp1, timestamp2)字段来指定,需满足 timestamp1 <= timestamp2。其中 timestamp1 为输出时间范围的起始值,即如果 timestamp1 时刻符合插值条件则 timestamp1 为输出的第一条记录,timestamp2 为输出时间范围的结束值,即输出的最后一条记录的 timestamp 不能大于 timestamp2。 +- INTERP 的输出时间范围根据 RANGE(timestamp1, timestamp2)字段来指定,需满足 timestamp1 \<= timestamp2。其中 timestamp1 为输出时间范围的起始值,即如果 timestamp1 时刻符合插值条件则 timestamp1 为输出的第一条记录,timestamp2 为输出时间范围的结束值,即输出的最后一条记录的 timestamp 不能大于 timestamp2。 - INTERP 根据 EVERY(time_unit) 字段来确定输出时间范围内的结果条数,即从 timestamp1 开始每隔固定长度的时间(time_unit 值)进行插值,time_unit 可取值时间单位:1a(毫秒),1s(秒),1m(分),1h(小时),1d(天),1w(周)。例如 EVERY(500a) 将对于指定数据每500毫秒间隔进行一次插值. - INTERP 根据 FILL 字段来决定在每个符合输出条件的时刻如何进行插值。关于 FILL 子句如何使用请参考 [FILL 子句](../distinguished/#fill-子句) - INTERP 可以在 RANGE 字段中只指定唯一的时间戳对单个时间点进行插值,在这种情况下,EVERY 字段可以省略。例如:SELECT INTERP(col) FROM tb RANGE('2023-01-01 00:00:00') FILL(linear). @@ -1158,7 +1158,7 @@ UNIQUE(expr) CSUM(expr) ``` -**功能说明**:累加和(Cumulative sum),输出行与输入行数相同。 +**功能说明**:累加和(Cumulative sum),忽略 NULL 值。 **返回结果类型**: 输入列如果是整数类型返回值为长整型 (int64_t),浮点数返回值为双精度浮点数(Double)。无符号整数类型返回值为无符号长整型(uint64_t)。 diff --git a/docs/zh/12-taos-sql/14-stream.md b/docs/zh/12-taos-sql/14-stream.md index 38d913dfaf..8d814d05a1 100644 --- a/docs/zh/12-taos-sql/14-stream.md +++ b/docs/zh/12-taos-sql/14-stream.md @@ -93,7 +93,7 @@ SELECT _wstart, count(*), avg(voltage) from meters PARTITION BY tbname COUNT_WIN CREATE STREAM avg_vol_s INTO avg_vol SUBTABLE(CONCAT('new-', tname)) AS SELECT _wstart, count(*), avg(voltage) FROM meters PARTITION BY tbname tname INTERVAL(1m); ``` -PARTITION 子句中,为 tbname 定义了一个别名 tname, 在PARTITION 子句中的别名可以用于 SUBTABLE 子句中的表达式计算,在上述示例中,流新创建的子表将以前缀 'new-' 连接原表名作为表名(从3.2.3.0开始,为了避免 SUBTABLE 中的表达式无法区分各个子表,即误将多个相同时间线写入一个子表,在指定的子表名后面加上 __stableName_groupId)。 +PARTITION 子句中,为 tbname 定义了一个别名 tname, 在PARTITION 子句中的别名可以用于 SUBTABLE 子句中的表达式计算,在上述示例中,流新创建的子表将以前缀 'new-' 连接原表名作为表名(从3.2.3.0开始,为了避免 SUBTABLE 中的表达式无法区分各个子表,即误将多个相同时间线写入一个子表,在指定的子表名后面加上 _stableName_groupId)。 注意,子表名的长度若超过 TDengine 的限制,将被截断。若要生成的子表名已经存在于另一超级表,由于 TDengine 的子表名是唯一的,因此对应新子表的创建以及数据的写入将会失败。 @@ -271,5 +271,4 @@ PAUSE STREAM [IF EXISTS] stream_name; 2.流计算恢复计算任务 RESUME STREAM [IF EXISTS] [IGNORE UNTREATED] stream_name; -没有指定IF EXISTS,如果该stream不存在,则报错,如果存在,则恢复流计算;指定了IF EXISTS,如果stream不存在,则返回成功;如果存在,则恢复流计算。如果指定IGNORE UNTREATED,则恢复流计算时,忽略流计算暂停期间写入的数据。 - +没有指定IF EXISTS,如果该stream不存在,则报错,如果存在,则恢复流计算;指定了IF EXISTS,如果stream不存在,则返回成功;如果存在,则恢复流计算。如果指定IGNORE UNTREATED,则恢复流计算时,忽略流计算暂停期间写入的数据。 \ No newline at end of file diff --git a/docs/zh/12-taos-sql/16-operators.md b/docs/zh/12-taos-sql/16-operators.md index c2f0cae9c4..76af4037c8 100644 --- a/docs/zh/12-taos-sql/16-operators.md +++ b/docs/zh/12-taos-sql/16-operators.md @@ -35,9 +35,9 @@ TDengine 支持 `UNION ALL` 和 `UNION` 操作符。UNION ALL 将查询返回的 | # | **运算符** | **支持的类型** | **说明** | | --- | :---------------: | -------------------------------------------------------------------- | -------------------- | | 1 | = | 除 BLOB、MEDIUMBLOB 和 JSON 外的所有类型 | 相等 | -| 2 | <\>, != | 除 BLOB、MEDIUMBLOB 和 JSON 外的所有类型,且不可以为表的时间戳主键列 | 不相等 | -| 3 | \>, < | 除 BLOB、MEDIUMBLOB 和 JSON 外的所有类型 | 大于,小于 | -| 4 | \>=, <= | 除 BLOB、MEDIUMBLOB 和 JSON 外的所有类型 | 大于等于,小于等于 | +| 2 | \<>, != | 除 BLOB、MEDIUMBLOB 和 JSON 外的所有类型,且不可以为表的时间戳主键列 | 不相等 | +| 3 | >, \< | 除 BLOB、MEDIUMBLOB 和 JSON 外的所有类型 | 大于,小于 | +| 4 | >=, \<= | 除 BLOB、MEDIUMBLOB 和 JSON 外的所有类型 | 大于等于,小于等于 | | 5 | IS [NOT] NULL | 所有类型 | 是否为空值 | | 6 | [NOT] BETWEEN AND | 除 BOOL、BLOB、MEDIUMBLOB、JSON 和 GEOMETRY 外的所有类型 | 闭区间比较 | | 7 | IN | 除 BLOB、MEDIUMBLOB 和 JSON 外的所有类型,且不可以为表的时间戳主键列 | 与列表内的任意值相等 | diff --git a/docs/zh/12-taos-sql/21-node.md b/docs/zh/12-taos-sql/21-node.md index 778d699099..1f57411838 100644 --- a/docs/zh/12-taos-sql/21-node.md +++ b/docs/zh/12-taos-sql/21-node.md @@ -27,10 +27,10 @@ SHOW DNODES; ## 删除数据节点 ```sql -DROP DNODE {dnode_id | dnode_endpoint} +DROP DNODE dnode_id ``` -可以用 dnoe_id 或 endpoint 两种方式从集群中删除一个 dnode。注意删除 dnode 不等于停止相应的进程。实际中推荐先将一个 dnode 删除之后再停止其所对应的进程。 +注意删除 dnode 不等于停止相应的进程。实际中推荐先将一个 dnode 删除之后再停止其所对应的进程。 ## 修改数据节点配置 diff --git a/docs/zh/12-taos-sql/22-meta.md b/docs/zh/12-taos-sql/22-meta.md index db53dd462b..35b71feb2c 100644 --- a/docs/zh/12-taos-sql/22-meta.md +++ b/docs/zh/12-taos-sql/22-meta.md @@ -98,8 +98,8 @@ TDengine 内置了一个名为 `INFORMATION_SCHEMA` 的数据库,提供对数 | 10 | buffer | INT | 每个 vnode 写缓存的内存块大小,单位 MB。需要注意,`buffer` 为 TDengine 关键字,作为列名使用时需要使用 ` 进行转义。 | | 11 | pagesize | INT | 每个 VNODE 中元数据存储引擎的页大小,单位为 KB。需要注意,`pagesize` 为 TDengine 关键字,作为列名使用时需要使用 ` 进行转义。 | | 12 | pages | INT | 每个 vnode 元数据存储引擎的缓存页个数。需要注意,`pages` 为 TDengine 关键字,作为列名使用时需要使用 ` 进行转义。 | -| 13 | minrows | INT | 文件块中记录的最大条数。需要注意,`minrows` 为 TDengine 关键字,作为列名使用时需要使用 ` 进行转义。 | -| 14 | maxrows | INT | 文件块中记录的最小条数。需要注意,`maxrows` 为 TDengine 关键字,作为列名使用时需要使用 ` 进行转义。 | +| 13 | minrows | INT | 文件块中记录的最小条数。需要注意,`minrows` 为 TDengine 关键字,作为列名使用时需要使用 ` 进行转义。 | +| 14 | maxrows | INT | 文件块中记录的最大条数。需要注意,`maxrows` 为 TDengine 关键字,作为列名使用时需要使用 ` 进行转义。 | | 15 | comp | INT | 数据压缩方式。需要注意,`comp` 为 TDengine 关键字,作为列名使用时需要使用 ` 进行转义。 | | 16 | precision | VARCHAR(2) | 时间分辨率。需要注意,`precision` 为 TDengine 关键字,作为列名使用时需要使用 ` 进行转义。 | | 17 | status | VARCHAR(10) | 数据库状态 | @@ -212,9 +212,12 @@ TDengine 内置了一个名为 `INFORMATION_SCHEMA` 的数据库,提供对数 | # | **列名** | **数据类型** | **说明** | | --- | :---------: | ------------ | -------- | -| 1 | user_name | VARCHAR(23) | 用户名 | -| 2 | privilege | VARCHAR(256) | 权限 | -| 3 | create_time | TIMESTAMP | 创建时间 | +| 1 | name | VARCHAR(24) | 用户名 | +| 2 | super | TINYINT | 用户是否为超级用户,1:是,0:否 | +| 3 | enable | TINYINT | 用户是否启用,1:是,0:否 | +| 4 | sysinfo | TINYINT | 用户是否可查看系统信息,1:是, 0:否 | +| 5 | create_time | TIMESTAMP | 创建时间 | +| 6 | allowed_host | VARCHAR(49152)| IP 白名单 | ## INS_GRANTS diff --git a/docs/zh/12-taos-sql/27-indexing.md b/docs/zh/12-taos-sql/27-indexing.md index 02605da843..fef64e978b 100644 --- a/docs/zh/12-taos-sql/27-indexing.md +++ b/docs/zh/12-taos-sql/27-indexing.md @@ -18,7 +18,7 @@ time_duration: number unit ``` -创建 TSMA 时需要指定 TSMA 名字, 表名字, 函数列表以及窗口大小. 当基于 TSMA 创建时 TSMA 时, 即使用 `RECURSIVE` 关键字, 不需要指定 `FUNCTION()`, 将创建与已有 TSMA 相同的函数列表的TSMA, 且 INTERVAL 必须为所基于的TSMA窗口的整数倍。 +创建 TSMA 时需要指定 TSMA 名字, 表名字, 函数列表以及窗口大小. 当基于一个已经存在的 TSMA 创建新的 TSMA 时, 需要使用 `RECURSIVE` 关键字但不能指定 `FUNCTION()`, 新创建的 TSMA 已有 TSMA 拥有相同的函数列表, 且此种情况下所指定的 INTERVAL 必须为所基于的 TSMA 窗口长度的整数倍。 其中 TSMA 命名规则与表名字类似, 长度最大限制为表名长度限制减去输出表后缀长度, 表名长度限制为193, 输出表后缀为`_tsma_res_stb_`, TSMA 名字最大长度为178. diff --git a/docs/zh/12-taos-sql/29-changes.md b/docs/zh/12-taos-sql/29-changes.md index 2a1e5f092c..4533777c15 100644 --- a/docs/zh/12-taos-sql/29-changes.md +++ b/docs/zh/12-taos-sql/29-changes.md @@ -71,7 +71,7 @@ description: "TDengine 3.0 版本的语法变更说明" | 44 | SHOW STREAMS | 调整 | 2.x版本此命令显示系统中已创建的连续查询的信息。3.0版本废除了连续查询,用流代替。此命令显示已创建的流。 | 45 | SHOW SUBSCRIPTIONS | 新增 | 显示当前数据库下的所有的订阅关系 | 46 | SHOW TABLES | 调整 | 3.0版本只显示表名。 -| 47 | SHOW TABLE DISTRIBUTED | 新增 | 显示表的数据分布信息。代替2.x版本中的SELECT _block_dist() FROM { tb_name | stb_name }方式。 +| 47 | SHOW TABLE DISTRIBUTED | 新增 | 显示表的数据分布信息。代替2.x版本中的SELECT _block_dist() FROM \{ tb_name | stb_name }方式。 | 48 | SHOW TOPICS | 新增 | 显示当前数据库下的所有订阅主题。 | 49 | SHOW TRANSACTIONS | 新增 | 显示当前系统中正在执行的事务的信息。 | 50 | SHOW DNODE VARIABLES | 新增 |显示指定DNODE的配置参数。 diff --git a/docs/zh/12-taos-sql/index.md b/docs/zh/12-taos-sql/index.md index 739d26b224..6095134554 100644 --- a/docs/zh/12-taos-sql/index.md +++ b/docs/zh/12-taos-sql/index.md @@ -3,7 +3,7 @@ title: TDengine SQL description: 'TDengine SQL 支持的语法规则、主要查询功能、支持的 SQL 查询函数,以及常用技巧等内容' --- -本文档说明 TDengine SQL 支持的语法规则、主要查询功能、支持的 SQL 查询函数,以及常用技巧等内容。阅读本文档需要读者具有基本的 SQL 语言的基础。TDengine 3.0 版本相比 2.x 版本做了大量改进和优化,特别是查询引擎进行了彻底的重构,因此 SQL 语法相比 2.x 版本有很多变更。详细的变更内容请见 [3.0 版本语法变更](/taos-sql/changes) 章节 +本文档说明 TDengine SQL 支持的语法规则、主要查询功能、支持的 SQL 查询函数,以及常用技巧等内容。阅读本文档需要读者具有基本的 SQL 语言的基础。TDengine 3.0 版本相比 2.x 版本做了大量改进和优化,特别是查询引擎进行了彻底的重构,因此 SQL 语法相比 2.x 版本有很多变更。详细的变更内容请见 [3.0 版本语法变更](./changes) 章节 TDengine SQL 是用户对 TDengine 进行数据写入和查询的主要工具。TDengine SQL 提供标准的 SQL 语法,并针对时序数据和业务的特点优化和新增了许多语法和功能。TDengine SQL 语句的最大长度为 1M。TDengine SQL 不支持关键字的缩写,例如 DELETE 不能缩写为 DEL。 diff --git a/docs/zh/14-reference/04-taosadapter.md b/docs/zh/14-reference/04-taosadapter.md index 54520a97bb..70c4450c06 100644 --- a/docs/zh/14-reference/04-taosadapter.md +++ b/docs/zh/14-reference/04-taosadapter.md @@ -166,8 +166,8 @@ AllowWebSockets - 兼容 InfluxDB v1 写接口 [https://docs.influxdata.com/influxdb/v2.0/reference/api/influxdb-1x/write/](https://docs.influxdata.com/influxdb/v2.0/reference/api/influxdb-1x/write/) - 兼容 OpenTSDB JSON 和 telnet 格式写入 - - - - + - \ + - \ - 与 collectd 无缝连接 collectd 是一个系统统计收集守护程序,请访问 [https://collectd.org/](https://collectd.org/) 了解更多信息。 - Seamless connection with StatsD @@ -202,7 +202,7 @@ AllowWebSockets - `precision` TDengine 使用的时间精度 - `u` TDengine 用户名 - `p` TDengine 密码 -- `ttl` 自动创建的子表生命周期,以子表的第一条数据的 TTL 参数为准,不可更新。更多信息请参考[创建表文档](taos-sql/table/#创建表)的 TTL 参数。 +- `ttl` 自动创建的子表生命周期,以子表的第一条数据的 TTL 参数为准,不可更新。更多信息请参考[创建表文档](../../taos-sql/table/#创建表)的 TTL 参数。 注意: 目前不支持 InfluxDB 的 token 验证方式,仅支持 Basic 验证和查询参数验证。 示例: curl --request POST http://127.0.0.1:6041/influxdb/v1/write?db=test --user "root:taosdata" --data-binary "measurement,host=host1 field1=2i,field2=2.0 1577836800000000000" diff --git a/docs/zh/14-reference/05-taosbenchmark.md b/docs/zh/14-reference/05-taosbenchmark.md index bb88c2dede..d102497d7d 100644 --- a/docs/zh/14-reference/05-taosbenchmark.md +++ b/docs/zh/14-reference/05-taosbenchmark.md @@ -94,67 +94,67 @@ taosBenchmark -f ## 命令行参数详解 -- **-f/--file ** : +- **-f/--file \** : 要使用的 JSON 配置文件,由该文件指定所有参数,本参数与命令行其他参数不能同时使用。没有默认值。 -- **-c/--config-dir ** : +- **-c/--config-dir \** : TDengine 集群配置文件所在的目录,默认路径是 /etc/taos 。 -- **-h/--host ** : +- **-h/--host \** : 指定要连接的 TDengine 服务端的 FQDN,默认值为 localhost 。 -- **-P/--port ** : +- **-P/--port \** : 要连接的 TDengine 服务器的端口号,默认值为 6030 。 -- **-I/--interface ** : +- **-I/--interface \** : 插入模式,可选项有 taosc, rest, stmt, sml, sml-rest, 分别对应普通写入、restful 接口写入、参数绑定接口写入、schemaless 接口写入、restful schemaless 接口写入 (由 taosAdapter 提供)。默认值为 taosc。 -- **-u/--user ** : +- **-u/--user \** : 用于连接 TDengine 服务端的用户名,默认为 root 。 - **-U/--supplement-insert ** : 写入数据而不提前建数据库和表,默认关闭。 -- **-p/--password ** : +- **-p/--password \** : 用于连接 TDengine 服务端的密码,默认值为 taosdata。 -- **-o/--output ** : +- **-o/--output \** : 结果输出文件的路径,默认值为 ./output.txt。 -- **-T/--thread ** : +- **-T/--thread \** : 插入数据的线程数量,默认为 8 。 -- **-B/--interlace-rows ** : +- **-B/--interlace-rows \** : 启用交错插入模式并同时指定向每个子表每次插入的数据行数。交错插入模式是指依次向每张子表插入由本参数所指定的行数并重复这个过程,直到所有子表的数据都插入完成。默认值为 0, 即向一张子表完成数据插入后才会向下一张子表进行数据插入。 -- **-i/--insert-interval ** : +- **-i/--insert-interval \** : 指定交错插入模式的插入间隔,单位为 ms,默认值为 0。 只有当 `-B/--interlace-rows` 大于 0 时才起作用。意味着数据插入线程在为每个子表插入隔行扫描记录后,会等待该值指定的时间间隔后再进行下一轮写入。 -- **-r/--rec-per-req ** : +- **-r/--rec-per-req \** : 每次向 TDengine 请求写入的数据行数,默认值为 30000 。 -- **-t/--tables ** : +- **-t/--tables \** : 指定子表的数量,默认为 10000 。 -- **-S/--timestampstep ** : +- **-S/--timestampstep \** : 每个子表中插入数据的时间戳步长,单位是 ms,默认值是 1。 -- **-n/--records ** : +- **-n/--records \** : 每个子表插入的记录数,默认值为 10000 。 -- **-d/--database ** : +- **-d/--database \** : 所使用的数据库的名称,默认值为 test 。 -- **-b/--data-type ** : +- **-b/--data-type \** : 超级表的数据列的类型。如果不使用则默认为有三个数据列,其类型分别为 FLOAT, INT, FLOAT 。 -- **-l/--columns ** : +- **-l/--columns \** : 超级表的数据列的总数量。如果同时设置了该参数和 `-b/--data-type`,则最后的结果列数为两者取大。如果本参数指定的数量大于 `-b/--data-type` 指定的列数,则未指定的列类型默认为 INT, 例如: `-l 5 -b float,double`, 那么最后的列为 `FLOAT,DOUBLE,INT,INT,INT`。如果 columns 指定的数量小于或等于 `-b/--data-type` 指定的列数,则结果为 `-b/--data-type` 指定的列和类型,例如: `-l 3 -b float,double,float,bigint`,那么最后的列为 `FLOAT,DOUBLE,FLOAT,BIGINT` 。 -- **-L/--partial-col-num **: +- **-L/--partial-col-num \ **: 指定某些列写入数据,其他列数据为 NULL。默认所有列都写入数据。 -- **-A/--tag-type ** : +- **-A/--tag-type \** : 超级表的标签列类型。nchar 和 binary 类型可以同时设置长度,例如: ``` @@ -168,10 +168,10 @@ taosBenchmark -A INT,DOUBLE,NCHAR,BINARY(16) taosBenchmark -A INT,DOUBLE,NCHAR,BINARY\(16\) ``` -- **-w/--binwidth **: +- **-w/--binwidth \**: nchar 和 binary 类型的默认长度,默认值为 64。 -- **-m/--table-prefix ** : +- **-m/--table-prefix \** : 子表名称的前缀,默认值为 "d"。 - **-E/--escape-character** : @@ -192,23 +192,23 @@ taosBenchmark -A INT,DOUBLE,NCHAR,BINARY\(16\) - **-y/--answer-yes** : 开关参数,要求用户在提示后确认才能继续。默认值为 false 。 -- **-O/--disorder ** : +- **-O/--disorder \** : 指定乱序数据的百分比概率,其值域为 [0,50]。默认为 0,即没有乱序数据。 -- **-R/--disorder-range ** : +- **-R/--disorder-range \** : 指定乱序数据的时间戳回退范围。所生成的乱序时间戳为非乱序情况下应该使用的时间戳减去这个范围内的一个随机值。仅在 `-O/--disorder` 指定的乱序数据百分比大于 0 时有效。 -- **-F/--prepare_rand ** : +- **-F/--prepare_rand \** : 生成的随机数据中唯一值的数量。若为 1 则表示所有数据都相同。默认值为 10000 。 -- **-a/--replica ** : +- **-a/--replica \** : 创建数据库时指定其副本数,默认值为 1 。 -- ** -k/--keep-trying ** : 失败后进行重试的次数,默认不重试。需使用 v3.0.9 以上版本。 +- ** -k/--keep-trying \** : 失败后进行重试的次数,默认不重试。需使用 v3.0.9 以上版本。 -- ** -z/--trying-interval ** : 失败重试间隔时间,单位为毫秒,仅在 -k 指定重试后有效。需使用 v3.0.9 以上版本。 +- ** -z/--trying-interval \** : 失败重试间隔时间,单位为毫秒,仅在 -k 指定重试后有效。需使用 v3.0.9 以上版本。 -- **-v/--vgroups ** : +- **-v/--vgroups \** : 创建数据库时指定 vgroups 数,仅对 TDengine v3.0+ 有效。 - **-V/--version** : @@ -287,7 +287,7 @@ taosBenchmark -A INT,DOUBLE,NCHAR,BINARY\(16\) - **escape_character** : 超级表和子表名称中是否包含转义字符,默认值为 "no",可选值为 "yes" 或 "no"。 -- **auto_create_table** : 仅当 insert_mode 为 taosc, rest, stmt 并且 childtable_exists 为 "no" 时生效,该参数为 "yes" 表示 taosBenchmark 在插入数据时会自动创建不存在的表;为 "no" 则表示先提前建好所有表再进行插入。 +- **auto_create_table** : 仅当 insert_mode 为 taosc, rest, stmt 并且 child_table_exists 为 "no" 时生效,该参数为 "yes" 表示 taosBenchmark 在插入数据时会自动创建不存在的表;为 "no" 则表示先提前建好所有表再进行插入。 - **batch_create_tbl_num** : 创建子表时每批次的建表数量,默认为 10。注:实际的批数不一定与该值相同,当执行的 SQL 语句大于支持的最大长度时,会自动截断再执行,继续创建。 @@ -303,9 +303,9 @@ taosBenchmark -A INT,DOUBLE,NCHAR,BINARY\(16\) - **insert_rows** : 每个子表插入的记录数,默认为 0 。 -- **childtable_offset** : 仅当 childtable_exists 为 yes 时生效,指定从超级表获取子表列表时的偏移量,即从第几个子表开始。 +- **childtable_offset** : 仅当 child_table_exists 为 yes 时生效,指定从超级表获取子表列表时的偏移量,即从第几个子表开始。 -- **childtable_limit** : 仅当 childtable_exists 为 yes 时生效,指定从超级表获取子表列表的上限。 +- **childtable_limit** : 仅当 child_table_exists 为 yes 时生效,指定从超级表获取子表列表的上限。 - **interlace_rows** : 启用交错插入模式并同时指定向每个子表每次插入的数据行数。交错插入模式是指依次向每张子表插入由本参数所指定的行数并重复这个过程,直到所有子表的数据都插入完成。默认值为 0, 即向一张子表完成数据插入后才会向下一张子表进行数据插入。 @@ -329,6 +329,11 @@ taosBenchmark -A INT,DOUBLE,NCHAR,BINARY\(16\) - **tags_file** : 仅当 insert_mode 为 taosc, rest 的模式下生效。 最终的 tag 的数值与 childtable_count 有关,如果 csv 文件内的 tag 数据行小于给定的子表数量,那么会循环读取 csv 文件数据直到生成 childtable_count 指定的子表数量;否则则只会读取 childtable_count 行 tag 数据。也即最终生成的子表数量为二者取小。 +- **primary_key** : 指定超级表是否有复合主键,取值 1 和 0, 复合主键列只能是超级表的第二列,指定生成复合主键后要确保第二列符合复合主键的数据类型,否则会报错 +- **repeat_ts_min** : 数值类型,复合主键开启情况下指定生成相同时间戳记录的最小个数,生成相同时间戳记录的个数是在范围[repeat_ts_min, repeat_ts_max] 内的随机值, 最小值等于最大值时为固定个数 +- **repeat_ts_max** : 数值类型,复合主键开启情况下指定生成相同时间戳记录的最大个数 +- **sqls** : 字符串数组类型,指定超级表创建成功后要执行的 sql 数组,sql 中指定表名前面要带数据库名,否则会报未指定数据库错误 + #### tsma配置参数 指定tsma的配置参数在 `super_tables` 中的 `tsmas` 中,具体参数如下。 @@ -368,10 +373,22 @@ taosBenchmark -A INT,DOUBLE,NCHAR,BINARY\(16\) - **sma**: 将该列加入 SMA 中,值为 "yes" 或者 "no",默认为 "no"。 +- **encode**: 字符串类型,指定此列两级压缩中的第一级编码算法,详细参见创建超级表 + +- **compress**: 字符串类型,指定此列两级压缩中的第二级加密算法,详细参见创建超级表 + +- **level**: 字符串类型,指定此列两级压缩中的第二级加密算法的压缩率高低,详细参见创建超级表 + +- **gen**: 字符串类型,指定此列生成数据的方式,不指定为随机,若指定为 “order”, 会按自然数顺序增长 + +- **fillNull**: 字符串类型,指定此列是否随机插入 NULL 值,可指定为 “true” 或 "false", 只有当 generate_row_rule 为 2 时有效 + #### 插入行为配置参数 - **thread_count** : 插入数据的线程数量,默认为 8。 +- **thread_bind_vgroup** : 写入时 vgroup 是否和写入线程绑定,绑定后可提升写入速度, 取值为 "yes" 或 "no",默认值为 “no”, 设置为 “no” 后与原来行为一致。 当设为 “yes” 时,如果 thread_count 数量大小写入数据库的 vgroups 数量, thread_count 自动调整为 vgroups 数量;如果 thread_count 数量小于 vgroups 数量,写入线程数量不做调整,一个线程写完一个 vgroup 数据后再写下一个,同时保持一个 vgroup 同时只能由一个线程写入的规则。 + - **create_table_thread_count** : 建表的线程数量,默认为 8。 - **connection_pool_size** : 预先建立的与 TDengine 服务端之间的连接的数量。若不配置,则与所指定的线程数相同。 @@ -392,6 +409,8 @@ taosBenchmark -A INT,DOUBLE,NCHAR,BINARY\(16\) - **prepare_rand** : 生成的随机数据中唯一值的数量。若为 1 则表示所有数据都相同。默认值为 10000 。 +- **pre_load_tb_meta** :是否提前加载子表的 meta 数据,取值为 “yes” or "no"。当子表数量非常多时,打开此选项可提高写入速度。 + ### 查询场景配置参数 查询场景下 `filetype` 必须设置为 `query`。 @@ -407,7 +426,7 @@ taosBenchmark -A INT,DOUBLE,NCHAR,BINARY\(16\) - **query_interval** : 查询时间间隔,单位是秒,默认值为 0。 -- **threads/concurrent** : 执行查询 SQL 的线程数,默认值为 1。 +- **threads** : 执行查询 SQL 的线程数,默认值为 1。 - **sqls**: - **sql**: 执行的 SQL 命令,必填。 @@ -440,6 +459,7 @@ taosBenchmark -A INT,DOUBLE,NCHAR,BINARY\(16\) - **sqls** : - **sql** : 执行的 SQL 命令,必填。 + #### 配置文件中数据类型书写对照表 diff --git a/docs/zh/14-reference/06-taosdump.md b/docs/zh/14-reference/06-taosdump.md index 8972e587b0..f024d6dde7 100644 --- a/docs/zh/14-reference/06-taosdump.md +++ b/docs/zh/14-reference/06-taosdump.md @@ -34,7 +34,7 @@ taosdump 有两种安装方式: 2. 备份多个指定数据库:使用 `-D db1,db2,...` 参数; 3. 备份指定数据库中的某些超级表或普通表:使用 `dbname stbname1 stbname2 tbname1 tbname2 ...` 参数,注意这种输入序列第一个参数为数据库名称,且只支持一个数据库,第二个和之后的参数为该数据库中的超级表或普通表名称,中间以空格分隔; 4. 备份系统 log 库:TDengine 集群通常会包含一个系统数据库,名为 `log`,这个数据库内的数据为 TDengine 自我运行的数据,taosdump 默认不会对 log 库进行备份。如果有特定需求对 log 库进行备份,可以使用 `-a` 或 `--allow-sys` 命令行参数。 -5. “宽容”模式备份:taosdump 1.4.1 之后的版本提供 `-n` 参数和 `-L` 参数,用于备份数据时不使用转义字符和“宽容”模式,可以在表名、列名、标签名没使用转义字符的情况下减少备份数据时间和备份数据占用空间。如果不确定符合使用 `-n` 和 `-L` 条件时请使用默认参数进行“严格”模式进行备份。转义字符的说明请参考[官方文档](/taos-sql/escape)。 +5. “宽容”模式备份:taosdump 1.4.1 之后的版本提供 `-n` 参数和 `-L` 参数,用于备份数据时不使用转义字符和“宽容”模式,可以在表名、列名、标签名没使用转义字符的情况下减少备份数据时间和备份数据占用空间。如果不确定符合使用 `-n` 和 `-L` 条件时请使用默认参数进行“严格”模式进行备份。转义字符的说明请参考[官方文档](../../taos-sql/escape)。 :::tip - taosdump 1.4.1 之后的版本提供 `-I` 参数,用于解析 avro 文件 schema 和数据,如果指定 `-s` 参数将只解析 schema。 diff --git a/docs/zh/14-reference/08-taos-shell.md b/docs/zh/14-reference/08-taos-shell.md index 1caa580b73..74da116f0a 100644 --- a/docs/zh/14-reference/08-taos-shell.md +++ b/docs/zh/14-reference/08-taos-shell.md @@ -58,6 +58,7 @@ taos> SET MAX_BINARY_DISPLAY_WIDTH ; - -a AUTHSTR: 连接服务端的授权信息 - -A: 通过用户名和密码计算授权信息 +- -B: 设置 BI 工具显示模式,设置后所有输出都遵循 BI 工具的格式进行输出 - -c CONFIGDIR: 指定配置文件目录,Linux 环境下默认为 `/etc/taos`,该目录下的配置文件默认名称为 `taos.cfg` - -C: 打印 -c 指定的目录中 `taos.cfg` 的配置参数 - -d DATABASE: 指定连接到服务端时使用的数据库 diff --git a/docs/zh/14-reference/12-config/index.md b/docs/zh/14-reference/12-config/index.md index effa72099a..814b543859 100755 --- a/docs/zh/14-reference/12-config/index.md +++ b/docs/zh/14-reference/12-config/index.md @@ -86,7 +86,7 @@ taos -C | 协议 | 默认端口 | 用途说明 | 修改方法 | | :--- | :------- | :----------------------------------------------- | :------------------------------------------------------------------------------------------------- | | TCP | 6030 | 客户端与服务端之间通讯,多节点集群的节点间通讯。 | 由配置文件设置 serverPort 决定。 | -| TCP | 6041 | 客户端与服务端之间的 RESTful 通讯。 | 随 serverPort 端口变化。注意 taosAdapter 配置或有不同,请参考相应[文档](/reference/taosadapter/)。 | +| TCP | 6041 | 客户端与服务端之间的 RESTful 通讯。 | 随 serverPort 端口变化。注意 taosAdapter 配置或有不同,请参考相应[文档](../taosadapter/)。 | | TCP | 6043 | taosKeeper 监控服务端口。 | 随 taosKeeper 启动参数设置变化。 | | TCP | 6044 | 支持 StatsD 的数据接入端口。 | 随 taosAdapter 启动参数设置变化 | | UDP | 6045 | 支持 collectd 数据接入端口。 | 随 taosAdapter 启动参数设置变化 | @@ -317,7 +317,7 @@ TDengine 为存储中文、日文、韩文等非 ASCII 编码的宽字符,提 客户端的输入的字符均采用操作系统当前默认的编码格式,在 Linux/macOS 系统上多为 UTF-8,部分中文系统编码则可能是 GB18030 或 GBK 等。在 docker 环境中默认的编码是 POSIX。在中文版 Windows 系统中,编码则是 CP936。客户端需要确保正确设置自己所使用的字符集,即客户端运行的操作系统当前编码字符集,才能保证 nchar 中的数据正确转换为 UCS4-LE 编码格式。 -在 Linux/macOS 中 locale 的命名规则为: <语言>\_<地区>.<字符集编码> 如:zh_CN.UTF-8,zh 代表中文,CN 代表大陆地区,UTF-8 表示字符集。字符集编码为客户端正确解析本地字符串提供编码转换的说明。Linux/macOS 可以通过设置 locale 来确定系统的字符编码,由于 Windows 使用的 locale 中不是 POSIX 标准的 locale 格式,因此在 Windows 下需要采用另一个配置参数 charset 来指定字符编码。在 Linux/macOS 中也可以使用 charset 来指定字符编码。 +在 Linux/macOS 中 locale 的命名规则为: \<语言>_\<地区>.\<字符集编码> 如:zh_CN.UTF-8,zh 代表中文,CN 代表大陆地区,UTF-8 表示字符集。字符集编码为客户端正确解析本地字符串提供编码转换的说明。Linux/macOS 可以通过设置 locale 来确定系统的字符编码,由于 Windows 使用的 locale 中不是 POSIX 标准的 locale 格式,因此在 Windows 下需要采用另一个配置参数 charset 来指定字符编码。在 Linux/macOS 中也可以使用 charset 来指定字符编码。 ::: diff --git a/docs/zh/14-reference/13-schemaless/13-schemaless.md b/docs/zh/14-reference/13-schemaless/13-schemaless.md index 7168aea18c..9d7e213a2d 100644 --- a/docs/zh/14-reference/13-schemaless/13-schemaless.md +++ b/docs/zh/14-reference/13-schemaless/13-schemaless.md @@ -117,7 +117,7 @@ st,t1=3,t2=4,t3=t3 c1=3i64,c3="passit",c2=false,c4=4f64 1626006833639000000 :::tip 无模式所有的处理逻辑,仍会遵循 TDengine 对数据结构的底层限制,例如每行数据的总长度不能超过 -48KB(从 3.0.5.0 版本开始为 64KB),标签值的总长度不超过16KB。这方面的具体限制约束请参见 [TDengine SQL 边界限制](/taos-sql/limit) +48KB(从 3.0.5.0 版本开始为 64KB),标签值的总长度不超过16KB。这方面的具体限制约束请参见 [TDengine SQL 边界限制](../../taos-sql/limit) ::: diff --git a/docs/zh/14-reference/14-taosKeeper.md b/docs/zh/14-reference/14-taosKeeper.md index 92c85e7a2c..82ce995610 100644 --- a/docs/zh/14-reference/14-taosKeeper.md +++ b/docs/zh/14-reference/14-taosKeeper.md @@ -140,26 +140,32 @@ 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" + +[environment] +# 容器模式收集信息 +incgroup = false + +[log] +# 日志文件滚动个数 +rotationCount = 5 +# 日志文件切割时间 +rotationTime = "24h" +# 日志文件切割大小 (字节) +rotationSize = 100000000 + ``` ### 获取监控指标 @@ -172,16 +178,16 @@ taosKeeper 作为 TDengine 监控指标的导出工具,可以将 TDengine 产 $ taos # 如上示例,使用 log 库作为监控日志存储位置 > use log; -> select * from cluster_info limit 1; +> select * from taosd_cluster_info limit 1; ``` 结果示例: ```shell - ts | first_ep | first_ep_dnode_id | version | master_uptime | monitor_interval | dbs_total | tbs_total | stbs_total | dnodes_total | dnodes_alive | mnodes_total | mnodes_alive | vgroups_total | vgroups_alive | vnodes_total | vnodes_alive | connections_total | protocol | cluster_id | -=============================================================================================================================================================================================================================================================================================================================================================================== - 2022-08-16 17:37:01.629 | hlb:6030 | 1 | 3.0.0.0 | 0.27250 | 15 | 2 | 27 | 38 | 1 | 1 | 1 | 1 | 4 | 4 | 4 | 4 | 14 | 1 | 5981392874047724755 | -Query OK, 1 rows in database (0.036162s) + _ts | cluster_uptime | dbs_total | tbs_total | stbs_total | vgroups_total | vgroups_alive | vnodes_total | vnodes_alive | mnodes_total | mnodes_alive | connections_total | topics_total | streams_total | dnodes_total | dnodes_alive | grants_expire_time | grants_timeseries_used | grants_timeseries_total | cluster_id | +=================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================== + 2024-06-04 03:03:34.341 | 0.000000000000000 | 2.000000000000000 | 1.000000000000000 | 4.000000000000000 | 4.000000000000000 | 4.000000000000000 | 4.000000000000000 | 4.000000000000000 | 1.000000000000000 | 1.000000000000000 | 2.000000000000000 | 0.000000000000000 | 0.000000000000000 | 1.000000000000000 | 1.000000000000000 | 0.000000000000000 | 3.000000000000000 | 0.000000000000000 | 554014120921134497 | +Query OK, 1 row(s) in set (0.001652s) ``` #### 导出监控指标 @@ -193,21 +199,24 @@ $ curl http://127.0.0.1:6043/metrics 部分结果集: ```shell -# HELP taos_cluster_info_connections_total +# HELP taos_cluster_info_connections_total # TYPE taos_cluster_info_connections_total counter -taos_cluster_info_connections_total{cluster_id="5981392874047724755"} 16 -# HELP taos_cluster_info_dbs_total +taos_cluster_info_connections_total{cluster_id="554014120921134497"} 8 +# HELP taos_cluster_info_dbs_total # TYPE taos_cluster_info_dbs_total counter -taos_cluster_info_dbs_total{cluster_id="5981392874047724755"} 2 -# HELP taos_cluster_info_dnodes_alive +taos_cluster_info_dbs_total{cluster_id="554014120921134497"} 2 +# HELP taos_cluster_info_dnodes_alive # TYPE taos_cluster_info_dnodes_alive counter -taos_cluster_info_dnodes_alive{cluster_id="5981392874047724755"} 1 -# HELP taos_cluster_info_dnodes_total +taos_cluster_info_dnodes_alive{cluster_id="554014120921134497"} 1 +# HELP taos_cluster_info_dnodes_total # TYPE taos_cluster_info_dnodes_total counter -taos_cluster_info_dnodes_total{cluster_id="5981392874047724755"} 1 -# HELP taos_cluster_info_first_ep +taos_cluster_info_dnodes_total{cluster_id="554014120921134497"} 1 +# HELP taos_cluster_info_first_ep # TYPE taos_cluster_info_first_ep gauge -taos_cluster_info_first_ep{cluster_id="5981392874047724755",value="hlb:6030"} 1 +taos_cluster_info_first_ep{cluster_id="554014120921134497",value="tdengine:6030"} 1 +# HELP taos_cluster_info_first_ep_dnode_id +# TYPE taos_cluster_info_first_ep_dnode_id counter +taos_cluster_info_first_ep_dnode_id{cluster_id="554014120921134497"} 1 ``` ### check\_health diff --git a/docs/zh/14-reference/15-explorer.md b/docs/zh/14-reference/15-explorer.md new file mode 100644 index 0000000000..05dd6d6534 --- /dev/null +++ b/docs/zh/14-reference/15-explorer.md @@ -0,0 +1,53 @@ +--- +title: 可视化管理 +sidebar_label: 可视化管理工具 +description: taos-explorer 的使用说明,包括安装、配置、使用等。 +--- + +taos-explorer 是一个为用户提供 TDengine 实例的可视化管理交互工具的 web 服务。 + +## 安装 + +taos-explorer 无需单独安装,从 TDengine 3.3.0.0 版本开始,它随着 TDengine Server 安装包一起发布,安装完成后,就可以看到 `taos-explorer` 服务。 + +## 配置 + +配置文件在 linux 平台上为`/etc/taos/explorer.toml`,配置内容如下: + +``` toml +port = 6060 +cluster = "http://localhost:6041" +``` + +配置文件中只需要关注这两项即可: + +- port:taos-explorer 对外的服务端口 +- cluster:taos-explorer 连接的 TDengine 实例,只支持 websocket 连接,所以该地址为 TDengine 集群中 taosAdapter 服务的地址 + +## 启动 & 停止 + +在启动之前,请先确保 TDengine 集群(主要服务是 `taosd` 和 `taosAdapter`)已经启动并在正确运行,并确保 taos-explorer 的配置正确。 + +### Linux 系统 + +使用 `systemctl` 命令可以管理 taos-explorer 服务: + +- 启动服务进程:`systemctl start taos-explorer` + +- 停止服务进程:`systemctl stop taos-explorer` + +- 重启服务进程:`systemctl restart taos-explorer` + +- 查看服务状态:`systemctl status taos-explorer` + +## 注册 & 登录 + +### 注册流程 + +安装好,打开浏览器,默认访问`http://ip:6060`来访问 taos-explorer 服务。如果还没有注册过,则首先进入注册界面。输入手机号获取验证码,输入正确的验证码后,即可注册成功。 + +### 登录 + +登录时,请使用数据库用户名和密码登录。首次使用,默认的用户名为 `root`,密码为 `taosdata`。登录成功后即可进入`数据浏览器`页面,您可以使用查看数据库、 创建数据库、创建超级表/子表等管理功能。 + +其他功能页面,如`数据写入-数据源`等页面,为企业版特有功能,您可以点击查看和简单体验,并不能实际使用。 diff --git a/docs/zh/14-reference/_collectd.mdx b/docs/zh/14-reference/_collectd.mdx index af3388f680..e12f3945e5 100644 --- a/docs/zh/14-reference/_collectd.mdx +++ b/docs/zh/14-reference/_collectd.mdx @@ -19,7 +19,7 @@ password = "taosdata" 其中 taosAdapter 默认写入的数据库名称为 `collectd`,也可以修改 taosAdapter 配置文件 dbs 项来指定不同的名称。user 和 password 填写实际 TDengine 配置的值。修改过配置文件 taosAdapter 需重新启动。 -- 也可以使用 taosAdapter 命令行参数或设置环境变量启动的方式,使能 taosAdapter 接收 collectd 数据功能,具体细节请参考 [taosAdapter 的使用手册](/reference/taosadapter) +- 也可以使用 taosAdapter 命令行参数或设置环境变量启动的方式,使能 taosAdapter 接收 collectd 数据功能,具体细节请参考 [taosAdapter 的使用手册](../../reference/taosadapter) ### 配置 collectd # @@ -36,7 +36,7 @@ LoadPlugin network ``` -其中 填写运行 taosAdapter 的服务器域名或 IP 地址。 填写 taosAdapter 用于接收 collectd 数据的端口(默认为 6045)。 +其中 \ 填写运行 taosAdapter 的服务器域名或 IP 地址。\ 填写 taosAdapter 用于接收 collectd 数据的端口(默认为 6045)。 示例如下: @@ -62,7 +62,7 @@ LoadPlugin write_tsdb ``` -其中 填写运行 taosAdapter 的服务器域名或 IP 地址。 填写 taosAdapter 用于接收 collectd write_tsdb 插件的数据(默认为 6047)。 +其中 \ 填写运行 taosAdapter 的服务器域名或 IP 地址。\ 填写 taosAdapter 用于接收 collectd write_tsdb 插件的数据(默认为 6047)。 ```text LoadPlugin write_tsdb diff --git a/docs/zh/14-reference/_icinga2.mdx b/docs/zh/14-reference/_icinga2.mdx index fdb341e264..ab1384f324 100644 --- a/docs/zh/14-reference/_icinga2.mdx +++ b/docs/zh/14-reference/_icinga2.mdx @@ -19,12 +19,12 @@ password = "taosdata" 其中 taosAdapter 默认写入的数据库名称为 `icinga2`,也可以修改 taosAdapter 配置文件 dbs 项来指定不同的名称。user 和 password 填写实际 TDengine 配置的值。修改过 taosAdapter 需重新启动。 -- 也可以使用 taosAdapter 命令行参数或设置环境变量启动的方式,使能 taosAdapter 接收 icinga2 数据功能,具体细节请参考 [taosAdapter 的使用手册](/reference/taosadapter) +- 也可以使用 taosAdapter 命令行参数或设置环境变量启动的方式,使能 taosAdapter 接收 icinga2 数据功能,具体细节请参考 [taosAdapter 的使用手册](../../reference/taosadapter) ### 配置 icinga2 - 使能 icinga2 的 opentsdb-writer(参考链接 https://icinga.com/docs/icinga-2/latest/doc/14-features/#opentsdb-writer) -- 修改配置文件 `/etc/icinga2/features-enabled/opentsdb.conf` 填写 为运行 taosAdapter 的服务器的域名或 IP 地址, 填写 taosAdapter 支持接收 icinga2 数据的相应端口(默认为 6048) +- 修改配置文件 `/etc/icinga2/features-enabled/opentsdb.conf` 填写 \ 为运行 taosAdapter 的服务器的域名或 IP 地址,\ 填写 taosAdapter 支持接收 icinga2 数据的相应端口(默认为 6048) ``` object OpenTsdbWriter "opentsdb" { diff --git a/docs/zh/14-reference/_prometheus.mdx b/docs/zh/14-reference/_prometheus.mdx index be73d95cbc..61d1dc3f2a 100644 --- a/docs/zh/14-reference/_prometheus.mdx +++ b/docs/zh/14-reference/_prometheus.mdx @@ -9,8 +9,8 @@ ### 配置 Basic 验证 -- username: -- password: +- username: \ +- password: \ ### prometheus.yml 文件中 remote_write 和 remote_read 相关部分配置示例 diff --git a/docs/zh/14-reference/_statsd.mdx b/docs/zh/14-reference/_statsd.mdx index 21c2e29f64..02419d338b 100644 --- a/docs/zh/14-reference/_statsd.mdx +++ b/docs/zh/14-reference/_statsd.mdx @@ -27,11 +27,11 @@ deleteTimings = true 其中 taosAdapter 默认写入的数据库名称为 `statsd`,也可以修改 taosAdapter 配置文件 db 项来指定不同的名称。user 和 password 填写实际 TDengine 配置的值。修改过配置文件 taosAdapter 需重新启动。 -- 也可以使用 taosAdapter 命令行参数或设置环境变量启动的方式,使能 taosAdapter 接收 StatsD 数据功能,具体细节请参考 [taosAdapter 的使用手册](/reference/taosadapter) +- 也可以使用 taosAdapter 命令行参数或设置环境变量启动的方式,使能 taosAdapter 接收 StatsD 数据功能,具体细节请参考 [taosAdapter 的使用手册](../../reference/taosadapter) ### 配置 StatsD -使用 StatsD 需要下载其[源代码](https://github.com/statsd/statsd)。其配置文件请参考其源代码下载到本地的根目录下的示例文件 `exampleConfig.js` 进行修改。其中 填写运行 taosAdapter 的服务器域名或 IP 地址,请填写 taosAdapter 接收 StatsD 数据的端口(默认为 6044)。 +使用 StatsD 需要下载其[源代码](https://github.com/statsd/statsd)。其配置文件请参考其源代码下载到本地的根目录下的示例文件 `exampleConfig.js` 进行修改。其中 \ 填写运行 taosAdapter 的服务器域名或 IP 地址,\ 请填写 taosAdapter 接收 StatsD 数据的端口(默认为 6044)。 ``` backends 部分添加 "./backends/repeater" diff --git a/docs/zh/14-reference/_tcollector.mdx b/docs/zh/14-reference/_tcollector.mdx index 8e51975b51..0ba373ef94 100644 --- a/docs/zh/14-reference/_tcollector.mdx +++ b/docs/zh/14-reference/_tcollector.mdx @@ -20,7 +20,7 @@ password = "taosdata" 其中 taosAdapter 默认写入的数据库名称为 `tcollector`,也可以修改 taosAdapter 配置文件 dbs 项来指定不同的名称。user 和 password 填写实际 TDengine 配置的值。修改过配置文件 taosAdapter 需重新启动。 -- 也可以使用 taosAdapter 命令行参数或设置环境变量启动的方式,使能 taosAdapter 接收 tcollector 数据功能,具体细节请参考 [taosAdapter 的使用手册](/reference/taosadapter) +- 也可以使用 taosAdapter 命令行参数或设置环境变量启动的方式,使能 taosAdapter 接收 tcollector 数据功能,具体细节请参考 [taosAdapter 的使用手册](../../reference/taosadapter) ### 配置 TCollector diff --git a/docs/zh/14-reference/_telegraf.mdx b/docs/zh/14-reference/_telegraf.mdx index 3f92e5dde0..a4ce8f0ae2 100644 --- a/docs/zh/14-reference/_telegraf.mdx +++ b/docs/zh/14-reference/_telegraf.mdx @@ -10,7 +10,7 @@ ... ``` -其中 请填写运行 taosAdapter 服务的服务器域名或 IP 地址, 请填写 REST 服务的端口(默认为 6041), 请填写当前运行的 TDengine 实际配置, 请填写希望在 TDengine 保存 Telegraf 数据的数据库名。 +其中 \ 请填写运行 taosAdapter 服务的服务器域名或 IP 地址,\ 请填写 REST 服务的端口(默认为 6041),\ 和 \ 请填写当前运行的 TDengine 实际配置,\ 请填写希望在 TDengine 保存 Telegraf 数据的数据库名。 示例如下: diff --git a/docs/zh/17-operation/06-monitor.md b/docs/zh/17-operation/06-monitor.md index 757dc3c69d..e97ded0787 100644 --- a/docs/zh/17-operation/06-monitor.md +++ b/docs/zh/17-operation/06-monitor.md @@ -3,7 +3,7 @@ title: 系统监控 description: 监控 TDengine 的运行状态 --- -TDengine 通过 [taosKeeper](/reference/taosKeeper/) 将服务器的 CPU、内存、硬盘空间、带宽、请求数、磁盘读写速度等信息定时写入指定数据库。TDengine 还将重要的系统操作(比如登录、创建、删除数据库等)日志以及各种错误报警信息进行记录。系统管理员可以从 CLI 直接查看这个数据库,也可以在 WEB 通过图形化界面查看这些监测信息。 +TDengine 通过 [taosKeeper](../../reference/taosKeeper/) 将服务器的 CPU、内存、硬盘空间、带宽、请求数、磁盘读写速度等信息定时写入指定数据库。TDengine 还将重要的系统操作(比如登录、创建、删除数据库等)日志以及各种错误报警信息进行记录。系统管理员可以从 CLI 直接查看这个数据库,也可以在 WEB 通过图形化界面查看这些监测信息。 这些监测信息的采集缺省是打开的,但可以修改配置文件里的选项 monitor 将其关闭或打开。 @@ -37,203 +37,205 @@ chmod +x TDinsight.sh ## log 库 -TDinsight dashboard 数据来源于 log 库(存放监控数据的默认db,可以在 taoskeeper 配置文件中修改,具体参考 [taoskeeper 文档](/reference/taosKeeper))。taoskeeper 启动后会自动创建 log 库,并将监控数据写入到该数据库中。 +TDinsight dashboard 数据来源于 log 库(存放监控数据的默认db,可以在 taoskeeper 配置文件中修改,具体参考 [taoskeeper 文档](../../reference/taosKeeper))。taoskeeper 启动后会自动创建 log 库,并将监控数据写入到该数据库中。 -### cluster\_info 表 +### taosd\_cluster\_basic 表 -`cluster_info` 表记录集群信息。 +`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| -|version|VARCHAR||tdengine version。例如:3.0.4.0| -|master\_uptime|FLOAT||当前 master 节点的uptime。单位:天| -|monitor\_interval|INT||monitor interval。单位:秒| -|dbs\_total|INT||database 总数| -|tbs\_total|BIGINT||当前集群 table 总数| -|stbs\_total|INT||当前集群 stable 总数| -|dnodes\_total|INT||当前集群 dnode 总数| -|dnodes\_alive|INT||当前集群 dnode 存活总数| -|mnodes\_total|INT||当前集群 mnode 总数| -|mnodes\_alive|INT||当前集群 mnode 存活总数| -|vgroups\_total|INT||当前集群 vgroup 总数| -|vgroups\_alive|INT||当前集群 vgroup 存活总数| -|vnodes\_total|INT||当前集群 vnode 总数| -|vnodes\_alive|INT||当前集群 vnode 存活总数| -|connections\_total|INT||当前集群连接总数| -|topics\_total|INT||当前集群 topic 总数| -|streams\_total|INT||当前集群 stream 总数| -|protocol|INT||协议版本,目前为 1| -|cluster\_id|NCHAR|TAG|cluster id| +|cluster_version|VARCHAR||tdengine version。例如:3.0.4.0| +|cluster\_id|VARCHAR|TAG|cluster id| -### d\_info 表 +### taosd\_cluster\_info 表 -`d_info` 表记录 dnode 状态信息。 +`taosd_cluster_info` 表记录集群信息。 |field|type|is\_tag|comment| |:----|:---|:-----|:------| |ts|TIMESTAMP||timestamp| -|status|VARCHAR||dnode 状态| -|dnode\_ep|NCHAR|TAG|dnode endpoint| -|cluster\_id|NCHAR|TAG|cluster id| +|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| -### m\_info 表 +### taosd\_vgroups\_info 表 -`m_info` 表记录 mnode 角色信息。 +`taosd_vgroups_info` 表记录虚拟节点组信息。 |field|type|is\_tag|comment| |:----|:---|:-----|:------| |ts|TIMESTAMP||timestamp| -|role|VARCHAR||mnode 角色, leader 或 follower| -|mnode\_id|INT|TAG|master node id| -|mnode\_ep|NCHAR|TAG|master node endpoint| -|cluster\_id|NCHAR|TAG|cluster id| +|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| -### dnodes\_info 表 +### taosd\_dnodes\_info 表 -`dnodes_info` 记录 dnode 信息。 +`taosd_dnodes_info` 记录 dnode 信息。 |field|type|is\_tag|comment| |:----|:---|:-----|:------| |ts|TIMESTAMP||timestamp| -|uptime|FLOAT||dnode uptime,单位:天| -|cpu\_engine|FLOAT||taosd cpu 使用率,从 `/proc//stat` 读取| -|cpu\_system|FLOAT||服务器 cpu 使用率,从 `/proc/stat` 读取| -|cpu\_cores|FLOAT||服务器 cpu 核数| -|mem\_engine|INT||taosd 内存使用率,从 `/proc//status` 读取| -|mem\_system|INT||服务器可用内存,单位 KB| -|mem\_total|INT||服务器内存总量,单位 KB| -|disk\_engine|INT||单位 bytes| -|disk\_used|BIGINT||data dir 挂载的磁盘使用量,单位 bytes| -|disk\_total|BIGINT||data dir 挂载的磁盘总容量,单位 bytes| -|net\_in|FLOAT||网络吞吐率,从 `/proc/net/dev` 中读取的 received bytes。单位 byte/s| -|net\_out|FLOAT||网络吞吐率,从 `/proc/net/dev` 中读取的 transmit bytes。单位 byte/s| -|io\_read|FLOAT||io 吞吐率,从 `/proc//io` 中读取的 rchar 与上次数值计算之后,计算得到速度。单位 byte/s| -|io\_write|FLOAT||io 吞吐率,从 `/proc//io` 中读取的 wchar 与上次数值计算之后,计算得到速度。单位 byte/s| -|io\_read\_disk|FLOAT||磁盘 io 吞吐率,从 `/proc//io` 中读取的 read_bytes。单位 byte/s| -|io\_write\_disk|FLOAT||磁盘 io 吞吐率,从 `/proc//io` 中读取的 write_bytes。单位 byte/s| -|req\_select|INT||两个间隔内发生的查询请求数目| -|req\_select\_rate|FLOAT||两个间隔内的查询请求速度 = `req_select / monitorInterval`| -|req\_insert|INT||两个间隔内发生的写入请求,包含的单条数据数目| -|req\_insert\_success|INT||两个间隔内发生的处理成功的写入请求,包含的单条数据数目| -|req\_insert\_rate|FLOAT||两个间隔内的单条数据写入速度 = `req_insert / monitorInterval`| -|req\_insert\_batch|INT||两个间隔内发生的写入请求数目| -|req\_insert\_batch\_success|INT||两个间隔内发生的成功的批量写入请求数目| -|req\_insert\_batch\_rate|FLOAT||两个间隔内的写入请求数目的速度 = `req_insert_batch / monitorInterval`| -|errors|INT||两个间隔内的出错的写入请求的数目| -|vnodes\_num|INT||dnode 上 vnodes 数量| -|masters|INT||dnode 上 master node 数量| -|has\_mnode|INT||dnode 是否包含 mnode| -|has\_qnode|INT||dnode 是否包含 qnode| -|has\_snode|INT||dnode 是否包含 snode| -|has\_bnode|INT||dnode 是否包含 bnode| -|dnode\_id|INT|TAG|dnode id| -|dnode\_ep|NCHAR|TAG|dnode endpoint| -|cluster\_id|NCHAR|TAG|cluster id| +|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| -### data\_dir 表 +### taosd\_dnodes\_status 表 -`data_dir` 表记录 data 目录信息。 +`taosd_dnodes_status` 表记录 dnode 状态信息。 |field|type|is\_tag|comment| |:----|:---|:-----|:------| |ts|TIMESTAMP||timestamp| -|name|NCHAR||data 目录,一般为 `/var/lib/taos`| -|level|INT||0、1、2 多级存储级别| -|avail|BIGINT||data 目录可用空间。单位 byte| -|used|BIGINT||data 目录已使用空间。单位 byte| -|total|BIGINT||data 目录空间。单位 byte| -|dnode\_id|INT|TAG|dnode id| -|dnode\_ep|NCHAR|TAG|dnode endpoint| -|cluster\_id|NCHAR|TAG|cluster id| +|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| -### log\_dir 表 +### taosd\_dnodes\_log\_dir 表 -`log_dir` 表记录 log 目录信息。 +`taosd_dnodes_log_dir` 表记录 log 目录信息。 |field|type|is\_tag|comment| |:----|:---|:-----|:------| |ts|TIMESTAMP||timestamp| -|name|NCHAR||log 目录名,一般为 `/var/log/taos/`| -|avail|BIGINT||log 目录可用空间。单位 byte| -|used|BIGINT||log 目录已使用空间。单位 byte| -|total|BIGINT||log 目录空间。单位 byte| -|dnode\_id|INT|TAG|dnode id| -|dnode\_ep|NCHAR|TAG|dnode endpoint| -|cluster\_id|NCHAR|TAG|cluster id| +|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| -### temp\_dir 表 +### taosd\_dnodes\_data\_dir 表 -`temp_dir` 表记录 temp 目录信息。 +`taosd_dnodes_data_dir` 表记录 data 目录信息。 |field|type|is\_tag|comment| |:----|:---|:-----|:------| |ts|TIMESTAMP||timestamp| -|name|NCHAR||temp 目录名,一般为 `/tmp/`| -|avail|BIGINT||temp 目录可用空间。单位 byte| -|used|BIGINT||temp 目录已使用空间。单位 byte| -|total|BIGINT||temp 目录空间。单位 byte| -|dnode\_id|INT|TAG|dnode id| -|dnode\_ep|NCHAR|TAG|dnode endpoint| -|cluster\_id|NCHAR|TAG|cluster id| +|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| -### vgroups\_info 表 +### taosd\_mnodes\_info 表 -`vgroups_info` 表记录虚拟节点组信息。 +`taosd_mnodes_info` 表记录 mnode 角色信息。 |field|type|is\_tag|comment| |:----|:---|:-----|:------| |ts|TIMESTAMP||timestamp| -|vgroup\_id|INT||vgroup id| -|database\_name|VARCHAR||vgroup 所属的 database 名字| -|tables\_num|BIGINT||vgroup 中 table 数量| -|status|VARCHAR||vgroup 状态| -|dnode\_id|INT|TAG|dnode id| -|dnode\_ep|NCHAR|TAG|dnode endpoint| -|cluster\_id|NCHAR|TAG|cluster id| +|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| -### vnodes\_role 表 +### taosd\_vnodes\_role 表 -`vnodes_role` 表记录虚拟节点角色信息。 +`taosd_vnodes_role` 表记录虚拟节点角色信息。 |field|type|is\_tag|comment| |:----|:---|:-----|:------| |ts|TIMESTAMP||timestamp| -|vnode\_role|VARCHAR||vnode 角色,leader 或 follower| -|dnode\_id|INT|TAG|dnode id| -|dnode\_ep|NCHAR|TAG|dnode endpoint| -|cluster\_id|NCHAR|TAG|cluster id| +|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| -### log\_summary 表 +### taosd\_sql\_req 表 -`log_summary` 记录日志统计信息。 +`taosd_sql_req` 记录授权信息。 |field|type|is\_tag|comment| |:----|:---|:-----|:------| |ts|TIMESTAMP||timestamp| -|error|INT||error 总数| -|info|INT||info 总数| -|debug|INT||debug 总数| -|trace|INT||trace 总数| -|dnode\_id|INT|TAG|dnode id| -|dnode\_ep|NCHAR|TAG|dnode endpoint| -|cluster\_id|NCHAR|TAG|cluster id| +|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| -### grants\_info 表 +### taos\_sql\_req 表 -`grants_info` 记录授权信息。 +`taos_sql_req` 记录授权信息。 |field|type|is\_tag|comment| |:----|:---|:-----|:------| |ts|TIMESTAMP||timestamp| -|expire\_time|BIGINT||认证过期时间,企业版有效,社区版为 bigint 最大值| -|timeseries\_used|BIGINT||已用测点数| -|timeseries\_total|BIGINT||总测点数,开源版本为 bigint 最大值| -|dnode\_id|INT|TAG|dnode id| -|dnode\_ep|NCHAR|TAG|dnode endpoint| -|cluster\_id|NCHAR|TAG|cluster id| +|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| ### keeper\_monitor 表 @@ -242,8 +244,8 @@ TDinsight dashboard 数据来源于 log 库(存放监控数据的默认db, |field|type|is\_tag|comment| |:----|:---|:-----|:------| |ts|TIMESTAMP||timestamp| -|cpu|FLOAT||cpu 使用率| -|mem|FLOAT||内存使用率| +|cpu|DOUBLE||cpu 使用率| +|mem|DOUBLE||内存使用率| |identify|NCHAR|TAG|| ### taosadapter\_restful\_http\_request\_total 表 diff --git a/docs/zh/17-operation/17-diagnose.md b/docs/zh/17-operation/17-diagnose.md index ef9923969e..dee83c9fef 100644 --- a/docs/zh/17-operation/17-diagnose.md +++ b/docs/zh/17-operation/17-diagnose.md @@ -15,7 +15,7 @@ description: 一些常见问题的诊断技巧 2. 服务端命令行输入:`taos -n server -P -l ` 以服务端身份启动对端口 port 为基准端口的监听 3. 客户端命令行输入:`taos -n client -h -P -l ` 以客户端身份启动对指定的服务器、指定的端口发送测试包 --l : 测试网络包的大小(单位:字节)。最小值是 11、最大值是 64000,默认值为 1000。 +-l \: 测试网络包的大小(单位:字节)。最小值是 11、最大值是 64000,默认值为 1000。 注:两端命令行中指定的测试包长度必须一致,否则测试显示失败。 服务端运行正常的话会输出以下信息: @@ -63,7 +63,7 @@ taosd 服务端日志文件标志位 debugflag 默认为 131,在 debug 时往 ## 客户端日志 -每个独立运行的客户端(一个进程)生成一个独立的客户端日志,其命名方式采用 taoslog+<序号> 的方式命名。文件标志位 debugflag 默认为 131,在 debug 时往往需要将其提升到 135 或 143 。 +每个独立运行的客户端(一个进程)生成一个独立的客户端日志,其命名方式采用 taoslog+\<序号> 的方式命名。文件标志位 debugflag 默认为 131,在 debug 时往往需要将其提升到 135 或 143 。 - taoslog 客户端(driver)生成的日志,默认记录客户端 INFO/ERROR/WARNING 级别日志,还根据设置的日志输出级别,记录 DEBUG(日志级别 135)、TRACE(日志级别是 143)。 diff --git a/docs/zh/20-third-party/01-grafana.mdx b/docs/zh/20-third-party/01-grafana.mdx index fae4ffde9e..065ce9e57e 100644 --- a/docs/zh/20-third-party/01-grafana.mdx +++ b/docs/zh/20-third-party/01-grafana.mdx @@ -170,7 +170,7 @@ docker run -d \ 3. 使用 docker-compose 命令启动 TDengine + Grafana :`docker-compose up -d`。 -打开 Grafana ,现在可以添加 Dashboard 了。 +打开 Grafana \,现在可以添加 Dashboard 了。 @@ -192,7 +192,7 @@ docker run -d \ :::note -由于 REST 接口无状态, 不能使用 `use db` 语句来切换数据库。Grafana 插件中 SQL 语句中可以使用 . 来指定数据库。 +由于 REST 接口无状态, 不能使用 `use db` 语句来切换数据库。Grafana 插件中 SQL 语句中可以使用 \.\ 来指定数据库。 ::: diff --git a/docs/zh/20-third-party/02-prometheus.md b/docs/zh/20-third-party/02-prometheus.md index 0f95053a78..a341068b53 100644 --- a/docs/zh/20-third-party/02-prometheus.md +++ b/docs/zh/20-third-party/02-prometheus.md @@ -16,7 +16,7 @@ Prometheus 提供了 `remote_write` 和 `remote_read` 接口来利用其它数 要将 Prometheus 数据写入 TDengine 需要以下几方面的准备工作。 - TDengine 集群已经部署并正常运行 -- taosAdapter 已经安装并正常运行。具体细节请参考 [taosAdapter 的使用手册](/reference/taosadapter) +- taosAdapter 已经安装并正常运行。具体细节请参考 [taosAdapter 的使用手册](../../reference/taosadapter) - Prometheus 已经安装。安装 Prometheus 请参考[官方文档](https://prometheus.io/docs/prometheus/latest/installation/) ## 配置步骤 diff --git a/docs/zh/20-third-party/03-telegraf.md b/docs/zh/20-third-party/03-telegraf.md index bb688a6ab6..a56fe706ef 100644 --- a/docs/zh/20-third-party/03-telegraf.md +++ b/docs/zh/20-third-party/03-telegraf.md @@ -14,7 +14,7 @@ Telegraf 是一款十分流行的指标采集开源软件。在数据采集和 要将 Telegraf 数据写入 TDengine 需要以下几方面的准备工作。 - TDengine 集群已经部署并正常运行 -- taosAdapter 已经安装并正常运行。具体细节请参考 [taosAdapter 的使用手册](/reference/taosadapter) +- taosAdapter 已经安装并正常运行。具体细节请参考 [taosAdapter 的使用手册](../../reference/taosadapter) - Telegraf 已经安装。安装 Telegraf 请参考[官方文档](https://docs.influxdata.com/telegraf/v1.22/install/) - Telegraf 默认采集系统运行状态数据。通过使能[输入插件](https://docs.influxdata.com/telegraf/v1.22/plugins/)方式可以输出[其他格式](https://docs.influxdata.com/telegraf/v1.24/data_formats/input/)的数据到 Telegraf 再写入到 TDengine中。 @@ -73,6 +73,6 @@ Query OK, 3 row(s) in set (0.013269s) - TDengine 接收 influxdb 格式数据默认生成的子表名是根据规则生成的唯一 ID 值。 用户如需指定生成的表名,可以通过在 taos.cfg 里配置 smlChildTableName 参数来指定。如果通过控制输入数据格式,即可利用 TDengine 这个功能指定生成的表名。 -举例如下:配置 smlChildTableName=tname 插入数据为 st,tname=cpu1,t1=4 c1=3 1626006833639000000 则创建的表名为 cpu1。如果多行数据 tname 相同,但是后面的 tag_set 不同,则使用第一行自动建表时指定的 tag_set,其他的行会忽略)。[TDengine 无模式写入参考指南](/reference/schemaless/#无模式写入行协议) +举例如下:配置 smlChildTableName=tname 插入数据为 st,tname=cpu1,t1=4 c1=3 1626006833639000000 则创建的表名为 cpu1。如果多行数据 tname 相同,但是后面的 tag_set 不同,则使用第一行自动建表时指定的 tag_set,其他的行会忽略)。[TDengine 无模式写入参考指南](../../reference/schemaless/#无模式写入行协议) ::: diff --git a/docs/zh/20-third-party/05-collectd.md b/docs/zh/20-third-party/05-collectd.md index bda66b3cce..a987c910ca 100644 --- a/docs/zh/20-third-party/05-collectd.md +++ b/docs/zh/20-third-party/05-collectd.md @@ -14,7 +14,7 @@ collectd 是一个用来收集系统性能的守护进程。collectd 提供各 要将 collectd 数据写入 TDengine,需要几方面的准备工作。 - TDengine 集群已经部署并正常运行 -- taosAdapter 已经安装并正常运行,具体细节请参考[ taosAdapter 的使用手册](/reference/taosadapter) +- taosAdapter 已经安装并正常运行,具体细节请参考[ taosAdapter 的使用手册](../../reference/taosadapter) - collectd 已经安装。安装 collectd 请参考[官方文档](https://collectd.org/download.shtml) ## 配置步骤 diff --git a/docs/zh/20-third-party/06-statsd.md b/docs/zh/20-third-party/06-statsd.md index 20b4d800be..e3217b62fb 100644 --- a/docs/zh/20-third-party/06-statsd.md +++ b/docs/zh/20-third-party/06-statsd.md @@ -14,7 +14,7 @@ StatsD 是汇总和总结应用指标的一个简单的守护进程,近些年 要将 StatsD 数据写入 TDengine 需要以下几方面的准备工作。 - TDengine 集群已经部署并正常运行 -- taosAdapter 已经安装并正常运行。具体细节请参考 [taosAdapter 的使用手册](/reference/taosadapter) +- taosAdapter 已经安装并正常运行。具体细节请参考 [taosAdapter 的使用手册](../../reference/taosadapter) - StatsD 已经安装。安装 StatsD 请参考[官方文档](https://github.com/statsd/statsd) ## 配置步骤 diff --git a/docs/zh/20-third-party/07-icinga2.md b/docs/zh/20-third-party/07-icinga2.md index d112a7a5e6..4d28e821a8 100644 --- a/docs/zh/20-third-party/07-icinga2.md +++ b/docs/zh/20-third-party/07-icinga2.md @@ -14,7 +14,7 @@ icinga2 是一款开源主机、网络监控软件,最初由 Nagios 网络监 要将 icinga2 数据写入 TDengine 需要以下几方面的准备工作。 - TDengine 集群已经部署并正常运行 -- taosAdapter 已经安装并正常运行。具体细节请参考[ taosAdapter 的使用手册](/reference/taosadapter) +- taosAdapter 已经安装并正常运行。具体细节请参考[ taosAdapter 的使用手册](../../reference/taosadapter) - icinga2 已经安装。安装 icinga2 请参考[官方文档](https://icinga.com/docs/icinga-2/latest/doc/02-installation/) ## 配置步骤 diff --git a/docs/zh/20-third-party/08-tcollector.md b/docs/zh/20-third-party/08-tcollector.md index a6ed0f89e2..7ecbb9bea1 100644 --- a/docs/zh/20-third-party/08-tcollector.md +++ b/docs/zh/20-third-party/08-tcollector.md @@ -14,7 +14,7 @@ TCollector 是 openTSDB 的一部分,它用来采集客户端日志发送给 要将 TCollector 数据写入 TDengine 需要以下几方面的准备工作。 - TDengine 集群已经部署并正常运行 -- taosAdapter 已经安装并正常运行。具体细节请参考 [taosAdapter 的使用手册](/reference/taosadapter) +- taosAdapter 已经安装并正常运行。具体细节请参考 [taosAdapter 的使用手册](../../reference/taosadapter) - TCollector 已经安装。安装 TCollector 请参考[官方文档](http://opentsdb.net/docs/build/html/user_guide/utilities/tcollector.html#installation-of-tcollector) ## 配置步骤 diff --git a/docs/zh/20-third-party/09-emq-broker.md b/docs/zh/20-third-party/09-emq-broker.md index 782a139e22..2d80d87db4 100644 --- a/docs/zh/20-third-party/09-emq-broker.md +++ b/docs/zh/20-third-party/09-emq-broker.md @@ -11,12 +11,12 @@ MQTT 是流行的物联网数据传输协议,[EMQX](https://github.com/emqx/em 要让 EMQX 能正常添加 TDengine 数据源,需要以下几方面的准备工作。 - TDengine 集群已经部署并正常运行 -- taosAdapter 已经安装并正常运行。具体细节请参考 [taosAdapter 的使用手册](/reference/taosadapter) +- taosAdapter 已经安装并正常运行。具体细节请参考 [taosAdapter 的使用手册](../../reference/taosadapter) - 如果使用后文介绍的模拟写入程序,需要安装合适版本的 Node.js,推荐安装 v12 ## 安装并启动 EMQX -用户可以根据当前的操作系统,到 EMQX 官网下载安装包,并执行安装。下载地址如下:。安装后使用 `sudo emqx start` 或 `sudo systemctl start emqx` 启动 EMQX 服务。 +用户可以根据当前的操作系统,到 EMQX 官网下载安装包,并执行安装。下载地址如下:\。安装后使用 `sudo emqx start` 或 `sudo systemctl start emqx` 启动 EMQX 服务。 注意:本文基于 EMQX v4.4.5 版本,其他版本由于相关配置界面、配置方法以及功能可能随着版本升级有所区别。 diff --git a/docs/zh/20-third-party/11-kafka.md b/docs/zh/20-third-party/11-kafka.md index 3d8822fdae..2ee315697f 100644 --- a/docs/zh/20-third-party/11-kafka.md +++ b/docs/zh/20-third-party/11-kafka.md @@ -93,7 +93,7 @@ curl http://localhost:8083/connectors TDengine Sink Connector 的作用是同步指定 topic 的数据到 TDengine。用户无需提前创建数据库和超级表。可手动指定目标数据库的名字(见配置参数 connection.database), 也可按一定规则生成(见配置参数 connection.database.prefix)。 -TDengine Sink Connector 内部使用 TDengine [无模式写入接口](../../connector/cpp#无模式写入-api)写数据到 TDengine,目前支持三种格式的数据:[InfluxDB 行协议格式](/develop/insert-data/influxdb-line)、 [OpenTSDB Telnet 协议格式](/develop/insert-data/opentsdb-telnet) 和 [OpenTSDB JSON 协议格式](/develop/insert-data/opentsdb-json)。 +TDengine Sink Connector 内部使用 TDengine [无模式写入接口](../../connector/cpp#无模式写入-api)写数据到 TDengine,目前支持三种格式的数据:[InfluxDB 行协议格式](../../develop/insert-data/influxdb-line)、 [OpenTSDB Telnet 协议格式](../../develop/insert-data/opentsdb-telnet) 和 [OpenTSDB JSON 协议格式](../../develop/insert-data/opentsdb-json)。 下面的示例将主题 meters 的数据,同步到目标数据库 power。数据格式为 InfluxDB Line 协议格式。 @@ -212,7 +212,7 @@ Query OK, 4 row(s) in set (0.004208s) TDengine Source Connector 的作用是将 TDengine 某个数据库某一时刻之后的数据全部推送到 Kafka。TDengine Source Connector 的实现原理是,先分批拉取历史数据,再用定时查询的策略同步增量数据。同时会监控表的变化,可以自动同步新增的表。如果重启 Kafka Connect, 会从上次中断的位置继续同步。 -TDengine Source Connector 会将 TDengine 数据表中的数据转换成 [InfluxDB Line 协议格式](/develop/insert-data/influxdb-line/) 或 [OpenTSDB JSON 协议格式](/develop/insert-data/opentsdb-json), 然后写入 Kafka。 +TDengine Source Connector 会将 TDengine 数据表中的数据转换成 [InfluxDB Line 协议格式](../../develop/insert-data/influxdb-line/) 或 [OpenTSDB JSON 协议格式](../../develop/insert-data/opentsdb-json), 然后写入 Kafka。 下面的示例程序同步数据库 test 中的数据到主题 tdengine-test-meters。 @@ -344,7 +344,7 @@ curl -X DELETE http://localhost:8083/connectors/TDengineSourceConnector ### TDengine Sink Connector 特有的配置 1. `connection.database`: 目标数据库名。如果指定的数据库不存在会则自动创建。自动建库使用的时间精度为纳秒。默认值为 null。为 null 时目标数据库命名规则参考 `connection.database.prefix` 参数的说明 -2. `connection.database.prefix`: 当 connection.database 为 null 时, 目标数据库的前缀。可以包含占位符 '${topic}'。 比如 kafka_${topic}, 对于主题 'orders' 将写入数据库 'kafka_orders'。 默认 null。当为 null 时,目标数据库的名字和主题的名字是一致的。 +2. `connection.database.prefix`: 当 connection.database 为 null 时, 目标数据库的前缀。可以包含占位符 '$\{topic}'。 比如 kafka_$\{topic}, 对于主题 'orders' 将写入数据库 'kafka_orders'。 默认 null。当为 null 时,目标数据库的名字和主题的名字是一致的。 3. `batch.size`: 分批写入每批记录数。当 Sink Connector 一次接收到的数据大于这个值时将分批写入。 4. `max.retries`: 发生错误时的最大重试次数。默认为 1。 5. `retry.backoff.ms`: 发送错误时重试的时间间隔。单位毫秒,默认为 3000。 @@ -375,12 +375,12 @@ curl -X DELETE http://localhost:8083/connectors/TDengineSourceConnector ## 其他说明 -1. 关于如何在独立安装的 Kafka 环境使用 Kafka Connect 插件, 请参考官方文档:。 +1. 关于如何在独立安装的 Kafka 环境使用 Kafka Connect 插件, 请参考官方文档:\。 ## 问题反馈 -无论遇到任何问题,都欢迎在本项目的 Github 仓库反馈:。 +无论遇到任何问题,都欢迎在本项目的 Github 仓库反馈:\。 ## 参考 -1. +1. \ diff --git a/docs/zh/20-third-party/13-dbeaver.md b/docs/zh/20-third-party/13-dbeaver.md index c096fd41a5..1da5dad6f2 100644 --- a/docs/zh/20-third-party/13-dbeaver.md +++ b/docs/zh/20-third-party/13-dbeaver.md @@ -11,7 +11,7 @@ DBeaver 是一款流行的跨平台数据库管理工具,方便开发者、数 使用 DBeaver 管理 TDengine 需要以下几方面的准备工作。 - 安装 DBeaver。DBeaver 支持主流操作系统包括 Windows、macOS 和 Linux。请注意[下载](https://dbeaver.io/download/)正确平台和版本(23.1.1+)的安装包。详细安装步骤请参考 [DBeaver 官方文档](https://github.com/dbeaver/dbeaver/wiki/Installation)。 -- 如果使用独立部署的 TDengine 集群,请确认 TDengine 正常运行,并且 taosAdapter 已经安装并正常运行,具体细节请参考 [taosAdapter 的使用手册](/reference/taosadapter)。 +- 如果使用独立部署的 TDengine 集群,请确认 TDengine 正常运行,并且 taosAdapter 已经安装并正常运行,具体细节请参考 [taosAdapter 的使用手册](../../reference/taosadapter)。 ## 使用 DBeaver 访问内部部署的 TDengine diff --git a/docs/zh/20-third-party/50-qstudio.md b/docs/zh/20-third-party/50-qstudio.md index 0fc1ba6970..0d95c55ea6 100644 --- a/docs/zh/20-third-party/50-qstudio.md +++ b/docs/zh/20-third-party/50-qstudio.md @@ -11,7 +11,7 @@ qStudio 是一款免费的多平台 SQL 数据分析工具,可以轻松浏览 使用 qStudio 连接 TDengine 需要以下几方面的准备工作。 - 安装 qStudio。qStudio 支持主流操作系统包括 Windows、macOS 和 Linux。请注意[下载](https://www.timestored.com/qstudio/download/)正确平台的安装包。 -- 安装 TDengine 实例,请确认 TDengine 正常运行,并且 taosAdapter 已经安装并正常运行,具体细节请参考 [taosAdapter 的使用手册](/reference/taosadapter)。 +- 安装 TDengine 实例,请确认 TDengine 正常运行,并且 taosAdapter 已经安装并正常运行,具体细节请参考 [taosAdapter 的使用手册](../../reference/taosadapter)。 ## 使用 qStudio 连接 TDengine diff --git a/docs/zh/20-third-party/75-powerbi.md b/docs/zh/20-third-party/75-powerbi.md index 255b23402b..50077d09c8 100644 --- a/docs/zh/20-third-party/75-powerbi.md +++ b/docs/zh/20-third-party/75-powerbi.md @@ -4,30 +4,43 @@ title: Power BI description: 如何使用 Power BI 和 TDengine 进行时序数据分析 --- -# 如何使用 Power BI 和 TDengine 进行时序数据分析 +# 工具 - Power BI -## 方案介绍 +![Power BI use step](./powerbi-step-zh.png) -使用 ODBC 连接器,Power BI 可以快速的访问 TDengine。您可以将标签数据、原始时序数据或按时间聚合后的时序数据从 TDengine 导入到 Power BI,制作报表或仪表盘,整个过程不需要任何的代码编写过程。 +[Power BI](https://powerbi.microsoft.com/) 是由 Microsoft 提供的一种商业分析工具。通过配置使用 ODBC 连接器,Power BI 可以快速的访问 TDengine。您可以将标签数据、原始时序数据或按时间聚合后的时序数据从 TDengine 导入到 Power BI,制作报表或仪表盘,整个过程不需要任何的代码编写过程。 -### 整体步骤 -![Power BI use step](./powerbi-step-zh.webp) +### 前置条件 +1. TDengine 服务端软件已经安装并运行。 +2. 安装完成 Power BI Desktop 软件并可以运行(如未安装,请从[官方地址](https://www.microsoft.com/zh-cn/download/details.aspx?id=58494)下载最新的 Windows X64 版本)。 -### 前置要求 -1. TDengine 服务端软件已经安装并运行 -2. Power BI Desktop 软件已经安装并运行(如未安装,请从[官方地址](https://www.microsoft.com/zh-cn/download/details.aspx?id=58494)下载最新的 Windows X64 版本)。 +### 安装 ODBC 连接器 +1. 仅支持 Windows 平台。Windows 上需要安装过 VC 运行时库,可在此下载安装 [VC 运行时库](https://learn.microsoft.com/zh-cn/cpp/windows/latest-supported-vc-redist?view=msvc-170)。如果已经安装 VS 开发工具可忽略。 +2. 下载和安装 [TDengine Windows 客户端安装包](https://docs.taosdata.com/get-started/package/)。 +### 配置 ODBC 数据源 +1. Windows 操作系统的【开始】菜单搜索打开【ODBC 数据源(64 位)】管理工具(注意不要选择 ODBC 数据源(32 位))。 +2. 选中【用户 DSN】标签页,通过【添加(D)】按钮进入“创建数据源”界面。 +3. 选择想要添加的数据源,然后选择【TDengine】,点击完成,进入 TDengine ODBC 数据源配置页面,填写如下必要信息: -### 安装驱动 -从 TDengine 官网下载最新的 Windows X64 客户端驱动程序 [下载地址](https://docs.taosdata.com/get-started/package/),并安装在 Power BI 运行的机器上 +  【DSN】:    数据源名称,必填,比如“MyTDengine” -### 配置数据源 -请参考 [ODBC](../../connector/odbc) 配置Websocket数据源。 +  【连接类型】:  选中【Websocket】 + +  【服务地址】:  taos://localhost:6041 + +  【数据库】:   需要连接的数据库,可选,比如“test” + +  【用户名】:   输入用户名,如果不填,默认为 root + +  【密码】:    输入用户密码,如果不填,默认为 taosdata + +4. 点击【测试连接】按钮测试连接情况,如果成功,会提示“成功连接到 taos://root:taosdata@localhost:6041”。 ### 导入 TDengine 数据到 Power BI -1. 打开 Power BI 并登录后,通过如下步骤添加数据源,“主页” -> “获取数据” -> “其他” -> “ODBC” -> “连接” -2. 选择数据源名称后,连接到配置好的数据源,进入导航器,浏览对应数据库的数据表并加载 -3. 如果需要输入 SQL 语句,可以点击“高级选项”,在展开的对话框中输入并加载数据 +1. 打开 Power BI 并登录后,通过如下步骤添加数据源,“主页” -> “获取数据” -> “其他” -> “ODBC” -> “连接”。 +2. 选择刚才创建的数据源名称,比如“MyTDengine”,点击“确定”按钮。在弹出的“ODBC 驱动程序”对话框中,在左边的菜单里面选择“默认或自定义”,点击“连接”按钮,可以连接到配置好的数据源。在进入“导航器”后,可以浏览对应数据库的数据表并加载。 +3. 如果需要输入 SQL 语句,可以点击“高级选项”,在展开的对话框中输入并加载数据。 为了更好的使用 Power BI 分析 TDengine 中的数据,您需要理解维度、度量、时序、相关性的概念,然后通过自定义的 SQL 语句导入数据。 @@ -39,12 +52,9 @@ description: 如何使用 Power BI 和 TDengine 进行时序数据分析 4. 相关性:告诉数据之间如何关联,度量和维度可以通过 tbname 列关联在一起,日期表和度量则可以通过 date 列关联,配合形成可视化报表。 ### 智能电表样例 -TDengine 有自己独特的数据模型,它使用超级表作为模板,为每个设备创建一个表,每个表最多可创建 4096 个数据列和 128 个标签列。在智能电表样例中,假如一个电表每秒产生一条记录,一天就有 86,400 条记录,一年就有 31,536,000 条记录,1000 个电表将占用 600 GB 原始磁盘空间。因此,Power BI 更多的应用方式是将标签列映射为维度列,数据列的聚合结果导入为度量列,最终为关键决策制定者提供所需的指标。 -1. 导入维度数据 -在 Power BI 中导入表的标签列,取名为 tags,SQL 如下 -select distinct tbname, groupid, location from test.meters; -2. 导入度量数据 -在 Power BI 中,按照 1 小时的时间窗口,导入每个电表的电流均值、电压均值、相位均值,取名为 data,SQL 如下 +TDengine 有自己独特的数据模型,它使用超级表作为模板,为每个设备创建一个表,每个表最多可创建 4096 个数据列和 128 个标签列。在 [the example of meters](https://docs.taosdata.com/concept/) 中,假如一个电表每秒产生一条记录,一天就有 86,400 条记录,一年就有 31,536,000 条记录,1000 个电表将占用 600 GB 原始磁盘空间。因此,Power BI 更多的应用方式是将标签列映射为维度列,数据列的聚合结果导入为度量列,最终为关键决策制定者提供所需的指标。 +1. 导入维度数据:在 Power BI 中导入表的标签列,取名为 tags,SQL 如下: +`select distinct tbname, groupid, location from test.meters;` +2. 导入度量数据:在 Power BI 中,按照 1 小时的时间窗口,导入每个电表的电流均值、电压均值、相位均值,取名为 data,SQL 如下: `select tbname, _wstart ws, avg(current), avg(voltage), avg(phase) from test.meters PARTITION by tbname interval(1h)` ; -3. 建立维度和度量的关联关系 -在 Power BI 中,打开模型视图,建立表 tags 和 data 的关联关系,将 tbname 设置为关联数据列。之后,就可以在柱状图、饼图等控件中使用这些数据。更多有关 Power BI 构建视觉效果的信息,请查询 [Power BI 文档](https://learn.microsoft.com/zh-cn/power-bi/)。 \ No newline at end of file +3. 建立维度和度量的关联关系:在 Power BI 中,打开模型视图,建立表 tags 和 data 的关联关系,将 tbname 设置为关联数据列。之后,就可以在柱状图、饼图等控件中使用这些数据。更多有关 Power BI 构建视觉效果的信息,请查询 [Power BI 文档](https://learn.microsoft.com/zh-cn/power-bi/)。 \ No newline at end of file diff --git a/docs/zh/20-third-party/76-yonghongbi.mdx b/docs/zh/20-third-party/76-yonghongbi.mdx new file mode 100644 index 0000000000..f328c5ee3a --- /dev/null +++ b/docs/zh/20-third-party/76-yonghongbi.mdx @@ -0,0 +1,64 @@ +--- +sidebar_label: 永洪 BI +title: 永洪 BI +description: 使用 TDengine 连接永洪 BI +--- + +# 工具 - 永洪 BI + +![永洪 BI use step](./yonghongbi-step-zh.png) + +[永洪一站式大数据 BI 平台](https://www.yonghongtech.com/)为各种规模的企业提供灵活易用的全业务链的大数据分析解决方案,让每一位用户都能使用这一平台轻松发掘大数据价值,获取深度洞察力。TDengine 可以通过 JDBC 连接器作为数据源添加到永洪 BI 中。完成数据源配置后,永洪 BI 就能从 TDengine 中读取数据,并提供数据展示、分析和预测等功能。 + +### 前置条件 + +1. Yonghong Desktop Basic 已经安装并运行(如果未安装,请到[永洪科技官方下载页面](https://www.yonghongtech.com/cp/desktop/)下载)。 +2. TDengine 已经安装并运行,并确保在 TDengine 服务端启动了 taosadapter 服务。 + +### 安装 JDBC 连接器 + +从 [maven.org](https://central.sonatype.com/artifact/com.taosdata.jdbc/taos-jdbcdriver/versions) 下载最新的 TDengine JDBC 连接器(目前的版本是 3.2.7),并安装在 BI 工具运行的机器上。 + +### 配置 TDengine JDBC 数据源 + +1. 在打开的 Yonghong Desktop BI 工具中点击“添加数据源”,选择 SQL 数据源中的“GENERIC”类型。 +2. 点击“选择自定义驱动”,在“驱动管理”对话框中,点击“驱动列表”旁边的“+”,输入名称“MyTDengine”。然后点击“上传文件”按钮上传刚刚下载的 TDengine JDBC 连接器文件"taos-jdbcdriver-3.2.7-dist.jar",并选择“com.taosdata.jdbc.rs.RestfulDriver”驱动,最后点击“确定”按钮完成驱动添加。 +3. 然后请复制下面的内容到“URL”字段: +``` +jdbc:TAOS-RS://localhost:6041?user=root&password=taosdata +``` +4. 接着在“认证方式”选择“无身份认证”。 +5. 在数据源的高级设置中,修改“Quote符号”的值为反引号“`”。 +6. 点击“测试连接”,弹出“测试成功”的对话框。点击“保存”按钮,输入“MyTDengine”来保存 TDengine 数据源。 + +### 创建 TDengine 数据集 + +1. 在 BI 工具中点击“添加数据源”,展开刚刚创建的数据源,并浏览 TDengine 中的超级表。 +2. 您可以将超级表的数据全部加载到 BI 工具中,也可以通过自定义 SQL 语句导入部分数据。 +3. 当勾选“数据库内计算”时,BI 工具将不再缓存 TDengine 的时序数据,并在处理查询时将 SQL 请求发送给 TDengine 直接处理。 + +当导入数据后,BI 工具会自动将数值类型设置为“度量”列,将文本类型设置为“维度”列。而在 TDengine 的超级表中,采用普通列作为数据的度量,采用标签列作为数据的维度,因此您可能需要在创建数据集时更改部分列的属性。TDengine 在支持标准 SQL 的基础之上,还提供了一系列满足时序业务场景需求的特色查询语法,例如数据切分查询、窗口切分查询等,具体参考[TDengine 特色查询功能介绍](https://docs.taosdata.com/taos-sql/distinguished/)。通过使用这些特色查询,当 BI 工具将 SQL 查询发送到 TDengine 数据库时,可以大大提高数据访问速度,减少网络传输带宽。 + +在 BI 工具中,您可以创建“参数”并在 SQL 语句中使用,通过手动、定时的方式动态执行这些 SQL 语句,即可实现可视化报告的刷新效果。如下 SQL 语句: + +```sql +select _wstart ws, count(*) cnt from supertable where tbname=?{metric} and ts >= ?{from} and ts < ?{to} interval(?{interval}) +``` + +可以从 TDengine 实时读取数据,其中: + +- `_wstart`:表示时间窗口起始时间。 +- `count(*)`:表示时间窗口内的聚合值。 +- `?{interval}`:表示在 SQL 语句中引入名称为 interval 的参数,当 BI 工具查询数据时,会给 interval 参数赋值,如果取值为 1m,则表示按照 1 分钟的时间窗口降采样数据。 +- `?{metric}`:该参数用来指定查询的数据表名称,当在 BI 工具中把某个“下拉参数组件”的 ID 也设置为 metric 时,该“下拉参数组件”的被选择项将会和该参数绑定在一起,实现动态选择的效果。 +- `?{from}` 和 `?{to}`:这两个参数用来表示查询数据集的时间范围,可以与“文本参数组件”绑定。 + +可以在 BI 工具的“编辑参数”对话框中修改“参数”的数据类型、数据范围、默认取值,并在“可视化报告”中动态设置这些参数的值。 + +### 制作可视化报告 + +1. 在永洪 BI 工具中点击“制作报告”,创建画布。 +2. 拖动可视化组件到画布中,例如“表格组件”。 +3. 在“数据集”侧边栏中选择待绑定的数据集,将数据列中的“维度”和“度量”按需绑定到“表格组件”。 +4. 点击“保存”后,即可查看报告。 +5. 更多有关永洪 BI 工具的信息,请查询其[帮助文档](https://www.yonghongtech.com/help/Z-Suite/10.0/ch/)。 \ No newline at end of file diff --git a/docs/zh/20-third-party/_deploytaosadapter.mdx b/docs/zh/20-third-party/_deploytaosadapter.mdx index ec40744c9a..054ec95573 100644 --- a/docs/zh/20-third-party/_deploytaosadapter.mdx +++ b/docs/zh/20-third-party/_deploytaosadapter.mdx @@ -14,5 +14,5 @@ systemctl start taosadapter systemctl status taosadapter ``` -taosAdapter 详细的配置参数和使用请参考 `taosadapter --help` 命令输出以及 [参考文档](/reference/taosadapter) 。 +taosAdapter 详细的配置参数和使用请参考 `taosadapter --help` 命令输出以及 [参考文档](../../reference/taosadapter) 。 diff --git a/docs/zh/20-third-party/powerbi-step-zh.png b/docs/zh/20-third-party/powerbi-step-zh.png new file mode 100644 index 0000000000000000000000000000000000000000..26c454a9de6f014d712040ed83029e587440b233 GIT binary patch literal 13402 zcmeHui9eLz7eBI8miCVsR@5z%5x!XR6cJzKIHB}>+l zC4?f>*wPp>%=kS%pYQhj6MnDP@AaDd%ze&t?z!ijd!6T;_goKPW(K@R1&^|@u<#lg z>RPg}uq^`p#KWAx^>+S%DGLkd;{ZMT0PhfthZmYfTu$eop17=xI}RTpE~hIlE34;) zMh5ufe1I-M_r{?-Fdk^sKhI=kWaXu0WTj;x)-tl8`|ez zf=qFt9$sE9;nJe}-UQX${oFYKJ0yVTYH4&H90s3AZSTyiufJS0*2SZT)F2UD5Z~!}4_B#0Q z8Fk$KaozxvzLMf~nd@@OGLY+FIS9b}UmNugo+tq%D;JE5zsLVfc3?d^z{Tw!%e`&U zineZWGY>i2fY3l}fG0HA{~wEk(EfN2z+Hbk@844&3=SapqW__y&_NzZK#v2eFW@2k zznBjwa(cirpgdgM{9U|(#~%Okw27jesSDK4#n9j1K^bmMkXO|H*NXVK04DuC84M65 z4gN=nl7kn@RRJHYZv-razNeM4k`~%k*WSoQ&(+LS*45T1P}WP9Ag`t65{z^Y@%FX? zxG+BOU>^e9*VDn*3hL_Zi_urYTgZi?LzLZRwHye65Cw=Y))WbFS{b=p!wo#$4e?qI zrdlEPW&q6#V`Lc&4YqL!*3*HyxEtAF!OBK}1GV93FF!pmu(!OWJh1HA{zf`_UJh7) zjJ`1vO9(Qs2rzNcu~9PfN7;H<1_XuLz>NHYjXY%SEc^m}J@x#d@;1hX?qC?s-bc?e z1Q1~7ibCP=K8lLEcoPFO!2@n?p`?wof@m2aZLr308C(A#-%vN(0IMK>8yvyaN6!6V z&spn0O+y{97BFiwGi?ms$N`Hn)R$B83?c-DdYCE8nA<4(;LK#8vPe9JAfx1^V`l_4 z^YO;J2YTz81LKu++<~hy)HT3g(d|H;>p+*Dr&1CjBv1%%re zDMJ+X{R0h*a7s{rQ#mCg`B1zk*aB~XK`Xgn0S=UpHWn=ts&5!%7HBPN2UCPA;5`-X z;c$ptfFGI=;_qM>fIkq0HVM?j`xtqc8OuYAJpIjOjUZsCj|&bb=Yhdn!R0NLP=GGV z{`Rs22VDzm1(YsSUSG?=;9zUI+k~3oJhhG8Vd!8u7!A`wTbV$SMj_~68PgC{NU)zB zFw|Y!B*@$kYXwHh`UlNg8+)3U5K2#hnEl30x4sL_CUkrklvmsjFFA4tuDb(*E`VIM@B)` z&CFcO2Q4EPtPlVv=(-^F2{N)WA!rYO7xU0Sw1O!R4n2b$lyGh`hB$9oa}OU^AG|CW zMo-TKj1aoh1aD7& zMXU`>Sw~jO-bmX!*bvwz!KT*siUugGHxLW)FmpXuBYRz}dq98}&IcFZqF@;+BdcpG zBPVYv3$qB23xLDTeXYC$bWpBfSDPR^Z%aV1r!EYwci=n=cT*!hZDm8dgKg^oQIdgp z2m8q=gt~y?5Fc+joUXQEh`pVet&6QcD!?Gr0vO_BAm8+As3M?$5EJnIo)*;TT z*|%Q_yWO58li^0=Ft0e}q9O@pd$Y{TkDm7wL8Q-~{QmgXsj4+eQD-xmwqU!lR{o=D zE813ORhM)=y@Gs}eb*EgvrWlN=X89Rc`Yjn>e%Pq4>iX>Iwi_$>iU4R^@~gN+j&=` z;H7bD|Jq9SbaOMcMy^?|c2=#MXuqA2v9z<-M(q!dptc%}Rcp}qR0)}f%Z;tn7R4|+KWC?| zQhUzRR0qu!S;6wUySN^_yd)ZWbgq0d%+85AC~}1TL>@Eh@W#5w$vo!p^FBP+f9^@T z4D>K5J!!xzmHW3t%oElX7L?;q`?W!~$^QFsPh%;N`(81geGPqzhrh(ryK=+6T)i`? zQfNL|du?g>H2+Yx2-^rE!qw(orX^7?-ZNrSm$4vWt5<%N#)ax7tvi&?lpaNP4G@<# zknJ3b>_wspa(*?_1NAS56|V%fJ0S~P>h*5rlFBsfb*pPl->?^xZ|O@VpJ%0j)LyIN zWq@ze3Fvc!NV=n2))Az$^OxE~&$fg3EIt-pu)m{RGz$&Sj58M#3EmvE^zoiE1v4WE@EmE}7Dg1hsAP+rIofk)(f5p;RfHTUYL-k<2ARW+V^!e@Hjd;3%usNJx)rnbFiMQ=-|uy1!VaHiBrIvw1ZMm>TEa zHmSp)C5_w1H)fb}SM#yRekz?L6rX?1pLQ(K(P5#pSL^=DAzyUu^(MAJKEJ?pKZDDS z%T~nq)!#Fo0jv2;ZvMo3U>P1G-^=MqXfinDgc`vhkPV+^r`*?<7rN1d-z+da(N(Wx zf9$I8s%%YAX}eL03$wMRk@UWx@Q2PfD3w1>38o(w`jUgl;k}nuSfdL_u#~@Cd3|K|d#5T?`USJ*bi(^G zZW_Pz;zjV{K;Dxachfc1V{hWs?CJ|_0>caYprf*T!0ENH%CyU`c6T0(9ykeQ_wF@K*Uw~Xh|A}Bfq(K&~@dQ0JuFi-smkHGHPM;{SMWVGb@+2*Yr%tc%D5lc;Zow`W8;>CN zZWUqrv$T?tKBK3PO>zg~IMV*q4fG^0bW)7NzBE##1_=6W^JSd0^j4CIWkNC8=wZ;- zq%i{xx}0Ktk%DV*FLLd_5hd0%o8zdi>ZDK`96%_kIVOp*x>QS~yi`rd7&KE^dsqUU2%me;+p?+UuO67T>_z-^eigV|Je5?$T+_0sgUwwc!(BX7$*g1AEg>` zmThEKW;0}^3fTHCM}GftISeCqd~noIv_Q@XMl(#X&)o83D>pk@)ywsUZuK@^A42{} zNgnw#sLik-&RdCS0$(Gu?ni#@+;EtQe%ZwYH8Me9$1t#C$DKxJ3$w%EbA`4+ii4N4MS=oqUyvAbiMXtPCp4}1iu4hy7a!Xx;}jhe14 zqh4X9;G@#nKGKB^Z%yy(&ssbw(V*ZqYp{r9I&xo0f9DRpfLTo5IBwXnFYemk7nd4+ z9K!NdwY)3QNEkaU>}aF1;G=E*tC}{SOx!QCB(l(y)NL#Qwf~H8!H6RwG$k{(_sckU zQtMG5%%7PQ`fn@oepB4K&8_|Vju;l)-^KwgJuS(USOA<1j!{YBPud) z1W8NNo={{muzrphvz1#rG%WXC;#pU zU^edX4$ti?wDd$~tnA`Kr`ios`rR9M^@OG}E-2=nxs5np-W8alT<-RxS?&9gVsb)W zG5OJ)QQlda0YhB0;B!$PVDjfVqpzAksABNC^4E>`fd#MjU!*G4E#bV7GaIkvM?WhXl~u7%^HdZ@ z_y75QE#E)7^;cxZ>0U$>z{`{=A+yJfEm;HpN^k4sl-VLyEK=(ao!%vHroLx_rkS8N zaHS2I@79glg*&)arzzE#h-W4Y-shA)2f*2gqwJiykLgf;$gTzjbTfwI*8){(bO|Q~ zMAh_5?HA`_oKTTwP5A0>S6Y1Y>qbg>YgNnRg{H+4pAkfZy9M!^vYxaaBj%z$ zBWC_|-Dw(s;#r_sjaZH#lwCW1fN}s0yjecUKLr#R;6Hn7G^aAXBPOLV>hO~ivZ(Ib z?9{m{H2#NnpS6Jj2ZPKkpn%l-|COv5&lD4)j{T_+X|l=kY#|~0cDddU_2yI>V9s6N zOR;%K=N7?HK&oX`;w9v&=Pdv27L$KHf1CcCIamFVvM-j^Ni#Ls^azU}g#Ga}!@T#e z@E3e!XF^|u8tLWIA)q0kmb~^yV=LkbLgyy&W5SLr zR_B7x4G~H0g~DF-;xF7j%BLUN!Sc6aIY$u3CAWc*S4{G`Er|IR7Q~OR7)et`%-fOc z@0cLL%DiEKCI2OV2Z;%40{oK0iV|udm=zyRO&avNXGK@Zb6eS2!0McPr+xbf;2uLt z9#ibwQ7ID!hZyCGCPQ!jjCGS!+1~L-72!6OG{+58hQY%h33biO~3yglOrB15IK43vUTn&{+guc=mg3Oqb@|LguU0X(k0C_duxQ+1UvHR0t?$Cu8P z;k($gNLy{;=kFlxrdARb~|^+LRMO2O=@J;cW;a~KDnx|NO?RGV&e%@u1 z+PI6cVBr+K*iUton~@PL?fhRacRFaKl22h*kF7Lv?T7LdD>}(aHh&SqDu8=VGF#S}m-k)Oet# zYU!O50~NRvTxBiS)_9ADw9>7Ly5!@4i5H+Ti9-_|oMvMs7hH;+T014D zpQM6DTIc?%+voP~pZ;p}Qvl_q_ZPM6Qu~D~%zGEW-%;CbMwKKL!S*;OY82m#|c_C6@4#nWfh}&2~x-2>7@#-#BjxX%nzwn0cm+B zHEq>QGKd)ZGF=M8)Wk&h6Jjl)5#a^ov{@egwY$d!t>wl;I>3E? zTk<~$;Ki8+JZnUk$Jf}D^^$#?zr;?~A$zp54dC}$mMa+xA8yx=b}Kq&&14jsH;>HT zQdqJfE;!JFY#8UDzMmfKEEE-CM~8+4_Vj@VRNz00UuN@7^pip^IP8Z$WbF6Gjv2+wP*q`YE!D-VTWIVD7z!yqtX z=f_+)hpF%~J`!IvK8_wEQFnwcwT#VDKkb{2V`kav5+5qBduU(>8mJC|l`IDR&tYLM zI|Ubnay+ZI92TGLt~R1S@$>EJ^^iW1xZVgqJ)yYg%W^m&oYZ&bt9>o$Zc^%;s&3_7 zu@q2jGxOI?J4lw|xqH2|uE_Gi@gxjdFMg~@o&p-anG9kChC|A2N@0-960VeH2;VOG z*UA3Z%!7cHB7QT>=?>0o=fRTC4d&Q9zv+}Lw!eh@YZwp3I1-};gYg8hv)$n*r^kc`#ikt2K#q-eNDiXm1Eh6T z%N+vrJ3G5ZVQFm{cb`NTk6*_^2Kbmz^4`zNX7n-=v|(*~S1^xSbn`g z0eO9?wY-jC%nGS*&6I#zw&(d7MpawSVR^}^jeJ)z{k0l9*FSZs&YdkN|MH@s)Bww~ z!^$!ola_iaK6E1KSeNe3tazNP!XQLL9#thQ);!ZIPwdd zW)qrCguhc7#YFg=GB#)X0|FA;v@0q`Wl}UdDMm*5B}F)Sa$3@Y!Os$LvYm{#W{$s) zp<8))NcIv+$gV$!c2$scvVy$x?&rPU{VfOkyG)RF*V8UlV}lPj)6VJOLWS(aHSb;6 zPV9XK-cCCr7wBZwxrZll?0Zy#At$Y5*XOoJF{d18$TxUY4lyJsJ`P?#> zp>wnyp&3%xa_G?vLQ+ZnF~7CwA;@$lS>tiBA$$Re}R< z$5H&gd~U;H7!zPKRl*oTslFcry#IM+7I(f7rsRw5_9fa|4_?XhGk3rH%}mYI6>Jfc zO_Aj2yX7%slf(tO+zf*@CaU*^)SB;iwH)Mh zksV+$z0xn|WAn|YsC`ox&-K6cW?K-X@f!dch_I(n(tk1#^@sGh?qr}P(4qGoc@A=+ zuu~!I%`1&f*(P;y#I)a*bv&I%iq$mf>6|pz{Vm ztLN>yKQwDET0*w}tSAg*d*{2A>2aR@18F}oM$LaGRw1KLTM8zz_OVV*5x)<|VEw|R zZ&N_RRkl|tpp!4Px}Gu+JWu|~kX-iplP&^jn`08c&K402>-vb0N4DiW)<6z{5JCls zrwE2&dTeEW*{#6~n*g+y_p#0V@d$!F2Wa+(9$FGNl!4aZEu$aK>|z5FthcYhNHaz! zn}7i^Ili&PhEN^K8HLqM5s)-oz1KKb-U{0es^tlk;`Y7-q?uM6H2wrTm75;np&(UxKhLNLq0ec z51E|5gK~6oF1I9}cm%XtF;|QkF_GV|Y5$$aX41LEM$r8`dkLIUc|g+5>-Oa!Dc-&; ziwP;ncmFXgee>ntrHir)_{4|R2d9C&8ZhtQxpgDQf7NP=2kO3$ksJbeI@wTle(NAh zug<7wfYI|En@hUlRwq*N$(|}&G1IF)j^z#2FxflXa)nGzpgsQhbYE%&5%L0Pwk}Xh zAdzPMZx_g^d%9XuIlxeHGo8ROXM5)(h|%|w@V~30vynDoZO%F6V@2bW{uO{}?NGqH z(*=iV{CvPx`*#WS--?E}fE?1*;;o!ohUB?#5vF(@aqI0>2&yFTjTJHakp=Mr$C+xN zcH;Y=`sx1~Y~R(Ap|Bm~GNQBf_V*iKo9w5S%h*Bx`XTveW(?jJlVc)Eb;ddoEBkE4 zK;^3!w3<@0&pok!XJ}Ubi6Nt~jQpo7+;52QzB=mK@8=-(NJ zS)m`^m1Tp)|iuiL@U<5(zI;btxSt+>k_?I5)dAuF;1tNk@S|dt9J%ChW}?>paOR8geO{0L+i-l~%i4o%OiLuA2}SV32tD77 zcf9KJ_KByLN=@2GZeJ!HYU6*CGm?Q&@;<;H&fQ6{d&bl^$Q_#9+*Eo3#tGgTiv@kr zEJDZkAOmh5qSb#qdjNA>ITGL;Hx&J`Mgm8{y?S_YU8^OfN#vBwVjIuKUEH(zsXf*Y zU3Gg=k0R1PCSS;B_QIa}mH@`ZRqk?g0EK&kZs0P%%l;Cwx^0Jdmwe*qBRbT8r~{D-ri!UMDV~Sj6Q&ie=*VQK$&dp9`5$D>2 zdTxWfyDM{F^M5{<(od9ccSaQh)ZZpddf!UY$A1f?k7;8(=q6-b2AI<=UucFXn zqsqU40^zKv9PDW)I$+gfMdPtEq#QC3+pPpL|TE5w#}Cer&HbT5~w zkIbSvZ(u75nKgeUzb`#TQwndf0xt>;5UOMAUfruC#g-0Ea`#>9xEJMS_*m0z3BcIi zr!5`5d;pueKG+1d;?L@ZjOZgU;+uHRRKo$%Zhzx@NyL$_@-GCHz7u$Y@U+z7Qv(lj zl>Y%W?{TvxnZ;?A;Q=_Dt)I#Vgh30@dElCmqF!mr=p5H91(4JwOh2vwRe$+z&Ygvo z-g*0#F_jVt2dG$%UWvsZ^2QugJlDXIxD)Sa;c3b^BmQ{Di;I>%)NrP7Yy{C__`;|E zCVmZ*Sz#5~!iG5|mOP<~&djc*Tm5+@s25O)%8p71ndoVoCD z$NLa1efIO2{KXN(=9R>Eq_9zkS@t)3aW6Hqb>K6v+Zw0K1KV%%eXNi@30GAQC57mo z1kj;NSAGlo0rva7-)Oi5_`?YH9Wn=?{clr*O2EOjOVHHYu*Q10UW!_`i%yX>)ZRiG3oU)>!1Dyf=;L1~Nj{Bf~LZ-GrYF@IRSysbfomXd&fqkX3BX!P25 zv++mEDOVu7%5UWCbqUg1j0FY%&SC(Gk5^u-J2QfCzGxR8YDHwsD+5@*%ZEb!MZ6Wqs0)6ktP7B(L)ErzM)^V$hr3Mk7j zG3De>uh%K`sk7-OXQ_eFYinm6IAx~cHfo33|N3#YZqrM+z$d;z**`%$i1bl@ zwf_%2#x~y*Uz_JO(^61ugPpda;7(T&5_s8*H6FS?1y;mwc&V<09LNj&3{ubAbYrLH z)uBZ2htD6K4z-RCoecuNn2uM2@V|p&Po7*`ROLXn{uq@Ie0w{gHI0all$2fvU{)Pu62#Tvnc#a0T3kU zX?a)=ceVW+U>9(3yANjSFcze4ag<%B*;Pd@mP1mC$xdgf5h|X8Ytti7%RL}d-)>!# zdK!()*!amZHt4I6`gA%5QhJ@1UU2J7Rp|(VH9E84vIP-x)Rwi(0mWFDB=N(rUMG({ zV)^%{{qD&;aPEip+KEe=Z2$R8h-o5oDnhCh|3d*fr;H=J!nF?#VN(7xn&k*8jwONc zdhX5Se?_t!F}cDjNIk-?-h!1dNVeiSZ?IR5z_+&Vw|t9rK0wQsSHd69kp>L8(DEPBBxlHD+` zhnyXlrUeNvG?%HrVdy9}Edu1%eF|@T| z2kYYF>G!=o2FgF<;}alA-9$dwz7}#jo&|L7^IxM$lHXHb{?$xW_pZ_CZ5FG!Nvuz@ zTJ~eHi>)YO z`{CQUWm8fyZJ+@oZB?PSHLi^xPyBG}PiQ;S&|L|)JN9`Gb|`&j(Y&T^pygsW$7kG0 zzgD_G$9Y)?`2~-DovzPZ6!@_m?iO2yRHPkEZlO)~asfqqT@l$D^#OYPLRqit#mgTO zMI!U~OKxtj6EYkIJC5&oP#;SqI<3wWmj;vg$$9rO*k%QuE7boyIsq?p#yyxRd^CRe zmo`U)R4F2+XMF-s+dq2eH9~k-&F~CgBsu0Qt6S|%a81*m(^v2ZPU|*aKXtF6bGzmH zD$dir&`~1a=&{nCYg2*I%m?%K>l!<2TuiTb-}*)u_Bh!*SGj&w6vw_8bt@bfu^=`M zTMwrO$~{aJzE{}1o8h#c>B4IW-QK-WQ*;BOSj@~lr|kipU<*Dt3t&oh3RN2^73@I8 zr|cX(@^t~`#Ot@^+!C@eQ8Hjf{r?qQDx%jUH~Ov?f)aA!Z@e6-u?NySf#XN#GVqM+EXd|_=7xxsB&>0518 z&#~J&3pZJqmUqnC-)`$}n6rL)_K++w*FQ<^W?oTTiV`6`0h=##pQqXRvc|eQ3wE_+ zA9v>yD(Ac`5jui@G$HUSUVZihbo~{I%9w_m#YC_@5;CTHA zPRB1?IMh?yV#rtoq4lt-Vf3^_b%w< ze$HL+dbjig=6!mWu1(NuNJ75U%$aAKapyXCcJFrb@Ye}94up4Z9z*^doyA}2L%BC2 zT*p+4SZ_Ot)^jBPIyOLAU=09B9MTO^kN9{)7!XVBPrj$eJAkV?mF#~rFx*B7N%MXvzFJRp4S2cgmj#t7<|48T`5 znU0q#>Chr#!oPmkp5{3*U#Tbhjz5dqs=l#JS@}dsD%R*7J%0ap)RIy&gRZS%lRLpI zyrSdqCA4laG%(3cX~^r7$Zq$YZCSJ12?V>YFS+N?U8=L&cZHyxqm0<%*pH$0ZK16) zg1Hh8rzMqr+S+B1GX_em8G~CU(|}f4omrSz$=RBr;MLoLJD5s8^mN6tg83WV3bdw? zqAtq0$7%X_veCZ6KB}ppILE0Nv^{#$drSJ+n9fnReahSFB%sj8pPy}0W={ zUzWt5of_tDiDd;c)Y83)kbP96R=7%k3nl4Y|GI~CIc(CY>5J6NS3awsPer|B`x{Q_ zwBozLi-dP!@ z&7CCD`&g~qGMf$DAaT9&SdL1FXN5=qwP0@FX7Prf@AiJ%y?y;)A(Q>BARajumMAi- z+?}5x&a=b)35awTT>fgYZ^pqYVsC1RuN+SuGnTYLI-hP(a*y(8##x0I41FI`xi$Uy zs8N_$of21%Ms;X$zSJU{Q--)*FOFDf&4|?=?C@V($>|r~JUO{hSbJuFjUTqo4b=fn z|7^P?i=07781bMt-1<9Ehgv3euV!5-ok>Ap$IlPv zK}UYY<*3_yf5f47zA7|xw|x6@%)_q@Y{6$cT4U=fLl>u7UeOgMK0pHnsVvf9q;r3k z^aI79SKRg_=#|JudZ-A?;wZKP+g9D zdjiz8Iww;hhxyOTkt>$TS>}TpzX}(l&DT*~s;DQ;4+&jgOw`%FAKi}W6%v|~TBbak zB5gN2SkIqEW%dUalS8T(EWRBM09lOVaLLYYRJ2Cen!?9P#M_(eEvVF&Yv%Ml^8MGLwFMT?-MBY{s`5pV_w2gtq zwD<{wsRULLbCC*eleN}ocnK${chXiErf%1?lVg6z3 zF0@lt$DJ>v8N55tLY(##r0!v-rowv}ViS`)wRK~+yJ4U5@}*+mg^CS9u1GdBpi-F+ z{s=AJ@Vx>hvT{t1xY(k9p6`9UNgCYi(;!O!5_;}@3fxNOT17448i|J^cY^l^^L#j8 z(2QQ&1x*rjXf|Nq*R!ZI!P!4WoHk^v5z&vS%Ln+6mJWQftmgjS#iBbxW^F}GJFP&9 z;vyja>bRs$u`tKxp{=(y0x#Ym;@a~@iF1O~=exjxp_+xv50A(>$ZAJK$l4F4;MuOT zGzWOWIAcVrXgsdxOe=kIjsM8D@FT|YqU%+3v)92kZ#EVh8zD_;&s+UqY!~C1^n|t_ z%-@3xCv^-j0WTgSw_V?^-y+q%b5o4B)SNt=zE zOLSf)nS%!QWM93KY%f%3{3Db!aGP@DYRvWYBHm^v*UET89$fnU!L;9jPb`iDlU zgs9bJA&?lzVzisFTyk)vadfGIOAjS+n)q#P?<)N^OQqapXX|}4XcIngy&9Ax>eblv zPDX$l(WUw`zLC|Kv0!XlVyoyb>w6YCqh`@y7sJKt;epoJ*7cr2}1?-`fS;zSsi_$ZSz~(ltCAq3suAWdrp5LqySe!9z-Du-RVU~ti zfU`{z!+nu7$4BZ{+9P@_dJ|=pTWlp*_kAUhFK=MHw*LS*&B)Z+pRfu}miC*{ox#j& z(U${)Mocc;z>Y3Ue{4SeTaeYcxIKx3E|J(fEwQwWP=w>wNzB~5I@VO2(4kfPLUH0& z>pJ*o_sXA=D2;&U0nfuO=1(76zkZYcCT=GG5s^1F_spBqc}vy{ABQpFKa$mGwIdVy zY#nE*>ba{OZf`E4`lmCkh);auB8iP}*Fe{`Qhs!lUtGPzr;?s!;_fXo^5hENABnZy zob6e8grsT48l67y7=FY^*iRzvby{N~7?ekeZm}0eo|jj8dka7*F23+Vw!OI@;G=&l ztt8BG*Lo&2b;jTHoBHj|bcOCBG$n1D$oQe)Rfvqx)?8^Cq-AU6!V37X?@!CyVy>_y z$JPX_S}&x<(d`u~rvNSwUTG|XUoFvr;Z?0_2Eg9${bFAJ}O%{kB4{Uf?a+5Sv z_4JL#r1B!o(?xD^RlL66EOwdPuCPh%jiFsXe@A9wJ)&Ywae|qx#xQ!4c$>Aq>aR|v zJpuoHSo9WsxE-41SX8+#eyc10Sd$XuiTEttWRetI`Dec96Y@=R|K?PNK<{H5qu)cdyj&{x4e6C;>nyyT@x-|CgvIs`>vi e=dXR%pKE%uzebI+f&Up|G14>BeW`sb>i+wZYENY$JJ`pNm`d9io} zOj#2>-d!8)26O;azBAu_D+FYIcD_@-B%bd!5>gCK{SUiP&i9@Tob_Mv+1@E$05^R{ z0y_ad002PdX79D&vEU@Y?nms$6F_zg{N?c;@n&%S53vXE{D*+O4|s97+dJYL4Nw3O z{ZxE$ycPrsO!bul@_$}Gqdoxum4}p1i#LT^|6_p)fcd`=w!SmI6$%kf2^RRD`vL$r zKPCZnFOUGXpXQ&w4umIyD}##wHb4O2nu@$KLw!U)b02=+D&GvRcZmr50=PZ4PYADf<9p742tVd`kLQ4U zgSEcuf0bjqFL=Bg>N^Rz2CVofyi?p5%nM-k{r;Ks3;x0SVt99WQCRs`py}Vjp9vFA<>dv-acjsqkX(8F19=8ld>)_zL@%;kw{JfK;H#FE9Z0 zN95b)Q{$@Q#o(ilJwO*A@gp%&at+uA+y|t-0spXkEq(UA20R5E3iJg0dn{4ReIX3@W&1)r1W9-Vu}tJ|E+o-;+`{gS&wLofr$716l}F*S>DB?Qc# zOO}~SK&Ug6uaP#5M8xe}TQ5_DnBPaVQM2C`q&U|9zN^#%rf#mStAYE|&)z z^1s=c;%GT(Wbta+9Mf~M$NG5Sr0?g>6%ixnZYFoau1Qw=_kIyUkTxesv1u3%qdTVR z4&0D7mW73uK**MhX~FknB|O1gx1G%mA{xtZKBcG`EnZPL6(XtP7r}Gc{+VTRvbJ(m z6O_~IyQDl$ce)_|{a-NjCw)}0jDckbZVe!f+Tc*8%C03D3g|0y5m3uI2ib^OO;cY| zW*EUFK(kjNtTk4eUEQ=*AOIy4vCV$JOaooFlQfH*aCoj8r~iW( zg8V<=WjE^hB6(hW1UY}FJ3B37E~ZoMIG=j{@xnIHTVy%44ZFF5P{QGKry)V(IO_Jd z{axW{~?2Ac7*Oc6G@^S<|e7^ z%EHM1@W4d3EuGP0NVvidQLb48qv5|jl_NT~$MYgOZg%mAB_e8CCeONYiSkt)Fpy5p zDujr*d1m=P{Icl#i!;|qB$S*W#zs-MjmGmo6e7u<-R+aFdW2)PG#!nKWB-rEZaft^ z3CGFhj`e_qtAtR*Kmg0x4_>n`odn(joC9wE9)A8W|7bSv=72GxZFjK=Uf+xOH}n6O z7Q>GrTGTiVhi(43FJOCC{)ODSysa0H6mjekrJLH?SQy?wQ^=RrYabcgRxPr+*t9jZ zGLfw$y1|P-)_M8$KO&Q{y6pd(v`p11L3i(f%X#L3NbKNnPu&q*dh|gA?ti86kJ-lN z=z6JPQwgOVZ|`Kz*b%LQ z7#`v8|3{wxQP=;q8OsujX_=Ps=aRq)2hQlb8Zbv6*rLKHR-eF2=gT|(8)K?Z$M107 zBt>^L34lM%akAz&!?G-j_8jKnzE#NK86-v27QFmvSgu@3d({MvUn`1G_}zNY*sPhO zSHC|WE9bCmK$pzJwlTwkSX*e zS;d|BiOXo44UN=QYE`Ezcmr32^zyj=6UaEWzd(iSHxRU6ki~8EM2-2`^6;yrV(7&? z5_f?}gHN|XM_tke;U=ygO}4FZ`N!R5?;BZj-hgi}18$izhmM@} z+HjAVFnvidMb5=}S0o?6*&AX_^+96yMR6d1uG;y+sQN;#vYl@7w2EO9D;ZPnTn^*LGtWJXZ(!R-8aCHPkaE4xT z4X>aAYO;3fhtTohiGz+%#s;EPyn!k=COZQ{Odyp{*22g&cxmyekHHszIf<$l0sH%* zJ2TAR$5PLc=WO{&N;u!(CgL&N)WK7Q9=dIM;V5ChjLu|LUGNar4y{66kq3eKc`v*h zyWD9^-L|C-1Z&ORw6HpkS+;LXZ<#yZ2VDodN@nUdXSK@79N1c$oOR=L@<3 zRy5+fZs-8zFNT+@0j14S4gqcF%!R!JHcF^p9SHSj5^mog)U(`~ZIRwX^eINgpZ?|8 zNL^X|87wImnf9tHU!E|>Zk+z97As6r?0xvAWor1jnqzeKX~3-IBnX_r{p7T@_n`t* zqi;+hgvy#9bF&gI=Gh}*Sf&&8YoqzTX@w>tNqmZ>I2Hy-a>_0#B+<2+rVCAVVQiQ177jhgEp4xmJd*32qv9+=LA~tii^J_O1Knb(8;u2V22)Y|OI}cdV zZ%`+TknwOzl?|=2&f=H=Aj1-CYt@i0cqr?^P|GDp$xRkYe)n~kF$F|Ppv4m_!`0*S zduiP|z3^!fRNM9R1pb$TKwTXCd|Qu~{)>yh{LSpSvCs4`NR;v1UYfP_boAx5CdoOBSy9z*J31+i=h6pMEtPHfC#bPQxh4uwc$5u4^h{bmdO`( z8~li_h%Z7S>yTE!66mMI;Sm3JLw_#gTX=5+BoYg~X}+{}J3^^qWB0&7Krne!8lBVx7jfCW^00Bh~tD#Swb_i5CZ`+u<8nQu}4FR%Iu%;!Jg`gp+L46 z4BUsRT{)eVSk0pE)r22ZcqogqS;1@B24xvSKL2X&!pfbh(+Se-G?F`!8H=$zK3K}{ z&r#1GjFCp}ZDo3izY*x>ugFSutDDXyn>hzE@Dy@0>k_fvMrn4B4#yFh74H5z(?V57KSmN}A=E%NY%5Z{uJ|l3bKW!$ zbsIr&ZiIRfG+MRFa*8Q%bO-C{CkKAPXqj@pSE6&U-+(K|eZ@@2dD;NTyVD2RlC{T8%Owj;JiFYnGW3-HUIN;3Du8^XD5 zUE?JT*b!mW4W#Qx%7@7e*GZxup~Kz_v;{U4k@oUZRi~vJJrh0*6|^2Z@tH6ItI7|i zAoAm0*H#Xy6yhT;cP`6oYLd(T)?^OSS0yi+A#b|CiwHcqsp zD9BB~%XrXY4!WLv)LSmK8$u(YKSzF<8csd0GWj-Nem-!kq7ic?0Hyngr)4k1b~>O; z?gw%c;zFr2Ka&F{YE@4%G6Hl;sNF8Bxm9ds#(Ppl-tO{$j_WKljG16v4THdABJ^Mx z))5jhpdpaq6D;}y@}}YRMt|Lt&AOcA>j|*I)QNX8s%Dn{+J?7egU93RKBYN;NEGxNMuf7D{9slg_^b+_5X+NI287|YK8ChO*3 zvYXiI%3Rxo5woG&drXVFw#g%&4pb zv);Ja^w`$)_*0d}6<`Ur?rF6#{Y|Q7Ia0@*+7X}e_VWrz%uBEo=cvesD8v%MlkZ~X zxZ&^Nz7)Rg6B(zR2rNZo%9(|ST14XA43JZWhN9yY-r$u5nNitT^7){IA5BakGa(fH zfz?v1zhD^{mkVDk9%C!&8$lSP_tXph+H0@ItP+AM}(`FbhZ>4VA#&OiT3AfgjZ; z;k1D!0SOOMQSTxY-F;tIz+v;Ij-ILX)oz`KKT*MFSG%aF2?=D$y$yqp>gRoOQ~jAl z9)&X&1sEDB)>l|1iYW2vLxHhQUklj?55uy51seUmS9wRq23KhVw=KxGrmPbl$pZ*IUzq$~Rbx+$z@k@HpMD^n1j&*PhkpW){S zd{NX@!TUMny*M&KGXyG#6~#5-(QBqV-%KViPJ{nX^I4UXc}fDN?#h{bR>e!Z&lll- z&Z9BlS&fSDkU2Nyl2St$ot@_hI^Iw24|2=oYMDhxQ!jxzi3Le zz7L9z&OV@}dPwlc#Rt3C<^&OO0fXp

^FlJ-kGpd;<0er8bV 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%0D*L2740ZEkWufsE?<-h3^yW{|Pt! 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 diff --git a/docs/zh/20-third-party/yonghongbi-step-zh.png b/docs/zh/20-third-party/yonghongbi-step-zh.png new file mode 100644 index 0000000000000000000000000000000000000000..f000f03c39b8f1b0be16a04bcc720a513c06c950 GIT binary patch literal 13563 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 literal 0 HcmV?d00001 diff --git a/docs/zh/25-application/01-telegraf.md b/docs/zh/25-application/01-telegraf.md index 07a3067a4f..dedac9e62d 100644 --- a/docs/zh/25-application/01-telegraf.md +++ b/docs/zh/25-application/01-telegraf.md @@ -41,7 +41,7 @@ IT 运维监测数据通常都是对时间特性比较敏感的数据,例如 ### 安装 Grafana Plugin 并配置数据源 -请参考[安装 Grafana Plugin 并配置数据源](/third-party/grafana/#%E5%AE%89%E8%A3%85-grafana-plugin-%E5%B9%B6%E9%85%8D%E7%BD%AE%E6%95%B0%E6%8D%AE%E6%BA%90)。 +请参考[安装 Grafana Plugin 并配置数据源](../../third-party/grafana/#%E5%AE%89%E8%A3%85-grafana-plugin-%E5%B9%B6%E9%85%8D%E7%BD%AE%E6%95%B0%E6%8D%AE%E6%BA%90)。 ### 修改 /etc/telegraf/telegraf.conf diff --git a/docs/zh/25-application/02-collectd.md b/docs/zh/25-application/02-collectd.md index 8b39a6431d..b916452efe 100644 --- a/docs/zh/25-application/02-collectd.md +++ b/docs/zh/25-application/02-collectd.md @@ -43,7 +43,7 @@ IT 运维监测数据通常都是对时间特性比较敏感的数据,例如 ### 安装 Grafana Plugin 并配置数据源 -请参考[安装 Grafana Plugin 并配置数据源](/third-party/grafana/#%E5%AE%89%E8%A3%85-grafana-plugin-%E5%B9%B6%E9%85%8D%E7%BD%AE%E6%95%B0%E6%8D%AE%E6%BA%90)。 +请参考[安装 Grafana Plugin 并配置数据源](../../third-party/grafana/#%E5%AE%89%E8%A3%85-grafana-plugin-%E5%B9%B6%E9%85%8D%E7%BD%AE%E6%95%B0%E6%8D%AE%E6%BA%90)。 ### 配置 collectd diff --git a/docs/zh/25-application/03-immigrate.md b/docs/zh/25-application/03-immigrate.md index 389a2b2c5a..1c29ccaf7d 100644 --- a/docs/zh/25-application/03-immigrate.md +++ b/docs/zh/25-application/03-immigrate.md @@ -429,7 +429,7 @@ WHERE ts>=1510560000 AND ts<=1515000009 ### 计算资源估算 -由于物联网数据的特殊性,数据产生的频率固定以后,TDengine 写入的过程对于(计算和存储)资源消耗都保持一个相对固定的量。《[TDengine 运维指南](/operation/)》上的描述,该系统中每秒 22000 个写入,消耗 CPU 不到 1 个核。 +由于物联网数据的特殊性,数据产生的频率固定以后,TDengine 写入的过程对于(计算和存储)资源消耗都保持一个相对固定的量。《[TDengine 运维指南](../../operation/)》上的描述,该系统中每秒 22000 个写入,消耗 CPU 不到 1 个核。 在针对查询所需要消耗的 CPU 资源的估算上,假设应用要求数据库提供的 QPS 为 10000,每次查询消耗的 CPU 时间约 1 ms,那么每个核每秒提供的查询为 1000 QPS,满足 10000 QPS 的查询请求,至少需要 10 个核。为了让系统整体上 CPU 负载小于 50%,整个集群需要 10 个核的两倍,即 20 个核。 diff --git a/docs/zh/27-train-faq/01-faq.md b/docs/zh/27-train-faq/01-faq.md index 15397049dd..dcc160d2e3 100644 --- a/docs/zh/27-train-faq/01-faq.md +++ b/docs/zh/27-train-faq/01-faq.md @@ -75,7 +75,7 @@ description: 一些常见问题的解决方法汇总 检查服务器侧 TCP 端口连接是否工作:`nc -l {port}` 检查客户端侧 TCP 端口连接是否工作:`nc {hostIP} {port}` - - Windows 系统请使用 PowerShell 命令 Test-NetConnection -ComputerName {fqdn} -Port {port} 检测服务段端口是否访问 + - Windows 系统请使用 PowerShell 命令 Test-NetConnection -ComputerName \{fqdn} -Port \{port} 检测服务段端口是否访问 11. 也可以使用 taos 程序内嵌的网络连通检测功能,来验证服务器和客户端之间指定的端口连接是否通畅:[诊断及其他](../../operation/diagnose/)。 @@ -243,7 +243,7 @@ sudo launchctl load -w /Library/LaunchDaemons/limit.maxfiles.plist ``` launchctl limit maxfiles ``` -### 20 建库时提示 Out of dnodes +### 20 建库时提示 Out of dnodes 或者建表时提示 Vnodes exhausted 该提示是创建 db 的 vnode 数量不够了,需要的 vnode 不能超过了 dnode 中 vnode 的上限。因为系统默认是一个 dnode 中有 CPU 核数两倍的 vnode,也可以通过配置文件中的参数 supportVnodes 控制。 正常调大 taos.cfg 中 supportVnodes 参数即可。 @@ -261,3 +261,12 @@ TDengine 在写入数据时如果有很严重的乱序写入问题,会严重 ### 25 我想统计下前后两条写入记录之间的时间差值是多少? 使用 DIFF 函数,可以查看时间列或数值列前后两条记录的差值,非常方便,详细说明见 SQL手册->函数->DIFF + +### 26 遇到报错 “DND ERROR Version not compatible,cliver : 3000700swr wer : 3020300” +说明客户端和服务端版本不兼容,这里cliver的版本是3.0.7.0,server版本是 3.2.3.0。目前的兼容策略是前三位一致,client 和 sever才能兼容。 + +### 27 修改database的root密码后,启动taos遇到报错 “failed to connect to server, reason: Authen tication failure” +默认情况,启动taos服务会使用系统默认的用户名(root)和密码尝试连接taosd,在root密码修改后,启用taos连接就需要指明用户名和密码,例如: taos -h xxx.xxx.xxx.xxx -u root -p,然后输入新密码进行连接。 + +### 28 修改database的root密码后,Grafana监控插件TDinsight无数据展示 +TDinsight插件中展示的数据是通过taosKeeper和taosAdapter服务收集并存储于TD的log库中,在root密码修改后,需要同步更新taosKeeper和taosAdapter配置文件中对应的密码信息,然后重启taosKeeper和taosAdapter服务(注:若是集群需要重启每个节点上的对应服务)。 diff --git a/docs/zh/28-releases/01-tdengine.md b/docs/zh/28-releases/01-tdengine.md index cddb8160f6..5b3abcb341 100644 --- a/docs/zh/28-releases/01-tdengine.md +++ b/docs/zh/28-releases/01-tdengine.md @@ -4,12 +4,30 @@ title: TDengine 发布历史及下载链接 description: TDengine 发布历史、Release Notes 及下载链接 --- -TDengine 3.x 各版本安装包下载链接如下: +## TDengine 版本规则说明 + +TDengine 版本号由四个数字组成,中间由点号分隔,定义如下 +- `[Major+].[Major].[Feature].[Maintenance]` +- `Major+`:产品有重大重构,不能直接升级,如有升级需要请联系 TDengine 客户支持团队 +- `Major`:有重大新特性,升级时不支持滚动升级,且升级后不可回退,如从 3.2.3.0 升级到 3.3.0.0 后不能回退 +- `Feature`:有新特性,升级时不支持滚动升级,但从相同的 Major Release 不同的 Feature Release 升级后可以回退,如从 3.3.0.0 升级到 3.3.1.0 后可以回退到 3.3.0.0。客户端驱动(libtaos.so) 与服务端需要同步升级。 +- `Maintenance`: 没有新特性,有且只有缺陷修复,支持滚动升级,升级后可以回退 +- 滚动升级:由三个或以上节点组成且使用三副本的集群,每次停止一个节点、对其进行升级、然后重新启动,按此过程循环完成对集群中所有节点的升级,在升级期间集群仍然可以对外提供服务。对于不支持滚动升级的版本变化,需要先停止整个集群、升级集群中所有节点、最后启动整个集群,在升级期间集群不能对外提供服务。 + +## TDengine 2.x 下载 TDengine 2.x 各版本安装包请访问[这里](https://www.taosdata.com/all-downloads) +## TDengine 3.x 下载 + +TDengine 3.x 各版本安装包下载链接如下: + import Release from "/components/ReleaseV3"; +## 3.3.2.0 + + + ## 3.3.1.0