diff --git a/src/.vuepress/sidebar/V1.3.3/en.ts b/src/.vuepress/sidebar/V1.3.3/en.ts index 874d18640..08a7151e0 100644 --- a/src/.vuepress/sidebar/V1.3.3/en.ts +++ b/src/.vuepress/sidebar/V1.3.3/en.ts @@ -147,7 +147,12 @@ export const enSidebar = { prefix: 'API/', // children: 'structure', children: [ - { text: 'Java Native API', link: 'Programming-Java-Native-API' }, + { text: 'Java Native Interface', collapsible: true, + children: [ + { text: 'Java Native API', link: 'Programming-Java-Native-API' }, + { text: 'Data Sync API', link: 'Programming-Data-Subscription' }, + ], + }, { text: 'Python Native API', link: 'Programming-Python-Native-API' }, { text: 'C++ Native API', link: 'Programming-Cpp-Native-API' }, { text: 'Go Native API', link: 'Programming-Go-Native-API' }, diff --git a/src/.vuepress/sidebar/V1.3.3/zh.ts b/src/.vuepress/sidebar/V1.3.3/zh.ts index 97ee05677..16d5e6166 100644 --- a/src/.vuepress/sidebar/V1.3.3/zh.ts +++ b/src/.vuepress/sidebar/V1.3.3/zh.ts @@ -134,7 +134,12 @@ export const zhSidebar = { prefix: 'API/', // children: 'structure', children: [ - { text: 'Java原生接口', link: 'Programming-Java-Native-API' }, + { text: 'Java原生接口', collapsible: true, + children: [ + { text: 'Java原生API', link: 'Programming-Java-Native-API' }, + { text: '数据订阅API', link: 'Programming-Data-Subscription' }, + ], + }, { text: 'Python原生接口', link: 'Programming-Python-Native-API' }, { text: 'C++原生接口', link: 'Programming-Cpp-Native-API' }, { text: 'Go原生接口', link: 'Programming-Go-Native-API' }, diff --git a/src/.vuepress/sidebar_timecho/V1.3.3/en.ts b/src/.vuepress/sidebar_timecho/V1.3.3/en.ts index a539be0dd..199c14a0b 100644 --- a/src/.vuepress/sidebar_timecho/V1.3.3/en.ts +++ b/src/.vuepress/sidebar_timecho/V1.3.3/en.ts @@ -163,7 +163,12 @@ export const enSidebar = { prefix: 'API/', // children: 'structure', children: [ - { text: 'Java Native API', link: 'Programming-Java-Native-API' }, + { text: 'Java Native Interface', collapsible: true, + children: [ + { text: 'Java Native API', link: 'Programming-Java-Native-API' }, + { text: 'Data Sync API', link: 'Programming-Data-Subscription' }, + ], + }, { text: 'Python Native API', link: 'Programming-Python-Native-API' }, { text: 'C++ Native API', link: 'Programming-Cpp-Native-API' }, { text: 'Go Native API', link: 'Programming-Go-Native-API' }, diff --git a/src/.vuepress/sidebar_timecho/V1.3.3/zh.ts b/src/.vuepress/sidebar_timecho/V1.3.3/zh.ts index acd81e02f..54dcb21e4 100644 --- a/src/.vuepress/sidebar_timecho/V1.3.3/zh.ts +++ b/src/.vuepress/sidebar_timecho/V1.3.3/zh.ts @@ -146,7 +146,12 @@ export const zhSidebar = { prefix: 'API/', // children: 'structure', children: [ - { text: 'Java原生接口', link: 'Programming-Java-Native-API' }, + { text: 'Java原生接口', collapsible: true, + children: [ + { text: 'Java原生API', link: 'Programming-Java-Native-API' }, + { text: '数据订阅API', link: 'Programming-Data-Subscription' }, + ], + }, { text: 'Python原生接口', link: 'Programming-Python-Native-API' }, { text: 'C++原生接口', link: 'Programming-Cpp-Native-API' }, { text: 'Go原生接口', link: 'Programming-Go-Native-API' }, diff --git a/src/.vuepress/sidebar_timecho/V2.0.1/zh-Tree.ts b/src/.vuepress/sidebar_timecho/V2.0.1/zh-Tree.ts index 79e849f58..dc0900727 100644 --- a/src/.vuepress/sidebar_timecho/V2.0.1/zh-Tree.ts +++ b/src/.vuepress/sidebar_timecho/V2.0.1/zh-Tree.ts @@ -146,7 +146,12 @@ export const zhSidebar = { prefix: 'API/', // children: 'structure', children: [ - { text: 'Java原生接口', link: 'Programming-Java-Native-API' }, + { text: 'Java原生接口', collapsible: true, + children: [ + { text: 'Java原生API', link: 'Programming-Java-Native-API' }, + { text: '数据订阅API', link: 'Programming-Data-Subscription' }, + ], + }, { text: 'Python原生接口', link: 'Programming-Python-Native-API' }, { text: 'C++原生接口', link: 'Programming-Cpp-Native-API' }, { text: 'Go原生接口', link: 'Programming-Go-Native-API' }, diff --git a/src/UserGuide/Master/Tree/API/Programming-Data-Subscription.md b/src/UserGuide/Master/Tree/API/Programming-Data-Subscription.md new file mode 100644 index 000000000..89dfbb33c --- /dev/null +++ b/src/UserGuide/Master/Tree/API/Programming-Data-Subscription.md @@ -0,0 +1,244 @@ + + +# Data Sync API +IoTDB provides a powerful data subscription feature, allowing users to obtain newly added data from IoTDB in real-time through the subscription SDK. For detailed functional definitions and introductions:[Data Sync](../../User-Manual/Data-Sync_timecho.md#Data Sync) + +## 1 Core Steps + +1. Create Topic: Create a Topic that includes the measurement points you wish to subscribe to. +2. Subscribe to Topic: Before a consumer subscribes to a topic, the topic must have been created, otherwise the subscription will fail. Consumers under the same consumer group will evenly distribute the data. +3. Consume Data: Only by explicitly subscribing to a specific topic will you receive data from that topic. +4. Unsubscribe: When a consumer is closed, it will exit the corresponding consumer group and cancel all existing subscriptions. + + +## 2 Detailed Steps +This section is used to illustrate the core development process and does not demonstrate all parameters and interfaces. For a comprehensive understanding of all features and parameters, please refer to: [Java Native API](./Programming-Java-Native-API.md#Java Native API) + + +### 2.1 Create a Maven project +Create a Maven project and import the following dependencies(JDK >= 1.8, Maven >= 3.6) + +```xml + + + org.apache.iotdb + iotdb-session + + ${project.version} + + +``` + +### 2.2 Code Example +#### 2.2.1 Topic operations +```java +import java.util.Optional; +import java.util.Properties; +import java.util.Set; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.apache.iotdb.rpc.subscription.config.TopicConstant; +import org.apache.iotdb.session.subscription.SubscriptionSession; +import org.apache.iotdb.session.subscription.model.Topic; + +public class DataConsumerExample { + + public static void main(String[] args) throws IoTDBConnectionException, StatementExecutionException { + try (SubscriptionSession session = new SubscriptionSession("127.0.0.1", 6667)) { + // 1. open session + session.open(); + + // 2. create a topic of all data + Properties sessionConfig = new Properties(); + sessionConfig.put(TopicConstant.PATH_KEY, "root.**"); + + session.createTopic("allData", sessionConfig); + + // 3. show all topics + Set topics = session.getTopics(); + System.out.println(topics); + + // 4. show a specific topic + Optional allData = session.getTopic("allData"); + System.out.println(allData.get()); + } + } +} +``` +#### 2.2.2 Data Consume + +##### Scenario-1: Subscribing to newly added real-time data in IoTDB (for scenarios such as dashboard or configuration display) + +```java +import java.io.IOException; +import java.util.List; +import java.util.Properties; +import org.apache.iotdb.rpc.subscription.config.ConsumerConstant; +import org.apache.iotdb.rpc.subscription.config.TopicConstant; +import org.apache.iotdb.session.subscription.consumer.SubscriptionPullConsumer; +import org.apache.iotdb.session.subscription.payload.SubscriptionMessage; +import org.apache.iotdb.session.subscription.payload.SubscriptionMessageType; +import org.apache.iotdb.session.subscription.payload.SubscriptionSessionDataSet; +import org.apache.tsfile.read.common.RowRecord; + +public class DataConsumerExample { + + public static void main(String[] args) throws IOException { + + // 5. create a pull consumer, the subscription is automatically cancelled when the logic in the try resources is completed + Properties consumerConfig = new Properties(); + consumerConfig.put(ConsumerConstant.CONSUMER_ID_KEY, "c1"); + consumerConfig.put(ConsumerConstant.CONSUMER_GROUP_ID_KEY, "cg1"); + consumerConfig.put(ConsumerConstant.CONSUME_LISTENER_KEY, TopicConstant.FORMAT_SESSION_DATA_SETS_HANDLER_VALUE); + try (SubscriptionPullConsumer pullConsumer = new SubscriptionPullConsumer(consumerConfig)) { + pullConsumer.open(); + pullConsumer.subscribe("topic_all"); + while (true) { + List messages = pullConsumer.poll(10000); + for (final SubscriptionMessage message : messages) { + final short messageType = message.getMessageType(); + if (SubscriptionMessageType.isValidatedMessageType(messageType)) { + for (final SubscriptionSessionDataSet dataSet : message.getSessionDataSetsHandler()) { + while (dataSet.hasNext()) { + final RowRecord record = dataSet.next(); + System.out.println(record); + } + } + } + } + } + } + } +} + + +``` +##### Scenario-2: Subscribing to newly added TsFiles (for scenarios such as regular data backup) + +Prerequisite: The format of the topic to be consumed must be of the TsfileHandler type. For example:`create topic topic_all_tsfile with ('path'='root.**','format'='TsFileHandler')` + +```java +import java.io.IOException; +import java.util.List; +import java.util.Properties; +import org.apache.iotdb.rpc.subscription.config.ConsumerConstant; +import org.apache.iotdb.rpc.subscription.config.TopicConstant; +import org.apache.iotdb.session.subscription.consumer.SubscriptionPullConsumer; +import org.apache.iotdb.session.subscription.payload.SubscriptionMessage; + + +public class DataConsumerExample { + + public static void main(String[] args) throws IOException { + // 1. create a pull consumer, the subscription is automatically cancelled when the logic in the try resources is completed + Properties consumerConfig = new Properties(); + consumerConfig.put(ConsumerConstant.CONSUMER_ID_KEY, "c1"); + consumerConfig.put(ConsumerConstant.CONSUMER_GROUP_ID_KEY, "cg1"); + // 2. Specify the consumption type as the tsfile type + consumerConfig.put(TopicConstant.FORMAT_KEY, TopicConstant.FORMAT_TS_FILE_HANDLER_VALUE); + consumerConfig.put(ConsumerConstant.FILE_SAVE_DIR_KEY, "/Users/iotdb/Downloads"); + try (SubscriptionPullConsumer pullConsumer = new SubscriptionPullConsumer(consumerConfig)) { + pullConsumer.open(); + pullConsumer.subscribe("topic_all_tsfile"); + while (true) { + List messages = pullConsumer.poll(10000); + for (final SubscriptionMessage message : messages) { + message.getTsFileHandler().copyFile("/Users/iotdb/Downloads/1.tsfile"); + } + } + } + } +} +``` + + + + +## 3 Java Native API Description + +### 3.1 Parameter List +The consumer-related parameters can be set through the Properties parameter object. The specific parameters are as follows: + +#### SubscriptionConsumer + + +| **Parameter** | **required or optional with default** | **Parameter Meaning** | +| :---------------------- | :----------------------------------------------------------- | :----------------------------------------------------------- | +| host | optional: 127.0.0.1 | `String`: The RPC host of a DataNode in IoTDB | +| port | optional: 6667 | `Integer`: The RPC port of a DataNode in IoTDB | +| node-urls | optional: 127.0.0.1:6667 | `List`: The RPC addresses of all DataNodes in IoTDB, which can be multiple; either host:port or node-urls can be filled. If both host:port and node-urls are filled, the **union** of host:port and node-urls will be taken to form a new node-urls for application | +| username | optional: root | `String`: The username of the DataNode in IoTDB | +| password | optional: root | `String`: The password of the DataNode in IoTDB | +| groupId | optional | `String`: consumer group id,if not specified, it will be randomly assigned (a new consumer group),ensuring that the consumer group id of different consumer groups are all different | +| consumerId | optional | `String`: consumer client id,if not specified, it will be randomly assigned,ensuring that each consumer client id in the same consumer group is different | +| heartbeatIntervalMs | optional: 30000 (min: 1000) | `Long`: The interval at which the consumer sends periodic heartbeat requests to the IoTDB DataNode | +| endpointsSyncIntervalMs | optional: 120000 (min: 5000) | `Long`: The interval at which the consumer detects the expansion or contraction of IoTDB cluster nodes and adjusts the subscription connection | +| fileSaveDir | optional: Paths.get(System.getProperty("user.dir"), "iotdb-subscription").toString() | `String`: The temporary directory path where the consumer stores the subscribed TsFile files | +| fileSaveFsync | optional: false | `Boolean`: Whether the consumer actively calls fsync during the subscription of TsFiles | + +Special configurations in `SubscriptionPushConsumer` : + +| **Parameter** | **required or optional with default** | **Parameter Meaning** | +| :----------------- | :------------------------------------ | :----------------------------------------------------------------------------- | +| ackStrategy | optional: `ACKStrategy.AFTER_CONSUME` | The acknowledgment mechanism for consumption progress includes the following options: `ACKStrategy.BEFORE_CONSUME`(the consumer submits the consumption progress immediately upon receiving the data, before `onReceive` )`ACKStrategy.AFTER_CONSUME`(the consumer submits the consumption progress after consuming the data, after `onReceive` ) | +| consumeListener | optional | The callback function for consuming data, which needs to implement the `ConsumeListener` interface, defining the processing logic for consuming `SessionDataSetsHandler` and `TsFileHandler` formatted data | +| autoPollIntervalMs | optional: 5000 (min: 500) | Long: The time interval at which the consumer automatically pulls data, in **ms** | +| autoPollTimeoutMs | optional: 10000 (min: 1000) | Long: The timeout duration for the consumer to pull data each time, in **ms** | + +Special configurations in `SubscriptionPullConsumer` : + +| **Parameter** | **required or optional with default** | **Parameter Meaning** | +| :-------------------------------------------- | :--------------------------------- | :----------------------------------------------------------- | +| autoCommit | optional: true | Boolean: Whether to automatically commit the consumption progress. If this parameter is set to false, the `commit` method needs to be called manually to submit the consumption progress | +| autoCommitInterval | optional: 5000 (min: 500) | Long: The time interval for automatically committing the consumption progress, in **ms** .This parameter only takes effect when the `autoCommit` parameter is set to true | + + +### 3.2 Function List +#### Data Sync +##### SubscriptionPullConsumer + +| **Function name** | **Description** | **Parameter** | +|-------------------------------------|--------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `open()` | Opens the consumer connection and starts message consumption. If `autoCommit` is enabled, it will start the automatic commit worker. | None | +| `close()` | Closes the consumer connection. If `autoCommit` is enabled, it will commit all uncommitted messages before closing. | None | +| `poll(final Duration timeout)` | Pulls messages with a specified timeout. | `timeout` : The timeout duration. | +| `poll(final long timeoutMs)` | Pulls messages with a specified timeout in milliseconds. | `timeoutMs` : The timeout duration in milliseconds. | +| `poll(final Set topicNames, final Duration timeout)` | Pulls messages from specified topics with a specified timeout. | `topicNames` : The set of topics to pull messages from. `timeout`: The timeout duration。 | +| `poll(final Set topicNames, final long timeoutMs)` | Pulls messages from specified topics with a specified timeout in milliseconds. | `topicNames` : The set of topics to pull messages from.`timeoutMs`: The timeout duration in milliseconds. | +| `commitSync(final SubscriptionMessage message)` | Synchronously commits a single message. | `message` : The message object to be committed. | +| `commitSync(final Iterable messages)` | Synchronously commits multiple messages. | `messages` : The collection of message objects to be committed. | +| `commitAsync(final SubscriptionMessage message)` | Asynchronously commits a single message. | `message` : The message object to be committed. | +| `commitAsync(final Iterable messages)` | Asynchronously commits multiple messages. | `messages` : The collection of message objects to be committed. | +| `commitAsync(final SubscriptionMessage message, final AsyncCommitCallback callback)` | Asynchronously commits a single message with a specified callback. | `message` : The message object to be committed. `callback` : The callback function to be executed after asynchronous commit. | +| `commitAsync(final Iterable messages, final AsyncCommitCallback callback)` | Asynchronously commits multiple messages with a specified callback. | `messages` : The collection of message objects to be committed.`callback` : The callback function to be executed after asynchronous commit. | + +##### SubscriptionPushConsumer + +| **Function name** | **Description** | **Parameter** | +|-------------------------------------|----------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `open()` | Opens the consumer connection, starts message consumption, and submits the automatic polling worker. | None | +| `close()` | Closes the consumer connection and stops message consumption. | None | +| `toString()` | Returns the core configuration information of the consumer object. | None | +| `coreReportMessage()` | Obtains the key-value representation of the consumer's core configuration. | None | +| `allReportMessage()` | Obtains the key-value representation of all the consumer's configurations. | None | +| `buildPushConsumer()` | Builds a `SubscriptionPushConsumer` instance through the `Builder` | None | +| `ackStrategy(final AckStrategy ackStrategy)` | Configures the message acknowledgment strategy for the consumer. | `ackStrategy`: The specified message acknowledgment strategy. | +| `consumeListener(final ConsumeListener consumeListener)` |Configures the message consumption logic for the consumer. | `consumeListener`: The processing logic when the consumer receives messages. | +| `autoPollIntervalMs(final long autoPollIntervalMs)` | Configures the interval for automatic polling. | `autoPollIntervalMs` : The interval for automatic polling, in milliseconds. | +| `autoPollTimeoutMs(final long autoPollTimeoutMs)` | Configures the timeout for automatic polling.间。 | `autoPollTimeoutMs`: The timeout for automatic polling, in milliseconds. | \ No newline at end of file diff --git a/src/UserGuide/Master/Tree/API/Programming-Java-Native-API.md b/src/UserGuide/Master/Tree/API/Programming-Java-Native-API.md index 984b06ab4..e4c04fa89 100644 --- a/src/UserGuide/Master/Tree/API/Programming-Java-Native-API.md +++ b/src/UserGuide/Master/Tree/API/Programming-Java-Native-API.md @@ -1,845 +1,468 @@ -# Java Native API +# Session Native API -## Installation +In the native API of IoTDB, the `Session` is the core interface for interacting with the database. It integrates a rich set of methods that support data writing, querying, and metadata operations. By instantiating a `Session`, you can establish a connection to the IoTDB server and perform various database operations within the environment constructed by this connection. The `Session` is not thread-safe and should not be called simultaneously by multiple threads. -### Dependencies +`SessionPool` is a connection pool for `Session`, and it is recommended to use `SessionPool` for programming. In scenarios with multi-threaded concurrency, `SessionPool` can manage and allocate connection resources effectively, thereby improving system performance and resource utilization efficiency. -* JDK >= 1.8 -* Maven >= 3.6 +## 1 Overview of Steps +1. Create a Connection Pool Instance: Initialize a SessionPool object to manage multiple Session instances. +2. Perform Operations: Directly obtain a Session instance from the SessionPool and execute database operations, without the need to open and close connections each time. +3. Close Connection Pool Resources: When database operations are no longer needed, close the SessionPool to release all related resources. -### Using IoTDB Java Native API with Maven +## 2 Detailed Steps +This section provides an overview of the core development process and does not demonstrate all parameters and interfaces. For a complete list of functionalities and parameters, please refer to:[Java Native API](./Programming-Java-Native-API.md#3-native-interface-description) or check the: [Source Code](https://github.com/apache/iotdb/tree/master/example/session/src/main/java/org/apache/iotdb) + +### 2.1 Create a Maven Project +Create a Maven project and add the following dependencies to the pom.xml file (JDK >= 1.8, Maven >= 3.6): ```xml org.apache.iotdb iotdb-session - 1.0.0 + + ${project.version} ``` +### 2.2 Creating a Connection Pool Instance -## Syntax Convention - -- **IoTDB-SQL interface:** The input SQL parameter needs to conform to the [syntax conventions](../User-Manual/Syntax-Rule.md#Literal-Values) and be escaped for JAVA strings. For example, you need to add a backslash before the double-quotes. (That is: after JAVA escaping, it is consistent with the SQL statement executed on the command line.) -- **Other interfaces:** - - The node names in path or path prefix as parameter: The node names which should be escaped by backticks (`) in the SQL statement, escaping is required here. - - Identifiers (such as template names) as parameters: The identifiers which should be escaped by backticks (`) in the SQL statement, and escaping is not required here. -- **Code example for syntax convention could be found at:** `example/session/src/main/java/org/apache/iotdb/SyntaxConventionRelatedExample.java` - -## Native APIs - -Here we show the commonly used interfaces and their parameters in the Native API: - -### Session Management - -* Initialize a Session - -``` java -// use default configuration -session = new Session.Builder.build(); - -// initialize with a single node -session = - new Session.Builder() - .host(String host) - .port(int port) - .build(); - -// initialize with multiple nodes -session = - new Session.Builder() - .nodeUrls(List nodeUrls) - .build(); - -// other configurations -session = - new Session.Builder() - .fetchSize(int fetchSize) - .username(String username) - .password(String password) - .thriftDefaultBufferSize(int thriftDefaultBufferSize) - .thriftMaxFrameSize(int thriftMaxFrameSize) - .enableRedirection(boolean enableRedirection) - .version(Version version) - .build(); -``` - -Version represents the SQL semantic version used by the client, which is used to be compatible with the SQL semantics of 0.12 when upgrading 0.13. The possible values are: `V_0_12`, `V_0_13`, `V_1_0`, etc. - - -* Open a Session - -``` java -void open() -``` - -* Open a session, with a parameter to specify whether to enable RPC compression - -``` java -void open(boolean enableRPCCompression) -``` - -Notice: this RPC compression status of client must comply with that of IoTDB server - -* Close a Session - -``` java -void close() -``` - -* SessionPool - -We provide a connection pool (`SessionPool) for Native API. -Using the interface, you need to define the pool size. - -If you can not get a session connection in 60 seconds, there is a warning log but the program will hang. - -If a session has finished an operation, it will be put back to the pool automatically. -If a session connection is broken, the session will be removed automatically and the pool will try -to create a new session and redo the operation. -You can also specify an url list of multiple reachable nodes when creating a SessionPool, just as you would when creating a Session. To ensure high availability of clients in distributed cluster. - -For query operations: - -1. When using SessionPool to query data, the result set is `SessionDataSetWrapper`; -2. Given a `SessionDataSetWrapper`, if you have not scanned all the data in it and stop to use it, -you have to call `SessionPool.closeResultSet(wrapper)` manually; -3. When you call `hasNext()` and `next()` of a `SessionDataSetWrapper` and there is an exception, then -you have to call `SessionPool.closeResultSet(wrapper)` manually; -4. You can call `getColumnNames()` of `SessionDataSetWrapper` to get the column names of query result; - -Examples: ```session/src/test/java/org/apache/iotdb/session/pool/SessionPoolTest.java``` - -Or `example/session/src/main/java/org/apache/iotdb/SessionPoolExample.java` - - -### Database & Timeseries Management API - -#### Database Management - -* CREATE DATABASE - -``` java -void setStorageGroup(String storageGroupId) -``` - -* Delete one or several databases - -``` java -void deleteStorageGroup(String storageGroup) -void deleteStorageGroups(List storageGroups) -``` - -#### Timeseries Management - -* Create one or multiple timeseries - -``` java -void createTimeseries(String path, TSDataType dataType, - TSEncoding encoding, CompressionType compressor, Map props, - Map tags, Map attributes, String measurementAlias) - -void createMultiTimeseries(List paths, List dataTypes, - List encodings, List compressors, - List> propsList, List> tagsList, - List> attributesList, List measurementAliasList) -``` - -* Create aligned timeseries -``` -void createAlignedTimeseries(String prefixPath, List measurements, - List dataTypes, List encodings, - List compressors, List measurementAliasList); -``` - -Attention: Alias of measurements are **not supported** currently. - -* Delete one or several timeseries - -``` java -void deleteTimeseries(String path) -void deleteTimeseries(List paths) -``` - -* Check whether the specific timeseries exists. - -``` java -boolean checkTimeseriesExists(String path) -``` - -#### Schema Template - - -Create a schema template for massive identical devices will help to improve memory performance. You can use Template, InternalNode and MeasurementNode to depict the structure of the template, and use belowed interface to create it inside session. - -``` java -public void createSchemaTemplate(Template template); - -Class Template { - private String name; - private boolean directShareTime; - Map children; - public Template(String name, boolean isShareTime); - - public void addToTemplate(Node node); - public void deleteFromTemplate(String name); - public void setShareTime(boolean shareTime); -} - -Abstract Class Node { - private String name; - public void addChild(Node node); - public void deleteChild(Node node); -} - -Class MeasurementNode extends Node { - TSDataType dataType; - TSEncoding encoding; - CompressionType compressor; - public MeasurementNode(String name, - TSDataType dataType, - TSEncoding encoding, - CompressionType compressor); -} -``` - -We strongly suggest you implement templates only with flat-measurement (like object 'flatTemplate' in belowed snippet), since tree-structured template may not be a long-term supported feature in further version of IoTDB. - -A snippet of using above Method and Class: - -``` java -MeasurementNode nodeX = new MeasurementNode("x", TSDataType.FLOAT, TSEncoding.RLE, CompressionType.SNAPPY); -MeasurementNode nodeY = new MeasurementNode("y", TSDataType.FLOAT, TSEncoding.RLE, CompressionType.SNAPPY); -MeasurementNode nodeSpeed = new MeasurementNode("speed", TSDataType.DOUBLE, TSEncoding.GORILLA, CompressionType.SNAPPY); - -// This is the template we suggest to implement -Template flatTemplate = new Template("flatTemplate"); -template.addToTemplate(nodeX); -template.addToTemplate(nodeY); -template.addToTemplate(nodeSpeed); - -createSchemaTemplate(flatTemplate); -``` - -You can query measurement inside templates with these APIS: ```java -// Return the amount of measurements inside a template -public int countMeasurementsInTemplate(String templateName); - -// Return true if path points to a measurement, otherwise returne false -public boolean isMeasurementInTemplate(String templateName, String path); - -// Return true if path exists in template, otherwise return false -public boolean isPathExistInTemplate(String templateName, String path); - -// Return all measurements paths inside template -public List showMeasurementsInTemplate(String templateName); - -// Return all measurements paths under the designated patter inside template -public List showMeasurementsInTemplate(String templateName, String pattern); -``` - -To implement schema template, you can set the measurement template named 'templateName' at path 'prefixPath'. - -**Please notice that, we strongly recommend not setting templates on the nodes above the database to accommodate future updates and collaboration between modules.** - -``` java -void setSchemaTemplate(String templateName, String prefixPath) -``` - -Before setting template, you should firstly create the template using - -``` java -void createSchemaTemplate(Template template) -``` - -After setting template to a certain path, you can use the template to create timeseries on given device paths through the following interface, or you can write data directly to trigger timeseries auto creation using schema template under target devices. - -``` java -void createTimeseriesUsingSchemaTemplate(List devicePathList) -``` - -After setting template to a certain path, you can query for info about template using belowed interface in session: - -``` java -/** @return All template names. */ -public List showAllTemplates(); - -/** @return All paths have been set to designated template. */ -public List showPathsTemplateSetOn(String templateName); - -/** @return All paths are using designated template. */ -public List showPathsTemplateUsingOn(String templateName) -``` - -If you are ready to get rid of schema template, you can drop it with belowed interface. Make sure the template to drop has been unset from MTree. - -``` java -void unsetSchemaTemplate(String prefixPath, String templateName); -public void dropSchemaTemplate(String templateName); -``` - -Unset the measurement template named 'templateName' from path 'prefixPath'. When you issue this interface, you should assure that there is a template named 'templateName' set at the path 'prefixPath'. - -Attention: Unsetting the template named 'templateName' from node at path 'prefixPath' or descendant nodes which have already inserted records using template is **not supported**. - - -### Data Manipulation Interface (DML Interface) - -### Data Insert API - -It is recommended to use insertTablet to help improve write efficiency. - -* Insert a Tablet,which is multiple rows of a device, each row has the same measurements - * **Better Write Performance** - * **Support batch write** - * **Support null values**: fill the null value with any value, and then mark the null value via BitMap - -``` java -void insertTablet(Tablet tablet) - -public class Tablet { - /** DeviceId if using tree-view interfaces or TableName when using table-view interfaces. */ - private String insertTargetName; - /** the list of measurement schemas for creating the tablet */ - private List schemas; - /** - * Marking the type of each column, namely ID or MEASUREMENT. Notice: the ID columns must be the - * FIRST ones. - */ - private List columnCategories; - /** timestamps in this tablet */ - private long[] timestamps; - /** each object is a primitive type array, which represents values of one measurement */ - private Object[] values; - /** each bitmap represents the existence of each value in the current column. */ - private BitMap[] bitMaps; - /** the number of rows to include in this tablet */ - private int rowSize; - /** the maximum number of rows for this tablet */ - private int maxRowNumber; -} -``` - -* Insert multiple Tablets - -``` java -void insertTablets(Map tablet) -``` - -* Insert a Record, which contains multiple measurement value of a device at a timestamp. This method is equivalent to providing a common interface for multiple data types of values. Later, the value can be cast to the original type through TSDataType. - - The correspondence between the Object type and the TSDataType type is shown in the following table. - - | TSDataType | Object | - |------------|--------------| - | BOOLEAN | Boolean | - | INT32 | Integer | - | DATE | LocalDate | - | INT64 | Long | - | TIMESTAMP | Long | - | FLOAT | Float | - | DOUBLE | Double | - | TEXT | String, Binary | - | STRING | String, Binary | - | BLOB | Binary | -``` java -void insertRecord(String deviceId, long time, List measurements, - List types, List values) -``` - -* Insert multiple Records - -``` java -void insertRecords(List deviceIds, List times, - List> measurementsList, List> typesList, - List> valuesList) -``` -* Insert multiple Records that belong to the same device. - With type info the server has no need to do type inference, which leads a better performance - -``` java -void insertRecordsOfOneDevice(String deviceId, List times, - List> measurementsList, List> typesList, - List> valuesList) -``` - -#### Insert with type inference - -When the data is of String type, we can use the following interface to perform type inference based on the value of the value itself. For example, if value is "true" , it can be automatically inferred to be a boolean type. If value is "3.2" , it can be automatically inferred as a flout type. Without type information, server has to do type inference, which may cost some time. - -* Insert a Record, which contains multiple measurement value of a device at a timestamp - -``` java -void insertRecord(String prefixPath, long time, List measurements, List values) -``` - -* Insert multiple Records - -``` java -void insertRecords(List deviceIds, List times, - List> measurementsList, List> valuesList) -``` - -* Insert multiple Records that belong to the same device. - -``` java -void insertStringRecordsOfOneDevice(String deviceId, List times, - List> measurementsList, List> valuesList) -``` - -#### Insert of Aligned Timeseries - -The Insert of aligned timeseries uses interfaces like insertAlignedXXX, and others are similar to the above interfaces: - -* insertAlignedRecord -* insertAlignedRecords -* insertAlignedRecordsOfOneDevice -* insertAlignedStringRecordsOfOneDevice -* insertAlignedTablet -* insertAlignedTablets - -### Data Delete API - -* Delete data before or equal to a timestamp of one or several timeseries - -``` java -void deleteData(String path, long time) -void deleteData(List paths, long time) -``` - -### Data Query API - -* Time-series raw data query with time range: - - The specified query time range is a left-closed right-open interval, including the start time but excluding the end time. - -``` java -SessionDataSet executeRawDataQuery(List paths, long startTime, long endTime); -``` - -* Last query: - - Query the last data, whose timestamp is greater than or equal LastTime. - ``` java - SessionDataSet executeLastDataQuery(List paths, long LastTime); - ``` - - Query the latest point of the specified series of single device quickly, and support redirection; - If you are sure that the query path is valid, set 'isLegalPathNodes' to true to avoid performance penalties from path verification. - ``` java - SessionDataSet executeLastDataQueryForOneDevice( - String db, String device, List sensors, boolean isLegalPathNodes); - ``` - -* Aggregation query: - - Support specified query time range: The specified query time range is a left-closed right-open interval, including the start time but not the end time. - - Support GROUP BY TIME. - -``` java -SessionDataSet executeAggregationQuery(List paths, List aggregations); - -SessionDataSet executeAggregationQuery( - List paths, List aggregations, long startTime, long endTime); - -SessionDataSet executeAggregationQuery( - List paths, - List aggregations, - long startTime, - long endTime, - long interval); - -SessionDataSet executeAggregationQuery( - List paths, - List aggregations, - long startTime, - long endTime, - long interval, - long slidingStep); -``` - -* Execute query statement - -``` java -SessionDataSet executeQueryStatement(String sql) -``` - -### Data Subscription - -#### 1 Topic Management - -The `SubscriptionSession` class in the IoTDB subscription client provides interfaces for topic management. The status changes of topics are illustrated in the diagram below: - -
- -
- -##### 1.1 Create Topic - -```Java - void createTopicIfNotExists(String topicName, Properties properties) throws Exception; -``` - -Example: - -```Java -try (final SubscriptionSession session = new SubscriptionSession(host, port)) { - session.open(); - final Properties config = new Properties(); - config.put(TopicConstant.PATH_KEY, "root.db.**"); - session.createTopic(topicName, config); +import java.util.ArrayList; +import java.util.List; +import org.apache.iotdb.session.pool.SessionPool; + +public class IoTDBSessionPoolExample { + private static SessionPool sessionPool; + + public static void main(String[] args) { + // Using nodeUrls ensures that when one node goes down, other nodes are automatically connected to retry + List nodeUrls = new ArrayList<>(); + nodeUrls.add("127.0.0.1:6667"); + nodeUrls.add("127.0.0.1:6668"); + sessionPool = + new SessionPool.Builder() + .nodeUrls(nodeUrls) + .user("root") + .password("root") + .maxSize(3) + .build(); + } } ``` -##### 1.2 Delete Topic +### 2.3 Performing Database Operations -```Java -void dropTopicIfExists(String topicName) throws Exception; -``` +#### 2.3.1 Data Insertion -##### 1.3 View Topic +In industrial scenarios, data insertion can be categorized into the following types: inserting multiple rows of data, and inserting multiple rows of data for a single device. Below, we introduce the insertion interfaces for different scenarios. -```Java -// Get all topics -Set getTopics() throws Exception; - -// Get a specific topic -Optional getTopic(String topicName) throws Exception; -``` - -#### 2 Check Subscription Status -The `SubscriptionSession` class in the IoTDB subscription client provides interfaces to check the subscription status: - -```Java -Set getSubscriptions() throws Exception; -Set getSubscriptions(final String topicName) throws Exception; -``` - -#### 3 Create Consumer - -When creating a consumer using the JAVA native interface, you need to specify the parameters applied to the consumer. - -For both `SubscriptionPullConsumer` and `SubscriptionPushConsumer`, the following common configurations are available: +##### Multi-Row Data Insertion Interface +Interface Description: Supports inserting multiple rows of data at once, where each row corresponds to multiple measurement values for a device at a specific timestamp. -| key | **required or optional with default** | description | -| :---------------------- | :----------------------------------------------------------- | :----------------------------------------------------------- | -| host | optional: 127.0.0.1 | `String`: The RPC host of a certain DataNode in IoTDB | -| port | optional: 6667 | Integer: The RPC port of a certain DataNode in IoTDB | -| node-urls | optional: 127.0.0.1:6667 | `List`: The RPC addresses of all DataNodes in IoTDB, can be multiple; either host:port or node-urls can be filled in. If both host:port and node-urls are filled in, the union of host:port and node-urls will be used to form a new node-urls application | -| username | optional: root | `String`: The username of a DataNode in IoTDB | -| password | optional: root | `String`: The password of a DataNode in IoTDB | -| groupId | optional | `String`: consumer group id, if not specified, a new consumer group will be randomly assigned, ensuring that different consumer groups have different consumer group ids | -| consumerId | optional | `String`: consumer client id, if not specified, it will be randomly assigned, ensuring that each consumer client id in the same consumer group is unique | -| heartbeatIntervalMs | optional: 30000 (min: 1000) | `Long`: The interval at which the consumer sends heartbeat requests to the IoTDB DataNode | -| endpointsSyncIntervalMs | optional: 120000 (min: 5000) | `Long`: The interval at which the consumer detects the expansion and contraction of IoTDB cluster nodes and adjusts the subscription connection | -| fileSaveDir | optional: Paths.get(System.getProperty("user.dir"), "iotdb-subscription").toString() | `String`: The temporary directory path where the TsFile files subscribed by the consumer are stored | -| fileSaveFsync | optional: false | `Boolean`: Whether the consumer actively calls fsync during the subscription of TsFile | +Interface List: +| **Interface Name** | **Function Description** | +|------------------------------------------------------------------------------------------------------------------------|-----------------------| +| `insertRecords(List deviceIds, List times, List> measurementsList, List> typesList, List> valuesList)` | Inserts multiple rows of data, suitable for scenarios where measurements are independently collected. | -##### 3.1 SubscriptionPushConsumer +Code Example: -The following are special configurations for `SubscriptionPushConsumer`: - - -| key | **required or optional with default** | description | -| :----------------- | :------------------------------------ | :----------------------------------------------------------- | -| ackStrategy | optional: `ACKStrategy.AFTER_CONSUME` | Consumption progress confirmation mechanism includes the following options: `ACKStrategy.BEFORE_CONSUME` (submit consumption progress immediately when the consumer receives data, before `onReceive`) `ACKStrategy.AFTER_CONSUME` (submit consumption progress after the consumer has consumed the data, after `onReceive`) | -| consumeListener | optional | Consumption data callback function, need to implement the `ConsumeListener` interface, define the consumption logic of `SessionDataSetsHandler` and `TsFileHandler` form data| -| autoPollIntervalMs | optional: 5000 (min: 500) | Long: The interval at which the consumer automatically pulls data, in ms | -| autoPollTimeoutMs | optional: 10000 (min: 1000) | Long: The timeout time for the consumer to pull data each time, in ms | - -Among them, the ConsumerListener interface is defined as follows: - - -```Java -@FunctionInterface -interface ConsumeListener { - default ConsumeResult onReceive(Message message) { - return ConsumeResult.SUCCESS; +```java +import java.util.ArrayList; +import java.util.List; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.apache.iotdb.session.pool.SessionPool; +import org.apache.tsfile.enums.TSDataType; + +public class SessionPoolExample { + private static SessionPool sessionPool; + public static void main(String[] args) throws IoTDBConnectionException, StatementExecutionException { + // 1. init SessionPool + constructSessionPool(); + // 2. execute insert data + insertRecordsExample(); + // 3. close SessionPool + closeSessionPool(); } -} - -enum ConsumeResult { - SUCCESS, - FAILURE, -} -``` - -##### 3.2 SubscriptionPullConsumer - -The following are special configurations for `SubscriptionPullConsumer` : - -| key | **required or optional with default** | description | -| :----------------- | :------------------------------------ | :----------------------------------------------------------- | -| autoCommit | optional: true | Boolean: Whether to automatically commit consumption progress. If this parameter is set to false, the commit method must be called to manually `commit` consumption progress. | -| autoCommitInterval | optional: 5000 (min: 500) | Long: The interval at which consumption progress is automatically committed, in milliseconds. This only takes effect when the autoCommit parameter is true. - | - -After creating a consumer, you need to manually call the consumer's open method: - - -```Java -void open() throws Exception; -``` - -At this point, the IoTDB subscription client will verify the correctness of the consumer's configuration. After a successful verification, the consumer will join the corresponding consumer group. That is, only after opening the consumer can you use the returned consumer object to subscribe to topics, consume data, and perform other operations. - -#### 4 Subscribe to Topics - -Both `SubscriptionPushConsumer` and `SubscriptionPullConsumer` provide the following JAVA native interfaces for subscribing to topics: - -```Java -// Subscribe to topics -void subscribe(String topic) throws Exception; -void subscribe(List topics) throws Exception; -``` - -- Before a consumer subscribes to a topic, the topic must have been created, otherwise, the subscription will fail. - -- If a consumer subscribes to a topic that it has already subscribed to, no error will occur. - -- If there are other consumers in the same consumer group that have subscribed to the same topics, the consumer will reuse the corresponding consumption progress. - - -#### 5 Consume Data - -For both push and pull mode consumers: - - -- Only after explicitly subscribing to a topic will the consumer receive data for that topic. - -- If no topics are subscribed to after creation, the consumer will not be able to consume any data, even if other consumers in the same consumer group have subscribed to some topics. - -##### 5.1 SubscriptionPushConsumer - -After `SubscriptionPushConsumer` subscribes to topics, there is no need to manually pull data. - -The data consumption logic is within the `consumeListener` configuration specified when creating `SubscriptionPushConsumer`. - -##### 5.2 SubscriptionPullConsumer - -After SubscriptionPullConsumer subscribes to topics, it needs to actively call the poll method to pull data: - -```Java -List poll(final Duration timeout) throws Exception; -List poll(final long timeoutMs) throws Exception; -List poll(final Set topicNames, final Duration timeout) throws Exception; -List poll(final Set topicNames, final long timeoutMs) throws Exception; -``` -In the poll method, you can specify the topic names to be pulled (if not specified, it defaults to pulling all topics that the consumer has subscribed to) and the timeout period. - - -When the SubscriptionPullConsumer is configured with the autoCommit parameter set to false, it is necessary to manually call the commitSync and commitAsync methods to synchronously or asynchronously commit the consumption progress of a batch of data: - - -```Java -void commitSync(final SubscriptionMessage message) throws Exception; -void commitSync(final Iterable messages) throws Exception; - -CompletableFuture commitAsync(final SubscriptionMessage message); -CompletableFuture commitAsync(final Iterable messages); -void commitAsync(final SubscriptionMessage message, final AsyncCommitCallback callback); -void commitAsync(final Iterable messages, final AsyncCommitCallback callback); -``` - -The AsyncCommitCallback class is defined as follows: - -```Java -public interface AsyncCommitCallback { - default void onComplete() { - // Do nothing - } + private static void constructSessionPool() { + // Using nodeUrls ensures that when one node goes down, other nodes are automatically connected to retry + List nodeUrls = new ArrayList<>(); + nodeUrls.add("127.0.0.1:6667"); + nodeUrls.add("127.0.0.1:6668"); + sessionPool = + new SessionPool.Builder() + .nodeUrls(nodeUrls) + .user("root") + .password("root") + .maxSize(3) + .build(); + } - default void onFailure(final Throwable e) { - // Do nothing - } + public static void insertRecordsExample() throws IoTDBConnectionException, StatementExecutionException { + String deviceId = "root.sg1.d1"; + List measurements = new ArrayList<>(); + measurements.add("s1"); + measurements.add("s2"); + measurements.add("s3"); + List deviceIds = new ArrayList<>(); + List> measurementsList = new ArrayList<>(); + List> valuesList = new ArrayList<>(); + List timestamps = new ArrayList<>(); + List> typesList = new ArrayList<>(); + + for (long time = 0; time < 500; time++) { + List values = new ArrayList<>(); + List types = new ArrayList<>(); + values.add(1L); + values.add(2L); + values.add(3L); + types.add(TSDataType.INT64); + types.add(TSDataType.INT64); + types.add(TSDataType.INT64); + + deviceIds.add(deviceId); + measurementsList.add(measurements); + valuesList.add(values); + typesList.add(types); + timestamps.add(time); + if (time != 0 && time % 100 == 0) { + try { + sessionPool.insertRecords(deviceIds, timestamps, measurementsList, typesList, valuesList); + } catch (IoTDBConnectionException | StatementExecutionException e) { + // solve exception + } + deviceIds.clear(); + measurementsList.clear(); + valuesList.clear(); + typesList.clear(); + timestamps.clear(); + } + } + try { + sessionPool.insertRecords(deviceIds, timestamps, measurementsList, typesList, valuesList); + } catch (IoTDBConnectionException | StatementExecutionException e) { + // solve exception + } + } + + public static void closeSessionPool(){ + sessionPool.close(); + } } ``` +##### Single-Device Multi-Row Data Insertion Interface +Interface Description: Supports inserting multiple rows of data for a single device at once, where each row corresponds to multiple measurement values for a specific timestamp. -#### 6 Unsubscribe +Interface List: -The `SubscriptionPushConsumer` and `SubscriptionPullConsumer` provide the following JAVA native interfaces for unsubscribing and closing the consumer: +| **Interface Name** | **Function Description** | +|-----------------------------------------------------------------------------------------|----------------------------| +| `insertTablet(Tablet tablet)` | Inserts multiple rows of data for a single device, suitable for scenarios where measurements are independently collected. | -```Java -// Unsubscribe from topics -void unsubscribe(String topic) throws Exception; -void unsubscribe(List topics) throws Exception; +Code Example: -// Close consumer -void close(); -``` - -- If a consumer unsubscribes from a topic that it has not subscribed to, no error will occur. -- When a consumer is closed, it will exit the corresponding consumer group and automatically unsubscribe from all topics it is currently subscribed to. -- Once a consumer is closed, its lifecycle ends, and it cannot be reopened to subscribe to and consume data again. - - -#### 7 Code Examples - -##### 7.1 Single Pull Consumer Consuming SessionDataSetsHandler Format Data - -```Java -// Create topics -try (final SubscriptionSession session = new SubscriptionSession(HOST, PORT)) { - session.open(); - final Properties config = new Properties(); - config.put(TopicConstant.PATH_KEY, "root.db.**"); - session.createTopic(TOPIC_1, config); -} - -// Subscription: property-style ctor -final Properties config = new Properties(); -config.put(ConsumerConstant.CONSUMER_ID_KEY, "c1"); -config.put(ConsumerConstant.CONSUMER_GROUP_ID_KEY, "cg1"); - -final SubscriptionPullConsumer consumer1 = new SubscriptionPullConsumer(config); -consumer1.open(); -consumer1.subscribe(TOPIC_1); -while (true) { - LockSupport.parkNanos(SLEEP_NS); // wait some time - final List messages = consumer1.poll(POLL_TIMEOUT_MS); - for (final SubscriptionMessage message : messages) { - for (final SubscriptionSessionDataSet dataSet : message.getSessionDataSetsHandler()) { - System.out.println(dataSet.getColumnNames()); - System.out.println(dataSet.getColumnTypes()); - while (dataSet.hasNext()) { - System.out.println(dataSet.next()); - } +```java +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.apache.iotdb.session.pool.SessionPool; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.write.record.Tablet; +import org.apache.tsfile.write.schema.MeasurementSchema; + +public class SessionPoolExample { + private static SessionPool sessionPool; + public static void main(String[] args) throws IoTDBConnectionException, StatementExecutionException { + // 1. init SessionPool + constructSessionPool(); + // 2. execute insert data + insertTabletExample(); + // 3. close SessionPool + closeSessionPool(); + } + + private static void constructSessionPool() { + // Using nodeUrls ensures that when one node goes down, other nodes are automatically connected to retry + List nodeUrls = new ArrayList<>(); + nodeUrls.add("127.0.0.1:6667"); + nodeUrls.add("127.0.0.1:6668"); + sessionPool = + new SessionPool.Builder() + .nodeUrls(nodeUrls) + .user("root") + .password("root") + .maxSize(3) + .build(); } - } - // Auto commit -} - -// Show topics and subscriptions -try (final SubscriptionSession session = new SubscriptionSession(HOST, PORT)) { - session.open(); - session.getTopics().forEach((System.out::println)); - session.getSubscriptions().forEach((System.out::println)); -} - -consumer1.unsubscribe(TOPIC_1); -consumer1.close(); -``` - -##### 7.2 Multiple Push Consumers Consuming TsFileHandler Format Data -```Java -// Create topics -try (final SubscriptionSession subscriptionSession = new SubscriptionSession(HOST, PORT)) { - subscriptionSession.open(); - final Properties config = new Properties(); - config.put(TopicConstant.FORMAT_KEY, TopicConstant.FORMAT_TS_FILE_HANDLER_VALUE); - subscriptionSession.createTopic(TOPIC_2, config); -} - -final List threads = new ArrayList<>(); -for (int i = 0; i < 8; ++i) { - final int idx = i; - final Thread thread = - new Thread( - () -> { - // Subscription: builder-style ctor - try (final SubscriptionPushConsumer consumer2 = - new SubscriptionPushConsumer.Builder() - .consumerId("c" + idx) - .consumerGroupId("cg2") - .fileSaveDir(System.getProperty("java.io.tmpdir")) - .ackStrategy(AckStrategy.AFTER_CONSUME) - .consumeListener( - message -> { - doSomething(message.getTsFileHandler()); - return ConsumeResult.SUCCESS; - }) - .buildPushConsumer()) { - consumer2.open(); - consumer2.subscribe(TOPIC_2); - // block the consumer main thread - Thread.sleep(Long.MAX_VALUE); - } catch (final IOException | InterruptedException e) { - throw new RuntimeException(e); + private static void insertTabletExample() throws IoTDBConnectionException, StatementExecutionException { + /* + * A Tablet example: + * device1 + * time s1, s2, s3 + * 1, 1, 1, 1 + * 2, 2, 2, 2 + * 3, 3, 3, 3 + */ + // The schema of measurements of one device + // only measurementId and data type in MeasurementSchema take effects in Tablet + List schemaList = new ArrayList<>(); + schemaList.add(new MeasurementSchema("s1", TSDataType.INT64)); + schemaList.add(new MeasurementSchema("s2", TSDataType.INT64)); + schemaList.add(new MeasurementSchema("s3", TSDataType.INT64)); + + Tablet tablet = new Tablet("root.sg.d1", schemaList, 100); + + // Method 1 to add tablet data + long timestamp = System.currentTimeMillis(); + + Random random = new Random(); + for (long row = 0; row < 100; row++) { + int rowIndex = tablet.rowSize++; + tablet.addTimestamp(rowIndex, timestamp); + for (int s = 0; s < 3; s++) { + long value = random.nextLong(); + tablet.addValue(schemaList.get(s).getMeasurementId(), rowIndex, value); } - }); - thread.start(); - threads.add(thread); -} + if (tablet.rowSize == tablet.getMaxRowNumber()) { + sessionPool.insertTablet(tablet); + tablet.reset(); + } + timestamp++; + } + if (tablet.rowSize != 0) { + sessionPool.insertTablet(tablet); + tablet.reset(); + } + } -for (final Thread thread : threads) { - thread.join(); + public static void closeSessionPool(){ + sessionPool.close(); + } } ``` -### Other Modules (Execute SQL Directly) - -* Execute non query statement - -``` java -void executeNonQueryStatement(String sql) -``` - - -### Write Test Interface (to profile network cost) - -These methods **don't** insert data into database and server just return after accept the request. - -* Test the network and client cost of insertRecord - -``` java -void testInsertRecord(String deviceId, long time, List measurements, List values) - -void testInsertRecord(String deviceId, long time, List measurements, - List types, List values) -``` +#### 2.3.2 SQL Operations -* Test the network and client cost of insertRecords +SQL operations are divided into two categories: queries and non-queries. The corresponding interfaces are executeQuery and executeNonQuery. The difference between them is that the former executes specific query statements and returns a result set, while the latter performs insert, delete, and update operations and does not return a result set. -``` java -void testInsertRecords(List deviceIds, List times, - List> measurementsList, List> valuesList) - -void testInsertRecords(List deviceIds, List times, - List> measurementsList, List> typesList - List> valuesList) -``` - -* Test the network and client cost of insertTablet - -``` java -void testInsertTablet(Tablet tablet) -``` +```java +import java.util.ArrayList; +import java.util.List; +import org.apache.iotdb.isession.pool.SessionDataSetWrapper; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.apache.iotdb.session.pool.SessionPool; + +public class SessionPoolExample { + private static SessionPool sessionPool; + public static void main(String[] args) throws IoTDBConnectionException, StatementExecutionException { + // 1. init SessionPool + constructSessionPool(); + // 2. executes a non-query SQL statement, such as a DDL or DML command. + executeQueryExample(); + // 3. executes a query SQL statement and returns the result set. + executeNonQueryExample(); + // 4. close SessionPool + closeSessionPool(); + } -* Test the network and client cost of insertTablets + private static void executeNonQueryExample() throws IoTDBConnectionException, StatementExecutionException { + // 1. create a nonAligned time series + sessionPool.executeNonQueryStatement("create timeseries root.test.d1.s1 with dataType = int32"); + // 2. set ttl + sessionPool.executeNonQueryStatement("set TTL to root.test.** 10000"); + // 3. delete time series + sessionPool.executeNonQueryStatement("delete timeseries root.test.d1.s1"); + private static void executeQueryExample() throws IoTDBConnectionException, StatementExecutionException { + // 1. execute normal query + try(SessionDataSetWrapper wrapper = sessionPool.executeQueryStatement("select s1 from root.sg1.d1 limit 10")) { + while (wrapper.hasNext()) { + System.out.println(wrapper.next()); + } + } + // 2. execute aggregate query + try(SessionDataSetWrapper wrapper = sessionPool.executeQueryStatement("select count(s1) from root.sg1.d1 group by ([0, 40), 5ms) ")) { + while (wrapper.hasNext()) { + System.out.println(wrapper.next()); + } + } + } + + private static void constructSessionPool() { + // Using nodeUrls ensures that when one node goes down, other nodes are automatically connected to retry + List nodeUrls = new ArrayList<>(); + nodeUrls.add("127.0.0.1:6667"); + nodeUrls.add("127.0.0.1:6668"); + sessionPool = + new SessionPool.Builder() + .nodeUrls(nodeUrls) + .user("root") + .password("root") + .maxSize(3) + .build(); + } -``` java -void testInsertTablets(Map tablets) + public static void closeSessionPool(){ + sessionPool.close(); + } +} ``` - -### Coding Examples - -To get more information of the following interfaces, please view session/src/main/java/org/apache/iotdb/session/Session.java - -The sample code of using these interfaces is in example/session/src/main/java/org/apache/iotdb/SessionExample.java,which provides an example of how to open an IoTDB session, execute a batch insertion. - -For examples of aligned timeseries and measurement template, you can refer to `example/session/src/main/java/org/apache/iotdb/AlignedTimeseriesSessionExample.java` \ No newline at end of file +### 3 Native Interface Description + +#### 3.1 Parameter List +The Session class has the following fields, which can be set through the constructor or the Session.Builder method: + +| **Field Name** | **Type** | **Description** | +|--------------------------------|-------------------------------|----------------------------------------------------------------------| +| `nodeUrls` | `List` | List of URLs for database nodes, supporting multiple node connections | +| `username` | `String` | Username | +| `password` | `String` | Password | +| `fetchSize` | `int` | Default batch size for query results | +| `useSSL` | `boolean` | Whether to enable SSL | +| `trustStore` | `String` | Path to the trust store | +| `trustStorePwd` | `String` | Password for the trust store | +| `queryTimeoutInMs` | `long` | Query timeout in milliseconds | +| `enableRPCCompression` | `boolean` | Whether to enable RPC compression | +| `connectionTimeoutInMs` | `int` | Connection timeout in milliseconds | +| `zoneId` | `ZoneId` | Time zone setting for the session | +| `thriftDefaultBufferSize` | `int` | Default buffer size for Thrift Thrift | +| `thriftMaxFrameSize` | `int` | Maximum frame size for Thrift Thrift | +| `defaultEndPoint` | `TEndPoint` | Default database endpoint information | +| `defaultSessionConnection` | `SessionConnection` | Default session connection object | +| `isClosed` | `boolean` | Whether the current session is closed | +| `enableRedirection` | `boolean` | Whether to enable redirection | +| `enableRecordsAutoConvertTablet` | `boolean` | Whether to enable the function of recording the automatic transfer to Tablet | +| `deviceIdToEndpoint` | `Map` | Mapping of device IDs to database endpoints | +| `endPointToSessionConnection` | `Map` | Mapping of database endpoints to session connections | +| `executorService` | `ScheduledExecutorService` | Thread pool for periodically updating the node list | +| `availableNodes` | `INodeSupplier` | Supplier of available nodes | +| `enableQueryRedirection` | `boolean` | Whether to enable query redirection | +| `version` | `Version` | Client version number, used for compatibility judgment with the server | +| `enableAutoFetch` | `boolean` | Whether to enable automatic fetching | +| `maxRetryCount` | `int` | Maximum number of retries | +| `retryIntervalInMs` | `long` | Retry interval in milliseconds | + + + +#### 3.2 Interface list + +##### 3.2.1 Metadata Management + +| **Method Name** | **Function Description** | **Parameter Explanation** | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `createDatabase(String database)` | Create a database | `database`: The name of the database to be created | +| `deleteDatabase(String database)` | Delete a specified database | `database`: The name of the database to be deleted | +| `deleteDatabases(List databases)` | Batch delete databases | `databases`: A list of database names to be deleted | +| `createTimeseries(String path, TSDataType dataType, TSEncoding encoding, CompressionType compressor)` | Create a single time series | `path`: The path of the time series,`dataType`: The data type,`encoding`: The encoding type,`compressor`: The compression type | +| `createAlignedTimeseries(...)` | Create aligned time series | Device ID, list of measurement points, list of data types, list of encodings, list of compression types | +| `createMultiTimeseries(...)` | Batch create time series | Multiple paths, data types, encodings, compression types, properties, tags, aliases, etc. | +| `deleteTimeseries(String path)` | Delete a time series | `path`: The path of the time series to be deleted | +| `deleteTimeseries(List paths)` | Batch delete time series | `paths`: A list of time series paths to be deleted | +| `setSchemaTemplate(String templateName, String prefixPath)` | Set a schema template | `templateName`: The name of template,`prefixPath`: The path where the template is applied | +| `createSchemaTemplate(Template template)` | Create a schema template | `template`: The template object | +| `dropSchemaTemplate(String templateName)` | Delete a schema template | `templateName`: The name of template to be deleted | +| `addAlignedMeasurementsInTemplate(...)` | Add aligned measurements to a template | Template name, list of measurement paths, data type, encoding type, compression type | +| `addUnalignedMeasurementsInTemplate(...)` | Add unaligned measurements to a template | Same as above | +| `deleteNodeInTemplate(String templateName, String path)` | Delete a node in a template | `templateName`: The name of template,`path`: The path to be deleted | +| `countMeasurementsInTemplate(String name)` | Count the number of measurements in a template | `name`: The name of template | +| `isMeasurementInTemplate(String templateName, String path)` | Check if a measurement exists in a template | `templateName`: The name of template,`path`: The path of the measurement | +| `isPathExistInTemplate(String templateName, String path)` | Check if a path exists in a template | same as above | +| `showMeasurementsInTemplate(String templateName)` | Show measurements in a template | `templateName`: The name of template | +| `showMeasurementsInTemplate(String templateName, String pattern)` | Show measurements in a template by pattern | `templateName`: The name of template,`pattern`: The matching pattern | +| `showAllTemplates()` | Show all templates | No parameters | +| `showPathsTemplateSetOn(String templateName)` | Show paths where a template is set | `templateName`: The name of the template | +| `showPathsTemplateUsingOn(String templateName)` | Show actual paths using a template | Same as above上 | +| `unsetSchemaTemplate(String prefixPath, String templateName)` | Unset the template setting for a path | `prefixPath`: The path,`templateName`: The name of template | + + +##### 3.2.2 Data Insertion + +| **Method Name** | **Function Description** | **Parameter Explanation** | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `insertRecord(String deviceId, long time, List measurements, List types, Object... values)` | Insert a single record | `deviceId`: Device ID,`time`: Timestamp,`measurements`: List of measurement points,`types`: List of data types,`values`: List of values | +| `insertRecord(String deviceId, long time, List measurements, List values)` | Insert a single record | `deviceId`: Device ID,`time`: Timestamp,`measurements`: List of measurement points,`values`: List of values | +| `insertRecords(List deviceIds, List times, List> measurementsList, List> valuesList)` | Insert multiple records | `deviceIds`: List of device IDs,`times`: List of timestamps,`measurementsList`: List of timestamps,`valuesList`: List of lists of values | +| `insertRecords(List deviceIds, List times, List> measurementsList, List> typesList, List> valuesList)` | Insert multiple records | Same as above,plus `typesList`: List of lists of data types | +| `insertRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> typesList, List> valuesList)` | Insert multiple records for a single device | `deviceId`: Device ID,`times`: List of timestamps,`measurementsList`: List of lists of measurement points,`typesList`: List of lists of types,`valuesList`: List of lists of values | +| `insertRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> typesList, List> valuesList, boolean haveSorted)` | Insert sorted multiple records for a single device | Same as above, plus `haveSorted`: Whether the data is already sorted | +| `insertStringRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> valuesList)` | Insert string-formatted records for a single device | `deviceId`: Device ID,`times`: List of timestamps,`measurementsList`: List of lists of measurement points,`valuesList`: List of lists of values | +| `insertStringRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> valuesList, boolean haveSorted)` | Insert sorted string-formatted records for a single device | Same as above, plus `haveSorted`: Whether the data is already sorted序 | +| `insertAlignedRecord(String deviceId, long time, List measurements, List types, List values)` | Insert a single aligned record | `deviceId`: Device ID,`time`: Timestamp,`measurements`: List of measurement points,`types`: List of types,`values`: List of values | +| `insertAlignedRecord(String deviceId, long time, List measurements, List values)` | Insert a single string-formatted aligned record | `deviceId`: Device ID`time`: Timestamp,`measurements`: List of measurement points,`values`: List of values | +| `insertAlignedRecords(List deviceIds, List times, List> measurementsList, List> valuesList)` | Insert multiple aligned records | `deviceIds`: List of device IDs,`times`: List of timestamps,`measurementsList`: List of lists of measurement points,`valuesList`: List of lists of values | +| `insertAlignedRecords(List deviceIds, List times, List> measurementsList, List> typesList, List> valuesList)` | Insert multiple aligned records | Same as above, plus `typesList`: List of lists of data types | +| `insertAlignedRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> typesList, List> valuesList)` | Insert multiple aligned records for a single device | Same as above | +| `insertAlignedRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> typesList, List> valuesList, boolean haveSorted)` | Insert sorted multiple aligned records for a single device | Same as above, plus `haveSorted`: Whether the data is already sorted | +| `insertAlignedStringRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> valuesList)` | Insert string-formatted aligned records for a single device | `deviceId`: Device ID,`times`: List of timestamps,`measurementsList`: List of lists of measurement points,`valuesList`: List of lists of values | +| `insertAlignedStringRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> valuesList, boolean haveSorted)` | Insert sorted string-formatted aligned records for a single device | Same as above, plus w `haveSorted`: whether the data is already sorted | +| `insertTablet(Tablet tablet)` | Insert a single Tablet data | `tablet`: The Tablet data to be inserted | +| `insertTablet(Tablet tablet, boolean sorted)` | Insert a sorted Tablet data | Same as above, plus `sorted`: whether the data is already sorted | +| `insertAlignedTablet(Tablet tablet)` | Insert an aligned Tablet data | `tablet`: The Tablet data to be inserted | +| `insertAlignedTablet(Tablet tablet, boolean sorted)` | Insert a sorted aligned Tablet data | Same as above, plus `sorted`: whether the data is already sorted | +| `insertTablets(Map tablets)` | Insert multiple Tablet data in batch | `tablets`: Mapping from device IDs to Tablet data | +| `insertTablets(Map tablets, boolean sorted)` | Insert sorted multiple Tablet data in batch | Same as above, plus `sorted`: whether the data is already sorted | +| `insertAlignedTablets(Map tablets)` | Insert multiple aligned Tablet data in batch | `tablets`: Mapping from device IDs to Tablet data | +| `insertAlignedTablets(Map tablets, boolean sorted)` | Insert sorted multiple aligned Tablet data in batch | Same as above, plus `sorted`: whether the data is already sorted | + +##### 3.2.3 Data Deletion + +| **Method Name** | **Function Description** | **Parameter Explanation** | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `deleteTimeseries(String path)` | Delete a single time series | `path`: The path of the time series | +| `deleteTimeseries(List paths)` | Batch delete time series | `paths`: A list of time series paths | +| `deleteData(String path, long endTime)` | Delete historical data for a specified path | `path`: The path,`endTime`: The end timestamp | +| `deleteData(List paths, long endTime)` | Batch delete historical data for specified paths | `paths`: A list of paths,`endTime`: The end timestamp | +| `deleteData(List paths, long startTime, long endTime)` | Delete historical data within a time range for specified paths | Same as above, plus `startTime`: The start timestamp | + + +##### 3.2.4 Data Query + +| **Method Name** | **Function Description** | **Parameter Explanation** | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `executeQueryStatement(String sql)` | Execute a query statement | `sql`: The query SQL statement | +| `executeQueryStatement(String sql, long timeoutInMs)` | Execute a query statement with timeout | `sql`: The query SQL statement, `timeoutInMs`: The query timeout (in milliseconds) | +| `executeRawDataQuery(List paths, long startTime, long endTime)` | Query raw data for specified paths | paths: A list of query paths, `startTime`: The start timestamp, `endTime`: The end timestamp | +| `executeRawDataQuery(List paths, long startTime, long endTime, long timeOut)` | Query raw data for specified paths (with timeout) | Same as above, plus `timeOut`: The timeout time | +| `executeLastDataQuery(List paths)` | Query the latest data | `paths`: A list of query paths | +| `executeLastDataQuery(List paths, long lastTime)` | Query the latest data at a specified time | `paths`: A list of query paths, `lastTime`: The specified timestamp | +| `executeLastDataQuery(List paths, long lastTime, long timeOut)` | Query the latest data at a specified time (with timeout) | Same as above, plus `timeOut`: The timeout time | +| `executeLastDataQueryForOneDevice(String db, String device, List sensors, boolean isLegalPathNodes)` | Query the latest data for a single device | `db`: The database name, `device`: The device name, `sensors`: A list of sensors, `isLegalPathNodes`: Whether the path nodes are legal | +| `executeAggregationQuery(List paths, List aggregations)` | Execute an aggregation query | `paths`: A list of query paths, `aggregations`: A list of aggregation types | +| `executeAggregationQuery(List paths, List aggregations, long startTime, long endTime)` | Execute an aggregation query with a time range | Same as above, plus `startTime`: The start timestamp, `endTime`:` The end timestamp | +| `executeAggregationQuery(List paths, List aggregations, long startTime, long endTime, long interval)` | Execute an aggregation query with a time interval | Same as above, plus `interval`: The time interval | +| `executeAggregationQuery(List paths, List aggregations, long startTime, long endTime, long interval, long slidingStep)` | Execute a sliding window aggregation query | Same as above, plus `slidingStep`: The sliding step | +| `fetchAllConnections()` | Get information of all active connections | No parameters | + +##### 3.2.5 System Status and Backup +|**Method Name** | **Function Description** | **Parameter Explanation** | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `getBackupConfiguration()` | Get backup configuration information | No parameters | +| `fetchAllConnections()` | Get information of all active connections | No parameters | +| `getSystemStatus()` | Get the system status | Deprecated, returns `SystemStatus.NORMAL` | \ No newline at end of file diff --git a/src/UserGuide/V2.0.1/Tree/API/Programming-Data-Subscription.md b/src/UserGuide/V2.0.1/Tree/API/Programming-Data-Subscription.md new file mode 100644 index 000000000..89dfbb33c --- /dev/null +++ b/src/UserGuide/V2.0.1/Tree/API/Programming-Data-Subscription.md @@ -0,0 +1,244 @@ + + +# Data Sync API +IoTDB provides a powerful data subscription feature, allowing users to obtain newly added data from IoTDB in real-time through the subscription SDK. For detailed functional definitions and introductions:[Data Sync](../../User-Manual/Data-Sync_timecho.md#Data Sync) + +## 1 Core Steps + +1. Create Topic: Create a Topic that includes the measurement points you wish to subscribe to. +2. Subscribe to Topic: Before a consumer subscribes to a topic, the topic must have been created, otherwise the subscription will fail. Consumers under the same consumer group will evenly distribute the data. +3. Consume Data: Only by explicitly subscribing to a specific topic will you receive data from that topic. +4. Unsubscribe: When a consumer is closed, it will exit the corresponding consumer group and cancel all existing subscriptions. + + +## 2 Detailed Steps +This section is used to illustrate the core development process and does not demonstrate all parameters and interfaces. For a comprehensive understanding of all features and parameters, please refer to: [Java Native API](./Programming-Java-Native-API.md#Java Native API) + + +### 2.1 Create a Maven project +Create a Maven project and import the following dependencies(JDK >= 1.8, Maven >= 3.6) + +```xml + + + org.apache.iotdb + iotdb-session + + ${project.version} + + +``` + +### 2.2 Code Example +#### 2.2.1 Topic operations +```java +import java.util.Optional; +import java.util.Properties; +import java.util.Set; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.apache.iotdb.rpc.subscription.config.TopicConstant; +import org.apache.iotdb.session.subscription.SubscriptionSession; +import org.apache.iotdb.session.subscription.model.Topic; + +public class DataConsumerExample { + + public static void main(String[] args) throws IoTDBConnectionException, StatementExecutionException { + try (SubscriptionSession session = new SubscriptionSession("127.0.0.1", 6667)) { + // 1. open session + session.open(); + + // 2. create a topic of all data + Properties sessionConfig = new Properties(); + sessionConfig.put(TopicConstant.PATH_KEY, "root.**"); + + session.createTopic("allData", sessionConfig); + + // 3. show all topics + Set topics = session.getTopics(); + System.out.println(topics); + + // 4. show a specific topic + Optional allData = session.getTopic("allData"); + System.out.println(allData.get()); + } + } +} +``` +#### 2.2.2 Data Consume + +##### Scenario-1: Subscribing to newly added real-time data in IoTDB (for scenarios such as dashboard or configuration display) + +```java +import java.io.IOException; +import java.util.List; +import java.util.Properties; +import org.apache.iotdb.rpc.subscription.config.ConsumerConstant; +import org.apache.iotdb.rpc.subscription.config.TopicConstant; +import org.apache.iotdb.session.subscription.consumer.SubscriptionPullConsumer; +import org.apache.iotdb.session.subscription.payload.SubscriptionMessage; +import org.apache.iotdb.session.subscription.payload.SubscriptionMessageType; +import org.apache.iotdb.session.subscription.payload.SubscriptionSessionDataSet; +import org.apache.tsfile.read.common.RowRecord; + +public class DataConsumerExample { + + public static void main(String[] args) throws IOException { + + // 5. create a pull consumer, the subscription is automatically cancelled when the logic in the try resources is completed + Properties consumerConfig = new Properties(); + consumerConfig.put(ConsumerConstant.CONSUMER_ID_KEY, "c1"); + consumerConfig.put(ConsumerConstant.CONSUMER_GROUP_ID_KEY, "cg1"); + consumerConfig.put(ConsumerConstant.CONSUME_LISTENER_KEY, TopicConstant.FORMAT_SESSION_DATA_SETS_HANDLER_VALUE); + try (SubscriptionPullConsumer pullConsumer = new SubscriptionPullConsumer(consumerConfig)) { + pullConsumer.open(); + pullConsumer.subscribe("topic_all"); + while (true) { + List messages = pullConsumer.poll(10000); + for (final SubscriptionMessage message : messages) { + final short messageType = message.getMessageType(); + if (SubscriptionMessageType.isValidatedMessageType(messageType)) { + for (final SubscriptionSessionDataSet dataSet : message.getSessionDataSetsHandler()) { + while (dataSet.hasNext()) { + final RowRecord record = dataSet.next(); + System.out.println(record); + } + } + } + } + } + } + } +} + + +``` +##### Scenario-2: Subscribing to newly added TsFiles (for scenarios such as regular data backup) + +Prerequisite: The format of the topic to be consumed must be of the TsfileHandler type. For example:`create topic topic_all_tsfile with ('path'='root.**','format'='TsFileHandler')` + +```java +import java.io.IOException; +import java.util.List; +import java.util.Properties; +import org.apache.iotdb.rpc.subscription.config.ConsumerConstant; +import org.apache.iotdb.rpc.subscription.config.TopicConstant; +import org.apache.iotdb.session.subscription.consumer.SubscriptionPullConsumer; +import org.apache.iotdb.session.subscription.payload.SubscriptionMessage; + + +public class DataConsumerExample { + + public static void main(String[] args) throws IOException { + // 1. create a pull consumer, the subscription is automatically cancelled when the logic in the try resources is completed + Properties consumerConfig = new Properties(); + consumerConfig.put(ConsumerConstant.CONSUMER_ID_KEY, "c1"); + consumerConfig.put(ConsumerConstant.CONSUMER_GROUP_ID_KEY, "cg1"); + // 2. Specify the consumption type as the tsfile type + consumerConfig.put(TopicConstant.FORMAT_KEY, TopicConstant.FORMAT_TS_FILE_HANDLER_VALUE); + consumerConfig.put(ConsumerConstant.FILE_SAVE_DIR_KEY, "/Users/iotdb/Downloads"); + try (SubscriptionPullConsumer pullConsumer = new SubscriptionPullConsumer(consumerConfig)) { + pullConsumer.open(); + pullConsumer.subscribe("topic_all_tsfile"); + while (true) { + List messages = pullConsumer.poll(10000); + for (final SubscriptionMessage message : messages) { + message.getTsFileHandler().copyFile("/Users/iotdb/Downloads/1.tsfile"); + } + } + } + } +} +``` + + + + +## 3 Java Native API Description + +### 3.1 Parameter List +The consumer-related parameters can be set through the Properties parameter object. The specific parameters are as follows: + +#### SubscriptionConsumer + + +| **Parameter** | **required or optional with default** | **Parameter Meaning** | +| :---------------------- | :----------------------------------------------------------- | :----------------------------------------------------------- | +| host | optional: 127.0.0.1 | `String`: The RPC host of a DataNode in IoTDB | +| port | optional: 6667 | `Integer`: The RPC port of a DataNode in IoTDB | +| node-urls | optional: 127.0.0.1:6667 | `List`: The RPC addresses of all DataNodes in IoTDB, which can be multiple; either host:port or node-urls can be filled. If both host:port and node-urls are filled, the **union** of host:port and node-urls will be taken to form a new node-urls for application | +| username | optional: root | `String`: The username of the DataNode in IoTDB | +| password | optional: root | `String`: The password of the DataNode in IoTDB | +| groupId | optional | `String`: consumer group id,if not specified, it will be randomly assigned (a new consumer group),ensuring that the consumer group id of different consumer groups are all different | +| consumerId | optional | `String`: consumer client id,if not specified, it will be randomly assigned,ensuring that each consumer client id in the same consumer group is different | +| heartbeatIntervalMs | optional: 30000 (min: 1000) | `Long`: The interval at which the consumer sends periodic heartbeat requests to the IoTDB DataNode | +| endpointsSyncIntervalMs | optional: 120000 (min: 5000) | `Long`: The interval at which the consumer detects the expansion or contraction of IoTDB cluster nodes and adjusts the subscription connection | +| fileSaveDir | optional: Paths.get(System.getProperty("user.dir"), "iotdb-subscription").toString() | `String`: The temporary directory path where the consumer stores the subscribed TsFile files | +| fileSaveFsync | optional: false | `Boolean`: Whether the consumer actively calls fsync during the subscription of TsFiles | + +Special configurations in `SubscriptionPushConsumer` : + +| **Parameter** | **required or optional with default** | **Parameter Meaning** | +| :----------------- | :------------------------------------ | :----------------------------------------------------------------------------- | +| ackStrategy | optional: `ACKStrategy.AFTER_CONSUME` | The acknowledgment mechanism for consumption progress includes the following options: `ACKStrategy.BEFORE_CONSUME`(the consumer submits the consumption progress immediately upon receiving the data, before `onReceive` )`ACKStrategy.AFTER_CONSUME`(the consumer submits the consumption progress after consuming the data, after `onReceive` ) | +| consumeListener | optional | The callback function for consuming data, which needs to implement the `ConsumeListener` interface, defining the processing logic for consuming `SessionDataSetsHandler` and `TsFileHandler` formatted data | +| autoPollIntervalMs | optional: 5000 (min: 500) | Long: The time interval at which the consumer automatically pulls data, in **ms** | +| autoPollTimeoutMs | optional: 10000 (min: 1000) | Long: The timeout duration for the consumer to pull data each time, in **ms** | + +Special configurations in `SubscriptionPullConsumer` : + +| **Parameter** | **required or optional with default** | **Parameter Meaning** | +| :-------------------------------------------- | :--------------------------------- | :----------------------------------------------------------- | +| autoCommit | optional: true | Boolean: Whether to automatically commit the consumption progress. If this parameter is set to false, the `commit` method needs to be called manually to submit the consumption progress | +| autoCommitInterval | optional: 5000 (min: 500) | Long: The time interval for automatically committing the consumption progress, in **ms** .This parameter only takes effect when the `autoCommit` parameter is set to true | + + +### 3.2 Function List +#### Data Sync +##### SubscriptionPullConsumer + +| **Function name** | **Description** | **Parameter** | +|-------------------------------------|--------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `open()` | Opens the consumer connection and starts message consumption. If `autoCommit` is enabled, it will start the automatic commit worker. | None | +| `close()` | Closes the consumer connection. If `autoCommit` is enabled, it will commit all uncommitted messages before closing. | None | +| `poll(final Duration timeout)` | Pulls messages with a specified timeout. | `timeout` : The timeout duration. | +| `poll(final long timeoutMs)` | Pulls messages with a specified timeout in milliseconds. | `timeoutMs` : The timeout duration in milliseconds. | +| `poll(final Set topicNames, final Duration timeout)` | Pulls messages from specified topics with a specified timeout. | `topicNames` : The set of topics to pull messages from. `timeout`: The timeout duration。 | +| `poll(final Set topicNames, final long timeoutMs)` | Pulls messages from specified topics with a specified timeout in milliseconds. | `topicNames` : The set of topics to pull messages from.`timeoutMs`: The timeout duration in milliseconds. | +| `commitSync(final SubscriptionMessage message)` | Synchronously commits a single message. | `message` : The message object to be committed. | +| `commitSync(final Iterable messages)` | Synchronously commits multiple messages. | `messages` : The collection of message objects to be committed. | +| `commitAsync(final SubscriptionMessage message)` | Asynchronously commits a single message. | `message` : The message object to be committed. | +| `commitAsync(final Iterable messages)` | Asynchronously commits multiple messages. | `messages` : The collection of message objects to be committed. | +| `commitAsync(final SubscriptionMessage message, final AsyncCommitCallback callback)` | Asynchronously commits a single message with a specified callback. | `message` : The message object to be committed. `callback` : The callback function to be executed after asynchronous commit. | +| `commitAsync(final Iterable messages, final AsyncCommitCallback callback)` | Asynchronously commits multiple messages with a specified callback. | `messages` : The collection of message objects to be committed.`callback` : The callback function to be executed after asynchronous commit. | + +##### SubscriptionPushConsumer + +| **Function name** | **Description** | **Parameter** | +|-------------------------------------|----------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `open()` | Opens the consumer connection, starts message consumption, and submits the automatic polling worker. | None | +| `close()` | Closes the consumer connection and stops message consumption. | None | +| `toString()` | Returns the core configuration information of the consumer object. | None | +| `coreReportMessage()` | Obtains the key-value representation of the consumer's core configuration. | None | +| `allReportMessage()` | Obtains the key-value representation of all the consumer's configurations. | None | +| `buildPushConsumer()` | Builds a `SubscriptionPushConsumer` instance through the `Builder` | None | +| `ackStrategy(final AckStrategy ackStrategy)` | Configures the message acknowledgment strategy for the consumer. | `ackStrategy`: The specified message acknowledgment strategy. | +| `consumeListener(final ConsumeListener consumeListener)` |Configures the message consumption logic for the consumer. | `consumeListener`: The processing logic when the consumer receives messages. | +| `autoPollIntervalMs(final long autoPollIntervalMs)` | Configures the interval for automatic polling. | `autoPollIntervalMs` : The interval for automatic polling, in milliseconds. | +| `autoPollTimeoutMs(final long autoPollTimeoutMs)` | Configures the timeout for automatic polling.间。 | `autoPollTimeoutMs`: The timeout for automatic polling, in milliseconds. | \ No newline at end of file diff --git a/src/UserGuide/V2.0.1/Tree/API/Programming-Java-Native-API.md b/src/UserGuide/V2.0.1/Tree/API/Programming-Java-Native-API.md index 08c0cb1f6..e4c04fa89 100644 --- a/src/UserGuide/V2.0.1/Tree/API/Programming-Java-Native-API.md +++ b/src/UserGuide/V2.0.1/Tree/API/Programming-Java-Native-API.md @@ -1,842 +1,468 @@ -# Java Native API +# Session Native API + +In the native API of IoTDB, the `Session` is the core interface for interacting with the database. It integrates a rich set of methods that support data writing, querying, and metadata operations. By instantiating a `Session`, you can establish a connection to the IoTDB server and perform various database operations within the environment constructed by this connection. The `Session` is not thread-safe and should not be called simultaneously by multiple threads. -## Installation +`SessionPool` is a connection pool for `Session`, and it is recommended to use `SessionPool` for programming. In scenarios with multi-threaded concurrency, `SessionPool` can manage and allocate connection resources effectively, thereby improving system performance and resource utilization efficiency. -### Dependencies +## 1 Overview of Steps +1. Create a Connection Pool Instance: Initialize a SessionPool object to manage multiple Session instances. +2. Perform Operations: Directly obtain a Session instance from the SessionPool and execute database operations, without the need to open and close connections each time. +3. Close Connection Pool Resources: When database operations are no longer needed, close the SessionPool to release all related resources. -* JDK >= 1.8 -* Maven >= 3.6 +## 2 Detailed Steps +This section provides an overview of the core development process and does not demonstrate all parameters and interfaces. For a complete list of functionalities and parameters, please refer to:[Java Native API](./Programming-Java-Native-API.md#3-native-interface-description) or check the: [Source Code](https://github.com/apache/iotdb/tree/master/example/session/src/main/java/org/apache/iotdb) -### Using IoTDB Java Native API with Maven +### 2.1 Create a Maven Project +Create a Maven project and add the following dependencies to the pom.xml file (JDK >= 1.8, Maven >= 3.6): ```xml org.apache.iotdb iotdb-session - 1.0.0 + + ${project.version} ``` +### 2.2 Creating a Connection Pool Instance -## Syntax Convention - -- **IoTDB-SQL interface:** The input SQL parameter needs to conform to the [syntax conventions](../Reference/Syntax-Rule.md#Literal-Values) and be escaped for JAVA strings. For example, you need to add a backslash before the double-quotes. (That is: after JAVA escaping, it is consistent with the SQL statement executed on the command line.) -- **Other interfaces:** - - The node names in path or path prefix as parameter: The node names which should be escaped by backticks (`) in the SQL statement, escaping is required here. - - Identifiers (such as template names) as parameters: The identifiers which should be escaped by backticks (`) in the SQL statement, and escaping is not required here. -- **Code example for syntax convention could be found at:** `example/session/src/main/java/org/apache/iotdb/SyntaxConventionRelatedExample.java` - -## Native APIs - -Here we show the commonly used interfaces and their parameters in the Native API: - -### Session Management - -* Initialize a Session - -``` java -// use default configuration -session = new Session.Builder.build(); - -// initialize with a single node -session = - new Session.Builder() - .host(String host) - .port(int port) - .build(); - -// initialize with multiple nodes -session = - new Session.Builder() - .nodeUrls(List nodeUrls) - .build(); - -// other configurations -session = - new Session.Builder() - .fetchSize(int fetchSize) - .username(String username) - .password(String password) - .thriftDefaultBufferSize(int thriftDefaultBufferSize) - .thriftMaxFrameSize(int thriftMaxFrameSize) - .enableRedirection(boolean enableRedirection) - .version(Version version) - .build(); -``` - -Version represents the SQL semantic version used by the client, which is used to be compatible with the SQL semantics of 0.12 when upgrading 0.13. The possible values are: `V_0_12`, `V_0_13`, `V_1_0`, etc. - - -* Open a Session - -``` java -void open() -``` - -* Open a session, with a parameter to specify whether to enable RPC compression - -``` java -void open(boolean enableRPCCompression) -``` - -Notice: this RPC compression status of client must comply with that of IoTDB server - -* Close a Session - -``` java -void close() -``` - -* SessionPool - -We provide a connection pool (`SessionPool) for Native API. -Using the interface, you need to define the pool size. - -If you can not get a session connection in 60 seconds, there is a warning log but the program will hang. - -If a session has finished an operation, it will be put back to the pool automatically. -If a session connection is broken, the session will be removed automatically and the pool will try -to create a new session and redo the operation. -You can also specify an url list of multiple reachable nodes when creating a SessionPool, just as you would when creating a Session. To ensure high availability of clients in distributed cluster. - -For query operations: - -1. When using SessionPool to query data, the result set is `SessionDataSetWrapper`; -2. Given a `SessionDataSetWrapper`, if you have not scanned all the data in it and stop to use it, -you have to call `SessionPool.closeResultSet(wrapper)` manually; -3. When you call `hasNext()` and `next()` of a `SessionDataSetWrapper` and there is an exception, then -you have to call `SessionPool.closeResultSet(wrapper)` manually; -4. You can call `getColumnNames()` of `SessionDataSetWrapper` to get the column names of query result; - -Examples: ```session/src/test/java/org/apache/iotdb/session/pool/SessionPoolTest.java``` - -Or `example/session/src/main/java/org/apache/iotdb/SessionPoolExample.java` - - -### Database & Timeseries Management API - -#### Database Management - -* CREATE DATABASE - -``` java -void setStorageGroup(String storageGroupId) -``` - -* Delete one or several databases - -``` java -void deleteStorageGroup(String storageGroup) -void deleteStorageGroups(List storageGroups) -``` - -#### Timeseries Management - -* Create one or multiple timeseries - -``` java -void createTimeseries(String path, TSDataType dataType, - TSEncoding encoding, CompressionType compressor, Map props, - Map tags, Map attributes, String measurementAlias) - -void createMultiTimeseries(List paths, List dataTypes, - List encodings, List compressors, - List> propsList, List> tagsList, - List> attributesList, List measurementAliasList) -``` - -* Create aligned timeseries -``` -void createAlignedTimeseries(String prefixPath, List measurements, - List dataTypes, List encodings, - List compressors, List measurementAliasList); -``` - -Attention: Alias of measurements are **not supported** currently. - -* Delete one or several timeseries - -``` java -void deleteTimeseries(String path) -void deleteTimeseries(List paths) -``` - -* Check whether the specific timeseries exists. - -``` java -boolean checkTimeseriesExists(String path) -``` - -#### Schema Template - - -Create a schema template for massive identical devices will help to improve memory performance. You can use Template, InternalNode and MeasurementNode to depict the structure of the template, and use belowed interface to create it inside session. - -``` java -public void createSchemaTemplate(Template template); - -Class Template { - private String name; - private boolean directShareTime; - Map children; - public Template(String name, boolean isShareTime); - - public void addToTemplate(Node node); - public void deleteFromTemplate(String name); - public void setShareTime(boolean shareTime); -} - -Abstract Class Node { - private String name; - public void addChild(Node node); - public void deleteChild(Node node); -} - -Class MeasurementNode extends Node { - TSDataType dataType; - TSEncoding encoding; - CompressionType compressor; - public MeasurementNode(String name, - TSDataType dataType, - TSEncoding encoding, - CompressionType compressor); -} -``` - -We strongly suggest you implement templates only with flat-measurement (like object 'flatTemplate' in belowed snippet), since tree-structured template may not be a long-term supported feature in further version of IoTDB. - -A snippet of using above Method and Class: - -``` java -MeasurementNode nodeX = new MeasurementNode("x", TSDataType.FLOAT, TSEncoding.RLE, CompressionType.SNAPPY); -MeasurementNode nodeY = new MeasurementNode("y", TSDataType.FLOAT, TSEncoding.RLE, CompressionType.SNAPPY); -MeasurementNode nodeSpeed = new MeasurementNode("speed", TSDataType.DOUBLE, TSEncoding.GORILLA, CompressionType.SNAPPY); - -// This is the template we suggest to implement -Template flatTemplate = new Template("flatTemplate"); -template.addToTemplate(nodeX); -template.addToTemplate(nodeY); -template.addToTemplate(nodeSpeed); - -createSchemaTemplate(flatTemplate); -``` - -You can query measurement inside templates with these APIS: ```java -// Return the amount of measurements inside a template -public int countMeasurementsInTemplate(String templateName); - -// Return true if path points to a measurement, otherwise returne false -public boolean isMeasurementInTemplate(String templateName, String path); - -// Return true if path exists in template, otherwise return false -public boolean isPathExistInTemplate(String templateName, String path); - -// Return all measurements paths inside template -public List showMeasurementsInTemplate(String templateName); - -// Return all measurements paths under the designated patter inside template -public List showMeasurementsInTemplate(String templateName, String pattern); -``` - -To implement schema template, you can set the measurement template named 'templateName' at path 'prefixPath'. - -**Please notice that, we strongly recommend not setting templates on the nodes above the database to accommodate future updates and collaboration between modules.** - -``` java -void setSchemaTemplate(String templateName, String prefixPath) -``` - -Before setting template, you should firstly create the template using - -``` java -void createSchemaTemplate(Template template) -``` - -After setting template to a certain path, you can use the template to create timeseries on given device paths through the following interface, or you can write data directly to trigger timeseries auto creation using schema template under target devices. - -``` java -void createTimeseriesUsingSchemaTemplate(List devicePathList) -``` - -After setting template to a certain path, you can query for info about template using belowed interface in session: - -``` java -/** @return All template names. */ -public List showAllTemplates(); - -/** @return All paths have been set to designated template. */ -public List showPathsTemplateSetOn(String templateName); - -/** @return All paths are using designated template. */ -public List showPathsTemplateUsingOn(String templateName) -``` - -If you are ready to get rid of schema template, you can drop it with belowed interface. Make sure the template to drop has been unset from MTree. - -``` java -void unsetSchemaTemplate(String prefixPath, String templateName); -public void dropSchemaTemplate(String templateName); -``` - -Unset the measurement template named 'templateName' from path 'prefixPath'. When you issue this interface, you should assure that there is a template named 'templateName' set at the path 'prefixPath'. - -Attention: Unsetting the template named 'templateName' from node at path 'prefixPath' or descendant nodes which have already inserted records using template is **not supported**. - - -### Data Manipulation Interface (DML Interface) - -### Data Insert API - -It is recommended to use insertTablet to help improve write efficiency. - -* Insert a Tablet,which is multiple rows of a device, each row has the same measurements - * **Better Write Performance** - * **Support batch write** - * **Support null values**: fill the null value with any value, and then mark the null value via BitMap - -``` java -void insertTablet(Tablet tablet) - -public class Tablet { - /** deviceId of this tablet */ - public String prefixPath; - /** the list of measurement schemas for creating the tablet */ - private List schemas; - /** timestamps in this tablet */ - public long[] timestamps; - /** each object is a primitive type array, which represents values of one measurement */ - public Object[] values; - /** each bitmap represents the existence of each value in the current column. */ - public BitMap[] bitMaps; - /** the number of rows to include in this tablet */ - public int rowSize; - /** the maximum number of rows for this tablet */ - private int maxRowNumber; - /** whether this tablet store data of aligned timeseries or not */ - private boolean isAligned; -} -``` - -* Insert multiple Tablets - -``` java -void insertTablets(Map tablet) -``` - -* Insert a Record, which contains multiple measurement value of a device at a timestamp. This method is equivalent to providing a common interface for multiple data types of values. Later, the value can be cast to the original type through TSDataType. - - The correspondence between the Object type and the TSDataType type is shown in the following table. - - | TSDataType | Object | - |------------|--------------| - | BOOLEAN | Boolean | - | INT32 | Integer | - | DATE | LocalDate | - | INT64 | Long | - | TIMESTAMP | Long | - | FLOAT | Float | - | DOUBLE | Double | - | TEXT | String, Binary | - | STRING | String, Binary | - | BLOB | Binary | -``` java -void insertRecord(String deviceId, long time, List measurements, - List types, List values) -``` - -* Insert multiple Records - -``` java -void insertRecords(List deviceIds, List times, - List> measurementsList, List> typesList, - List> valuesList) -``` -* Insert multiple Records that belong to the same device. - With type info the server has no need to do type inference, which leads a better performance - -``` java -void insertRecordsOfOneDevice(String deviceId, List times, - List> measurementsList, List> typesList, - List> valuesList) -``` - -#### Insert with type inference - -When the data is of String type, we can use the following interface to perform type inference based on the value of the value itself. For example, if value is "true" , it can be automatically inferred to be a boolean type. If value is "3.2" , it can be automatically inferred as a flout type. Without type information, server has to do type inference, which may cost some time. - -* Insert a Record, which contains multiple measurement value of a device at a timestamp - -``` java -void insertRecord(String prefixPath, long time, List measurements, List values) -``` - -* Insert multiple Records - -``` java -void insertRecords(List deviceIds, List times, - List> measurementsList, List> valuesList) -``` - -* Insert multiple Records that belong to the same device. - -``` java -void insertStringRecordsOfOneDevice(String deviceId, List times, - List> measurementsList, List> valuesList) -``` - -#### Insert of Aligned Timeseries - -The Insert of aligned timeseries uses interfaces like insertAlignedXXX, and others are similar to the above interfaces: - -* insertAlignedRecord -* insertAlignedRecords -* insertAlignedRecordsOfOneDevice -* insertAlignedStringRecordsOfOneDevice -* insertAlignedTablet -* insertAlignedTablets - -### Data Delete API - -* Delete data before or equal to a timestamp of one or several timeseries - -``` java -void deleteData(String path, long time) -void deleteData(List paths, long time) -``` - -### Data Query API - -* Time-series raw data query with time range: - - The specified query time range is a left-closed right-open interval, including the start time but excluding the end time. - -``` java -SessionDataSet executeRawDataQuery(List paths, long startTime, long endTime); -``` - -* Last query: - - Query the last data, whose timestamp is greater than or equal LastTime. - ``` java - SessionDataSet executeLastDataQuery(List paths, long LastTime); - ``` - - Query the latest point of the specified series of single device quickly, and support redirection; - If you are sure that the query path is valid, set 'isLegalPathNodes' to true to avoid performance penalties from path verification. - ``` java - SessionDataSet executeLastDataQueryForOneDevice( - String db, String device, List sensors, boolean isLegalPathNodes); - ``` - -* Aggregation query: - - Support specified query time range: The specified query time range is a left-closed right-open interval, including the start time but not the end time. - - Support GROUP BY TIME. - -``` java -SessionDataSet executeAggregationQuery(List paths, List aggregations); - -SessionDataSet executeAggregationQuery( - List paths, List aggregations, long startTime, long endTime); - -SessionDataSet executeAggregationQuery( - List paths, - List aggregations, - long startTime, - long endTime, - long interval); - -SessionDataSet executeAggregationQuery( - List paths, - List aggregations, - long startTime, - long endTime, - long interval, - long slidingStep); -``` - -* Execute query statement - -``` java -SessionDataSet executeQueryStatement(String sql) -``` - -### Data Subscription - -#### 1 Topic Management - -The `SubscriptionSession` class in the IoTDB subscription client provides interfaces for topic management. The status changes of topics are illustrated in the diagram below: - -
- -
- -##### 1.1 Create Topic - -```Java - void createTopicIfNotExists(String topicName, Properties properties) throws Exception; -``` - -Example: - -```Java -try (final SubscriptionSession session = new SubscriptionSession(host, port)) { - session.open(); - final Properties config = new Properties(); - config.put(TopicConstant.PATH_KEY, "root.db.**"); - session.createTopic(topicName, config); +import java.util.ArrayList; +import java.util.List; +import org.apache.iotdb.session.pool.SessionPool; + +public class IoTDBSessionPoolExample { + private static SessionPool sessionPool; + + public static void main(String[] args) { + // Using nodeUrls ensures that when one node goes down, other nodes are automatically connected to retry + List nodeUrls = new ArrayList<>(); + nodeUrls.add("127.0.0.1:6667"); + nodeUrls.add("127.0.0.1:6668"); + sessionPool = + new SessionPool.Builder() + .nodeUrls(nodeUrls) + .user("root") + .password("root") + .maxSize(3) + .build(); + } } ``` -##### 1.2 Delete Topic - -```Java -void dropTopicIfExists(String topicName) throws Exception; -``` - -##### 1.3 View Topic - -```Java -// Get all topics -Set getTopics() throws Exception; - -// Get a specific topic -Optional getTopic(String topicName) throws Exception; -``` - -#### 2 Check Subscription Status -The `SubscriptionSession` class in the IoTDB subscription client provides interfaces to check the subscription status: - -```Java -Set getSubscriptions() throws Exception; -Set getSubscriptions(final String topicName) throws Exception; -``` - -#### 3 Create Consumer - -When creating a consumer using the JAVA native interface, you need to specify the parameters applied to the consumer. - -For both `SubscriptionPullConsumer` and `SubscriptionPushConsumer`, the following common configurations are available: - +### 2.3 Performing Database Operations -| key | **required or optional with default** | description | -| :---------------------- | :----------------------------------------------------------- | :----------------------------------------------------------- | -| host | optional: 127.0.0.1 | `String`: The RPC host of a certain DataNode in IoTDB | -| port | optional: 6667 | Integer: The RPC port of a certain DataNode in IoTDB | -| node-urls | optional: 127.0.0.1:6667 | `List`: The RPC addresses of all DataNodes in IoTDB, can be multiple; either host:port or node-urls can be filled in. If both host:port and node-urls are filled in, the union of host:port and node-urls will be used to form a new node-urls application | -| username | optional: root | `String`: The username of a DataNode in IoTDB | -| password | optional: root | `String`: The password of a DataNode in IoTDB | -| groupId | optional | `String`: consumer group id, if not specified, a new consumer group will be randomly assigned, ensuring that different consumer groups have different consumer group ids | -| consumerId | optional | `String`: consumer client id, if not specified, it will be randomly assigned, ensuring that each consumer client id in the same consumer group is unique | -| heartbeatIntervalMs | optional: 30000 (min: 1000) | `Long`: The interval at which the consumer sends heartbeat requests to the IoTDB DataNode | -| endpointsSyncIntervalMs | optional: 120000 (min: 5000) | `Long`: The interval at which the consumer detects the expansion and contraction of IoTDB cluster nodes and adjusts the subscription connection | -| fileSaveDir | optional: Paths.get(System.getProperty("user.dir"), "iotdb-subscription").toString() | `String`: The temporary directory path where the TsFile files subscribed by the consumer are stored | -| fileSaveFsync | optional: false | `Boolean`: Whether the consumer actively calls fsync during the subscription of TsFile | +#### 2.3.1 Data Insertion +In industrial scenarios, data insertion can be categorized into the following types: inserting multiple rows of data, and inserting multiple rows of data for a single device. Below, we introduce the insertion interfaces for different scenarios. -##### 3.1 SubscriptionPushConsumer +##### Multi-Row Data Insertion Interface +Interface Description: Supports inserting multiple rows of data at once, where each row corresponds to multiple measurement values for a device at a specific timestamp. -The following are special configurations for `SubscriptionPushConsumer`: +Interface List: -| key | **required or optional with default** | description | -| :----------------- | :------------------------------------ | :----------------------------------------------------------- | -| ackStrategy | optional: `ACKStrategy.AFTER_CONSUME` | Consumption progress confirmation mechanism includes the following options: `ACKStrategy.BEFORE_CONSUME` (submit consumption progress immediately when the consumer receives data, before `onReceive`) `ACKStrategy.AFTER_CONSUME` (submit consumption progress after the consumer has consumed the data, after `onReceive`) | -| consumeListener | optional | Consumption data callback function, need to implement the `ConsumeListener` interface, define the consumption logic of `SessionDataSetsHandler` and `TsFileHandler` form data| -| autoPollIntervalMs | optional: 5000 (min: 500) | Long: The interval at which the consumer automatically pulls data, in ms | -| autoPollTimeoutMs | optional: 10000 (min: 1000) | Long: The timeout time for the consumer to pull data each time, in ms | +| **Interface Name** | **Function Description** | +|------------------------------------------------------------------------------------------------------------------------|-----------------------| +| `insertRecords(List deviceIds, List times, List> measurementsList, List> typesList, List> valuesList)` | Inserts multiple rows of data, suitable for scenarios where measurements are independently collected. | -Among them, the ConsumerListener interface is defined as follows: +Code Example: - -```Java -@FunctionInterface -interface ConsumeListener { - default ConsumeResult onReceive(Message message) { - return ConsumeResult.SUCCESS; +```java +import java.util.ArrayList; +import java.util.List; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.apache.iotdb.session.pool.SessionPool; +import org.apache.tsfile.enums.TSDataType; + +public class SessionPoolExample { + private static SessionPool sessionPool; + public static void main(String[] args) throws IoTDBConnectionException, StatementExecutionException { + // 1. init SessionPool + constructSessionPool(); + // 2. execute insert data + insertRecordsExample(); + // 3. close SessionPool + closeSessionPool(); } -} - -enum ConsumeResult { - SUCCESS, - FAILURE, -} -``` - -##### 3.2 SubscriptionPullConsumer - -The following are special configurations for `SubscriptionPullConsumer` : - -| key | **required or optional with default** | description | -| :----------------- | :------------------------------------ | :----------------------------------------------------------- | -| autoCommit | optional: true | Boolean: Whether to automatically commit consumption progress. If this parameter is set to false, the commit method must be called to manually `commit` consumption progress. | -| autoCommitInterval | optional: 5000 (min: 500) | Long: The interval at which consumption progress is automatically committed, in milliseconds. This only takes effect when the autoCommit parameter is true. - | - -After creating a consumer, you need to manually call the consumer's open method: - - -```Java -void open() throws Exception; -``` - -At this point, the IoTDB subscription client will verify the correctness of the consumer's configuration. After a successful verification, the consumer will join the corresponding consumer group. That is, only after opening the consumer can you use the returned consumer object to subscribe to topics, consume data, and perform other operations. - -#### 4 Subscribe to Topics - -Both `SubscriptionPushConsumer` and `SubscriptionPullConsumer` provide the following JAVA native interfaces for subscribing to topics: - -```Java -// Subscribe to topics -void subscribe(String topic) throws Exception; -void subscribe(List topics) throws Exception; -``` - -- Before a consumer subscribes to a topic, the topic must have been created, otherwise, the subscription will fail. - -- If a consumer subscribes to a topic that it has already subscribed to, no error will occur. - -- If there are other consumers in the same consumer group that have subscribed to the same topics, the consumer will reuse the corresponding consumption progress. - - -#### 5 Consume Data - -For both push and pull mode consumers: - - -- Only after explicitly subscribing to a topic will the consumer receive data for that topic. - -- If no topics are subscribed to after creation, the consumer will not be able to consume any data, even if other consumers in the same consumer group have subscribed to some topics. - -##### 5.1 SubscriptionPushConsumer - -After `SubscriptionPushConsumer` subscribes to topics, there is no need to manually pull data. - -The data consumption logic is within the `consumeListener` configuration specified when creating `SubscriptionPushConsumer`. - -##### 5.2 SubscriptionPullConsumer - -After SubscriptionPullConsumer subscribes to topics, it needs to actively call the poll method to pull data: -```Java -List poll(final Duration timeout) throws Exception; -List poll(final long timeoutMs) throws Exception; -List poll(final Set topicNames, final Duration timeout) throws Exception; -List poll(final Set topicNames, final long timeoutMs) throws Exception; -``` - -In the poll method, you can specify the topic names to be pulled (if not specified, it defaults to pulling all topics that the consumer has subscribed to) and the timeout period. - - -When the SubscriptionPullConsumer is configured with the autoCommit parameter set to false, it is necessary to manually call the commitSync and commitAsync methods to synchronously or asynchronously commit the consumption progress of a batch of data: - - -```Java -void commitSync(final SubscriptionMessage message) throws Exception; -void commitSync(final Iterable messages) throws Exception; - -CompletableFuture commitAsync(final SubscriptionMessage message); -CompletableFuture commitAsync(final Iterable messages); -void commitAsync(final SubscriptionMessage message, final AsyncCommitCallback callback); -void commitAsync(final Iterable messages, final AsyncCommitCallback callback); -``` - -The AsyncCommitCallback class is defined as follows: - -```Java -public interface AsyncCommitCallback { - default void onComplete() { - // Do nothing - } + private static void constructSessionPool() { + // Using nodeUrls ensures that when one node goes down, other nodes are automatically connected to retry + List nodeUrls = new ArrayList<>(); + nodeUrls.add("127.0.0.1:6667"); + nodeUrls.add("127.0.0.1:6668"); + sessionPool = + new SessionPool.Builder() + .nodeUrls(nodeUrls) + .user("root") + .password("root") + .maxSize(3) + .build(); + } - default void onFailure(final Throwable e) { - // Do nothing - } + public static void insertRecordsExample() throws IoTDBConnectionException, StatementExecutionException { + String deviceId = "root.sg1.d1"; + List measurements = new ArrayList<>(); + measurements.add("s1"); + measurements.add("s2"); + measurements.add("s3"); + List deviceIds = new ArrayList<>(); + List> measurementsList = new ArrayList<>(); + List> valuesList = new ArrayList<>(); + List timestamps = new ArrayList<>(); + List> typesList = new ArrayList<>(); + + for (long time = 0; time < 500; time++) { + List values = new ArrayList<>(); + List types = new ArrayList<>(); + values.add(1L); + values.add(2L); + values.add(3L); + types.add(TSDataType.INT64); + types.add(TSDataType.INT64); + types.add(TSDataType.INT64); + + deviceIds.add(deviceId); + measurementsList.add(measurements); + valuesList.add(values); + typesList.add(types); + timestamps.add(time); + if (time != 0 && time % 100 == 0) { + try { + sessionPool.insertRecords(deviceIds, timestamps, measurementsList, typesList, valuesList); + } catch (IoTDBConnectionException | StatementExecutionException e) { + // solve exception + } + deviceIds.clear(); + measurementsList.clear(); + valuesList.clear(); + typesList.clear(); + timestamps.clear(); + } + } + try { + sessionPool.insertRecords(deviceIds, timestamps, measurementsList, typesList, valuesList); + } catch (IoTDBConnectionException | StatementExecutionException e) { + // solve exception + } + } + + public static void closeSessionPool(){ + sessionPool.close(); + } } ``` +##### Single-Device Multi-Row Data Insertion Interface +Interface Description: Supports inserting multiple rows of data for a single device at once, where each row corresponds to multiple measurement values for a specific timestamp. -#### 6 Unsubscribe - -The `SubscriptionPushConsumer` and `SubscriptionPullConsumer` provide the following JAVA native interfaces for unsubscribing and closing the consumer: - -```Java -// Unsubscribe from topics -void unsubscribe(String topic) throws Exception; -void unsubscribe(List topics) throws Exception; - -// Close consumer -void close(); -``` - -- If a consumer unsubscribes from a topic that it has not subscribed to, no error will occur. -- When a consumer is closed, it will exit the corresponding consumer group and automatically unsubscribe from all topics it is currently subscribed to. -- Once a consumer is closed, its lifecycle ends, and it cannot be reopened to subscribe to and consume data again. - - -#### 7 Code Examples +Interface List: -##### 7.1 Single Pull Consumer Consuming SessionDataSetsHandler Format Data +| **Interface Name** | **Function Description** | +|-----------------------------------------------------------------------------------------|----------------------------| +| `insertTablet(Tablet tablet)` | Inserts multiple rows of data for a single device, suitable for scenarios where measurements are independently collected. | -```Java -// Create topics -try (final SubscriptionSession session = new SubscriptionSession(HOST, PORT)) { - session.open(); - final Properties config = new Properties(); - config.put(TopicConstant.PATH_KEY, "root.db.**"); - session.createTopic(TOPIC_1, config); -} +Code Example: -// Subscription: property-style ctor -final Properties config = new Properties(); -config.put(ConsumerConstant.CONSUMER_ID_KEY, "c1"); -config.put(ConsumerConstant.CONSUMER_GROUP_ID_KEY, "cg1"); - -final SubscriptionPullConsumer consumer1 = new SubscriptionPullConsumer(config); -consumer1.open(); -consumer1.subscribe(TOPIC_1); -while (true) { - LockSupport.parkNanos(SLEEP_NS); // wait some time - final List messages = consumer1.poll(POLL_TIMEOUT_MS); - for (final SubscriptionMessage message : messages) { - for (final SubscriptionSessionDataSet dataSet : message.getSessionDataSetsHandler()) { - System.out.println(dataSet.getColumnNames()); - System.out.println(dataSet.getColumnTypes()); - while (dataSet.hasNext()) { - System.out.println(dataSet.next()); - } +```java +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.apache.iotdb.session.pool.SessionPool; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.write.record.Tablet; +import org.apache.tsfile.write.schema.MeasurementSchema; + +public class SessionPoolExample { + private static SessionPool sessionPool; + public static void main(String[] args) throws IoTDBConnectionException, StatementExecutionException { + // 1. init SessionPool + constructSessionPool(); + // 2. execute insert data + insertTabletExample(); + // 3. close SessionPool + closeSessionPool(); + } + + private static void constructSessionPool() { + // Using nodeUrls ensures that when one node goes down, other nodes are automatically connected to retry + List nodeUrls = new ArrayList<>(); + nodeUrls.add("127.0.0.1:6667"); + nodeUrls.add("127.0.0.1:6668"); + sessionPool = + new SessionPool.Builder() + .nodeUrls(nodeUrls) + .user("root") + .password("root") + .maxSize(3) + .build(); } - } - // Auto commit -} - -// Show topics and subscriptions -try (final SubscriptionSession session = new SubscriptionSession(HOST, PORT)) { - session.open(); - session.getTopics().forEach((System.out::println)); - session.getSubscriptions().forEach((System.out::println)); -} - -consumer1.unsubscribe(TOPIC_1); -consumer1.close(); -``` -##### 7.2 Multiple Push Consumers Consuming TsFileHandler Format Data - -```Java -// Create topics -try (final SubscriptionSession subscriptionSession = new SubscriptionSession(HOST, PORT)) { - subscriptionSession.open(); - final Properties config = new Properties(); - config.put(TopicConstant.FORMAT_KEY, TopicConstant.FORMAT_TS_FILE_HANDLER_VALUE); - subscriptionSession.createTopic(TOPIC_2, config); -} - -final List threads = new ArrayList<>(); -for (int i = 0; i < 8; ++i) { - final int idx = i; - final Thread thread = - new Thread( - () -> { - // Subscription: builder-style ctor - try (final SubscriptionPushConsumer consumer2 = - new SubscriptionPushConsumer.Builder() - .consumerId("c" + idx) - .consumerGroupId("cg2") - .fileSaveDir(System.getProperty("java.io.tmpdir")) - .ackStrategy(AckStrategy.AFTER_CONSUME) - .consumeListener( - message -> { - doSomething(message.getTsFileHandler()); - return ConsumeResult.SUCCESS; - }) - .buildPushConsumer()) { - consumer2.open(); - consumer2.subscribe(TOPIC_2); - // block the consumer main thread - Thread.sleep(Long.MAX_VALUE); - } catch (final IOException | InterruptedException e) { - throw new RuntimeException(e); + private static void insertTabletExample() throws IoTDBConnectionException, StatementExecutionException { + /* + * A Tablet example: + * device1 + * time s1, s2, s3 + * 1, 1, 1, 1 + * 2, 2, 2, 2 + * 3, 3, 3, 3 + */ + // The schema of measurements of one device + // only measurementId and data type in MeasurementSchema take effects in Tablet + List schemaList = new ArrayList<>(); + schemaList.add(new MeasurementSchema("s1", TSDataType.INT64)); + schemaList.add(new MeasurementSchema("s2", TSDataType.INT64)); + schemaList.add(new MeasurementSchema("s3", TSDataType.INT64)); + + Tablet tablet = new Tablet("root.sg.d1", schemaList, 100); + + // Method 1 to add tablet data + long timestamp = System.currentTimeMillis(); + + Random random = new Random(); + for (long row = 0; row < 100; row++) { + int rowIndex = tablet.rowSize++; + tablet.addTimestamp(rowIndex, timestamp); + for (int s = 0; s < 3; s++) { + long value = random.nextLong(); + tablet.addValue(schemaList.get(s).getMeasurementId(), rowIndex, value); } - }); - thread.start(); - threads.add(thread); -} + if (tablet.rowSize == tablet.getMaxRowNumber()) { + sessionPool.insertTablet(tablet); + tablet.reset(); + } + timestamp++; + } + if (tablet.rowSize != 0) { + sessionPool.insertTablet(tablet); + tablet.reset(); + } + } -for (final Thread thread : threads) { - thread.join(); + public static void closeSessionPool(){ + sessionPool.close(); + } } ``` -### Other Modules (Execute SQL Directly) - -* Execute non query statement - -``` java -void executeNonQueryStatement(String sql) -``` - - -### Write Test Interface (to profile network cost) - -These methods **don't** insert data into database and server just return after accept the request. - -* Test the network and client cost of insertRecord - -``` java -void testInsertRecord(String deviceId, long time, List measurements, List values) - -void testInsertRecord(String deviceId, long time, List measurements, - List types, List values) -``` - -* Test the network and client cost of insertRecords - -``` java -void testInsertRecords(List deviceIds, List times, - List> measurementsList, List> valuesList) - -void testInsertRecords(List deviceIds, List times, - List> measurementsList, List> typesList - List> valuesList) -``` +#### 2.3.2 SQL Operations -* Test the network and client cost of insertTablet +SQL operations are divided into two categories: queries and non-queries. The corresponding interfaces are executeQuery and executeNonQuery. The difference between them is that the former executes specific query statements and returns a result set, while the latter performs insert, delete, and update operations and does not return a result set. -``` java -void testInsertTablet(Tablet tablet) -``` +```java +import java.util.ArrayList; +import java.util.List; +import org.apache.iotdb.isession.pool.SessionDataSetWrapper; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.apache.iotdb.session.pool.SessionPool; + +public class SessionPoolExample { + private static SessionPool sessionPool; + public static void main(String[] args) throws IoTDBConnectionException, StatementExecutionException { + // 1. init SessionPool + constructSessionPool(); + // 2. executes a non-query SQL statement, such as a DDL or DML command. + executeQueryExample(); + // 3. executes a query SQL statement and returns the result set. + executeNonQueryExample(); + // 4. close SessionPool + closeSessionPool(); + } -* Test the network and client cost of insertTablets + private static void executeNonQueryExample() throws IoTDBConnectionException, StatementExecutionException { + // 1. create a nonAligned time series + sessionPool.executeNonQueryStatement("create timeseries root.test.d1.s1 with dataType = int32"); + // 2. set ttl + sessionPool.executeNonQueryStatement("set TTL to root.test.** 10000"); + // 3. delete time series + sessionPool.executeNonQueryStatement("delete timeseries root.test.d1.s1"); + private static void executeQueryExample() throws IoTDBConnectionException, StatementExecutionException { + // 1. execute normal query + try(SessionDataSetWrapper wrapper = sessionPool.executeQueryStatement("select s1 from root.sg1.d1 limit 10")) { + while (wrapper.hasNext()) { + System.out.println(wrapper.next()); + } + } + // 2. execute aggregate query + try(SessionDataSetWrapper wrapper = sessionPool.executeQueryStatement("select count(s1) from root.sg1.d1 group by ([0, 40), 5ms) ")) { + while (wrapper.hasNext()) { + System.out.println(wrapper.next()); + } + } + } + + private static void constructSessionPool() { + // Using nodeUrls ensures that when one node goes down, other nodes are automatically connected to retry + List nodeUrls = new ArrayList<>(); + nodeUrls.add("127.0.0.1:6667"); + nodeUrls.add("127.0.0.1:6668"); + sessionPool = + new SessionPool.Builder() + .nodeUrls(nodeUrls) + .user("root") + .password("root") + .maxSize(3) + .build(); + } -``` java -void testInsertTablets(Map tablets) + public static void closeSessionPool(){ + sessionPool.close(); + } +} ``` - -### Coding Examples - -To get more information of the following interfaces, please view session/src/main/java/org/apache/iotdb/session/Session.java - -The sample code of using these interfaces is in example/session/src/main/java/org/apache/iotdb/SessionExample.java,which provides an example of how to open an IoTDB session, execute a batch insertion. - -For examples of aligned timeseries and measurement template, you can refer to `example/session/src/main/java/org/apache/iotdb/AlignedTimeseriesSessionExample.java` \ No newline at end of file +### 3 Native Interface Description + +#### 3.1 Parameter List +The Session class has the following fields, which can be set through the constructor or the Session.Builder method: + +| **Field Name** | **Type** | **Description** | +|--------------------------------|-------------------------------|----------------------------------------------------------------------| +| `nodeUrls` | `List` | List of URLs for database nodes, supporting multiple node connections | +| `username` | `String` | Username | +| `password` | `String` | Password | +| `fetchSize` | `int` | Default batch size for query results | +| `useSSL` | `boolean` | Whether to enable SSL | +| `trustStore` | `String` | Path to the trust store | +| `trustStorePwd` | `String` | Password for the trust store | +| `queryTimeoutInMs` | `long` | Query timeout in milliseconds | +| `enableRPCCompression` | `boolean` | Whether to enable RPC compression | +| `connectionTimeoutInMs` | `int` | Connection timeout in milliseconds | +| `zoneId` | `ZoneId` | Time zone setting for the session | +| `thriftDefaultBufferSize` | `int` | Default buffer size for Thrift Thrift | +| `thriftMaxFrameSize` | `int` | Maximum frame size for Thrift Thrift | +| `defaultEndPoint` | `TEndPoint` | Default database endpoint information | +| `defaultSessionConnection` | `SessionConnection` | Default session connection object | +| `isClosed` | `boolean` | Whether the current session is closed | +| `enableRedirection` | `boolean` | Whether to enable redirection | +| `enableRecordsAutoConvertTablet` | `boolean` | Whether to enable the function of recording the automatic transfer to Tablet | +| `deviceIdToEndpoint` | `Map` | Mapping of device IDs to database endpoints | +| `endPointToSessionConnection` | `Map` | Mapping of database endpoints to session connections | +| `executorService` | `ScheduledExecutorService` | Thread pool for periodically updating the node list | +| `availableNodes` | `INodeSupplier` | Supplier of available nodes | +| `enableQueryRedirection` | `boolean` | Whether to enable query redirection | +| `version` | `Version` | Client version number, used for compatibility judgment with the server | +| `enableAutoFetch` | `boolean` | Whether to enable automatic fetching | +| `maxRetryCount` | `int` | Maximum number of retries | +| `retryIntervalInMs` | `long` | Retry interval in milliseconds | + + + +#### 3.2 Interface list + +##### 3.2.1 Metadata Management + +| **Method Name** | **Function Description** | **Parameter Explanation** | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `createDatabase(String database)` | Create a database | `database`: The name of the database to be created | +| `deleteDatabase(String database)` | Delete a specified database | `database`: The name of the database to be deleted | +| `deleteDatabases(List databases)` | Batch delete databases | `databases`: A list of database names to be deleted | +| `createTimeseries(String path, TSDataType dataType, TSEncoding encoding, CompressionType compressor)` | Create a single time series | `path`: The path of the time series,`dataType`: The data type,`encoding`: The encoding type,`compressor`: The compression type | +| `createAlignedTimeseries(...)` | Create aligned time series | Device ID, list of measurement points, list of data types, list of encodings, list of compression types | +| `createMultiTimeseries(...)` | Batch create time series | Multiple paths, data types, encodings, compression types, properties, tags, aliases, etc. | +| `deleteTimeseries(String path)` | Delete a time series | `path`: The path of the time series to be deleted | +| `deleteTimeseries(List paths)` | Batch delete time series | `paths`: A list of time series paths to be deleted | +| `setSchemaTemplate(String templateName, String prefixPath)` | Set a schema template | `templateName`: The name of template,`prefixPath`: The path where the template is applied | +| `createSchemaTemplate(Template template)` | Create a schema template | `template`: The template object | +| `dropSchemaTemplate(String templateName)` | Delete a schema template | `templateName`: The name of template to be deleted | +| `addAlignedMeasurementsInTemplate(...)` | Add aligned measurements to a template | Template name, list of measurement paths, data type, encoding type, compression type | +| `addUnalignedMeasurementsInTemplate(...)` | Add unaligned measurements to a template | Same as above | +| `deleteNodeInTemplate(String templateName, String path)` | Delete a node in a template | `templateName`: The name of template,`path`: The path to be deleted | +| `countMeasurementsInTemplate(String name)` | Count the number of measurements in a template | `name`: The name of template | +| `isMeasurementInTemplate(String templateName, String path)` | Check if a measurement exists in a template | `templateName`: The name of template,`path`: The path of the measurement | +| `isPathExistInTemplate(String templateName, String path)` | Check if a path exists in a template | same as above | +| `showMeasurementsInTemplate(String templateName)` | Show measurements in a template | `templateName`: The name of template | +| `showMeasurementsInTemplate(String templateName, String pattern)` | Show measurements in a template by pattern | `templateName`: The name of template,`pattern`: The matching pattern | +| `showAllTemplates()` | Show all templates | No parameters | +| `showPathsTemplateSetOn(String templateName)` | Show paths where a template is set | `templateName`: The name of the template | +| `showPathsTemplateUsingOn(String templateName)` | Show actual paths using a template | Same as above上 | +| `unsetSchemaTemplate(String prefixPath, String templateName)` | Unset the template setting for a path | `prefixPath`: The path,`templateName`: The name of template | + + +##### 3.2.2 Data Insertion + +| **Method Name** | **Function Description** | **Parameter Explanation** | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `insertRecord(String deviceId, long time, List measurements, List types, Object... values)` | Insert a single record | `deviceId`: Device ID,`time`: Timestamp,`measurements`: List of measurement points,`types`: List of data types,`values`: List of values | +| `insertRecord(String deviceId, long time, List measurements, List values)` | Insert a single record | `deviceId`: Device ID,`time`: Timestamp,`measurements`: List of measurement points,`values`: List of values | +| `insertRecords(List deviceIds, List times, List> measurementsList, List> valuesList)` | Insert multiple records | `deviceIds`: List of device IDs,`times`: List of timestamps,`measurementsList`: List of timestamps,`valuesList`: List of lists of values | +| `insertRecords(List deviceIds, List times, List> measurementsList, List> typesList, List> valuesList)` | Insert multiple records | Same as above,plus `typesList`: List of lists of data types | +| `insertRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> typesList, List> valuesList)` | Insert multiple records for a single device | `deviceId`: Device ID,`times`: List of timestamps,`measurementsList`: List of lists of measurement points,`typesList`: List of lists of types,`valuesList`: List of lists of values | +| `insertRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> typesList, List> valuesList, boolean haveSorted)` | Insert sorted multiple records for a single device | Same as above, plus `haveSorted`: Whether the data is already sorted | +| `insertStringRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> valuesList)` | Insert string-formatted records for a single device | `deviceId`: Device ID,`times`: List of timestamps,`measurementsList`: List of lists of measurement points,`valuesList`: List of lists of values | +| `insertStringRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> valuesList, boolean haveSorted)` | Insert sorted string-formatted records for a single device | Same as above, plus `haveSorted`: Whether the data is already sorted序 | +| `insertAlignedRecord(String deviceId, long time, List measurements, List types, List values)` | Insert a single aligned record | `deviceId`: Device ID,`time`: Timestamp,`measurements`: List of measurement points,`types`: List of types,`values`: List of values | +| `insertAlignedRecord(String deviceId, long time, List measurements, List values)` | Insert a single string-formatted aligned record | `deviceId`: Device ID`time`: Timestamp,`measurements`: List of measurement points,`values`: List of values | +| `insertAlignedRecords(List deviceIds, List times, List> measurementsList, List> valuesList)` | Insert multiple aligned records | `deviceIds`: List of device IDs,`times`: List of timestamps,`measurementsList`: List of lists of measurement points,`valuesList`: List of lists of values | +| `insertAlignedRecords(List deviceIds, List times, List> measurementsList, List> typesList, List> valuesList)` | Insert multiple aligned records | Same as above, plus `typesList`: List of lists of data types | +| `insertAlignedRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> typesList, List> valuesList)` | Insert multiple aligned records for a single device | Same as above | +| `insertAlignedRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> typesList, List> valuesList, boolean haveSorted)` | Insert sorted multiple aligned records for a single device | Same as above, plus `haveSorted`: Whether the data is already sorted | +| `insertAlignedStringRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> valuesList)` | Insert string-formatted aligned records for a single device | `deviceId`: Device ID,`times`: List of timestamps,`measurementsList`: List of lists of measurement points,`valuesList`: List of lists of values | +| `insertAlignedStringRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> valuesList, boolean haveSorted)` | Insert sorted string-formatted aligned records for a single device | Same as above, plus w `haveSorted`: whether the data is already sorted | +| `insertTablet(Tablet tablet)` | Insert a single Tablet data | `tablet`: The Tablet data to be inserted | +| `insertTablet(Tablet tablet, boolean sorted)` | Insert a sorted Tablet data | Same as above, plus `sorted`: whether the data is already sorted | +| `insertAlignedTablet(Tablet tablet)` | Insert an aligned Tablet data | `tablet`: The Tablet data to be inserted | +| `insertAlignedTablet(Tablet tablet, boolean sorted)` | Insert a sorted aligned Tablet data | Same as above, plus `sorted`: whether the data is already sorted | +| `insertTablets(Map tablets)` | Insert multiple Tablet data in batch | `tablets`: Mapping from device IDs to Tablet data | +| `insertTablets(Map tablets, boolean sorted)` | Insert sorted multiple Tablet data in batch | Same as above, plus `sorted`: whether the data is already sorted | +| `insertAlignedTablets(Map tablets)` | Insert multiple aligned Tablet data in batch | `tablets`: Mapping from device IDs to Tablet data | +| `insertAlignedTablets(Map tablets, boolean sorted)` | Insert sorted multiple aligned Tablet data in batch | Same as above, plus `sorted`: whether the data is already sorted | + +##### 3.2.3 Data Deletion + +| **Method Name** | **Function Description** | **Parameter Explanation** | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `deleteTimeseries(String path)` | Delete a single time series | `path`: The path of the time series | +| `deleteTimeseries(List paths)` | Batch delete time series | `paths`: A list of time series paths | +| `deleteData(String path, long endTime)` | Delete historical data for a specified path | `path`: The path,`endTime`: The end timestamp | +| `deleteData(List paths, long endTime)` | Batch delete historical data for specified paths | `paths`: A list of paths,`endTime`: The end timestamp | +| `deleteData(List paths, long startTime, long endTime)` | Delete historical data within a time range for specified paths | Same as above, plus `startTime`: The start timestamp | + + +##### 3.2.4 Data Query + +| **Method Name** | **Function Description** | **Parameter Explanation** | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `executeQueryStatement(String sql)` | Execute a query statement | `sql`: The query SQL statement | +| `executeQueryStatement(String sql, long timeoutInMs)` | Execute a query statement with timeout | `sql`: The query SQL statement, `timeoutInMs`: The query timeout (in milliseconds) | +| `executeRawDataQuery(List paths, long startTime, long endTime)` | Query raw data for specified paths | paths: A list of query paths, `startTime`: The start timestamp, `endTime`: The end timestamp | +| `executeRawDataQuery(List paths, long startTime, long endTime, long timeOut)` | Query raw data for specified paths (with timeout) | Same as above, plus `timeOut`: The timeout time | +| `executeLastDataQuery(List paths)` | Query the latest data | `paths`: A list of query paths | +| `executeLastDataQuery(List paths, long lastTime)` | Query the latest data at a specified time | `paths`: A list of query paths, `lastTime`: The specified timestamp | +| `executeLastDataQuery(List paths, long lastTime, long timeOut)` | Query the latest data at a specified time (with timeout) | Same as above, plus `timeOut`: The timeout time | +| `executeLastDataQueryForOneDevice(String db, String device, List sensors, boolean isLegalPathNodes)` | Query the latest data for a single device | `db`: The database name, `device`: The device name, `sensors`: A list of sensors, `isLegalPathNodes`: Whether the path nodes are legal | +| `executeAggregationQuery(List paths, List aggregations)` | Execute an aggregation query | `paths`: A list of query paths, `aggregations`: A list of aggregation types | +| `executeAggregationQuery(List paths, List aggregations, long startTime, long endTime)` | Execute an aggregation query with a time range | Same as above, plus `startTime`: The start timestamp, `endTime`:` The end timestamp | +| `executeAggregationQuery(List paths, List aggregations, long startTime, long endTime, long interval)` | Execute an aggregation query with a time interval | Same as above, plus `interval`: The time interval | +| `executeAggregationQuery(List paths, List aggregations, long startTime, long endTime, long interval, long slidingStep)` | Execute a sliding window aggregation query | Same as above, plus `slidingStep`: The sliding step | +| `fetchAllConnections()` | Get information of all active connections | No parameters | + +##### 3.2.5 System Status and Backup +|**Method Name** | **Function Description** | **Parameter Explanation** | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `getBackupConfiguration()` | Get backup configuration information | No parameters | +| `fetchAllConnections()` | Get information of all active connections | No parameters | +| `getSystemStatus()` | Get the system status | Deprecated, returns `SystemStatus.NORMAL` | \ No newline at end of file diff --git a/src/UserGuide/latest/API/Programming-Data-Subscription.md b/src/UserGuide/latest/API/Programming-Data-Subscription.md new file mode 100644 index 000000000..89dfbb33c --- /dev/null +++ b/src/UserGuide/latest/API/Programming-Data-Subscription.md @@ -0,0 +1,244 @@ + + +# Data Sync API +IoTDB provides a powerful data subscription feature, allowing users to obtain newly added data from IoTDB in real-time through the subscription SDK. For detailed functional definitions and introductions:[Data Sync](../../User-Manual/Data-Sync_timecho.md#Data Sync) + +## 1 Core Steps + +1. Create Topic: Create a Topic that includes the measurement points you wish to subscribe to. +2. Subscribe to Topic: Before a consumer subscribes to a topic, the topic must have been created, otherwise the subscription will fail. Consumers under the same consumer group will evenly distribute the data. +3. Consume Data: Only by explicitly subscribing to a specific topic will you receive data from that topic. +4. Unsubscribe: When a consumer is closed, it will exit the corresponding consumer group and cancel all existing subscriptions. + + +## 2 Detailed Steps +This section is used to illustrate the core development process and does not demonstrate all parameters and interfaces. For a comprehensive understanding of all features and parameters, please refer to: [Java Native API](./Programming-Java-Native-API.md#Java Native API) + + +### 2.1 Create a Maven project +Create a Maven project and import the following dependencies(JDK >= 1.8, Maven >= 3.6) + +```xml + + + org.apache.iotdb + iotdb-session + + ${project.version} + + +``` + +### 2.2 Code Example +#### 2.2.1 Topic operations +```java +import java.util.Optional; +import java.util.Properties; +import java.util.Set; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.apache.iotdb.rpc.subscription.config.TopicConstant; +import org.apache.iotdb.session.subscription.SubscriptionSession; +import org.apache.iotdb.session.subscription.model.Topic; + +public class DataConsumerExample { + + public static void main(String[] args) throws IoTDBConnectionException, StatementExecutionException { + try (SubscriptionSession session = new SubscriptionSession("127.0.0.1", 6667)) { + // 1. open session + session.open(); + + // 2. create a topic of all data + Properties sessionConfig = new Properties(); + sessionConfig.put(TopicConstant.PATH_KEY, "root.**"); + + session.createTopic("allData", sessionConfig); + + // 3. show all topics + Set topics = session.getTopics(); + System.out.println(topics); + + // 4. show a specific topic + Optional allData = session.getTopic("allData"); + System.out.println(allData.get()); + } + } +} +``` +#### 2.2.2 Data Consume + +##### Scenario-1: Subscribing to newly added real-time data in IoTDB (for scenarios such as dashboard or configuration display) + +```java +import java.io.IOException; +import java.util.List; +import java.util.Properties; +import org.apache.iotdb.rpc.subscription.config.ConsumerConstant; +import org.apache.iotdb.rpc.subscription.config.TopicConstant; +import org.apache.iotdb.session.subscription.consumer.SubscriptionPullConsumer; +import org.apache.iotdb.session.subscription.payload.SubscriptionMessage; +import org.apache.iotdb.session.subscription.payload.SubscriptionMessageType; +import org.apache.iotdb.session.subscription.payload.SubscriptionSessionDataSet; +import org.apache.tsfile.read.common.RowRecord; + +public class DataConsumerExample { + + public static void main(String[] args) throws IOException { + + // 5. create a pull consumer, the subscription is automatically cancelled when the logic in the try resources is completed + Properties consumerConfig = new Properties(); + consumerConfig.put(ConsumerConstant.CONSUMER_ID_KEY, "c1"); + consumerConfig.put(ConsumerConstant.CONSUMER_GROUP_ID_KEY, "cg1"); + consumerConfig.put(ConsumerConstant.CONSUME_LISTENER_KEY, TopicConstant.FORMAT_SESSION_DATA_SETS_HANDLER_VALUE); + try (SubscriptionPullConsumer pullConsumer = new SubscriptionPullConsumer(consumerConfig)) { + pullConsumer.open(); + pullConsumer.subscribe("topic_all"); + while (true) { + List messages = pullConsumer.poll(10000); + for (final SubscriptionMessage message : messages) { + final short messageType = message.getMessageType(); + if (SubscriptionMessageType.isValidatedMessageType(messageType)) { + for (final SubscriptionSessionDataSet dataSet : message.getSessionDataSetsHandler()) { + while (dataSet.hasNext()) { + final RowRecord record = dataSet.next(); + System.out.println(record); + } + } + } + } + } + } + } +} + + +``` +##### Scenario-2: Subscribing to newly added TsFiles (for scenarios such as regular data backup) + +Prerequisite: The format of the topic to be consumed must be of the TsfileHandler type. For example:`create topic topic_all_tsfile with ('path'='root.**','format'='TsFileHandler')` + +```java +import java.io.IOException; +import java.util.List; +import java.util.Properties; +import org.apache.iotdb.rpc.subscription.config.ConsumerConstant; +import org.apache.iotdb.rpc.subscription.config.TopicConstant; +import org.apache.iotdb.session.subscription.consumer.SubscriptionPullConsumer; +import org.apache.iotdb.session.subscription.payload.SubscriptionMessage; + + +public class DataConsumerExample { + + public static void main(String[] args) throws IOException { + // 1. create a pull consumer, the subscription is automatically cancelled when the logic in the try resources is completed + Properties consumerConfig = new Properties(); + consumerConfig.put(ConsumerConstant.CONSUMER_ID_KEY, "c1"); + consumerConfig.put(ConsumerConstant.CONSUMER_GROUP_ID_KEY, "cg1"); + // 2. Specify the consumption type as the tsfile type + consumerConfig.put(TopicConstant.FORMAT_KEY, TopicConstant.FORMAT_TS_FILE_HANDLER_VALUE); + consumerConfig.put(ConsumerConstant.FILE_SAVE_DIR_KEY, "/Users/iotdb/Downloads"); + try (SubscriptionPullConsumer pullConsumer = new SubscriptionPullConsumer(consumerConfig)) { + pullConsumer.open(); + pullConsumer.subscribe("topic_all_tsfile"); + while (true) { + List messages = pullConsumer.poll(10000); + for (final SubscriptionMessage message : messages) { + message.getTsFileHandler().copyFile("/Users/iotdb/Downloads/1.tsfile"); + } + } + } + } +} +``` + + + + +## 3 Java Native API Description + +### 3.1 Parameter List +The consumer-related parameters can be set through the Properties parameter object. The specific parameters are as follows: + +#### SubscriptionConsumer + + +| **Parameter** | **required or optional with default** | **Parameter Meaning** | +| :---------------------- | :----------------------------------------------------------- | :----------------------------------------------------------- | +| host | optional: 127.0.0.1 | `String`: The RPC host of a DataNode in IoTDB | +| port | optional: 6667 | `Integer`: The RPC port of a DataNode in IoTDB | +| node-urls | optional: 127.0.0.1:6667 | `List`: The RPC addresses of all DataNodes in IoTDB, which can be multiple; either host:port or node-urls can be filled. If both host:port and node-urls are filled, the **union** of host:port and node-urls will be taken to form a new node-urls for application | +| username | optional: root | `String`: The username of the DataNode in IoTDB | +| password | optional: root | `String`: The password of the DataNode in IoTDB | +| groupId | optional | `String`: consumer group id,if not specified, it will be randomly assigned (a new consumer group),ensuring that the consumer group id of different consumer groups are all different | +| consumerId | optional | `String`: consumer client id,if not specified, it will be randomly assigned,ensuring that each consumer client id in the same consumer group is different | +| heartbeatIntervalMs | optional: 30000 (min: 1000) | `Long`: The interval at which the consumer sends periodic heartbeat requests to the IoTDB DataNode | +| endpointsSyncIntervalMs | optional: 120000 (min: 5000) | `Long`: The interval at which the consumer detects the expansion or contraction of IoTDB cluster nodes and adjusts the subscription connection | +| fileSaveDir | optional: Paths.get(System.getProperty("user.dir"), "iotdb-subscription").toString() | `String`: The temporary directory path where the consumer stores the subscribed TsFile files | +| fileSaveFsync | optional: false | `Boolean`: Whether the consumer actively calls fsync during the subscription of TsFiles | + +Special configurations in `SubscriptionPushConsumer` : + +| **Parameter** | **required or optional with default** | **Parameter Meaning** | +| :----------------- | :------------------------------------ | :----------------------------------------------------------------------------- | +| ackStrategy | optional: `ACKStrategy.AFTER_CONSUME` | The acknowledgment mechanism for consumption progress includes the following options: `ACKStrategy.BEFORE_CONSUME`(the consumer submits the consumption progress immediately upon receiving the data, before `onReceive` )`ACKStrategy.AFTER_CONSUME`(the consumer submits the consumption progress after consuming the data, after `onReceive` ) | +| consumeListener | optional | The callback function for consuming data, which needs to implement the `ConsumeListener` interface, defining the processing logic for consuming `SessionDataSetsHandler` and `TsFileHandler` formatted data | +| autoPollIntervalMs | optional: 5000 (min: 500) | Long: The time interval at which the consumer automatically pulls data, in **ms** | +| autoPollTimeoutMs | optional: 10000 (min: 1000) | Long: The timeout duration for the consumer to pull data each time, in **ms** | + +Special configurations in `SubscriptionPullConsumer` : + +| **Parameter** | **required or optional with default** | **Parameter Meaning** | +| :-------------------------------------------- | :--------------------------------- | :----------------------------------------------------------- | +| autoCommit | optional: true | Boolean: Whether to automatically commit the consumption progress. If this parameter is set to false, the `commit` method needs to be called manually to submit the consumption progress | +| autoCommitInterval | optional: 5000 (min: 500) | Long: The time interval for automatically committing the consumption progress, in **ms** .This parameter only takes effect when the `autoCommit` parameter is set to true | + + +### 3.2 Function List +#### Data Sync +##### SubscriptionPullConsumer + +| **Function name** | **Description** | **Parameter** | +|-------------------------------------|--------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `open()` | Opens the consumer connection and starts message consumption. If `autoCommit` is enabled, it will start the automatic commit worker. | None | +| `close()` | Closes the consumer connection. If `autoCommit` is enabled, it will commit all uncommitted messages before closing. | None | +| `poll(final Duration timeout)` | Pulls messages with a specified timeout. | `timeout` : The timeout duration. | +| `poll(final long timeoutMs)` | Pulls messages with a specified timeout in milliseconds. | `timeoutMs` : The timeout duration in milliseconds. | +| `poll(final Set topicNames, final Duration timeout)` | Pulls messages from specified topics with a specified timeout. | `topicNames` : The set of topics to pull messages from. `timeout`: The timeout duration。 | +| `poll(final Set topicNames, final long timeoutMs)` | Pulls messages from specified topics with a specified timeout in milliseconds. | `topicNames` : The set of topics to pull messages from.`timeoutMs`: The timeout duration in milliseconds. | +| `commitSync(final SubscriptionMessage message)` | Synchronously commits a single message. | `message` : The message object to be committed. | +| `commitSync(final Iterable messages)` | Synchronously commits multiple messages. | `messages` : The collection of message objects to be committed. | +| `commitAsync(final SubscriptionMessage message)` | Asynchronously commits a single message. | `message` : The message object to be committed. | +| `commitAsync(final Iterable messages)` | Asynchronously commits multiple messages. | `messages` : The collection of message objects to be committed. | +| `commitAsync(final SubscriptionMessage message, final AsyncCommitCallback callback)` | Asynchronously commits a single message with a specified callback. | `message` : The message object to be committed. `callback` : The callback function to be executed after asynchronous commit. | +| `commitAsync(final Iterable messages, final AsyncCommitCallback callback)` | Asynchronously commits multiple messages with a specified callback. | `messages` : The collection of message objects to be committed.`callback` : The callback function to be executed after asynchronous commit. | + +##### SubscriptionPushConsumer + +| **Function name** | **Description** | **Parameter** | +|-------------------------------------|----------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `open()` | Opens the consumer connection, starts message consumption, and submits the automatic polling worker. | None | +| `close()` | Closes the consumer connection and stops message consumption. | None | +| `toString()` | Returns the core configuration information of the consumer object. | None | +| `coreReportMessage()` | Obtains the key-value representation of the consumer's core configuration. | None | +| `allReportMessage()` | Obtains the key-value representation of all the consumer's configurations. | None | +| `buildPushConsumer()` | Builds a `SubscriptionPushConsumer` instance through the `Builder` | None | +| `ackStrategy(final AckStrategy ackStrategy)` | Configures the message acknowledgment strategy for the consumer. | `ackStrategy`: The specified message acknowledgment strategy. | +| `consumeListener(final ConsumeListener consumeListener)` |Configures the message consumption logic for the consumer. | `consumeListener`: The processing logic when the consumer receives messages. | +| `autoPollIntervalMs(final long autoPollIntervalMs)` | Configures the interval for automatic polling. | `autoPollIntervalMs` : The interval for automatic polling, in milliseconds. | +| `autoPollTimeoutMs(final long autoPollTimeoutMs)` | Configures the timeout for automatic polling.间。 | `autoPollTimeoutMs`: The timeout for automatic polling, in milliseconds. | \ No newline at end of file diff --git a/src/UserGuide/latest/API/Programming-Java-Native-API.md b/src/UserGuide/latest/API/Programming-Java-Native-API.md index baefdf68e..e4c04fa89 100644 --- a/src/UserGuide/latest/API/Programming-Java-Native-API.md +++ b/src/UserGuide/latest/API/Programming-Java-Native-API.md @@ -16,818 +16,453 @@ * limitations under the License. --> -# Java Native API +# Session Native API -## Installation +In the native API of IoTDB, the `Session` is the core interface for interacting with the database. It integrates a rich set of methods that support data writing, querying, and metadata operations. By instantiating a `Session`, you can establish a connection to the IoTDB server and perform various database operations within the environment constructed by this connection. The `Session` is not thread-safe and should not be called simultaneously by multiple threads. -### Dependencies +`SessionPool` is a connection pool for `Session`, and it is recommended to use `SessionPool` for programming. In scenarios with multi-threaded concurrency, `SessionPool` can manage and allocate connection resources effectively, thereby improving system performance and resource utilization efficiency. -- JDK >= 1.8 -- Maven >= 3.6 +## 1 Overview of Steps +1. Create a Connection Pool Instance: Initialize a SessionPool object to manage multiple Session instances. +2. Perform Operations: Directly obtain a Session instance from the SessionPool and execute database operations, without the need to open and close connections each time. +3. Close Connection Pool Resources: When database operations are no longer needed, close the SessionPool to release all related resources. -### Using IoTDB Java Native API with Maven + +## 2 Detailed Steps +This section provides an overview of the core development process and does not demonstrate all parameters and interfaces. For a complete list of functionalities and parameters, please refer to:[Java Native API](./Programming-Java-Native-API.md#3-native-interface-description) or check the: [Source Code](https://github.com/apache/iotdb/tree/master/example/session/src/main/java/org/apache/iotdb) + +### 2.1 Create a Maven Project +Create a Maven project and add the following dependencies to the pom.xml file (JDK >= 1.8, Maven >= 3.6): ```xml org.apache.iotdb iotdb-session - 1.0.0 + + ${project.version} ``` +### 2.2 Creating a Connection Pool Instance -## Syntax Convention - -- **IoTDB-SQL interface:** The input SQL parameter needs to conform to the [syntax conventions](../Reference/Syntax-Rule.md#Literal-Values) and be escaped for JAVA strings. For example, you need to add a backslash before the double-quotes. (That is: after JAVA escaping, it is consistent with the SQL statement executed on the command line.) -- **Other interfaces:** - - The node names in path or path prefix as parameter: The node names which should be escaped by backticks (`) in the SQL statement, escaping is required here. - - Identifiers (such as template names) as parameters: The identifiers which should be escaped by backticks (`) in the SQL statement, and escaping is not required here. -- **Code example for syntax convention could be found at:** `example/session/src/main/java/org/apache/iotdb/SyntaxConventionRelatedExample.java` - -## Native APIs - -Here we show the commonly used interfaces and their parameters in the Native API: - -### Session Management - -- Initialize a Session - -```java -// use default configuration -session = new Session.Builder.build(); - -// initialize with a single node -session = - new Session.Builder() - .host(String host) - .port(int port) - .build(); - -// initialize with multiple nodes -session = - new Session.Builder() - .nodeUrls(List nodeUrls) - .build(); - -// other configurations -session = - new Session.Builder() - .fetchSize(int fetchSize) - .username(String username) - .password(String password) - .thriftDefaultBufferSize(int thriftDefaultBufferSize) - .thriftMaxFrameSize(int thriftMaxFrameSize) - .enableRedirection(boolean enableRedirection) - .version(Version version) - .build(); -``` - -Version represents the SQL semantic version used by the client, which is used to be compatible with the SQL semantics of 0.12 when upgrading 0.13. The possible values are: `V_0_12`, `V_0_13`, `V_1_0`, etc. - -- Open a Session - -```java -void open() -``` - -- Open a session, with a parameter to specify whether to enable RPC compression - -```java -void open(boolean enableRPCCompression) -``` - -Notice: this RPC compression status of client must comply with that of IoTDB server - -- Close a Session - -```java -void close() -``` - -- SessionPool - -We provide a connection pool (`SessionPool) for Native API. -Using the interface, you need to define the pool size. - -If you can not get a session connection in 60 seconds, there is a warning log but the program will hang. - -If a session has finished an operation, it will be put back to the pool automatically. -If a session connection is broken, the session will be removed automatically and the pool will try -to create a new session and redo the operation. -You can also specify an url list of multiple reachable nodes when creating a SessionPool, just as you would when creating a Session. To ensure high availability of clients in distributed cluster. - -For query operations: - -1. When using SessionPool to query data, the result set is `SessionDataSetWrapper`; -2. Given a `SessionDataSetWrapper`, if you have not scanned all the data in it and stop to use it, - you have to call `SessionPool.closeResultSet(wrapper)` manually; -3. When you call `hasNext()` and `next()` of a `SessionDataSetWrapper` and there is an exception, then - you have to call `SessionPool.closeResultSet(wrapper)` manually; -4. You can call `getColumnNames()` of `SessionDataSetWrapper` to get the column names of query result; - -Examples: `session/src/test/java/org/apache/iotdb/session/pool/SessionPoolTest.java` - -Or `example/session/src/main/java/org/apache/iotdb/SessionPoolExample.java` - -### Database & Timeseries Management API - -#### Database Management - -- CREATE DATABASE - -```java -void setStorageGroup(String storageGroupId) -``` - -- Delete one or several databases - -```java -void deleteStorageGroup(String storageGroup) -void deleteStorageGroups(List storageGroups) -``` - -#### Timeseries Management - -- Create one or multiple timeseries - -```java -void createTimeseries(String path, TSDataType dataType, - TSEncoding encoding, CompressionType compressor, Map props, - Map tags, Map attributes, String measurementAlias) - -void createMultiTimeseries(List paths, List dataTypes, - List encodings, List compressors, - List> propsList, List> tagsList, - List> attributesList, List measurementAliasList) -``` - -- Create aligned timeseries - -```java -void createAlignedTimeseries(String prefixPath, List measurements, - List dataTypes, List encodings, - List compressors, List measurementAliasList); -``` - -Attention: Alias of measurements are **not supported** currently. - -- Delete one or several timeseries - -```java -void deleteTimeseries(String path) -void deleteTimeseries(List paths) -``` - -- Check whether the specific timeseries exists. - -```java -boolean checkTimeseriesExists(String path) -``` - -#### Schema Template - -Create a schema template for massive identical devices will help to improve memory performance. You can use Template, InternalNode and MeasurementNode to depict the structure of the template, and use belowed interface to create it inside session. - -```java -public void createSchemaTemplate(Template template); - -Class Template { - private String name; - private boolean directShareTime; - Map children; - public Template(String name, boolean isShareTime); - - public void addToTemplate(Node node); - public void deleteFromTemplate(String name); - public void setShareTime(boolean shareTime); -} - -Abstract Class Node { - private String name; - public void addChild(Node node); - public void deleteChild(Node node); -} - -Class MeasurementNode extends Node { - TSDataType dataType; - TSEncoding encoding; - CompressionType compressor; - public MeasurementNode(String name, - TSDataType dataType, - TSEncoding encoding, - CompressionType compressor); -} -``` - -We strongly suggest you implement templates only with flat-measurement (like object 'flatTemplate' in belowed snippet), since tree-structured template may not be a long-term supported feature in further version of IoTDB. - -A snippet of using above Method and Class: - -```java -MeasurementNode nodeX = new MeasurementNode("x", TSDataType.FLOAT, TSEncoding.RLE, CompressionType.SNAPPY); -MeasurementNode nodeY = new MeasurementNode("y", TSDataType.FLOAT, TSEncoding.RLE, CompressionType.SNAPPY); -MeasurementNode nodeSpeed = new MeasurementNode("speed", TSDataType.DOUBLE, TSEncoding.GORILLA, CompressionType.SNAPPY); - -// This is the template we suggest to implement -Template flatTemplate = new Template("flatTemplate"); -template.addToTemplate(nodeX); -template.addToTemplate(nodeY); -template.addToTemplate(nodeSpeed); - -createSchemaTemplate(flatTemplate); -``` - -You can query measurement inside templates with these APIS: - -```java -// Return the amount of measurements inside a template -public int countMeasurementsInTemplate(String templateName); - -// Return true if path points to a measurement, otherwise returne false -public boolean isMeasurementInTemplate(String templateName, String path); - -// Return true if path exists in template, otherwise return false -public boolean isPathExistInTemplate(String templateName, String path); - -// Return all measurements paths inside template -public List showMeasurementsInTemplate(String templateName); - -// Return all measurements paths under the designated patter inside template -public List showMeasurementsInTemplate(String templateName, String pattern); -``` - -To implement schema template, you can set the measurement template named 'templateName' at path 'prefixPath'. - -**Please notice that, we strongly recommend not setting templates on the nodes above the database to accommodate future updates and collaboration between modules.** - -```java -void setSchemaTemplate(String templateName, String prefixPath) -``` - -Before setting template, you should firstly create the template using - -```java -void createSchemaTemplate(Template template) -``` - -After setting template to a certain path, you can use the template to create timeseries on given device paths through the following interface, or you can write data directly to trigger timeseries auto creation using schema template under target devices. - -```java -void createTimeseriesUsingSchemaTemplate(List devicePathList) -``` - -After setting template to a certain path, you can query for info about template using belowed interface in session: ```java -/** @return All template names. */ -public List showAllTemplates(); - -/** @return All paths have been set to designated template. */ -public List showPathsTemplateSetOn(String templateName); - -/** @return All paths are using designated template. */ -public List showPathsTemplateUsingOn(String templateName) -``` - -If you are ready to get rid of schema template, you can drop it with belowed interface. Make sure the template to drop has been unset from MTree. - -```java -void unsetSchemaTemplate(String prefixPath, String templateName); -public void dropSchemaTemplate(String templateName); -``` +import java.util.ArrayList; +import java.util.List; +import org.apache.iotdb.session.pool.SessionPool; -Unset the measurement template named 'templateName' from path 'prefixPath'. When you issue this interface, you should assure that there is a template named 'templateName' set at the path 'prefixPath'. +public class IoTDBSessionPoolExample { + private static SessionPool sessionPool; -Attention: Unsetting the template named 'templateName' from node at path 'prefixPath' or descendant nodes which have already inserted records using template is **not supported**. - -### Data Manipulation Interface (DML Interface) - -### Data Insert API - -It is recommended to use insertTablet to help improve write efficiency. - -- Insert a Tablet,which is multiple rows of a device, each row has the same measurements - - **Better Write Performance** - - **Support batch write** - - **Support null values**: fill the null value with any value, and then mark the null value via BitMap - -```java -void insertTablet(Tablet tablet) - -public class Tablet { - /** deviceId of this tablet */ - public String prefixPath; - /** the list of measurement schemas for creating the tablet */ - private List schemas; - /** timestamps in this tablet */ - public long[] timestamps; - /** each object is a primitive type array, which represents values of one measurement */ - public Object[] values; - /** each bitmap represents the existence of each value in the current column. */ - public BitMap[] bitMaps; - /** the number of rows to include in this tablet */ - public int rowSize; - /** the maximum number of rows for this tablet */ - private int maxRowNumber; - /** whether this tablet store data of aligned timeseries or not */ - private boolean isAligned; + public static void main(String[] args) { + // Using nodeUrls ensures that when one node goes down, other nodes are automatically connected to retry + List nodeUrls = new ArrayList<>(); + nodeUrls.add("127.0.0.1:6667"); + nodeUrls.add("127.0.0.1:6668"); + sessionPool = + new SessionPool.Builder() + .nodeUrls(nodeUrls) + .user("root") + .password("root") + .maxSize(3) + .build(); + } } ``` -- Insert multiple Tablets - -```java -void insertTablets(Map tablet) -``` - -- Insert a Record, which contains multiple measurement value of a device at a timestamp. This method is equivalent to providing a common interface for multiple data types of values. Later, the value can be cast to the original type through TSDataType. - - The correspondence between the Object type and the TSDataType type is shown in the following table. - - | TSDataType | Object | - | ---------- | -------------- | - | BOOLEAN | Boolean | - | INT32 | Integer | - | DATE | LocalDate | - | INT64 | Long | - | TIMESTAMP | Long | - | FLOAT | Float | - | DOUBLE | Double | - | TEXT | String, Binary | - | STRING | String, Binary | - | BLOB | Binary | - -```java -void insertRecord(String deviceId, long time, List measurements, - List types, List values) -``` - -- Insert multiple Records - -```java -void insertRecords(List deviceIds, List times, - List> measurementsList, List> typesList, - List> valuesList) -``` - -- Insert multiple Records that belong to the same device. - With type info the server has no need to do type inference, which leads a better performance +### 2.3 Performing Database Operations -```java -void insertRecordsOfOneDevice(String deviceId, List times, - List> measurementsList, List> typesList, - List> valuesList) -``` +#### 2.3.1 Data Insertion -#### Insert with type inference +In industrial scenarios, data insertion can be categorized into the following types: inserting multiple rows of data, and inserting multiple rows of data for a single device. Below, we introduce the insertion interfaces for different scenarios. -When the data is of String type, we can use the following interface to perform type inference based on the value of the value itself. For example, if value is "true" , it can be automatically inferred to be a boolean type. If value is "3.2" , it can be automatically inferred as a flout type. Without type information, server has to do type inference, which may cost some time. +##### Multi-Row Data Insertion Interface +Interface Description: Supports inserting multiple rows of data at once, where each row corresponds to multiple measurement values for a device at a specific timestamp. -- Insert a Record, which contains multiple measurement value of a device at a timestamp -```java -void insertRecord(String prefixPath, long time, List measurements, List values) -``` +Interface List: -- Insert multiple Records - -```java -void insertRecords(List deviceIds, List times, - List> measurementsList, List> valuesList) -``` +| **Interface Name** | **Function Description** | +|------------------------------------------------------------------------------------------------------------------------|-----------------------| +| `insertRecords(List deviceIds, List times, List> measurementsList, List> typesList, List> valuesList)` | Inserts multiple rows of data, suitable for scenarios where measurements are independently collected. | -- Insert multiple Records that belong to the same device. +Code Example: ```java -void insertStringRecordsOfOneDevice(String deviceId, List times, - List> measurementsList, List> valuesList) -``` - -#### Insert of Aligned Timeseries - -The Insert of aligned timeseries uses interfaces like insertAlignedXXX, and others are similar to the above interfaces: - -- insertAlignedRecord -- insertAlignedRecords -- insertAlignedRecordsOfOneDevice -- insertAlignedStringRecordsOfOneDevice -- insertAlignedTablet -- insertAlignedTablets +import java.util.ArrayList; +import java.util.List; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.apache.iotdb.session.pool.SessionPool; +import org.apache.tsfile.enums.TSDataType; -### Data Delete API - -- Delete data before or equal to a timestamp of one or several timeseries - -```java -void deleteData(String path, long time) -void deleteData(List paths, long time) -``` - -### Data Query API - -- Time-series raw data query with time range: - - The specified query time range is a left-closed right-open interval, including the start time but excluding the end time. - -```java -SessionDataSet executeRawDataQuery(List paths, long startTime, long endTime); -``` - -- Last query: - - - Query the last data, whose timestamp is greater than or equal LastTime. - - ```java - SessionDataSet executeLastDataQuery(List paths, long LastTime); - ``` - - - Query the latest point of the specified series of single device quickly, and support redirection; - If you are sure that the query path is valid, set 'isLegalPathNodes' to true to avoid performance penalties from path verification. - - ```java - SessionDataSet executeLastDataQueryForOneDevice( - String db, String device, List sensors, boolean isLegalPathNodes); - ``` - -- Aggregation query: - - Support specified query time range: The specified query time range is a left-closed right-open interval, including the start time but not the end time. - - Support GROUP BY TIME. - -```java -SessionDataSet executeAggregationQuery(List paths, List aggregations); - -SessionDataSet executeAggregationQuery( - List paths, List aggregations, long startTime, long endTime); - -SessionDataSet executeAggregationQuery( - List paths, - List aggregations, - long startTime, - long endTime, - long interval); - -SessionDataSet executeAggregationQuery( - List paths, - List aggregations, - long startTime, - long endTime, - long interval, - long slidingStep); -``` - -- Execute query statement - -```java -SessionDataSet executeQueryStatement(String sql) -``` - -### Data Subscription - -#### 1 Topic Management - -The `SubscriptionSession` class in the IoTDB subscription client provides interfaces for topic management. The status changes of topics are illustrated in the diagram below: - -::: center - - - -::: - -##### 1.1 Create Topic - -```Java - void createTopicIfNotExists(String topicName, Properties properties) throws Exception; -``` - -Example: - -```Java -try (final SubscriptionSession session = new SubscriptionSession(host, port)) { - session.open(); - final Properties config = new Properties(); - config.put(TopicConstant.PATH_KEY, "root.db.**"); - session.createTopic(topicName, config); -} -``` - -##### 1.2 Delete Topic - -```Java -void dropTopicIfExists(String topicName) throws Exception; -``` - -##### 1.3 View Topic - -```Java -// Get all topics -Set getTopics() throws Exception; - -// Get a specific topic -Optional getTopic(String topicName) throws Exception; -``` - -#### 2 Check Subscription Status - -The `SubscriptionSession` class in the IoTDB subscription client provides interfaces to check the subscription status: - -```Java -Set getSubscriptions() throws Exception; -Set getSubscriptions(final String topicName) throws Exception; -``` - -#### 3 Create Consumer - -When creating a consumer using the JAVA native interface, you need to specify the parameters applied to the consumer. - -For both `SubscriptionPullConsumer` and `SubscriptionPushConsumer`, the following common configurations are available: - -| key | **required or optional with default** | description | -| :---------------------- | :----------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| host | optional: 127.0.0.1 | `String`: The RPC host of a certain DataNode in IoTDB | -| port | optional: 6667 | Integer: The RPC port of a certain DataNode in IoTDB | -| node-urls | optional: 127.0.0.1:6667 | `List`: The RPC addresses of all DataNodes in IoTDB, can be multiple; either host:port or node-urls can be filled in. If both host:port and node-urls are filled in, the union of host:port and node-urls will be used to form a new node-urls application | -| username | optional: root | `String`: The username of a DataNode in IoTDB | -| password | optional: root | `String`: The password of a DataNode in IoTDB | -| groupId | optional | `String`: consumer group id, if not specified, a new consumer group will be randomly assigned, ensuring that different consumer groups have different consumer group ids | -| consumerId | optional | `String`: consumer client id, if not specified, it will be randomly assigned, ensuring that each consumer client id in the same consumer group is unique | -| heartbeatIntervalMs | optional: 30000 (min: 1000) | `Long`: The interval at which the consumer sends heartbeat requests to the IoTDB DataNode | -| endpointsSyncIntervalMs | optional: 120000 (min: 5000) | `Long`: The interval at which the consumer detects the expansion and contraction of IoTDB cluster nodes and adjusts the subscription connection | -| fileSaveDir | optional: Paths.get(System.getProperty("user.dir"), "iotdb-subscription").toString() | `String`: The temporary directory path where the TsFile files subscribed by the consumer are stored | -| fileSaveFsync | optional: false | `Boolean`: Whether the consumer actively calls fsync during the subscription of TsFile | - -##### 3.1 SubscriptionPushConsumer - -The following are special configurations for `SubscriptionPushConsumer`: - -| key | **required or optional with default** | description | -| :----------------- | :------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| ackStrategy | optional: `ACKStrategy.AFTER_CONSUME` | Consumption progress confirmation mechanism includes the following options: `ACKStrategy.BEFORE_CONSUME` (submit consumption progress immediately when the consumer receives data, before `onReceive`) `ACKStrategy.AFTER_CONSUME` (submit consumption progress after the consumer has consumed the data, after `onReceive`) | -| consumeListener | optional | Consumption data callback function, need to implement the `ConsumeListener` interface, define the consumption logic of `SessionDataSetsHandler` and `TsFileHandler` form data | -| autoPollIntervalMs | optional: 5000 (min: 500) | Long: The interval at which the consumer automatically pulls data, in ms | -| autoPollTimeoutMs | optional: 10000 (min: 1000) | Long: The timeout time for the consumer to pull data each time, in ms | - -Among them, the ConsumerListener interface is defined as follows: - -```Java -@FunctionInterface -interface ConsumeListener { - default ConsumeResult onReceive(Message message) { - return ConsumeResult.SUCCESS; +public class SessionPoolExample { + private static SessionPool sessionPool; + public static void main(String[] args) throws IoTDBConnectionException, StatementExecutionException { + // 1. init SessionPool + constructSessionPool(); + // 2. execute insert data + insertRecordsExample(); + // 3. close SessionPool + closeSessionPool(); } -} - -enum ConsumeResult { - SUCCESS, - FAILURE, -} -``` - -##### 3.2 SubscriptionPullConsumer - -The following are special configurations for `SubscriptionPullConsumer` : - -| key | **required or optional with default** | description | -| :----------------- | :------------------------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| autoCommit | optional: true | Boolean: Whether to automatically commit consumption progress. If this parameter is set to false, the commit method must be called to manually `commit` consumption progress. | -| autoCommitInterval | optional: 5000 (min: 500) | Long: The interval at which consumption progress is automatically committed, in milliseconds. This only takes effect when the autoCommit parameter is true. | -| | - -After creating a consumer, you need to manually call the consumer's open method: - -```Java -void open() throws Exception; -``` - -At this point, the IoTDB subscription client will verify the correctness of the consumer's configuration. After a successful verification, the consumer will join the corresponding consumer group. That is, only after opening the consumer can you use the returned consumer object to subscribe to topics, consume data, and perform other operations. - -#### 4 Subscribe to Topics - -Both `SubscriptionPushConsumer` and `SubscriptionPullConsumer` provide the following JAVA native interfaces for subscribing to topics: - -```Java -// Subscribe to topics -void subscribe(String topic) throws Exception; -void subscribe(List topics) throws Exception; -``` - -- Before a consumer subscribes to a topic, the topic must have been created, otherwise, the subscription will fail. - -- If a consumer subscribes to a topic that it has already subscribed to, no error will occur. - -- If there are other consumers in the same consumer group that have subscribed to the same topics, the consumer will reuse the corresponding consumption progress. - -#### 5 Consume Data - -For both push and pull mode consumers: - -- Only after explicitly subscribing to a topic will the consumer receive data for that topic. -- If no topics are subscribed to after creation, the consumer will not be able to consume any data, even if other consumers in the same consumer group have subscribed to some topics. - -##### 5.1 SubscriptionPushConsumer - -After `SubscriptionPushConsumer` subscribes to topics, there is no need to manually pull data. - -The data consumption logic is within the `consumeListener` configuration specified when creating `SubscriptionPushConsumer`. - -##### 5.2 SubscriptionPullConsumer - -After SubscriptionPullConsumer subscribes to topics, it needs to actively call the poll method to pull data: - -```Java -List poll(final Duration timeout) throws Exception; -List poll(final long timeoutMs) throws Exception; -List poll(final Set topicNames, final Duration timeout) throws Exception; -List poll(final Set topicNames, final long timeoutMs) throws Exception; -``` - -In the poll method, you can specify the topic names to be pulled (if not specified, it defaults to pulling all topics that the consumer has subscribed to) and the timeout period. - -When the SubscriptionPullConsumer is configured with the autoCommit parameter set to false, it is necessary to manually call the commitSync and commitAsync methods to synchronously or asynchronously commit the consumption progress of a batch of data: - -```Java -void commitSync(final SubscriptionMessage message) throws Exception; -void commitSync(final Iterable messages) throws Exception; - -CompletableFuture commitAsync(final SubscriptionMessage message); -CompletableFuture commitAsync(final Iterable messages); -void commitAsync(final SubscriptionMessage message, final AsyncCommitCallback callback); -void commitAsync(final Iterable messages, final AsyncCommitCallback callback); -``` - -The AsyncCommitCallback class is defined as follows: - -```Java -public interface AsyncCommitCallback { - default void onComplete() { - // Do nothing - } - - default void onFailure(final Throwable e) { - // Do nothing - } -} -``` - -#### 6 Unsubscribe - -The `SubscriptionPushConsumer` and `SubscriptionPullConsumer` provide the following JAVA native interfaces for unsubscribing and closing the consumer: - -```Java -// Unsubscribe from topics -void unsubscribe(String topic) throws Exception; -void unsubscribe(List topics) throws Exception; - -// Close consumer -void close(); -``` - -- If a consumer unsubscribes from a topic that it has not subscribed to, no error will occur. -- When a consumer is closed, it will exit the corresponding consumer group and automatically unsubscribe from all topics it is currently subscribed to. -- Once a consumer is closed, its lifecycle ends, and it cannot be reopened to subscribe to and consume data again. - -#### 7 Code Examples - -##### 7.1 Single Pull Consumer Consuming SessionDataSetsHandler Format Data - -```Java -// Create topics -try (final SubscriptionSession session = new SubscriptionSession(HOST, PORT)) { - session.open(); - final Properties config = new Properties(); - config.put(TopicConstant.PATH_KEY, "root.db.**"); - session.createTopic(TOPIC_1, config); -} - -// Subscription: property-style ctor -final Properties config = new Properties(); -config.put(ConsumerConstant.CONSUMER_ID_KEY, "c1"); -config.put(ConsumerConstant.CONSUMER_GROUP_ID_KEY, "cg1"); - -final SubscriptionPullConsumer consumer1 = new SubscriptionPullConsumer(config); -consumer1.open(); -consumer1.subscribe(TOPIC_1); -while (true) { - LockSupport.parkNanos(SLEEP_NS); // wait some time - final List messages = consumer1.poll(POLL_TIMEOUT_MS); - for (final SubscriptionMessage message : messages) { - for (final SubscriptionSessionDataSet dataSet : message.getSessionDataSetsHandler()) { - System.out.println(dataSet.getColumnNames()); - System.out.println(dataSet.getColumnTypes()); - while (dataSet.hasNext()) { - System.out.println(dataSet.next()); - } + private static void constructSessionPool() { + // Using nodeUrls ensures that when one node goes down, other nodes are automatically connected to retry + List nodeUrls = new ArrayList<>(); + nodeUrls.add("127.0.0.1:6667"); + nodeUrls.add("127.0.0.1:6668"); + sessionPool = + new SessionPool.Builder() + .nodeUrls(nodeUrls) + .user("root") + .password("root") + .maxSize(3) + .build(); } - } - // Auto commit -} -// Show topics and subscriptions -try (final SubscriptionSession session = new SubscriptionSession(HOST, PORT)) { - session.open(); - session.getTopics().forEach((System.out::println)); - session.getSubscriptions().forEach((System.out::println)); + public static void insertRecordsExample() throws IoTDBConnectionException, StatementExecutionException { + String deviceId = "root.sg1.d1"; + List measurements = new ArrayList<>(); + measurements.add("s1"); + measurements.add("s2"); + measurements.add("s3"); + List deviceIds = new ArrayList<>(); + List> measurementsList = new ArrayList<>(); + List> valuesList = new ArrayList<>(); + List timestamps = new ArrayList<>(); + List> typesList = new ArrayList<>(); + + for (long time = 0; time < 500; time++) { + List values = new ArrayList<>(); + List types = new ArrayList<>(); + values.add(1L); + values.add(2L); + values.add(3L); + types.add(TSDataType.INT64); + types.add(TSDataType.INT64); + types.add(TSDataType.INT64); + + deviceIds.add(deviceId); + measurementsList.add(measurements); + valuesList.add(values); + typesList.add(types); + timestamps.add(time); + if (time != 0 && time % 100 == 0) { + try { + sessionPool.insertRecords(deviceIds, timestamps, measurementsList, typesList, valuesList); + } catch (IoTDBConnectionException | StatementExecutionException e) { + // solve exception + } + deviceIds.clear(); + measurementsList.clear(); + valuesList.clear(); + typesList.clear(); + timestamps.clear(); + } + } + try { + sessionPool.insertRecords(deviceIds, timestamps, measurementsList, typesList, valuesList); + } catch (IoTDBConnectionException | StatementExecutionException e) { + // solve exception + } + } + + public static void closeSessionPool(){ + sessionPool.close(); + } } - -consumer1.unsubscribe(TOPIC_1); -consumer1.close(); ``` +##### Single-Device Multi-Row Data Insertion Interface +Interface Description: Supports inserting multiple rows of data for a single device at once, where each row corresponds to multiple measurement values for a specific timestamp. + +Interface List: + +| **Interface Name** | **Function Description** | +|-----------------------------------------------------------------------------------------|----------------------------| +| `insertTablet(Tablet tablet)` | Inserts multiple rows of data for a single device, suitable for scenarios where measurements are independently collected. | + +Code Example: + +```java +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.apache.iotdb.session.pool.SessionPool; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.write.record.Tablet; +import org.apache.tsfile.write.schema.MeasurementSchema; + +public class SessionPoolExample { + private static SessionPool sessionPool; + public static void main(String[] args) throws IoTDBConnectionException, StatementExecutionException { + // 1. init SessionPool + constructSessionPool(); + // 2. execute insert data + insertTabletExample(); + // 3. close SessionPool + closeSessionPool(); + } + + private static void constructSessionPool() { + // Using nodeUrls ensures that when one node goes down, other nodes are automatically connected to retry + List nodeUrls = new ArrayList<>(); + nodeUrls.add("127.0.0.1:6667"); + nodeUrls.add("127.0.0.1:6668"); + sessionPool = + new SessionPool.Builder() + .nodeUrls(nodeUrls) + .user("root") + .password("root") + .maxSize(3) + .build(); + } -##### 7.2 Multiple Push Consumers Consuming TsFileHandler Format Data - -```Java -// Create topics -try (final SubscriptionSession subscriptionSession = new SubscriptionSession(HOST, PORT)) { - subscriptionSession.open(); - final Properties config = new Properties(); - config.put(TopicConstant.FORMAT_KEY, TopicConstant.FORMAT_TS_FILE_HANDLER_VALUE); - subscriptionSession.createTopic(TOPIC_2, config); -} - -final List threads = new ArrayList<>(); -for (int i = 0; i < 8; ++i) { - final int idx = i; - final Thread thread = - new Thread( - () -> { - // Subscription: builder-style ctor - try (final SubscriptionPushConsumer consumer2 = - new SubscriptionPushConsumer.Builder() - .consumerId("c" + idx) - .consumerGroupId("cg2") - .fileSaveDir(System.getProperty("java.io.tmpdir")) - .ackStrategy(AckStrategy.AFTER_CONSUME) - .consumeListener( - message -> { - doSomething(message.getTsFileHandler()); - return ConsumeResult.SUCCESS; - }) - .buildPushConsumer()) { - consumer2.open(); - consumer2.subscribe(TOPIC_2); - // block the consumer main thread - Thread.sleep(Long.MAX_VALUE); - } catch (final IOException | InterruptedException e) { - throw new RuntimeException(e); + private static void insertTabletExample() throws IoTDBConnectionException, StatementExecutionException { + /* + * A Tablet example: + * device1 + * time s1, s2, s3 + * 1, 1, 1, 1 + * 2, 2, 2, 2 + * 3, 3, 3, 3 + */ + // The schema of measurements of one device + // only measurementId and data type in MeasurementSchema take effects in Tablet + List schemaList = new ArrayList<>(); + schemaList.add(new MeasurementSchema("s1", TSDataType.INT64)); + schemaList.add(new MeasurementSchema("s2", TSDataType.INT64)); + schemaList.add(new MeasurementSchema("s3", TSDataType.INT64)); + + Tablet tablet = new Tablet("root.sg.d1", schemaList, 100); + + // Method 1 to add tablet data + long timestamp = System.currentTimeMillis(); + + Random random = new Random(); + for (long row = 0; row < 100; row++) { + int rowIndex = tablet.rowSize++; + tablet.addTimestamp(rowIndex, timestamp); + for (int s = 0; s < 3; s++) { + long value = random.nextLong(); + tablet.addValue(schemaList.get(s).getMeasurementId(), rowIndex, value); } - }); - thread.start(); - threads.add(thread); -} + if (tablet.rowSize == tablet.getMaxRowNumber()) { + sessionPool.insertTablet(tablet); + tablet.reset(); + } + timestamp++; + } + if (tablet.rowSize != 0) { + sessionPool.insertTablet(tablet); + tablet.reset(); + } + } -for (final Thread thread : threads) { - thread.join(); + public static void closeSessionPool(){ + sessionPool.close(); + } } ``` -### Other Modules (Execute SQL Directly) - -- Execute non query statement - -```java -void executeNonQueryStatement(String sql) -``` - -### Write Test Interface (to profile network cost) - -These methods **don't** insert data into database and server just return after accept the request. +#### 2.3.2 SQL Operations -- Test the network and client cost of insertRecord +SQL operations are divided into two categories: queries and non-queries. The corresponding interfaces are executeQuery and executeNonQuery. The difference between them is that the former executes specific query statements and returns a result set, while the latter performs insert, delete, and update operations and does not return a result set. ```java -void testInsertRecord(String deviceId, long time, List measurements, List values) +import java.util.ArrayList; +import java.util.List; +import org.apache.iotdb.isession.pool.SessionDataSetWrapper; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.apache.iotdb.session.pool.SessionPool; -void testInsertRecord(String deviceId, long time, List measurements, - List types, List values) -``` - -- Test the network and client cost of insertRecords - -```java -void testInsertRecords(List deviceIds, List times, - List> measurementsList, List> valuesList) - -void testInsertRecords(List deviceIds, List times, - List> measurementsList, List> typesList - List> valuesList) -``` - -- Test the network and client cost of insertTablet - -```java -void testInsertTablet(Tablet tablet) -``` +public class SessionPoolExample { + private static SessionPool sessionPool; + public static void main(String[] args) throws IoTDBConnectionException, StatementExecutionException { + // 1. init SessionPool + constructSessionPool(); + // 2. executes a non-query SQL statement, such as a DDL or DML command. + executeQueryExample(); + // 3. executes a query SQL statement and returns the result set. + executeNonQueryExample(); + // 4. close SessionPool + closeSessionPool(); + } -- Test the network and client cost of insertTablets + private static void executeNonQueryExample() throws IoTDBConnectionException, StatementExecutionException { + // 1. create a nonAligned time series + sessionPool.executeNonQueryStatement("create timeseries root.test.d1.s1 with dataType = int32"); + // 2. set ttl + sessionPool.executeNonQueryStatement("set TTL to root.test.** 10000"); + // 3. delete time series + sessionPool.executeNonQueryStatement("delete timeseries root.test.d1.s1"); + private static void executeQueryExample() throws IoTDBConnectionException, StatementExecutionException { + // 1. execute normal query + try(SessionDataSetWrapper wrapper = sessionPool.executeQueryStatement("select s1 from root.sg1.d1 limit 10")) { + while (wrapper.hasNext()) { + System.out.println(wrapper.next()); + } + } + // 2. execute aggregate query + try(SessionDataSetWrapper wrapper = sessionPool.executeQueryStatement("select count(s1) from root.sg1.d1 group by ([0, 40), 5ms) ")) { + while (wrapper.hasNext()) { + System.out.println(wrapper.next()); + } + } + } + + private static void constructSessionPool() { + // Using nodeUrls ensures that when one node goes down, other nodes are automatically connected to retry + List nodeUrls = new ArrayList<>(); + nodeUrls.add("127.0.0.1:6667"); + nodeUrls.add("127.0.0.1:6668"); + sessionPool = + new SessionPool.Builder() + .nodeUrls(nodeUrls) + .user("root") + .password("root") + .maxSize(3) + .build(); + } -```java -void testInsertTablets(Map tablets) + public static void closeSessionPool(){ + sessionPool.close(); + } +} ``` - -### Coding Examples - -To get more information of the following interfaces, please view `session/src/main/java/org/apache/iotdb/session/Session.java` - -The sample code of using these interfaces is in `example/session/src/main/java/org/apache/iotdb/SessionExample.java`,which provides an example of how to open an IoTDB session, execute a batch insertion. - -For examples of aligned timeseries and measurement template, you can refer to `example/session/src/main/java/org/apache/iotdb/AlignedTimeseriesSessionExample.java` +### 3 Native Interface Description + +#### 3.1 Parameter List +The Session class has the following fields, which can be set through the constructor or the Session.Builder method: + +| **Field Name** | **Type** | **Description** | +|--------------------------------|-------------------------------|----------------------------------------------------------------------| +| `nodeUrls` | `List` | List of URLs for database nodes, supporting multiple node connections | +| `username` | `String` | Username | +| `password` | `String` | Password | +| `fetchSize` | `int` | Default batch size for query results | +| `useSSL` | `boolean` | Whether to enable SSL | +| `trustStore` | `String` | Path to the trust store | +| `trustStorePwd` | `String` | Password for the trust store | +| `queryTimeoutInMs` | `long` | Query timeout in milliseconds | +| `enableRPCCompression` | `boolean` | Whether to enable RPC compression | +| `connectionTimeoutInMs` | `int` | Connection timeout in milliseconds | +| `zoneId` | `ZoneId` | Time zone setting for the session | +| `thriftDefaultBufferSize` | `int` | Default buffer size for Thrift Thrift | +| `thriftMaxFrameSize` | `int` | Maximum frame size for Thrift Thrift | +| `defaultEndPoint` | `TEndPoint` | Default database endpoint information | +| `defaultSessionConnection` | `SessionConnection` | Default session connection object | +| `isClosed` | `boolean` | Whether the current session is closed | +| `enableRedirection` | `boolean` | Whether to enable redirection | +| `enableRecordsAutoConvertTablet` | `boolean` | Whether to enable the function of recording the automatic transfer to Tablet | +| `deviceIdToEndpoint` | `Map` | Mapping of device IDs to database endpoints | +| `endPointToSessionConnection` | `Map` | Mapping of database endpoints to session connections | +| `executorService` | `ScheduledExecutorService` | Thread pool for periodically updating the node list | +| `availableNodes` | `INodeSupplier` | Supplier of available nodes | +| `enableQueryRedirection` | `boolean` | Whether to enable query redirection | +| `version` | `Version` | Client version number, used for compatibility judgment with the server | +| `enableAutoFetch` | `boolean` | Whether to enable automatic fetching | +| `maxRetryCount` | `int` | Maximum number of retries | +| `retryIntervalInMs` | `long` | Retry interval in milliseconds | + + + +#### 3.2 Interface list + +##### 3.2.1 Metadata Management + +| **Method Name** | **Function Description** | **Parameter Explanation** | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `createDatabase(String database)` | Create a database | `database`: The name of the database to be created | +| `deleteDatabase(String database)` | Delete a specified database | `database`: The name of the database to be deleted | +| `deleteDatabases(List databases)` | Batch delete databases | `databases`: A list of database names to be deleted | +| `createTimeseries(String path, TSDataType dataType, TSEncoding encoding, CompressionType compressor)` | Create a single time series | `path`: The path of the time series,`dataType`: The data type,`encoding`: The encoding type,`compressor`: The compression type | +| `createAlignedTimeseries(...)` | Create aligned time series | Device ID, list of measurement points, list of data types, list of encodings, list of compression types | +| `createMultiTimeseries(...)` | Batch create time series | Multiple paths, data types, encodings, compression types, properties, tags, aliases, etc. | +| `deleteTimeseries(String path)` | Delete a time series | `path`: The path of the time series to be deleted | +| `deleteTimeseries(List paths)` | Batch delete time series | `paths`: A list of time series paths to be deleted | +| `setSchemaTemplate(String templateName, String prefixPath)` | Set a schema template | `templateName`: The name of template,`prefixPath`: The path where the template is applied | +| `createSchemaTemplate(Template template)` | Create a schema template | `template`: The template object | +| `dropSchemaTemplate(String templateName)` | Delete a schema template | `templateName`: The name of template to be deleted | +| `addAlignedMeasurementsInTemplate(...)` | Add aligned measurements to a template | Template name, list of measurement paths, data type, encoding type, compression type | +| `addUnalignedMeasurementsInTemplate(...)` | Add unaligned measurements to a template | Same as above | +| `deleteNodeInTemplate(String templateName, String path)` | Delete a node in a template | `templateName`: The name of template,`path`: The path to be deleted | +| `countMeasurementsInTemplate(String name)` | Count the number of measurements in a template | `name`: The name of template | +| `isMeasurementInTemplate(String templateName, String path)` | Check if a measurement exists in a template | `templateName`: The name of template,`path`: The path of the measurement | +| `isPathExistInTemplate(String templateName, String path)` | Check if a path exists in a template | same as above | +| `showMeasurementsInTemplate(String templateName)` | Show measurements in a template | `templateName`: The name of template | +| `showMeasurementsInTemplate(String templateName, String pattern)` | Show measurements in a template by pattern | `templateName`: The name of template,`pattern`: The matching pattern | +| `showAllTemplates()` | Show all templates | No parameters | +| `showPathsTemplateSetOn(String templateName)` | Show paths where a template is set | `templateName`: The name of the template | +| `showPathsTemplateUsingOn(String templateName)` | Show actual paths using a template | Same as above上 | +| `unsetSchemaTemplate(String prefixPath, String templateName)` | Unset the template setting for a path | `prefixPath`: The path,`templateName`: The name of template | + + +##### 3.2.2 Data Insertion + +| **Method Name** | **Function Description** | **Parameter Explanation** | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `insertRecord(String deviceId, long time, List measurements, List types, Object... values)` | Insert a single record | `deviceId`: Device ID,`time`: Timestamp,`measurements`: List of measurement points,`types`: List of data types,`values`: List of values | +| `insertRecord(String deviceId, long time, List measurements, List values)` | Insert a single record | `deviceId`: Device ID,`time`: Timestamp,`measurements`: List of measurement points,`values`: List of values | +| `insertRecords(List deviceIds, List times, List> measurementsList, List> valuesList)` | Insert multiple records | `deviceIds`: List of device IDs,`times`: List of timestamps,`measurementsList`: List of timestamps,`valuesList`: List of lists of values | +| `insertRecords(List deviceIds, List times, List> measurementsList, List> typesList, List> valuesList)` | Insert multiple records | Same as above,plus `typesList`: List of lists of data types | +| `insertRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> typesList, List> valuesList)` | Insert multiple records for a single device | `deviceId`: Device ID,`times`: List of timestamps,`measurementsList`: List of lists of measurement points,`typesList`: List of lists of types,`valuesList`: List of lists of values | +| `insertRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> typesList, List> valuesList, boolean haveSorted)` | Insert sorted multiple records for a single device | Same as above, plus `haveSorted`: Whether the data is already sorted | +| `insertStringRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> valuesList)` | Insert string-formatted records for a single device | `deviceId`: Device ID,`times`: List of timestamps,`measurementsList`: List of lists of measurement points,`valuesList`: List of lists of values | +| `insertStringRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> valuesList, boolean haveSorted)` | Insert sorted string-formatted records for a single device | Same as above, plus `haveSorted`: Whether the data is already sorted序 | +| `insertAlignedRecord(String deviceId, long time, List measurements, List types, List values)` | Insert a single aligned record | `deviceId`: Device ID,`time`: Timestamp,`measurements`: List of measurement points,`types`: List of types,`values`: List of values | +| `insertAlignedRecord(String deviceId, long time, List measurements, List values)` | Insert a single string-formatted aligned record | `deviceId`: Device ID`time`: Timestamp,`measurements`: List of measurement points,`values`: List of values | +| `insertAlignedRecords(List deviceIds, List times, List> measurementsList, List> valuesList)` | Insert multiple aligned records | `deviceIds`: List of device IDs,`times`: List of timestamps,`measurementsList`: List of lists of measurement points,`valuesList`: List of lists of values | +| `insertAlignedRecords(List deviceIds, List times, List> measurementsList, List> typesList, List> valuesList)` | Insert multiple aligned records | Same as above, plus `typesList`: List of lists of data types | +| `insertAlignedRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> typesList, List> valuesList)` | Insert multiple aligned records for a single device | Same as above | +| `insertAlignedRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> typesList, List> valuesList, boolean haveSorted)` | Insert sorted multiple aligned records for a single device | Same as above, plus `haveSorted`: Whether the data is already sorted | +| `insertAlignedStringRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> valuesList)` | Insert string-formatted aligned records for a single device | `deviceId`: Device ID,`times`: List of timestamps,`measurementsList`: List of lists of measurement points,`valuesList`: List of lists of values | +| `insertAlignedStringRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> valuesList, boolean haveSorted)` | Insert sorted string-formatted aligned records for a single device | Same as above, plus w `haveSorted`: whether the data is already sorted | +| `insertTablet(Tablet tablet)` | Insert a single Tablet data | `tablet`: The Tablet data to be inserted | +| `insertTablet(Tablet tablet, boolean sorted)` | Insert a sorted Tablet data | Same as above, plus `sorted`: whether the data is already sorted | +| `insertAlignedTablet(Tablet tablet)` | Insert an aligned Tablet data | `tablet`: The Tablet data to be inserted | +| `insertAlignedTablet(Tablet tablet, boolean sorted)` | Insert a sorted aligned Tablet data | Same as above, plus `sorted`: whether the data is already sorted | +| `insertTablets(Map tablets)` | Insert multiple Tablet data in batch | `tablets`: Mapping from device IDs to Tablet data | +| `insertTablets(Map tablets, boolean sorted)` | Insert sorted multiple Tablet data in batch | Same as above, plus `sorted`: whether the data is already sorted | +| `insertAlignedTablets(Map tablets)` | Insert multiple aligned Tablet data in batch | `tablets`: Mapping from device IDs to Tablet data | +| `insertAlignedTablets(Map tablets, boolean sorted)` | Insert sorted multiple aligned Tablet data in batch | Same as above, plus `sorted`: whether the data is already sorted | + +##### 3.2.3 Data Deletion + +| **Method Name** | **Function Description** | **Parameter Explanation** | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `deleteTimeseries(String path)` | Delete a single time series | `path`: The path of the time series | +| `deleteTimeseries(List paths)` | Batch delete time series | `paths`: A list of time series paths | +| `deleteData(String path, long endTime)` | Delete historical data for a specified path | `path`: The path,`endTime`: The end timestamp | +| `deleteData(List paths, long endTime)` | Batch delete historical data for specified paths | `paths`: A list of paths,`endTime`: The end timestamp | +| `deleteData(List paths, long startTime, long endTime)` | Delete historical data within a time range for specified paths | Same as above, plus `startTime`: The start timestamp | + + +##### 3.2.4 Data Query + +| **Method Name** | **Function Description** | **Parameter Explanation** | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `executeQueryStatement(String sql)` | Execute a query statement | `sql`: The query SQL statement | +| `executeQueryStatement(String sql, long timeoutInMs)` | Execute a query statement with timeout | `sql`: The query SQL statement, `timeoutInMs`: The query timeout (in milliseconds) | +| `executeRawDataQuery(List paths, long startTime, long endTime)` | Query raw data for specified paths | paths: A list of query paths, `startTime`: The start timestamp, `endTime`: The end timestamp | +| `executeRawDataQuery(List paths, long startTime, long endTime, long timeOut)` | Query raw data for specified paths (with timeout) | Same as above, plus `timeOut`: The timeout time | +| `executeLastDataQuery(List paths)` | Query the latest data | `paths`: A list of query paths | +| `executeLastDataQuery(List paths, long lastTime)` | Query the latest data at a specified time | `paths`: A list of query paths, `lastTime`: The specified timestamp | +| `executeLastDataQuery(List paths, long lastTime, long timeOut)` | Query the latest data at a specified time (with timeout) | Same as above, plus `timeOut`: The timeout time | +| `executeLastDataQueryForOneDevice(String db, String device, List sensors, boolean isLegalPathNodes)` | Query the latest data for a single device | `db`: The database name, `device`: The device name, `sensors`: A list of sensors, `isLegalPathNodes`: Whether the path nodes are legal | +| `executeAggregationQuery(List paths, List aggregations)` | Execute an aggregation query | `paths`: A list of query paths, `aggregations`: A list of aggregation types | +| `executeAggregationQuery(List paths, List aggregations, long startTime, long endTime)` | Execute an aggregation query with a time range | Same as above, plus `startTime`: The start timestamp, `endTime`:` The end timestamp | +| `executeAggregationQuery(List paths, List aggregations, long startTime, long endTime, long interval)` | Execute an aggregation query with a time interval | Same as above, plus `interval`: The time interval | +| `executeAggregationQuery(List paths, List aggregations, long startTime, long endTime, long interval, long slidingStep)` | Execute a sliding window aggregation query | Same as above, plus `slidingStep`: The sliding step | +| `fetchAllConnections()` | Get information of all active connections | No parameters | + +##### 3.2.5 System Status and Backup +|**Method Name** | **Function Description** | **Parameter Explanation** | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `getBackupConfiguration()` | Get backup configuration information | No parameters | +| `fetchAllConnections()` | Get information of all active connections | No parameters | +| `getSystemStatus()` | Get the system status | Deprecated, returns `SystemStatus.NORMAL` | \ No newline at end of file diff --git a/src/zh/UserGuide/Master/Tree/API/Programming-Data-Subscription.md b/src/zh/UserGuide/Master/Tree/API/Programming-Data-Subscription.md new file mode 100644 index 000000000..20e21caf0 --- /dev/null +++ b/src/zh/UserGuide/Master/Tree/API/Programming-Data-Subscription.md @@ -0,0 +1,244 @@ + + +# 数据订阅API +IoTDB 提供了强大的数据订阅功能,允许用户通过订阅SDK实时获取IoTDB新增的数据。详细的功能定义及介绍:[数据订阅](../User-Manual/Data-Sync.md) + +## 1 核心步骤 + +1. 创建Topic:创建一个Topic,Topic中包含希望订阅的测点。 +2. 订阅Topic:在 consumer 订阅 topic 前,topic 必须已经被创建,否则订阅会失败。同一个 consumer group 下的 consumers 会均分数据。 +3. 消费数据:只有显式订阅了某个 topic,才会收到对应 topic 的数据。 +4. 取消订阅: consumer close 时会退出对应的 consumer group,同时取消现存的所有订阅。 + + +## 2 详细步骤 +本章节用于说明开发的核心流程,并未演示所有的参数和接口,如需了解全部功能及参数请参见: [全量接口说明](./Programming-Java-Native-API.md#全量接口说明) + + +### 2.1 创建maven项目 +创建一个maven项目,并导入以下依赖(JDK >= 1.8, Maven >= 3.6) + +```xml + + + org.apache.iotdb + iotdb-session + + ${project.version} + + +``` + +### 2.2 代码案例 +#### 2.2.1 Topic操作 +```java +import java.util.Optional; +import java.util.Properties; +import java.util.Set; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.apache.iotdb.rpc.subscription.config.TopicConstant; +import org.apache.iotdb.session.subscription.SubscriptionSession; +import org.apache.iotdb.session.subscription.model.Topic; + +public class DataConsumerExample { + + public static void main(String[] args) throws IoTDBConnectionException, StatementExecutionException { + try (SubscriptionSession session = new SubscriptionSession("127.0.0.1", 6667)) { + // 1. open session + session.open(); + + // 2. create a topic of all data + Properties sessionConfig = new Properties(); + sessionConfig.put(TopicConstant.PATH_KEY, "root.**"); + + session.createTopic("allData", sessionConfig); + + // 3. show all topics + Set topics = session.getTopics(); + System.out.println(topics); + + // 4. show a specific topic + Optional allData = session.getTopic("allData"); + System.out.println(allData.get()); + } + } +} +``` +#### 2.2.2 数据消费 + +##### 场景-1: 订阅IoTDB中新增的实时数据(大屏或组态展示的场景) + +```java +import java.io.IOException; +import java.util.List; +import java.util.Properties; +import org.apache.iotdb.rpc.subscription.config.ConsumerConstant; +import org.apache.iotdb.rpc.subscription.config.TopicConstant; +import org.apache.iotdb.session.subscription.consumer.SubscriptionPullConsumer; +import org.apache.iotdb.session.subscription.payload.SubscriptionMessage; +import org.apache.iotdb.session.subscription.payload.SubscriptionMessageType; +import org.apache.iotdb.session.subscription.payload.SubscriptionSessionDataSet; +import org.apache.tsfile.read.common.RowRecord; + +public class DataConsumerExample { + + public static void main(String[] args) throws IOException { + + // 5. create a pull consumer, the subscription is automatically cancelled when the logic in the try resources is completed + Properties consumerConfig = new Properties(); + consumerConfig.put(ConsumerConstant.CONSUMER_ID_KEY, "c1"); + consumerConfig.put(ConsumerConstant.CONSUMER_GROUP_ID_KEY, "cg1"); + consumerConfig.put(ConsumerConstant.CONSUME_LISTENER_KEY, TopicConstant.FORMAT_SESSION_DATA_SETS_HANDLER_VALUE); + try (SubscriptionPullConsumer pullConsumer = new SubscriptionPullConsumer(consumerConfig)) { + pullConsumer.open(); + pullConsumer.subscribe("topic_all"); + while (true) { + List messages = pullConsumer.poll(10000); + for (final SubscriptionMessage message : messages) { + final short messageType = message.getMessageType(); + if (SubscriptionMessageType.isValidatedMessageType(messageType)) { + for (final SubscriptionSessionDataSet dataSet : message.getSessionDataSetsHandler()) { + while (dataSet.hasNext()) { + final RowRecord record = dataSet.next(); + System.out.println(record); + } + } + } + } + } + } + } +} + + +``` +##### 场景-2:订阅新增的 TsFile(定期数据备份的场景) + +前提:需要被消费的topic的格式为TsfileHandler类型,举例:`create topic topic_all_tsfile with ('path'='root.**','format'='TsFileHandler')` + +```java +import java.io.IOException; +import java.util.List; +import java.util.Properties; +import org.apache.iotdb.rpc.subscription.config.ConsumerConstant; +import org.apache.iotdb.rpc.subscription.config.TopicConstant; +import org.apache.iotdb.session.subscription.consumer.SubscriptionPullConsumer; +import org.apache.iotdb.session.subscription.payload.SubscriptionMessage; + + +public class DataConsumerExample { + + public static void main(String[] args) throws IOException { + // 1. create a pull consumer, the subscription is automatically cancelled when the logic in the try resources is completed + Properties consumerConfig = new Properties(); + consumerConfig.put(ConsumerConstant.CONSUMER_ID_KEY, "c1"); + consumerConfig.put(ConsumerConstant.CONSUMER_GROUP_ID_KEY, "cg1"); + // 2. Specify the consumption type as the tsfile type + consumerConfig.put(TopicConstant.FORMAT_KEY, TopicConstant.FORMAT_TS_FILE_HANDLER_VALUE); + consumerConfig.put(ConsumerConstant.FILE_SAVE_DIR_KEY, "/Users/iotdb/Downloads"); + try (SubscriptionPullConsumer pullConsumer = new SubscriptionPullConsumer(consumerConfig)) { + pullConsumer.open(); + pullConsumer.subscribe("topic_all_tsfile"); + while (true) { + List messages = pullConsumer.poll(10000); + for (final SubscriptionMessage message : messages) { + message.getTsFileHandler().copyFile("/Users/iotdb/Downloads/1.tsfile"); + } + } + } + } +} +``` + + + + +## 2 全量接口说明 + +### 2.1 参数列表 +可通过Properties参数对象设置消费者相关参数,具体参数如下。 + +#### 2.1.1 SubscriptionConsumer + + +| 参数 | 是否必填(默认值) | 参数含义 | +| :-------------------------------------------- | :--------------------------------- | :----------------------------------------------------------- | +| host | optional: 127.0.0.1 | `String`: IoTDB 中某 DataNode 的 RPC host | +| port | optional: 6667 | `Integer`: IoTDB 中某 DataNode 的 RPC port | +| node-urls | optional: 127.0.0.1:6667 | `List`: IoTDB 中所有 DataNode 的 RPC 地址,可以是多个;host:port 和 node-urls 选填一个即可。当 host:port 和 node-urls 都填写了,则取 host:port 和 node-urls 的**并集**构成新的 node-urls 应用 | +| username | optional: root | `String`: IoTDB 中 DataNode 的用户名 | +| password | optional: root | `String`: IoTDB 中 DataNode 的密码 | +| groupId | optional | `String`: consumer group id,若未指定则随机分配(新的 consumer group),保证不同的 consumer group 对应的 consumer group id 均不相同 | +| consumerId | optional | `String`: consumer client id,若未指定则随机分配,保证同一个 consumer group 中每一个 consumer client id 均不相同 | +| heartbeatIntervalMs | optional: 30000 (min: 1000) | `Long`: consumer 向 IoTDB DataNode 定期发送心跳请求的间隔 | +| endpointsSyncIntervalMs | optional: 120000 (min: 5000) | `Long`: consumer 探测 IoTDB 集群节点扩缩容情况调整订阅连接的间隔 | +| fileSaveDir | optional: Paths.get(System.getProperty("user.dir"), "iotdb-subscription").toString() | `String`: consumer 订阅出的 TsFile 文件临时存放的目录路径 | +| fileSaveFsync | optional: false | `Boolean`: consumer 订阅 TsFile 的过程中是否主动调用 fsync | + +`SubscriptionPushConsumer` 中的特殊配置: + +| 参数 | 是否必填(默认值) | 参数含义 | +| :-------------------------------------------- | :--------------------------------- | :----------------------------------------------------------- | +| ackStrategy | optional: `ACKStrategy.AFTER_CONSUME` | 消费进度的确认机制包含以下选项:`ACKStrategy.BEFORE_CONSUME`(当 consumer 收到数据时立刻提交消费进度,`onReceive` 前)`ACKStrategy.AFTER_CONSUME`(当 consumer 消费完数据再去提交消费进度,`onReceive` 后) | +| consumeListener | optional | 消费数据的回调函数,需实现 `ConsumeListener` 接口,定义消费 `SessionDataSetsHandler` 和 `TsFileHandler` 形式数据的处理逻辑 | +| autoPollIntervalMs | optional: 5000 (min: 500) | Long: consumer 自动拉取数据的时间间隔,单位为**毫秒** | +| autoPollTimeoutMs | optional: 10000 (min: 1000) | Long: consumer 每次拉取数据的超时时间,单位为**毫秒** | + +`SubscriptionPullConsumer` 中的特殊配置: + +| 参数 | 是否必填(默认值) | 参数含义 | +| :-------------------------------------------- | :--------------------------------- | :----------------------------------------------------------- | +| autoCommit | optional: true | Boolean: 是否自动提交消费进度如果此参数设置为 false,则需要调用 `commit` 方法来手动提交消费进度 | +| autoCommitInterval | optional: 5000 (min: 500) | Long: 自动提交消费进度的时间间隔,单位为**毫秒**仅当 autoCommit 参数为 true 的时候才会生效 | + + +### 函数列表 +#### 数据订阅 +##### SubscriptionPullConsumer + +| **函数名** | **说明** | **参数** | +|-------------------------------------|--------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `open()` | 打开消费者连接,启动消息消费。如果 `autoCommit` 启用,会启动自动提交工作器。 | 无 | +| `close()` | 关闭消费者连接。如果 `autoCommit` 启用,会在关闭前提交所有未提交的消息。 | 无 | +| `poll(final Duration timeout)` | 拉取消息,指定超时时间。 | `timeout` : 拉取的超时时间。 | +| `poll(final long timeoutMs)` | 拉取消息,指定超时时间(毫秒)。 | `timeoutMs` : 超时时间,单位为毫秒。 | +| `poll(final Set topicNames, final Duration timeout)` | 拉取指定主题的消息,指定超时时间。 | `topicNames` : 要拉取的主题集合。`timeout`: 超时时间。 | +| `poll(final Set topicNames, final long timeoutMs)` | 拉取指定主题的消息,指定超时时间(毫秒)。 | `topicNames` : 要拉取的主题集合。`timeoutMs`: 超时时间,单位为毫秒。 | +| `commitSync(final SubscriptionMessage message)` | 同步提交单条消息。 | `message` : 需要提交的消息对象。 | +| `commitSync(final Iterable messages)` | 同步提交多条消息。 | `messages` : 需要提交的消息集合。 | +| `commitAsync(final SubscriptionMessage message)` | 异步提交单条消息。 | `message` : 需要提交的消息对象。 | +| `commitAsync(final Iterable messages)` | 异步提交多条消息。 | `messages` : 需要提交的消息集合。 | +| `commitAsync(final SubscriptionMessage message, final AsyncCommitCallback callback)` | 异步提交单条消息并指定回调函数。 | `message` : 需要提交的消息对象。`callback` : 异步提交完成后的回调函数。 | +| `commitAsync(final Iterable messages, final AsyncCommitCallback callback)` | 异步提交多条消息并指定回调函数。 | `messages` : 需要提交的消息集合。`callback` : 异步提交完成后的回调函数。 | + +##### SubscriptionPushConsumer + +| **函数名** | **说明** | **参数** | +|-------------------------------------|----------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `open()` | 打开消费者连接,启动消息消费,提交自动轮询工作器。 | 无 | +| `close()` | 关闭消费者连接,停止消息消费。 | 无 | +| `toString()` | 返回消费者对象的核心配置信息。 | 无 | +| `coreReportMessage()` | 获取消费者核心配置的键值对表示形式。 | 无 | +| `allReportMessage()` | 获取消费者所有配置的键值对表示形式。 | 无 | +| `buildPushConsumer()` | 通过 `Builder` 构建 `SubscriptionPushConsumer` 实例。 | 无 | +| `ackStrategy(final AckStrategy ackStrategy)` | 配置消费者的消息确认策略。 | `ackStrategy`: 指定的消息确认策略。 | +| `consumeListener(final ConsumeListener consumeListener)` | 配置消费者的消息消费逻辑。 | `consumeListener`: 消费者接收消息时的处理逻辑。 | +| `autoPollIntervalMs(final long autoPollIntervalMs)` | 配置自动轮询的时间间隔。 | `autoPollIntervalMs` : 自动轮询的间隔时间,单位为毫秒。 | +| `autoPollTimeoutMs(final long autoPollTimeoutMs)` | 配置自动轮询的超时时间。 | `autoPollTimeoutMs`: 自动轮询的超时时间,单位为毫秒。 | diff --git a/src/zh/UserGuide/Master/Tree/API/Programming-Java-Native-API.md b/src/zh/UserGuide/Master/Tree/API/Programming-Java-Native-API.md index 7646aea3a..26ec9720d 100644 --- a/src/zh/UserGuide/Master/Tree/API/Programming-Java-Native-API.md +++ b/src/zh/UserGuide/Master/Tree/API/Programming-Java-Native-API.md @@ -19,778 +19,447 @@ --> -# Java 原生接口 +# Session原生API -## 安装 +IoTDB 原生 API 中的 Session 是实现与数据库交互的核心接口,它集成了丰富的方法,支持数据写入、查询以及元数据操作等功能。通过实例化 Session,能够建立与 IoTDB 服务器的连接,在该连接所构建的环境中执行各类数据库操作。Session为非线程安全,不能被多线程同时调用。 -### 依赖 +SessionPool 是 Session 的连接池,推荐使用SessionPool编程。在多线程并发的情形下,SessionPool 能够合理地管理和分配连接资源,以提升系统性能与资源利用效率。 -* JDK >= 1.8 -* Maven >= 3.6 +## 1 步骤概览 +1. 创建连接池实例:初始化一个SessionPool对象,用于管理多个Session实例。 +2. 执行操作:直接从SessionPool中获取Session实例,并执行数据库操作,无需每次都打开和关闭连接。 +3. 关闭连接池资源:在不再需要进行数据库操作时,关闭SessionPool,释放所有相关资源。 +## 2 详细步骤 +本章节用于说明开发的核心流程,并未演示所有的参数和接口,如需了解全部功能及参数请参见: [全量接口说明](./Programming-Java-Native-API.md#3-全量接口说明) 或 查阅: [源码](https://github.com/apache/iotdb/tree/master/example/session/src/main/java/org/apache/iotdb) -### 在 MAVEN 中使用原生接口 +### 2.1 创建maven项目 +创建一个maven项目,并在pom.xml文件中添加以下依赖(JDK >= 1.8, Maven >= 3.6) ```xml org.apache.iotdb iotdb-session + ${project.version} ``` -## 语法说明 - - - 对于 IoTDB-SQL 接口:传入的 SQL 参数需要符合 [语法规范](../User-Manual/Syntax-Rule.md#字面值常量) ,并且针对 JAVA 字符串进行反转义,如双引号前需要加反斜杠。(即:经 JAVA 转义之后与命令行执行的 SQL 语句一致。) - - 对于其他接口: - - 经参数传入的路径或路径前缀中的节点: 在 SQL 语句中需要使用反引号(`)进行转义的,此处均需要进行转义。 - - 经参数传入的标识符(如模板名):在 SQL 语句中需要使用反引号(`)进行转义的,均可以不用进行转义。 - - 语法说明相关代码示例可以参考:`example/session/src/main/java/org/apache/iotdb/SyntaxConventionRelatedExample.java` - -## 基本接口说明 - -下面将给出 Session 对应的接口的简要介绍和对应参数: - -### Session管理 - -* 初始化 Session - -``` java -// 全部使用默认配置 -session = new Session.Builder.build(); - -// 指定一个可连接节点 -session = - new Session.Builder() - .host(String host) - .port(int port) - .build(); - -// 指定多个可连接节点 -session = - new Session.Builder() - .nodeUrls(List nodeUrls) - .build(); - -// 其他配置项 -session = - new Session.Builder() - .fetchSize(int fetchSize) - .username(String username) - .password(String password) - .thriftDefaultBufferSize(int thriftDefaultBufferSize) - .thriftMaxFrameSize(int thriftMaxFrameSize) - .enableRedirection(boolean enableRedirection) - .version(Version version) - .build(); -``` - -其中,version 表示客户端使用的 SQL 语义版本,用于升级 0.13 时兼容 0.12 的 SQL 语义,可能取值有:`V_0_12`、`V_0_13`、`V_1_0`等。 - - -* 开启 Session - -``` java -void open() -``` - -* 开启 Session,并决定是否开启 RPC 压缩 - -``` java -void open(boolean enableRPCCompression) -``` - -注意: 客户端的 RPC 压缩开启状态需和服务端一致 - -* 关闭 Session - -``` java -void close() -``` - -* SessionPool - -我们提供了一个针对原生接口的连接池 (`SessionPool`),使用该接口时,你只需要指定连接池的大小,就可以在使用时从池中获取连接。 -如果超过 60s 都没得到一个连接的话,那么会打印一条警告日志,但是程序仍将继续等待。 - -当一个连接被用完后,他会自动返回池中等待下次被使用; -当一个连接损坏后,他会从池中被删除,并重建一个连接重新执行用户的操作; -你还可以像创建 Session 那样在创建 SessionPool 时指定多个可连接节点的 url,以保证分布式集群中客户端的高可用性。 - -对于查询操作: - -1. 使用 SessionPool 进行查询时,得到的结果集是`SessionDataSet`的封装类`SessionDataSetWrapper`; -2. 若对于一个查询的结果集,用户并没有遍历完且不再想继续遍历时,需要手动调用释放连接的操作`closeResultSet`; -3. 若对一个查询的结果集遍历时出现异常,也需要手动调用释放连接的操作`closeResultSet`. -4. 可以调用 `SessionDataSetWrapper` 的 `getColumnNames()` 方法得到结果集列名 - -使用示例可以参见 `session/src/test/java/org/apache/iotdb/session/pool/SessionPoolTest.java` - -或 `example/session/src/main/java/org/apache/iotdb/SessionPoolExample.java` - - -### 测点管理接口 - -#### Database 管理 - -* 设置 database - -``` java -void setStorageGroup(String storageGroupId) -``` - -* 删除单个或多个 database - -``` java -void deleteStorageGroup(String storageGroup) -void deleteStorageGroups(List storageGroups) -``` -#### 时间序列管理 - -* 创建单个或多个时间序列 - -``` java -void createTimeseries(String path, TSDataType dataType, - TSEncoding encoding, CompressionType compressor, Map props, - Map tags, Map attributes, String measurementAlias) - -void createMultiTimeseries(List paths, List dataTypes, - List encodings, List compressors, - List> propsList, List> tagsList, - List> attributesList, List measurementAliasList) -``` - -* 创建对齐时间序列 - -``` -void createAlignedTimeseries(String prefixPath, List measurements, - List dataTypes, List encodings, - List compressors, List measurementAliasList); -``` - -注意:目前**暂不支持**使用传感器别名。 - -* 删除一个或多个时间序列 - -``` java -void deleteTimeseries(String path) -void deleteTimeseries(List paths) -``` - -* 检测时间序列是否存在 - -``` java -boolean checkTimeseriesExists(String path) -``` - -#### 元数据模版 - -* 创建元数据模板,可以通过先后创建 Template、MeasurementNode 的对象,描述模板内物理量结构与类型、编码方式、压缩方式等信息,并通过以下接口创建模板 - -``` java -public void createSchemaTemplate(Template template); - -Class Template { - private String name; - private boolean directShareTime; - Map children; - public Template(String name, boolean isShareTime); - - public void addToTemplate(Node node); - public void deleteFromTemplate(String name); - public void setShareTime(boolean shareTime); -} - -Abstract Class Node { - private String name; - public void addChild(Node node); - public void deleteChild(Node node); -} - -Class MeasurementNode extends Node { - TSDataType dataType; - TSEncoding encoding; - CompressionType compressor; - public MeasurementNode(String name, - TSDataType dataType, - TSEncoding encoding, - CompressionType compressor); -} -``` - -通过上述类的实例描述模板时,Template 内应当仅能包含单层的 MeasurementNode,具体可以参见如下示例: - -``` java -MeasurementNode nodeX = new MeasurementNode("x", TSDataType.FLOAT, TSEncoding.RLE, CompressionType.SNAPPY); -MeasurementNode nodeY = new MeasurementNode("y", TSDataType.FLOAT, TSEncoding.RLE, CompressionType.SNAPPY); -MeasurementNode nodeSpeed = new MeasurementNode("speed", TSDataType.DOUBLE, TSEncoding.GORILLA, CompressionType.SNAPPY); - -// This is the template we suggest to implement -Template flatTemplate = new Template("flatTemplate"); -template.addToTemplate(nodeX); -template.addToTemplate(nodeY); -template.addToTemplate(nodeSpeed); - -createSchemaTemplate(flatTemplate); -``` - -* 完成模板挂载操作后,可以通过如下的接口在给定的设备上使用模板注册序列,或者也可以直接向相应的设备写入数据以自动使用模板注册序列。 - -``` java -void createTimeseriesUsingSchemaTemplate(List devicePathList) -``` - -* 将名为'templateName'的元数据模板挂载到'prefixPath'路径下,在执行这一步之前,你需要创建名为'templateName'的元数据模板 -* **请注意,我们强烈建议您将模板设置在 database 或 database 下层的节点中,以更好地适配未来版本更新及各模块的协作** - -``` java -void setSchemaTemplate(String templateName, String prefixPath) -``` - -- 将模板挂载到 MTree 上之后,你可以随时查询所有模板的名称、某模板被设置到 MTree 的所有路径、所有正在使用某模板的所有路径,即如下接口: - -``` java -/** @return All template names. */ -public List showAllTemplates(); - -/** @return All paths have been set to designated template. */ -public List showPathsTemplateSetOn(String templateName); - -/** @return All paths are using designated template. */ -public List showPathsTemplateUsingOn(String templateName) -``` - -- 如果你需要删除某一个模板,请确保在进行删除之前,MTree 上已经没有节点被挂载了模板,对于已经被挂载模板的节点,可以用如下接口卸载模板; - - -``` java -void unsetSchemaTemplate(String prefixPath, String templateName); -public void dropSchemaTemplate(String templateName); -``` - -* 请注意,如果一个子树中有多个孩子节点需要使用模板,可以在其共同父母节点上使用 setSchemaTemplate 。而只有在已有数据点插入模板对应的物理量时,模板才会被设置为激活状态,进而被 show timeseries 等查询检测到。 -* 卸载'prefixPath'路径下的名为'templateName'的元数据模板。你需要保证给定的路径'prefixPath'下需要有名为'templateName'的元数据模板。 - -注意:目前不支持从曾经在'prefixPath'路径及其后代节点使用模板插入数据后(即使数据已被删除)卸载模板。 - - -### 数据写入接口 - -推荐使用 insertTablet 帮助提高写入效率 - -* 插入一个 Tablet,Tablet 是一个设备若干行数据块,每一行的列都相同 - * **写入效率高** - * **支持批量写入** - * **支持写入空值**:空值处可以填入任意值,然后通过 BitMap 标记空值 - -``` java -void insertTablet(Tablet tablet) - -public class Tablet { - /** DeviceId if using tree-view interfaces or TableName when using table-view interfaces. */ - private String insertTargetName; - /** the list of measurement schemas for creating the tablet */ - private List schemas; - /** - * Marking the type of each column, namely ID or MEASUREMENT. Notice: the ID columns must be the - * FIRST ones. - */ - private List columnCategories; - /** timestamps in this tablet */ - private long[] timestamps; - /** each object is a primitive type array, which represents values of one measurement */ - private Object[] values; - /** each bitmap represents the existence of each value in the current column. */ - private BitMap[] bitMaps; - /** the number of rows to include in this tablet */ - private int rowSize; - /** the maximum number of rows for this tablet */ - private int maxRowNumber; -} -``` - -* 插入多个 Tablet - -``` java -void insertTablets(Map tablets) -``` - -* 插入一个 Record,一个 Record 是一个设备一个时间戳下多个测点的数据。这里的 value 是 Object 类型,相当于提供了一个公用接口,后面可以通过 TSDataType 将 value 强转为原类型 - - 其中,Object 类型与 TSDataType 类型的对应关系如下表所示: - - | TSDataType | Object | - |------------|--------------| - | BOOLEAN | Boolean | - | INT32 | Integer | - | DATE | LocalDate | - | INT64 | Long | - | TIMESTAMP | Long | - | FLOAT | Float | - | DOUBLE | Double | - | TEXT | String, Binary | - | STRING | String, Binary | - | BLOB | Binary | - -``` java -void insertRecord(String prefixPath, long time, List measurements, - List types, List values) -``` - -* 插入多个 Record - -``` java -void insertRecords(List deviceIds, - List times, - List> measurementsList, - List> typesList, - List> valuesList) -``` - -* 插入同属于一个 device 的多个 Record - -``` java -void insertRecordsOfOneDevice(String deviceId, List times, - List> measurementsList, List> typesList, - List> valuesList) -``` - -#### 带有类型推断的写入 - -当数据均是 String 类型时,我们可以使用如下接口,根据 value 的值进行类型推断。例如:value 为 "true" ,就可以自动推断为布尔类型。value 为 "3.2" ,就可以自动推断为数值类型。服务器需要做类型推断,可能会有额外耗时,速度较无需类型推断的写入慢 - -* 插入一个 Record,一个 Record 是一个设备一个时间戳下多个测点的数据 - -``` java -void insertRecord(String prefixPath, long time, List measurements, List values) -``` - -* 插入多个 Record - -``` java -void insertRecords(List deviceIds, List times, - List> measurementsList, List> valuesList) -``` - -* 插入同属于一个 device 的多个 Record - -``` java -void insertStringRecordsOfOneDevice(String deviceId, List times, - List> measurementsList, List> valuesList) -``` - -#### 对齐时间序列的写入 - -对齐时间序列的写入使用 insertAlignedXXX 接口,其余与上述接口类似: - -* insertAlignedRecord -* insertAlignedRecords -* insertAlignedRecordsOfOneDevice -* insertAlignedStringRecordsOfOneDevice -* insertAlignedTablet -* insertAlignedTablets - -### 数据删除接口 - -* 删除一个或多个时间序列在某个时间点前或这个时间点的数据 - -``` java -void deleteData(String path, long endTime) -void deleteData(List paths, long endTime) -``` - -### 数据查询接口 - -* 时间序列原始数据范围查询: - - 指定的查询时间范围为左闭右开区间,包含开始时间但不包含结束时间。 - -``` java -SessionDataSet executeRawDataQuery(List paths, long startTime, long endTime); -``` - -* 最新点查询: - - 查询最后一条时间戳大于等于某个时间点的数据。 - ``` java - SessionDataSet executeLastDataQuery(List paths, long lastTime); - ``` - - 快速查询单设备下指定序列最新点,支持重定向;如果您确认使用的查询路径是合法的,可将`isLegalPathNodes`置为true以避免路径校验带来的性能损失。 - ``` java - SessionDataSet executeLastDataQueryForOneDevice( - String db, String device, List sensors, boolean isLegalPathNodes); - ``` - -* 聚合查询: - - 支持指定查询时间范围。指定的查询时间范围为左闭右开区间,包含开始时间但不包含结束时间。 - - 支持按照时间区间分段查询。 - -``` java -SessionDataSet executeAggregationQuery(List paths, List aggregations); - -SessionDataSet executeAggregationQuery( - List paths, List aggregations, long startTime, long endTime); - -SessionDataSet executeAggregationQuery( - List paths, - List aggregations, - long startTime, - long endTime, - long interval); - -SessionDataSet executeAggregationQuery( - List paths, - List aggregations, - long startTime, - long endTime, - long interval, - long slidingStep); -``` - -* 直接执行查询语句 - -``` java -SessionDataSet executeQueryStatement(String sql) -``` - -### 数据订阅 - -#### 1 Topic 管理 - -IoTDB 订阅客户端中的 `SubscriptionSession` 类提供了 Topic 管理的相关接口。Topic状态变化如下图所示: - -
- -
- -##### 1.1 创建 Topic - -```Java - void createTopicIfNotExists(String topicName, Properties properties) throws Exception; -``` - -示例: - -```Java -try (final SubscriptionSession session = new SubscriptionSession(host, port)) { - session.open(); - final Properties config = new Properties(); - config.put(TopicConstant.PATH_KEY, "root.db.**"); - session.createTopic(topicName, config); +### 2.2 创建连接池实例 + +```java +import java.util.ArrayList; +import java.util.List; +import org.apache.iotdb.session.pool.SessionPool; + +public class IoTDBSessionPoolExample { + private static SessionPool sessionPool; + + public static void main(String[] args) { + // Using nodeUrls ensures that when one node goes down, other nodes are automatically connected to retry + List nodeUrls = new ArrayList<>(); + nodeUrls.add("127.0.0.1:6667"); + nodeUrls.add("127.0.0.1:6668"); + sessionPool = + new SessionPool.Builder() + .nodeUrls(nodeUrls) + .user("root") + .password("root") + .maxSize(3) + .build(); + } } ``` -##### 1.2 删除 Topic - -```Java -void dropTopicIfExists(String topicName) throws Exception; -``` - -##### 1.3 查看 Topic - -```Java -// 获取所有 topics -Set getTopics() throws Exception; - -// 获取单个 topic -Optional getTopic(String topicName) throws Exception; -``` - -#### 2 查看订阅状态 - -IoTDB 订阅客户端中的 `SubscriptionSession` 类提供了获取订阅状态的相关接口: - -```Java -Set getSubscriptions() throws Exception; -Set getSubscriptions(final String topicName) throws Exception; -``` - -#### 3 创建 Consumer - -在使用 JAVA 原生接口创建 consumer 时,需要指定 consumer 所应用的参数。 - -对于 `SubscriptionPullConsumer` 和 `SubscriptionPushConsumer` 而言,有以下公共配置: +### 2.3 执行数据库操作 +#### 2.3.1 数据写入 +在工业场景中,数据写入可分为以下几类:多行数据写入、单设备多行数据写入,下面按不同场景对写入接口进行介绍。 -| 参数 | 是否必填(默认值) | 参数含义 | -| :-------------------------------------------- | :--------------------------------- | :----------------------------------------------------------- | -| host | optional: 127.0.0.1 | `String`: IoTDB 中某 DataNode 的 RPC host | -| port | optional: 6667 | `Integer`: IoTDB 中某 DataNode 的 RPC port | -| node-urls | optional: 127.0.0.1:6667 | `List`: IoTDB 中所有 DataNode 的 RPC 地址,可以是多个;host:port 和 node-urls 选填一个即可。当 host:port 和 node-urls 都填写了,则取 host:port 和 node-urls 的**并集**构成新的 node-urls 应用 | -| username | optional: root | `String`: IoTDB 中 DataNode 的用户名 | -| password | optional: root | `String`: IoTDB 中 DataNode 的密码 | -| groupId | optional | `String`: consumer group id,若未指定则随机分配(新的 consumer group),保证不同的 consumer group 对应的 consumer group id 均不相同 | -| consumerId | optional | `String`: consumer client id,若未指定则随机分配,保证同一个 consumer group 中每一个 consumer client id 均不相同 | -| heartbeatIntervalMs | optional: 30000 (min: 1000) | `Long`: consumer 向 IoTDB DataNode 定期发送心跳请求的间隔 | -| endpointsSyncIntervalMs | optional: 120000 (min: 5000) | `Long`: consumer 探测 IoTDB 集群节点扩缩容情况调整订阅连接的间隔 | -| fileSaveDir | optional: Paths.get(System.getProperty("user.dir"), "iotdb-subscription").toString() | `String`: consumer 订阅出的 TsFile 文件临时存放的目录路径 | -| fileSaveFsync | optional: false | `Boolean`: consumer 订阅 TsFile 的过程中是否主动调用 fsync | +##### 多行数据写入接口 +接口说明:支持一次写入多行数据,每一行对应一个设备一个时间戳的多个测点值。 -##### 3.1 SubscriptionPushConsumer +接口列表: -以下为 `SubscriptionPushConsumer` 中的特殊配置: -| 参数 | 是否必填(默认值) | 参数含义 | -| :-------------------------------------------- | :--------------------------------- | :----------------------------------------------------------- | -| ackStrategy | optional: `ACKStrategy.AFTER_CONSUME` | 消费进度的确认机制包含以下选项:`ACKStrategy.BEFORE_CONSUME`(当 consumer 收到数据时立刻提交消费进度,`onReceive` 前)`ACKStrategy.AFTER_CONSUME`(当 consumer 消费完数据再去提交消费进度,`onReceive` 后) | -| consumeListener | optional | 消费数据的回调函数,需实现 `ConsumeListener` 接口,定义消费 `SessionDataSetsHandler` 和 `TsFileHandler` 形式数据的处理逻辑 | -| autoPollIntervalMs | optional: 5000 (min: 500) | Long: consumer 自动拉取数据的时间间隔,单位为**毫秒** | -| autoPollTimeoutMs | optional: 10000 (min: 1000) | Long: consumer 每次拉取数据的超时时间,单位为**毫秒** | +| 接口名称 | 功能描述 | +|--------------------------------------------------------|-----------------------| +| `insertRecords(List deviceIds, List times, List> measurementsList, List> typesList, List> valuesList)` | 插入多行数据,适用于不同测点独立采集的场景 | -其中,`ConsumerListener` 接口定义如下: +代码案例: +```java +import java.util.ArrayList; +import java.util.List; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.apache.iotdb.session.pool.SessionPool; +import org.apache.tsfile.enums.TSDataType; -```Java -@FunctionInterface -interface ConsumeListener { - default ConsumeResult onReceive(Message message) { - return ConsumeResult.SUCCESS; +public class SessionPoolExample { + private static SessionPool sessionPool; + public static void main(String[] args) throws IoTDBConnectionException, StatementExecutionException { + // 1. init SessionPool + constructSessionPool(); + // 2. execute insert data + insertRecordsExample(); + // 3. close SessionPool + closeSessionPool(); } -} - -enum ConsumeResult { - SUCCESS, - FAILURE, -} -``` - -##### 3.2 SubscriptionPullConsumer - -以下为 `SubscriptionPullConsumer` 中的特殊配置: - -| 参数 | 是否必填(默认值) | 参数含义 | -| :-------------------------------------------- | :--------------------------------- | :----------------------------------------------------------- | -| autoCommit | optional: true | Boolean: 是否自动提交消费进度如果此参数设置为 false,则需要调用 `commit` 方法来手动提交消费进度 | -| autoCommitInterval | optional: 5000 (min: 500) | Long: 自动提交消费进度的时间间隔,单位为**毫秒**仅当 autoCommit 参数为 true 的时候才会生效 | - -在创建 consumer 后,需要手动调用 consumer 的 open 方法: - -```Java -void open() throws Exception; -``` - -此时,IoTDB 订阅客户端才会校验 consumer 的配置正确性,在校验成功后 consumer 就会加入对应的 consumer group。也就是说,在打开 consumer 后,才可以使用返回的 consumer 对象进行订阅 Topic,消费数据等操作。 - -#### 4 订阅 Topic - -`SubscriptionPushConsumer` 和 `SubscriptionPullConsumer` 提供了下述 JAVA 原生接口用于订阅 Topics: - -```Java -// 订阅 topics -void subscribe(String topic) throws Exception; -void subscribe(List topics) throws Exception; -``` - -- 在 consumer 订阅 topic 前,topic 必须已经被创建,否则订阅会失败 -- 一个 consumer 在已经订阅了某个 topic 的情况下再次订阅这个 topic,不会报错 -- 如果该 consumer 所在的 consumer group 中已经有 consumers 订阅了相同的 topics,那么该 consumer 将会复用对应的消费进度 - -#### 5 消费数据 - -无论是 push 模式还是 pull 模式的 consumer: - -- 只有显式订阅了某个 topic,才会收到对应 topic 的数据 -- 若在创建后没有订阅任何 topics,此时该 consumer 无法消费到任何数据,即使该 consumer 所在的 consumer group 中其它的 consumers 订阅了一些 topics - -##### 5.1 SubscriptionPushConsumer - -SubscriptionPushConsumer 在订阅 topics 后,无需手动拉取数据,其消费数据的逻辑在创建 SubscriptionPushConsumer 指定的 `consumeListener` 配置中。 - -##### 5.2 SubscriptionPullConsumer - -SubscriptionPullConsumer 在订阅 topics 后,需要主动调用 `poll` 方法拉取数据: - -```Java -List poll(final Duration timeout) throws Exception; -List poll(final long timeoutMs) throws Exception; -List poll(final Set topicNames, final Duration timeout) throws Exception; -List poll(final Set topicNames, final long timeoutMs) throws Exception; -``` - -在 poll 方法中可以指定需要拉取的 topic 名称(如果不指定则默认拉取该 consumer 已订阅的所有 topics)和超时时间。 - -当 SubscriptionPullConsumer 配置 autoCommit 参数为 false 时,需要手动调用 commitSync 和 commitAsync 方法同步或异步提交某批数据的消费进度: - -```Java -void commitSync(final SubscriptionMessage message) throws Exception; -void commitSync(final Iterable messages) throws Exception; - -CompletableFuture commitAsync(final SubscriptionMessage message); -CompletableFuture commitAsync(final Iterable messages); -void commitAsync(final SubscriptionMessage message, final AsyncCommitCallback callback); -void commitAsync(final Iterable messages, final AsyncCommitCallback callback); -``` -AsyncCommitCallback 类定义如下: - -```Java -public interface AsyncCommitCallback { - default void onComplete() { - // Do nothing - } + private static void constructSessionPool() { + // Using nodeUrls ensures that when one node goes down, other nodes are automatically connected to retry + List nodeUrls = new ArrayList<>(); + nodeUrls.add("127.0.0.1:6667"); + nodeUrls.add("127.0.0.1:6668"); + sessionPool = + new SessionPool.Builder() + .nodeUrls(nodeUrls) + .user("root") + .password("root") + .maxSize(3) + .build(); + } - default void onFailure(final Throwable e) { - // Do nothing - } + public static void insertRecordsExample() throws IoTDBConnectionException, StatementExecutionException { + String deviceId = "root.sg1.d1"; + List measurements = new ArrayList<>(); + measurements.add("s1"); + measurements.add("s2"); + measurements.add("s3"); + List deviceIds = new ArrayList<>(); + List> measurementsList = new ArrayList<>(); + List> valuesList = new ArrayList<>(); + List timestamps = new ArrayList<>(); + List> typesList = new ArrayList<>(); + + for (long time = 0; time < 500; time++) { + List values = new ArrayList<>(); + List types = new ArrayList<>(); + values.add(1L); + values.add(2L); + values.add(3L); + types.add(TSDataType.INT64); + types.add(TSDataType.INT64); + types.add(TSDataType.INT64); + + deviceIds.add(deviceId); + measurementsList.add(measurements); + valuesList.add(values); + typesList.add(types); + timestamps.add(time); + if (time != 0 && time % 100 == 0) { + try { + sessionPool.insertRecords(deviceIds, timestamps, measurementsList, typesList, valuesList); + } catch (IoTDBConnectionException | StatementExecutionException e) { + // solve exception + } + deviceIds.clear(); + measurementsList.clear(); + valuesList.clear(); + typesList.clear(); + timestamps.clear(); + } + } + try { + sessionPool.insertRecords(deviceIds, timestamps, measurementsList, typesList, valuesList); + } catch (IoTDBConnectionException | StatementExecutionException e) { + // solve exception + } + } + + public static void closeSessionPool(){ + sessionPool.close(); + } } ``` +##### 单设备多行数据写入接口 +接口说明:支持一次写入单个设备的多行数据,每一行对应一个时间戳的多个测点值。 + +接口列表: + +| 接口名称 | 功能描述 | +|-----------------------------------------------------------------------------------------|----------------------------| +| `insertTablet(Tablet tablet)` | 插入单个设备的多行数据,适用于不同测点独立采集的场景 | + +代码案例: +```java +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.apache.iotdb.session.pool.SessionPool; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.write.record.Tablet; +import org.apache.tsfile.write.schema.MeasurementSchema; + +public class SessionPoolExample { + private static SessionPool sessionPool; + public static void main(String[] args) throws IoTDBConnectionException, StatementExecutionException { + // 1. init SessionPool + constructSessionPool(); + // 2. execute insert data + insertTabletExample(); + // 3. close SessionPool + closeSessionPool(); + } + + private static void constructSessionPool() { + // Using nodeUrls ensures that when one node goes down, other nodes are automatically connected to retry + List nodeUrls = new ArrayList<>(); + nodeUrls.add("127.0.0.1:6667"); + nodeUrls.add("127.0.0.1:6668"); + sessionPool = + new SessionPool.Builder() + .nodeUrls(nodeUrls) + .user("root") + .password("root") + .maxSize(3) + .build(); + } -#### 6 取消订阅 - -`SubscriptionPushConsumer` 和 `SubscriptionPullConsumer` 提供了下述 JAVA 原生接口用于取消订阅并关闭 consumer: - -```Java -// 取消订阅 topics -void unsubscribe(String topic) throws Exception; -void unsubscribe(List topics) throws Exception; - -// 关闭 consumer -void close(); -``` - -- 在 topic 存在的情况下,如果一个 consumer 在没有订阅了某个 topic 的情况下取消订阅某个 topic,不会报错 -- consumer close 时会退出对应的 consumer group,同时自动 unsubscribe 该 consumer 现存订阅的所有 topics -- consumer 在 close 后生命周期即结束,无法再重新 open 订阅并消费数据 - -#### 7 代码示例 - -##### 7.1 单 Pull Consumer 消费 SessionDataSetsHandler 形式的数据 - -```Java -// Create topics -try (final SubscriptionSession session = new SubscriptionSession(HOST, PORT)) { - session.open(); - final Properties config = new Properties(); - config.put(TopicConstant.PATH_KEY, "root.db.**"); - session.createTopic(TOPIC_1, config); -} - -// Subscription: property-style ctor -final Properties config = new Properties(); -config.put(ConsumerConstant.CONSUMER_ID_KEY, "c1"); -config.put(ConsumerConstant.CONSUMER_GROUP_ID_KEY, "cg1"); - -final SubscriptionPullConsumer consumer1 = new SubscriptionPullConsumer(config); -consumer1.open(); -consumer1.subscribe(TOPIC_1); -while (true) { - LockSupport.parkNanos(SLEEP_NS); // wait some time - final List messages = consumer1.poll(POLL_TIMEOUT_MS); - for (final SubscriptionMessage message : messages) { - for (final SubscriptionSessionDataSet dataSet : message.getSessionDataSetsHandler()) { - System.out.println(dataSet.getColumnNames()); - System.out.println(dataSet.getColumnTypes()); - while (dataSet.hasNext()) { - System.out.println(dataSet.next()); - } + private static void insertTabletExample() throws IoTDBConnectionException, StatementExecutionException { + /* + * A Tablet example: + * device1 + * time s1, s2, s3 + * 1, 1, 1, 1 + * 2, 2, 2, 2 + * 3, 3, 3, 3 + */ + // The schema of measurements of one device + // only measurementId and data type in MeasurementSchema take effects in Tablet + List schemaList = new ArrayList<>(); + schemaList.add(new MeasurementSchema("s1", TSDataType.INT64)); + schemaList.add(new MeasurementSchema("s2", TSDataType.INT64)); + schemaList.add(new MeasurementSchema("s3", TSDataType.INT64)); + + Tablet tablet = new Tablet("root.sg.d1", schemaList, 100); + + // Method 1 to add tablet data + long timestamp = System.currentTimeMillis(); + + Random random = new Random(); + for (long row = 0; row < 100; row++) { + int rowIndex = tablet.rowSize++; + tablet.addTimestamp(rowIndex, timestamp); + for (int s = 0; s < 3; s++) { + long value = random.nextLong(); + tablet.addValue(schemaList.get(s).getMeasurementId(), rowIndex, value); + } + if (tablet.rowSize == tablet.getMaxRowNumber()) { + sessionPool.insertTablet(tablet); + tablet.reset(); + } + timestamp++; + } + if (tablet.rowSize != 0) { + sessionPool.insertTablet(tablet); + tablet.reset(); + } } - } - // Auto commit -} -// Show topics and subscriptions -try (final SubscriptionSession session = new SubscriptionSession(HOST, PORT)) { - session.open(); - session.getTopics().forEach((System.out::println)); - session.getSubscriptions().forEach((System.out::println)); + public static void closeSessionPool(){ + sessionPool.close(); + } } - -consumer1.unsubscribe(TOPIC_1); -consumer1.close(); ``` -##### 7.2 多 Push Consumer 消费 TsFileHandler 形式的数据 - -```Java -// Create topics -try (final SubscriptionSession subscriptionSession = new SubscriptionSession(HOST, PORT)) { - subscriptionSession.open(); - final Properties config = new Properties(); - config.put(TopicConstant.FORMAT_KEY, TopicConstant.FORMAT_TS_FILE_HANDLER_VALUE); - subscriptionSession.createTopic(TOPIC_2, config); -} +#### 2.3.2 SQL操作 + +SQL操作分为查询和非查询两类操作,对应的接口为`executeQuery`和`executeNonQuery`操作,其区别为前者执行的是具体的查询语句,会返回一个结果集,后者是执行的是增、删、改操作,不返回结果集。 + +```java +import java.util.ArrayList; +import java.util.List; +import org.apache.iotdb.isession.pool.SessionDataSetWrapper; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.apache.iotdb.session.pool.SessionPool; + +public class SessionPoolExample { + private static SessionPool sessionPool; + public static void main(String[] args) throws IoTDBConnectionException, StatementExecutionException { + // 1. init SessionPool + constructSessionPool(); + // 2. executes a non-query SQL statement, such as a DDL or DML command. + executeQueryExample(); + // 3. executes a query SQL statement and returns the result set. + executeNonQueryExample(); + // 4. close SessionPool + closeSessionPool(); + } -final List threads = new ArrayList<>(); -for (int i = 0; i < 8; ++i) { - final int idx = i; - final Thread thread = - new Thread( - () -> { - // Subscription: builder-style ctor - try (final SubscriptionPushConsumer consumer2 = - new SubscriptionPushConsumer.Builder() - .consumerId("c" + idx) - .consumerGroupId("cg2") - .fileSaveDir(System.getProperty("java.io.tmpdir")) - .ackStrategy(AckStrategy.AFTER_CONSUME) - .consumeListener( - message -> { - doSomething(message.getTsFileHandler()); - return ConsumeResult.SUCCESS; - }) - .buildPushConsumer()) { - consumer2.open(); - consumer2.subscribe(TOPIC_2); - // block the consumer main thread - Thread.sleep(Long.MAX_VALUE); - } catch (final IOException | InterruptedException e) { - throw new RuntimeException(e); + private static void executeNonQueryExample() throws IoTDBConnectionException, StatementExecutionException { + // 1. create a nonAligned time series + sessionPool.executeNonQueryStatement("create timeseries root.test.d1.s1 with dataType = int32"); + // 2. set ttl + sessionPool.executeNonQueryStatement("set TTL to root.test.** 10000"); + // 3. delete time series + sessionPool.executeNonQueryStatement("delete timeseries root.test.d1.s1"); + private static void executeQueryExample() throws IoTDBConnectionException, StatementExecutionException { + // 1. execute normal query + try(SessionDataSetWrapper wrapper = sessionPool.executeQueryStatement("select s1 from root.sg1.d1 limit 10")) { + while (wrapper.hasNext()) { + System.out.println(wrapper.next()); } - }); - thread.start(); - threads.add(thread); -} + } + // 2. execute aggregate query + try(SessionDataSetWrapper wrapper = sessionPool.executeQueryStatement("select count(s1) from root.sg1.d1 group by ([0, 40), 5ms) ")) { + while (wrapper.hasNext()) { + System.out.println(wrapper.next()); + } + } + } + + private static void constructSessionPool() { + // Using nodeUrls ensures that when one node goes down, other nodes are automatically connected to retry + List nodeUrls = new ArrayList<>(); + nodeUrls.add("127.0.0.1:6667"); + nodeUrls.add("127.0.0.1:6668"); + sessionPool = + new SessionPool.Builder() + .nodeUrls(nodeUrls) + .user("root") + .password("root") + .maxSize(3) + .build(); + } -for (final Thread thread : threads) { - thread.join(); + public static void closeSessionPool(){ + sessionPool.close(); + } } ``` - -### 其他功能(直接执行SQL语句) - -``` java -void executeNonQueryStatement(String sql) -``` - -### 写入测试接口 (用于分析网络带宽) - -不实际写入数据,只将数据传输到 server 即返回 - -* 测试 insertRecord - -``` java -void testInsertRecord(String deviceId, long time, List measurements, List values) - -void testInsertRecord(String deviceId, long time, List measurements, - List types, List values) -``` - -* 测试 testInsertRecords - -``` java -void testInsertRecords(List deviceIds, List times, - List> measurementsList, List> valuesList) - -void testInsertRecords(List deviceIds, List times, - List> measurementsList, List> typesList, - List> valuesList) -``` - -* 测试 insertTablet - -``` java -void testInsertTablet(Tablet tablet) -``` - -* 测试 insertTablets - -``` java -void testInsertTablets(Map tablets) -``` - -### 示例代码 - -浏览上述接口的详细信息,请参阅代码 ```session/src/main/java/org/apache/iotdb/session/Session.java``` - -使用上述接口的示例代码在 ```example/session/src/main/java/org/apache/iotdb/SessionExample.java``` - -使用对齐时间序列和元数据模板的示例可以参见 `example/session/src/main/java/org/apache/iotdb/AlignedTimeseriesSessionExample.java` \ No newline at end of file +### 3 全量接口说明 + +#### 3.1 参数列表 +Session具有如下的字段,可以通过构造函数或Session.Builder方式设置如下参数 + +| 字段名 | 类型 | 说明 | +|--------------------------------|-------------------------------|----------------------------------------------------------------------| +| `nodeUrls` | `List` | 数据库节点的 URL 列表,支持多节点连接 | +| `username` | `String` | 用户名 | +| `password` | `String` | 密码 | +| `fetchSize` | `int` | 查询结果的默认批量返回大小 | +| `useSSL` | `boolean` | 是否启用 SSL | +| `trustStore` | `String` | 信任库路径 | +| `trustStorePwd` | `String` | 信任库密码 | +| `queryTimeoutInMs` | `long` | 查询的超时时间,单位毫秒 | +| `enableRPCCompression` | `boolean` | 是否启用 RPC 压缩 | +| `connectionTimeoutInMs` | `int` | 连接超时时间,单位毫秒 | +| `zoneId` | `ZoneId` | 会话的时区设置 | +| `thriftDefaultBufferSize` | `int` | Thrift 默认缓冲区大小 | +| `thriftMaxFrameSize` | `int` | Thrift 最大帧大小 | +| `defaultEndPoint` | `TEndPoint` | 默认的数据库端点信息 | +| `defaultSessionConnection` | `SessionConnection` | 默认的会话连接对象 | +| `isClosed` | `boolean` | 当前会话是否已关闭 | +| `enableRedirection` | `boolean` | 是否启用重定向功能 | +| `enableRecordsAutoConvertTablet` | `boolean` | 是否启用记录自动转换为 Tablet 的功能 | +| `deviceIdToEndpoint` | `Map` | 设备 ID 和数据库端点的映射关系 | +| `endPointToSessionConnection` | `Map` | 数据库端点和会话连接的映射关系 | +| `executorService` | `ScheduledExecutorService` | 用于定期更新节点列表的线程池 | +| `availableNodes` | `INodeSupplier` | 可用节点的供应器 | +| `enableQueryRedirection` | `boolean` | 是否启用查询重定向功能 | +| `version` | `Version` | 客户端的版本号,用于与服务端的兼容性判断 | +| `enableAutoFetch` | `boolean` | 是否启用自动获取功能 | +| `maxRetryCount` | `int` | 最大重试次数 | +| `retryIntervalInMs` | `long` | 重试的间隔时间,单位毫秒 | + + + +#### 3.2 接口列表 + +##### 3.2.1 元数据管理 + +| 方法名 | 功能描述 | 参数解释 | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `createDatabase(String database)` | 创建数据库 | `database`: 数据库名称 | +| `deleteDatabase(String database)` | 删除指定数据库 | `database`: 要删除的数据库名称 | +| `deleteDatabases(List databases)` | 批量删除数据库 | `databases`: 要删除的数据库名称列表 | +| `createTimeseries(String path, TSDataType dataType, TSEncoding encoding, CompressionType compressor)` | 创建单个时间序列 | `path`: 时间序列路径,`dataType`: 数据类型,`encoding`: 编码类型,`compressor`: 压缩类型 | +| `createAlignedTimeseries(...)` | 创建对齐时间序列 | 设备ID、测点列表、数据类型列表、编码列表、压缩类型列表 | +| `createMultiTimeseries(...)` | 批量创建时间序列 | 多个路径、数据类型、编码、压缩类型、属性、标签、别名等 | +| `deleteTimeseries(String path)` | 删除时间序列 | `path`: 要删除的时间序列路径 | +| `deleteTimeseries(List paths)` | 批量删除时间序列 | `paths`: 要删除的时间序列路径列表 | +| `setSchemaTemplate(String templateName, String prefixPath)` | 设置模式模板 | `templateName`: 模板名称,`prefixPath`: 应用模板的路径 | +| `createSchemaTemplate(Template template)` | 创建模式模板 | `template`: 模板对象 | +| `dropSchemaTemplate(String templateName)` | 删除模式模板 | `templateName`: 要删除的模板名称 | +| `addAlignedMeasurementsInTemplate(...)` | 添加对齐测点到模板 | 模板名称、测点路径列表、数据类型、编码类型、压缩类型 | +| `addUnalignedMeasurementsInTemplate(...)` | 添加非对齐测点到模板 | 同上 | +| `deleteNodeInTemplate(String templateName, String path)` | 删除模板中的节点 | `templateName`: 模板名称,`path`: 要删除的路径 | +| `countMeasurementsInTemplate(String name)` | 统计模板中测点数量 | `name`: 模板名称 | +| `isMeasurementInTemplate(String templateName, String path)` | 检查模板中是否存在某测点 | `templateName`: 模板名称,`path`: 测点路径 | +| `isPathExistInTemplate(String templateName, String path)` | 检查模板中路径是否存在 | 同上 | +| `showMeasurementsInTemplate(String templateName)` | 显示模板中的测点 | `templateName`: 模板名称 | +| `showMeasurementsInTemplate(String templateName, String pattern)` | 按模式显示模板中的测点 | `templateName`: 模板名称,`pattern`: 匹配模式 | +| `showAllTemplates()` | 显示所有模板 | 无参数 | +| `showPathsTemplateSetOn(String templateName)` | 显示模板应用的路径 | `templateName`: 模板名称 | +| `showPathsTemplateUsingOn(String templateName)` | 显示模板实际使用的路径 | 同上 | +| `unsetSchemaTemplate(String prefixPath, String templateName)` | 取消路径的模板设置 | `prefixPath`: 路径,`templateName`: 模板名称 | + + +##### 3.2.2 数据写入 +| 方法名 | 功能描述 | 参数解释 | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `insertRecord(String deviceId, long time, List measurements, List types, Object... values)` | 插入单条记录 | `deviceId`: 设备ID,`time`: 时间戳,`measurements`: 测点列表,`types`: 数据类型列表,`values`: 值列表 | +| `insertRecord(String deviceId, long time, List measurements, List values)` | 插入单条记录 | `deviceId`: 设备ID,`time`: 时间戳,`measurements`: 测点列表,`values`: 值列表 | +| `insertRecords(List deviceIds, List times, List> measurementsList, List> valuesList)` | 插入多条记录 | `deviceIds`: 设备ID列表,`times`: 时间戳列表,`measurementsList`: 测点列表列表,`valuesList`: 值列表 | +| `insertRecords(List deviceIds, List times, List> measurementsList, List> typesList, List> valuesList)` | 插入多条记录 | 同上,增加 `typesList`: 数据类型列表 | +| `insertRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> typesList, List> valuesList)` | 插入单设备的多条记录 | `deviceId`: 设备ID,`times`: 时间戳列表,`measurementsList`: 测点列表列表,`typesList`: 类型列表,`valuesList`: 值列表 | +| `insertRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> typesList, List> valuesList, boolean haveSorted)` | 插入排序后的单设备多条记录 | 同上,增加 `haveSorted`: 数据是否已排序 | +| `insertStringRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> valuesList)` | 插入字符串格式的单设备记录 | `deviceId`: 设备ID,`times`: 时间戳列表,`measurementsList`: 测点列表,`valuesList`: 值列表 | +| `insertStringRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> valuesList, boolean haveSorted)` | 插入排序的字符串格式单设备记录 | 同上,增加 `haveSorted`: 数据是否已排序 | +| `insertAlignedRecord(String deviceId, long time, List measurements, List types, List values)` | 插入单条对齐记录 | `deviceId`: 设备ID,`time`: 时间戳,`measurements`: 测点列表,`types`: 类型列表,`values`: 值列表 | +| `insertAlignedRecord(String deviceId, long time, List measurements, List values)` | 插入字符串格式的单条对齐记录 | `deviceId`: 设备ID,`time`: 时间戳,`measurements`: 测点列表,`values`: 值列表 | +| `insertAlignedRecords(List deviceIds, List times, List> measurementsList, List> valuesList)` | 插入多条对齐记录 | `deviceIds`: 设备ID列表,`times`: 时间戳列表,`measurementsList`: 测点列表,`valuesList`: 值列表 | +| `insertAlignedRecords(List deviceIds, List times, List> measurementsList, List> typesList, List> valuesList)` | 插入多条对齐记录 | 同上,增加 `typesList`: 数据类型列表 | +| `insertAlignedRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> typesList, List> valuesList)` | 插入单设备的多条对齐记录 | 同上 | +| `insertAlignedRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> typesList, List> valuesList, boolean haveSorted)` | 插入排序的单设备多条对齐记录 | 同上,增加 `haveSorted`: 数据是否已排序 | +| `insertAlignedStringRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> valuesList)` | 插入字符串格式的单设备对齐记录 | `deviceId`: 设备ID,`times`: 时间戳列表,`measurementsList`: 测点列表,`valuesList`: 值列表 | +| `insertAlignedStringRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> valuesList, boolean haveSorted)` | 插入排序的字符串格式单设备对齐记录 | 同上,增加 `haveSorted`: 数据是否已排序 | +| `insertTablet(Tablet tablet)` | 插入单个Tablet数据 | `tablet`: 要插入的Tablet数据 | +| `insertTablet(Tablet tablet, boolean sorted)` | 插入排序的Tablet数据 | 同上,增加 `sorted`: 数据是否已排序 | +| `insertAlignedTablet(Tablet tablet)` | 插入对齐的Tablet数据 | `tablet`: 要插入的Tablet数据 | +| `insertAlignedTablet(Tablet tablet, boolean sorted)` | 插入排序的对齐Tablet数据 | 同上,增加 `sorted`: 数据是否已排序 | +| `insertTablets(Map tablets)` | 批量插入多个Tablet数据 | `tablets`: 设备ID到Tablet的映射表 | +| `insertTablets(Map tablets, boolean sorted)` | 批量插入排序的多个Tablet数据 | 同上,增加 `sorted`: 数据是否已排序 | +| `insertAlignedTablets(Map tablets)` | 批量插入多个对齐Tablet数据 | `tablets`: 设备ID到Tablet的映射表 | +| `insertAlignedTablets(Map tablets, boolean sorted)` | 批量插入排序的多个对齐Tablet数据 | 同上,增加 `sorted`: 数据是否已排序 | + +##### 3.2.3 数据删除 + +| 方法名 | 功能描述 | 参数解释 | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `deleteTimeseries(String path)` | 删除单个时间序列 | `path`: 时间序列路径 | +| `deleteTimeseries(List paths)` | 批量删除时间序列 | `paths`: 时间序列路径列表 | +| `deleteData(String path, long endTime)` | 删除指定路径的历史数据 | `path`: 路径,`endTime`: 结束时间戳 | +| `deleteData(List paths, long endTime)` | 批量删除路径的历史数据 | `paths`: 路径列表,`endTime`: 结束时间戳 | +| `deleteData(List paths, long startTime, long endTime)` | 删除路径时间范围内的历史数据 | 同上,增加 `startTime`: 起始时间戳 | + + +##### 3.2.4 数据查询 +| 方法名 | 功能描述 | 参数解释 | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `executeQueryStatement(String sql)` | 执行查询语句 | `sql`: 查询SQL语句 | +| `executeQueryStatement(String sql, long timeoutInMs)` | 执行带超时的查询语句 | `sql`: 查询SQL语句,`timeoutInMs`: 查询超时时间(毫秒) | +| `executeRawDataQuery(List paths, long startTime, long endTime)` | 查询指定路径的原始数据 | `paths`: 查询路径列表,`startTime`: 起始时间戳,`endTime`: 结束时间戳 | +| `executeRawDataQuery(List paths, long startTime, long endTime, long timeOut)` | 查询指定路径的原始数据(带超时) | 同上,增加 `timeOut`: 超时时间 | +| `executeLastDataQuery(List paths)` | 查询最新数据 | `paths`: 查询路径列表 | +| `executeLastDataQuery(List paths, long lastTime)` | 查询指定时间的最新数据 | `paths`: 查询路径列表,`lastTime`: 指定的时间戳 | +| `executeLastDataQuery(List paths, long lastTime, long timeOut)` | 查询指定时间的最新数据(带超时) | 同上,增加 `timeOut`: 超时时间 | +| `executeLastDataQueryForOneDevice(String db, String device, List sensors, boolean isLegalPathNodes)` | 查询单个设备的最新数据 | `db`: 数据库名,`device`: 设备名,`sensors`: 传感器列表,`isLegalPathNodes`: 是否合法路径节点 | +| `executeAggregationQuery(List paths, List aggregations)` | 执行聚合查询 | `paths`: 查询路径列表,`aggregations`: 聚合类型列表 | +| `executeAggregationQuery(List paths, List aggregations, long startTime, long endTime)` | 执行带时间范围的聚合查询 | 同上,增加 `startTime`: 起始时间戳,`endTime`: 结束时间戳 | +| `executeAggregationQuery(List paths, List aggregations, long startTime, long endTime, long interval)` | 执行带时间间隔的聚合查询 | 同上,增加 `interval`: 时间间隔 | +| `executeAggregationQuery(List paths, List aggregations, long startTime, long endTime, long interval, long slidingStep)` | 执行滑动窗口聚合查询 | 同上,增加 `slidingStep`: 滑动步长 | +| `fetchAllConnections()` | 获取所有活动连接信息 | 无参数 | + +##### 3.2.5 系统状态与备份 +| 方法名 | 功能描述 | 参数解释 | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `getBackupConfiguration()` | 获取备份配置信息 | 无参数 | +| `fetchAllConnections()` | 获取所有活动的连接信息 | 无参数 | +| `getSystemStatus()` | 获取系统状态 | 已废弃,默认返回 `SystemStatus.NORMAL` | \ No newline at end of file diff --git a/src/zh/UserGuide/Master/Tree/User-Manual/Data-Sync.md b/src/zh/UserGuide/Master/Tree/User-Manual/Data-Sync.md new file mode 100644 index 000000000..0dc62fe47 --- /dev/null +++ b/src/zh/UserGuide/Master/Tree/User-Manual/Data-Sync.md @@ -0,0 +1,23 @@ +--- +redirectTo: Data-Sync_apache.html +--- + diff --git a/src/zh/UserGuide/V2.0.1/Tree/API/Programming-Data-Subscription.md b/src/zh/UserGuide/V2.0.1/Tree/API/Programming-Data-Subscription.md new file mode 100644 index 000000000..20e21caf0 --- /dev/null +++ b/src/zh/UserGuide/V2.0.1/Tree/API/Programming-Data-Subscription.md @@ -0,0 +1,244 @@ + + +# 数据订阅API +IoTDB 提供了强大的数据订阅功能,允许用户通过订阅SDK实时获取IoTDB新增的数据。详细的功能定义及介绍:[数据订阅](../User-Manual/Data-Sync.md) + +## 1 核心步骤 + +1. 创建Topic:创建一个Topic,Topic中包含希望订阅的测点。 +2. 订阅Topic:在 consumer 订阅 topic 前,topic 必须已经被创建,否则订阅会失败。同一个 consumer group 下的 consumers 会均分数据。 +3. 消费数据:只有显式订阅了某个 topic,才会收到对应 topic 的数据。 +4. 取消订阅: consumer close 时会退出对应的 consumer group,同时取消现存的所有订阅。 + + +## 2 详细步骤 +本章节用于说明开发的核心流程,并未演示所有的参数和接口,如需了解全部功能及参数请参见: [全量接口说明](./Programming-Java-Native-API.md#全量接口说明) + + +### 2.1 创建maven项目 +创建一个maven项目,并导入以下依赖(JDK >= 1.8, Maven >= 3.6) + +```xml + + + org.apache.iotdb + iotdb-session + + ${project.version} + + +``` + +### 2.2 代码案例 +#### 2.2.1 Topic操作 +```java +import java.util.Optional; +import java.util.Properties; +import java.util.Set; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.apache.iotdb.rpc.subscription.config.TopicConstant; +import org.apache.iotdb.session.subscription.SubscriptionSession; +import org.apache.iotdb.session.subscription.model.Topic; + +public class DataConsumerExample { + + public static void main(String[] args) throws IoTDBConnectionException, StatementExecutionException { + try (SubscriptionSession session = new SubscriptionSession("127.0.0.1", 6667)) { + // 1. open session + session.open(); + + // 2. create a topic of all data + Properties sessionConfig = new Properties(); + sessionConfig.put(TopicConstant.PATH_KEY, "root.**"); + + session.createTopic("allData", sessionConfig); + + // 3. show all topics + Set topics = session.getTopics(); + System.out.println(topics); + + // 4. show a specific topic + Optional allData = session.getTopic("allData"); + System.out.println(allData.get()); + } + } +} +``` +#### 2.2.2 数据消费 + +##### 场景-1: 订阅IoTDB中新增的实时数据(大屏或组态展示的场景) + +```java +import java.io.IOException; +import java.util.List; +import java.util.Properties; +import org.apache.iotdb.rpc.subscription.config.ConsumerConstant; +import org.apache.iotdb.rpc.subscription.config.TopicConstant; +import org.apache.iotdb.session.subscription.consumer.SubscriptionPullConsumer; +import org.apache.iotdb.session.subscription.payload.SubscriptionMessage; +import org.apache.iotdb.session.subscription.payload.SubscriptionMessageType; +import org.apache.iotdb.session.subscription.payload.SubscriptionSessionDataSet; +import org.apache.tsfile.read.common.RowRecord; + +public class DataConsumerExample { + + public static void main(String[] args) throws IOException { + + // 5. create a pull consumer, the subscription is automatically cancelled when the logic in the try resources is completed + Properties consumerConfig = new Properties(); + consumerConfig.put(ConsumerConstant.CONSUMER_ID_KEY, "c1"); + consumerConfig.put(ConsumerConstant.CONSUMER_GROUP_ID_KEY, "cg1"); + consumerConfig.put(ConsumerConstant.CONSUME_LISTENER_KEY, TopicConstant.FORMAT_SESSION_DATA_SETS_HANDLER_VALUE); + try (SubscriptionPullConsumer pullConsumer = new SubscriptionPullConsumer(consumerConfig)) { + pullConsumer.open(); + pullConsumer.subscribe("topic_all"); + while (true) { + List messages = pullConsumer.poll(10000); + for (final SubscriptionMessage message : messages) { + final short messageType = message.getMessageType(); + if (SubscriptionMessageType.isValidatedMessageType(messageType)) { + for (final SubscriptionSessionDataSet dataSet : message.getSessionDataSetsHandler()) { + while (dataSet.hasNext()) { + final RowRecord record = dataSet.next(); + System.out.println(record); + } + } + } + } + } + } + } +} + + +``` +##### 场景-2:订阅新增的 TsFile(定期数据备份的场景) + +前提:需要被消费的topic的格式为TsfileHandler类型,举例:`create topic topic_all_tsfile with ('path'='root.**','format'='TsFileHandler')` + +```java +import java.io.IOException; +import java.util.List; +import java.util.Properties; +import org.apache.iotdb.rpc.subscription.config.ConsumerConstant; +import org.apache.iotdb.rpc.subscription.config.TopicConstant; +import org.apache.iotdb.session.subscription.consumer.SubscriptionPullConsumer; +import org.apache.iotdb.session.subscription.payload.SubscriptionMessage; + + +public class DataConsumerExample { + + public static void main(String[] args) throws IOException { + // 1. create a pull consumer, the subscription is automatically cancelled when the logic in the try resources is completed + Properties consumerConfig = new Properties(); + consumerConfig.put(ConsumerConstant.CONSUMER_ID_KEY, "c1"); + consumerConfig.put(ConsumerConstant.CONSUMER_GROUP_ID_KEY, "cg1"); + // 2. Specify the consumption type as the tsfile type + consumerConfig.put(TopicConstant.FORMAT_KEY, TopicConstant.FORMAT_TS_FILE_HANDLER_VALUE); + consumerConfig.put(ConsumerConstant.FILE_SAVE_DIR_KEY, "/Users/iotdb/Downloads"); + try (SubscriptionPullConsumer pullConsumer = new SubscriptionPullConsumer(consumerConfig)) { + pullConsumer.open(); + pullConsumer.subscribe("topic_all_tsfile"); + while (true) { + List messages = pullConsumer.poll(10000); + for (final SubscriptionMessage message : messages) { + message.getTsFileHandler().copyFile("/Users/iotdb/Downloads/1.tsfile"); + } + } + } + } +} +``` + + + + +## 2 全量接口说明 + +### 2.1 参数列表 +可通过Properties参数对象设置消费者相关参数,具体参数如下。 + +#### 2.1.1 SubscriptionConsumer + + +| 参数 | 是否必填(默认值) | 参数含义 | +| :-------------------------------------------- | :--------------------------------- | :----------------------------------------------------------- | +| host | optional: 127.0.0.1 | `String`: IoTDB 中某 DataNode 的 RPC host | +| port | optional: 6667 | `Integer`: IoTDB 中某 DataNode 的 RPC port | +| node-urls | optional: 127.0.0.1:6667 | `List`: IoTDB 中所有 DataNode 的 RPC 地址,可以是多个;host:port 和 node-urls 选填一个即可。当 host:port 和 node-urls 都填写了,则取 host:port 和 node-urls 的**并集**构成新的 node-urls 应用 | +| username | optional: root | `String`: IoTDB 中 DataNode 的用户名 | +| password | optional: root | `String`: IoTDB 中 DataNode 的密码 | +| groupId | optional | `String`: consumer group id,若未指定则随机分配(新的 consumer group),保证不同的 consumer group 对应的 consumer group id 均不相同 | +| consumerId | optional | `String`: consumer client id,若未指定则随机分配,保证同一个 consumer group 中每一个 consumer client id 均不相同 | +| heartbeatIntervalMs | optional: 30000 (min: 1000) | `Long`: consumer 向 IoTDB DataNode 定期发送心跳请求的间隔 | +| endpointsSyncIntervalMs | optional: 120000 (min: 5000) | `Long`: consumer 探测 IoTDB 集群节点扩缩容情况调整订阅连接的间隔 | +| fileSaveDir | optional: Paths.get(System.getProperty("user.dir"), "iotdb-subscription").toString() | `String`: consumer 订阅出的 TsFile 文件临时存放的目录路径 | +| fileSaveFsync | optional: false | `Boolean`: consumer 订阅 TsFile 的过程中是否主动调用 fsync | + +`SubscriptionPushConsumer` 中的特殊配置: + +| 参数 | 是否必填(默认值) | 参数含义 | +| :-------------------------------------------- | :--------------------------------- | :----------------------------------------------------------- | +| ackStrategy | optional: `ACKStrategy.AFTER_CONSUME` | 消费进度的确认机制包含以下选项:`ACKStrategy.BEFORE_CONSUME`(当 consumer 收到数据时立刻提交消费进度,`onReceive` 前)`ACKStrategy.AFTER_CONSUME`(当 consumer 消费完数据再去提交消费进度,`onReceive` 后) | +| consumeListener | optional | 消费数据的回调函数,需实现 `ConsumeListener` 接口,定义消费 `SessionDataSetsHandler` 和 `TsFileHandler` 形式数据的处理逻辑 | +| autoPollIntervalMs | optional: 5000 (min: 500) | Long: consumer 自动拉取数据的时间间隔,单位为**毫秒** | +| autoPollTimeoutMs | optional: 10000 (min: 1000) | Long: consumer 每次拉取数据的超时时间,单位为**毫秒** | + +`SubscriptionPullConsumer` 中的特殊配置: + +| 参数 | 是否必填(默认值) | 参数含义 | +| :-------------------------------------------- | :--------------------------------- | :----------------------------------------------------------- | +| autoCommit | optional: true | Boolean: 是否自动提交消费进度如果此参数设置为 false,则需要调用 `commit` 方法来手动提交消费进度 | +| autoCommitInterval | optional: 5000 (min: 500) | Long: 自动提交消费进度的时间间隔,单位为**毫秒**仅当 autoCommit 参数为 true 的时候才会生效 | + + +### 函数列表 +#### 数据订阅 +##### SubscriptionPullConsumer + +| **函数名** | **说明** | **参数** | +|-------------------------------------|--------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `open()` | 打开消费者连接,启动消息消费。如果 `autoCommit` 启用,会启动自动提交工作器。 | 无 | +| `close()` | 关闭消费者连接。如果 `autoCommit` 启用,会在关闭前提交所有未提交的消息。 | 无 | +| `poll(final Duration timeout)` | 拉取消息,指定超时时间。 | `timeout` : 拉取的超时时间。 | +| `poll(final long timeoutMs)` | 拉取消息,指定超时时间(毫秒)。 | `timeoutMs` : 超时时间,单位为毫秒。 | +| `poll(final Set topicNames, final Duration timeout)` | 拉取指定主题的消息,指定超时时间。 | `topicNames` : 要拉取的主题集合。`timeout`: 超时时间。 | +| `poll(final Set topicNames, final long timeoutMs)` | 拉取指定主题的消息,指定超时时间(毫秒)。 | `topicNames` : 要拉取的主题集合。`timeoutMs`: 超时时间,单位为毫秒。 | +| `commitSync(final SubscriptionMessage message)` | 同步提交单条消息。 | `message` : 需要提交的消息对象。 | +| `commitSync(final Iterable messages)` | 同步提交多条消息。 | `messages` : 需要提交的消息集合。 | +| `commitAsync(final SubscriptionMessage message)` | 异步提交单条消息。 | `message` : 需要提交的消息对象。 | +| `commitAsync(final Iterable messages)` | 异步提交多条消息。 | `messages` : 需要提交的消息集合。 | +| `commitAsync(final SubscriptionMessage message, final AsyncCommitCallback callback)` | 异步提交单条消息并指定回调函数。 | `message` : 需要提交的消息对象。`callback` : 异步提交完成后的回调函数。 | +| `commitAsync(final Iterable messages, final AsyncCommitCallback callback)` | 异步提交多条消息并指定回调函数。 | `messages` : 需要提交的消息集合。`callback` : 异步提交完成后的回调函数。 | + +##### SubscriptionPushConsumer + +| **函数名** | **说明** | **参数** | +|-------------------------------------|----------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `open()` | 打开消费者连接,启动消息消费,提交自动轮询工作器。 | 无 | +| `close()` | 关闭消费者连接,停止消息消费。 | 无 | +| `toString()` | 返回消费者对象的核心配置信息。 | 无 | +| `coreReportMessage()` | 获取消费者核心配置的键值对表示形式。 | 无 | +| `allReportMessage()` | 获取消费者所有配置的键值对表示形式。 | 无 | +| `buildPushConsumer()` | 通过 `Builder` 构建 `SubscriptionPushConsumer` 实例。 | 无 | +| `ackStrategy(final AckStrategy ackStrategy)` | 配置消费者的消息确认策略。 | `ackStrategy`: 指定的消息确认策略。 | +| `consumeListener(final ConsumeListener consumeListener)` | 配置消费者的消息消费逻辑。 | `consumeListener`: 消费者接收消息时的处理逻辑。 | +| `autoPollIntervalMs(final long autoPollIntervalMs)` | 配置自动轮询的时间间隔。 | `autoPollIntervalMs` : 自动轮询的间隔时间,单位为毫秒。 | +| `autoPollTimeoutMs(final long autoPollTimeoutMs)` | 配置自动轮询的超时时间。 | `autoPollTimeoutMs`: 自动轮询的超时时间,单位为毫秒。 | diff --git a/src/zh/UserGuide/V2.0.1/Tree/API/Programming-Java-Native-API.md b/src/zh/UserGuide/V2.0.1/Tree/API/Programming-Java-Native-API.md index 81c7cac7d..26ec9720d 100644 --- a/src/zh/UserGuide/V2.0.1/Tree/API/Programming-Java-Native-API.md +++ b/src/zh/UserGuide/V2.0.1/Tree/API/Programming-Java-Native-API.md @@ -19,775 +19,447 @@ --> -# Java 原生接口 +# Session原生API -## 安装 +IoTDB 原生 API 中的 Session 是实现与数据库交互的核心接口,它集成了丰富的方法,支持数据写入、查询以及元数据操作等功能。通过实例化 Session,能够建立与 IoTDB 服务器的连接,在该连接所构建的环境中执行各类数据库操作。Session为非线程安全,不能被多线程同时调用。 -### 依赖 +SessionPool 是 Session 的连接池,推荐使用SessionPool编程。在多线程并发的情形下,SessionPool 能够合理地管理和分配连接资源,以提升系统性能与资源利用效率。 -* JDK >= 1.8 -* Maven >= 3.6 +## 1 步骤概览 +1. 创建连接池实例:初始化一个SessionPool对象,用于管理多个Session实例。 +2. 执行操作:直接从SessionPool中获取Session实例,并执行数据库操作,无需每次都打开和关闭连接。 +3. 关闭连接池资源:在不再需要进行数据库操作时,关闭SessionPool,释放所有相关资源。 +## 2 详细步骤 +本章节用于说明开发的核心流程,并未演示所有的参数和接口,如需了解全部功能及参数请参见: [全量接口说明](./Programming-Java-Native-API.md#3-全量接口说明) 或 查阅: [源码](https://github.com/apache/iotdb/tree/master/example/session/src/main/java/org/apache/iotdb) -### 在 MAVEN 中使用原生接口 +### 2.1 创建maven项目 +创建一个maven项目,并在pom.xml文件中添加以下依赖(JDK >= 1.8, Maven >= 3.6) ```xml org.apache.iotdb iotdb-session + ${project.version} ``` -## 语法说明 - - - 对于 IoTDB-SQL 接口:传入的 SQL 参数需要符合 [语法规范](../Reference/Syntax-Rule.md#字面值常量) ,并且针对 JAVA 字符串进行反转义,如双引号前需要加反斜杠。(即:经 JAVA 转义之后与命令行执行的 SQL 语句一致。) - - 对于其他接口: - - 经参数传入的路径或路径前缀中的节点: 在 SQL 语句中需要使用反引号(`)进行转义的,此处均需要进行转义。 - - 经参数传入的标识符(如模板名):在 SQL 语句中需要使用反引号(`)进行转义的,均可以不用进行转义。 - - 语法说明相关代码示例可以参考:`example/session/src/main/java/org/apache/iotdb/SyntaxConventionRelatedExample.java` - -## 基本接口说明 - -下面将给出 Session 对应的接口的简要介绍和对应参数: - -### Session管理 - -* 初始化 Session - -``` java -// 全部使用默认配置 -session = new Session.Builder.build(); - -// 指定一个可连接节点 -session = - new Session.Builder() - .host(String host) - .port(int port) - .build(); - -// 指定多个可连接节点 -session = - new Session.Builder() - .nodeUrls(List nodeUrls) - .build(); - -// 其他配置项 -session = - new Session.Builder() - .fetchSize(int fetchSize) - .username(String username) - .password(String password) - .thriftDefaultBufferSize(int thriftDefaultBufferSize) - .thriftMaxFrameSize(int thriftMaxFrameSize) - .enableRedirection(boolean enableRedirection) - .version(Version version) - .build(); -``` - -其中,version 表示客户端使用的 SQL 语义版本,用于升级 0.13 时兼容 0.12 的 SQL 语义,可能取值有:`V_0_12`、`V_0_13`、`V_1_0`等。 - - -* 开启 Session - -``` java -void open() -``` - -* 开启 Session,并决定是否开启 RPC 压缩 - -``` java -void open(boolean enableRPCCompression) -``` - -注意: 客户端的 RPC 压缩开启状态需和服务端一致 - -* 关闭 Session - -``` java -void close() -``` - -* SessionPool - -我们提供了一个针对原生接口的连接池 (`SessionPool`),使用该接口时,你只需要指定连接池的大小,就可以在使用时从池中获取连接。 -如果超过 60s 都没得到一个连接的话,那么会打印一条警告日志,但是程序仍将继续等待。 - -当一个连接被用完后,他会自动返回池中等待下次被使用; -当一个连接损坏后,他会从池中被删除,并重建一个连接重新执行用户的操作; -你还可以像创建 Session 那样在创建 SessionPool 时指定多个可连接节点的 url,以保证分布式集群中客户端的高可用性。 - -对于查询操作: - -1. 使用 SessionPool 进行查询时,得到的结果集是`SessionDataSet`的封装类`SessionDataSetWrapper`; -2. 若对于一个查询的结果集,用户并没有遍历完且不再想继续遍历时,需要手动调用释放连接的操作`closeResultSet`; -3. 若对一个查询的结果集遍历时出现异常,也需要手动调用释放连接的操作`closeResultSet`. -4. 可以调用 `SessionDataSetWrapper` 的 `getColumnNames()` 方法得到结果集列名 - -使用示例可以参见 `session/src/test/java/org/apache/iotdb/session/pool/SessionPoolTest.java` - -或 `example/session/src/main/java/org/apache/iotdb/SessionPoolExample.java` - - -### 测点管理接口 - -#### Database 管理 - -* 设置 database - -``` java -void setStorageGroup(String storageGroupId) -``` - -* 删除单个或多个 database - -``` java -void deleteStorageGroup(String storageGroup) -void deleteStorageGroups(List storageGroups) -``` -#### 时间序列管理 - -* 创建单个或多个时间序列 - -``` java -void createTimeseries(String path, TSDataType dataType, - TSEncoding encoding, CompressionType compressor, Map props, - Map tags, Map attributes, String measurementAlias) - -void createMultiTimeseries(List paths, List dataTypes, - List encodings, List compressors, - List> propsList, List> tagsList, - List> attributesList, List measurementAliasList) -``` - -* 创建对齐时间序列 - -``` -void createAlignedTimeseries(String prefixPath, List measurements, - List dataTypes, List encodings, - List compressors, List measurementAliasList); -``` - -注意:目前**暂不支持**使用传感器别名。 - -* 删除一个或多个时间序列 - -``` java -void deleteTimeseries(String path) -void deleteTimeseries(List paths) -``` - -* 检测时间序列是否存在 - -``` java -boolean checkTimeseriesExists(String path) -``` - -#### 元数据模版 - -* 创建元数据模板,可以通过先后创建 Template、MeasurementNode 的对象,描述模板内物理量结构与类型、编码方式、压缩方式等信息,并通过以下接口创建模板 - -``` java -public void createSchemaTemplate(Template template); - -Class Template { - private String name; - private boolean directShareTime; - Map children; - public Template(String name, boolean isShareTime); - - public void addToTemplate(Node node); - public void deleteFromTemplate(String name); - public void setShareTime(boolean shareTime); -} - -Abstract Class Node { - private String name; - public void addChild(Node node); - public void deleteChild(Node node); -} - -Class MeasurementNode extends Node { - TSDataType dataType; - TSEncoding encoding; - CompressionType compressor; - public MeasurementNode(String name, - TSDataType dataType, - TSEncoding encoding, - CompressionType compressor); -} -``` - -通过上述类的实例描述模板时,Template 内应当仅能包含单层的 MeasurementNode,具体可以参见如下示例: - -``` java -MeasurementNode nodeX = new MeasurementNode("x", TSDataType.FLOAT, TSEncoding.RLE, CompressionType.SNAPPY); -MeasurementNode nodeY = new MeasurementNode("y", TSDataType.FLOAT, TSEncoding.RLE, CompressionType.SNAPPY); -MeasurementNode nodeSpeed = new MeasurementNode("speed", TSDataType.DOUBLE, TSEncoding.GORILLA, CompressionType.SNAPPY); - -// This is the template we suggest to implement -Template flatTemplate = new Template("flatTemplate"); -template.addToTemplate(nodeX); -template.addToTemplate(nodeY); -template.addToTemplate(nodeSpeed); - -createSchemaTemplate(flatTemplate); -``` - -* 完成模板挂载操作后,可以通过如下的接口在给定的设备上使用模板注册序列,或者也可以直接向相应的设备写入数据以自动使用模板注册序列。 - -``` java -void createTimeseriesUsingSchemaTemplate(List devicePathList) -``` - -* 将名为'templateName'的元数据模板挂载到'prefixPath'路径下,在执行这一步之前,你需要创建名为'templateName'的元数据模板 -* **请注意,我们强烈建议您将模板设置在 database 或 database 下层的节点中,以更好地适配未来版本更新及各模块的协作** - -``` java -void setSchemaTemplate(String templateName, String prefixPath) -``` - -- 将模板挂载到 MTree 上之后,你可以随时查询所有模板的名称、某模板被设置到 MTree 的所有路径、所有正在使用某模板的所有路径,即如下接口: - -``` java -/** @return All template names. */ -public List showAllTemplates(); - -/** @return All paths have been set to designated template. */ -public List showPathsTemplateSetOn(String templateName); - -/** @return All paths are using designated template. */ -public List showPathsTemplateUsingOn(String templateName) -``` - -- 如果你需要删除某一个模板,请确保在进行删除之前,MTree 上已经没有节点被挂载了模板,对于已经被挂载模板的节点,可以用如下接口卸载模板; - - -``` java -void unsetSchemaTemplate(String prefixPath, String templateName); -public void dropSchemaTemplate(String templateName); -``` - -* 请注意,如果一个子树中有多个孩子节点需要使用模板,可以在其共同父母节点上使用 setSchemaTemplate 。而只有在已有数据点插入模板对应的物理量时,模板才会被设置为激活状态,进而被 show timeseries 等查询检测到。 -* 卸载'prefixPath'路径下的名为'templateName'的元数据模板。你需要保证给定的路径'prefixPath'下需要有名为'templateName'的元数据模板。 - -注意:目前不支持从曾经在'prefixPath'路径及其后代节点使用模板插入数据后(即使数据已被删除)卸载模板。 - - -### 数据写入接口 - -推荐使用 insertTablet 帮助提高写入效率 - -* 插入一个 Tablet,Tablet 是一个设备若干行数据块,每一行的列都相同 - * **写入效率高** - * **支持批量写入** - * **支持写入空值**:空值处可以填入任意值,然后通过 BitMap 标记空值 - -``` java -void insertTablet(Tablet tablet) - -public class Tablet { - /** deviceId of this tablet */ - public String prefixPath; - /** the list of measurement schemas for creating the tablet */ - private List schemas; - /** timestamps in this tablet */ - public long[] timestamps; - /** each object is a primitive type array, which represents values of one measurement */ - public Object[] values; - /** each bitmap represents the existence of each value in the current column. */ - public BitMap[] bitMaps; - /** the number of rows to include in this tablet */ - public int rowSize; - /** the maximum number of rows for this tablet */ - private int maxRowNumber; - /** whether this tablet store data of aligned timeseries or not */ - private boolean isAligned; -} -``` - -* 插入多个 Tablet - -``` java -void insertTablets(Map tablets) -``` - -* 插入一个 Record,一个 Record 是一个设备一个时间戳下多个测点的数据。这里的 value 是 Object 类型,相当于提供了一个公用接口,后面可以通过 TSDataType 将 value 强转为原类型 - - 其中,Object 类型与 TSDataType 类型的对应关系如下表所示: - - | TSDataType | Object | - |------------|--------------| - | BOOLEAN | Boolean | - | INT32 | Integer | - | DATE | LocalDate | - | INT64 | Long | - | TIMESTAMP | Long | - | FLOAT | Float | - | DOUBLE | Double | - | TEXT | String, Binary | - | STRING | String, Binary | - | BLOB | Binary | - -``` java -void insertRecord(String prefixPath, long time, List measurements, - List types, List values) -``` - -* 插入多个 Record - -``` java -void insertRecords(List deviceIds, - List times, - List> measurementsList, - List> typesList, - List> valuesList) -``` - -* 插入同属于一个 device 的多个 Record - -``` java -void insertRecordsOfOneDevice(String deviceId, List times, - List> measurementsList, List> typesList, - List> valuesList) -``` - -#### 带有类型推断的写入 - -当数据均是 String 类型时,我们可以使用如下接口,根据 value 的值进行类型推断。例如:value 为 "true" ,就可以自动推断为布尔类型。value 为 "3.2" ,就可以自动推断为数值类型。服务器需要做类型推断,可能会有额外耗时,速度较无需类型推断的写入慢 - -* 插入一个 Record,一个 Record 是一个设备一个时间戳下多个测点的数据 - -``` java -void insertRecord(String prefixPath, long time, List measurements, List values) -``` - -* 插入多个 Record - -``` java -void insertRecords(List deviceIds, List times, - List> measurementsList, List> valuesList) -``` - -* 插入同属于一个 device 的多个 Record - -``` java -void insertStringRecordsOfOneDevice(String deviceId, List times, - List> measurementsList, List> valuesList) -``` - -#### 对齐时间序列的写入 - -对齐时间序列的写入使用 insertAlignedXXX 接口,其余与上述接口类似: - -* insertAlignedRecord -* insertAlignedRecords -* insertAlignedRecordsOfOneDevice -* insertAlignedStringRecordsOfOneDevice -* insertAlignedTablet -* insertAlignedTablets - -### 数据删除接口 - -* 删除一个或多个时间序列在某个时间点前或这个时间点的数据 - -``` java -void deleteData(String path, long endTime) -void deleteData(List paths, long endTime) -``` - -### 数据查询接口 - -* 时间序列原始数据范围查询: - - 指定的查询时间范围为左闭右开区间,包含开始时间但不包含结束时间。 - -``` java -SessionDataSet executeRawDataQuery(List paths, long startTime, long endTime); -``` - -* 最新点查询: - - 查询最后一条时间戳大于等于某个时间点的数据。 - ``` java - SessionDataSet executeLastDataQuery(List paths, long lastTime); - ``` - - 快速查询单设备下指定序列最新点,支持重定向;如果您确认使用的查询路径是合法的,可将`isLegalPathNodes`置为true以避免路径校验带来的性能损失。 - ``` java - SessionDataSet executeLastDataQueryForOneDevice( - String db, String device, List sensors, boolean isLegalPathNodes); - ``` - -* 聚合查询: - - 支持指定查询时间范围。指定的查询时间范围为左闭右开区间,包含开始时间但不包含结束时间。 - - 支持按照时间区间分段查询。 - -``` java -SessionDataSet executeAggregationQuery(List paths, List aggregations); - -SessionDataSet executeAggregationQuery( - List paths, List aggregations, long startTime, long endTime); - -SessionDataSet executeAggregationQuery( - List paths, - List aggregations, - long startTime, - long endTime, - long interval); - -SessionDataSet executeAggregationQuery( - List paths, - List aggregations, - long startTime, - long endTime, - long interval, - long slidingStep); -``` - -* 直接执行查询语句 - -``` java -SessionDataSet executeQueryStatement(String sql) -``` - -### 数据订阅 - -#### 1 Topic 管理 - -IoTDB 订阅客户端中的 `SubscriptionSession` 类提供了 Topic 管理的相关接口。Topic状态变化如下图所示: - -
- -
- -##### 1.1 创建 Topic - -```Java - void createTopicIfNotExists(String topicName, Properties properties) throws Exception; -``` - -示例: - -```Java -try (final SubscriptionSession session = new SubscriptionSession(host, port)) { - session.open(); - final Properties config = new Properties(); - config.put(TopicConstant.PATH_KEY, "root.db.**"); - session.createTopic(topicName, config); +### 2.2 创建连接池实例 + +```java +import java.util.ArrayList; +import java.util.List; +import org.apache.iotdb.session.pool.SessionPool; + +public class IoTDBSessionPoolExample { + private static SessionPool sessionPool; + + public static void main(String[] args) { + // Using nodeUrls ensures that when one node goes down, other nodes are automatically connected to retry + List nodeUrls = new ArrayList<>(); + nodeUrls.add("127.0.0.1:6667"); + nodeUrls.add("127.0.0.1:6668"); + sessionPool = + new SessionPool.Builder() + .nodeUrls(nodeUrls) + .user("root") + .password("root") + .maxSize(3) + .build(); + } } ``` -##### 1.2 删除 Topic - -```Java -void dropTopicIfExists(String topicName) throws Exception; -``` - -##### 1.3 查看 Topic - -```Java -// 获取所有 topics -Set getTopics() throws Exception; - -// 获取单个 topic -Optional getTopic(String topicName) throws Exception; -``` - -#### 2 查看订阅状态 - -IoTDB 订阅客户端中的 `SubscriptionSession` 类提供了获取订阅状态的相关接口: - -```Java -Set getSubscriptions() throws Exception; -Set getSubscriptions(final String topicName) throws Exception; -``` - -#### 3 创建 Consumer - -在使用 JAVA 原生接口创建 consumer 时,需要指定 consumer 所应用的参数。 - -对于 `SubscriptionPullConsumer` 和 `SubscriptionPushConsumer` 而言,有以下公共配置: +### 2.3 执行数据库操作 +#### 2.3.1 数据写入 +在工业场景中,数据写入可分为以下几类:多行数据写入、单设备多行数据写入,下面按不同场景对写入接口进行介绍。 -| 参数 | 是否必填(默认值) | 参数含义 | -| :-------------------------------------------- | :--------------------------------- | :----------------------------------------------------------- | -| host | optional: 127.0.0.1 | `String`: IoTDB 中某 DataNode 的 RPC host | -| port | optional: 6667 | `Integer`: IoTDB 中某 DataNode 的 RPC port | -| node-urls | optional: 127.0.0.1:6667 | `List`: IoTDB 中所有 DataNode 的 RPC 地址,可以是多个;host:port 和 node-urls 选填一个即可。当 host:port 和 node-urls 都填写了,则取 host:port 和 node-urls 的**并集**构成新的 node-urls 应用 | -| username | optional: root | `String`: IoTDB 中 DataNode 的用户名 | -| password | optional: root | `String`: IoTDB 中 DataNode 的密码 | -| groupId | optional | `String`: consumer group id,若未指定则随机分配(新的 consumer group),保证不同的 consumer group 对应的 consumer group id 均不相同 | -| consumerId | optional | `String`: consumer client id,若未指定则随机分配,保证同一个 consumer group 中每一个 consumer client id 均不相同 | -| heartbeatIntervalMs | optional: 30000 (min: 1000) | `Long`: consumer 向 IoTDB DataNode 定期发送心跳请求的间隔 | -| endpointsSyncIntervalMs | optional: 120000 (min: 5000) | `Long`: consumer 探测 IoTDB 集群节点扩缩容情况调整订阅连接的间隔 | -| fileSaveDir | optional: Paths.get(System.getProperty("user.dir"), "iotdb-subscription").toString() | `String`: consumer 订阅出的 TsFile 文件临时存放的目录路径 | -| fileSaveFsync | optional: false | `Boolean`: consumer 订阅 TsFile 的过程中是否主动调用 fsync | +##### 多行数据写入接口 +接口说明:支持一次写入多行数据,每一行对应一个设备一个时间戳的多个测点值。 -##### 3.1 SubscriptionPushConsumer +接口列表: -以下为 `SubscriptionPushConsumer` 中的特殊配置: -| 参数 | 是否必填(默认值) | 参数含义 | -| :-------------------------------------------- | :--------------------------------- | :----------------------------------------------------------- | -| ackStrategy | optional: `ACKStrategy.AFTER_CONSUME` | 消费进度的确认机制包含以下选项:`ACKStrategy.BEFORE_CONSUME`(当 consumer 收到数据时立刻提交消费进度,`onReceive` 前)`ACKStrategy.AFTER_CONSUME`(当 consumer 消费完数据再去提交消费进度,`onReceive` 后) | -| consumeListener | optional | 消费数据的回调函数,需实现 `ConsumeListener` 接口,定义消费 `SessionDataSetsHandler` 和 `TsFileHandler` 形式数据的处理逻辑 | -| autoPollIntervalMs | optional: 5000 (min: 500) | Long: consumer 自动拉取数据的时间间隔,单位为**毫秒** | -| autoPollTimeoutMs | optional: 10000 (min: 1000) | Long: consumer 每次拉取数据的超时时间,单位为**毫秒** | +| 接口名称 | 功能描述 | +|--------------------------------------------------------|-----------------------| +| `insertRecords(List deviceIds, List times, List> measurementsList, List> typesList, List> valuesList)` | 插入多行数据,适用于不同测点独立采集的场景 | -其中,`ConsumerListener` 接口定义如下: +代码案例: +```java +import java.util.ArrayList; +import java.util.List; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.apache.iotdb.session.pool.SessionPool; +import org.apache.tsfile.enums.TSDataType; -```Java -@FunctionInterface -interface ConsumeListener { - default ConsumeResult onReceive(Message message) { - return ConsumeResult.SUCCESS; +public class SessionPoolExample { + private static SessionPool sessionPool; + public static void main(String[] args) throws IoTDBConnectionException, StatementExecutionException { + // 1. init SessionPool + constructSessionPool(); + // 2. execute insert data + insertRecordsExample(); + // 3. close SessionPool + closeSessionPool(); } -} - -enum ConsumeResult { - SUCCESS, - FAILURE, -} -``` - -##### 3.2 SubscriptionPullConsumer - -以下为 `SubscriptionPullConsumer` 中的特殊配置: - -| 参数 | 是否必填(默认值) | 参数含义 | -| :-------------------------------------------- | :--------------------------------- | :----------------------------------------------------------- | -| autoCommit | optional: true | Boolean: 是否自动提交消费进度如果此参数设置为 false,则需要调用 `commit` 方法来手动提交消费进度 | -| autoCommitInterval | optional: 5000 (min: 500) | Long: 自动提交消费进度的时间间隔,单位为**毫秒**仅当 autoCommit 参数为 true 的时候才会生效 | - -在创建 consumer 后,需要手动调用 consumer 的 open 方法: - -```Java -void open() throws Exception; -``` - -此时,IoTDB 订阅客户端才会校验 consumer 的配置正确性,在校验成功后 consumer 就会加入对应的 consumer group。也就是说,在打开 consumer 后,才可以使用返回的 consumer 对象进行订阅 Topic,消费数据等操作。 - -#### 4 订阅 Topic - -`SubscriptionPushConsumer` 和 `SubscriptionPullConsumer` 提供了下述 JAVA 原生接口用于订阅 Topics: - -```Java -// 订阅 topics -void subscribe(String topic) throws Exception; -void subscribe(List topics) throws Exception; -``` - -- 在 consumer 订阅 topic 前,topic 必须已经被创建,否则订阅会失败 -- 一个 consumer 在已经订阅了某个 topic 的情况下再次订阅这个 topic,不会报错 -- 如果该 consumer 所在的 consumer group 中已经有 consumers 订阅了相同的 topics,那么该 consumer 将会复用对应的消费进度 - -#### 5 消费数据 - -无论是 push 模式还是 pull 模式的 consumer: - -- 只有显式订阅了某个 topic,才会收到对应 topic 的数据 -- 若在创建后没有订阅任何 topics,此时该 consumer 无法消费到任何数据,即使该 consumer 所在的 consumer group 中其它的 consumers 订阅了一些 topics - -##### 5.1 SubscriptionPushConsumer - -SubscriptionPushConsumer 在订阅 topics 后,无需手动拉取数据,其消费数据的逻辑在创建 SubscriptionPushConsumer 指定的 `consumeListener` 配置中。 - -##### 5.2 SubscriptionPullConsumer - -SubscriptionPullConsumer 在订阅 topics 后,需要主动调用 `poll` 方法拉取数据: - -```Java -List poll(final Duration timeout) throws Exception; -List poll(final long timeoutMs) throws Exception; -List poll(final Set topicNames, final Duration timeout) throws Exception; -List poll(final Set topicNames, final long timeoutMs) throws Exception; -``` - -在 poll 方法中可以指定需要拉取的 topic 名称(如果不指定则默认拉取该 consumer 已订阅的所有 topics)和超时时间。 - -当 SubscriptionPullConsumer 配置 autoCommit 参数为 false 时,需要手动调用 commitSync 和 commitAsync 方法同步或异步提交某批数据的消费进度: - -```Java -void commitSync(final SubscriptionMessage message) throws Exception; -void commitSync(final Iterable messages) throws Exception; - -CompletableFuture commitAsync(final SubscriptionMessage message); -CompletableFuture commitAsync(final Iterable messages); -void commitAsync(final SubscriptionMessage message, final AsyncCommitCallback callback); -void commitAsync(final Iterable messages, final AsyncCommitCallback callback); -``` -AsyncCommitCallback 类定义如下: - -```Java -public interface AsyncCommitCallback { - default void onComplete() { - // Do nothing - } + private static void constructSessionPool() { + // Using nodeUrls ensures that when one node goes down, other nodes are automatically connected to retry + List nodeUrls = new ArrayList<>(); + nodeUrls.add("127.0.0.1:6667"); + nodeUrls.add("127.0.0.1:6668"); + sessionPool = + new SessionPool.Builder() + .nodeUrls(nodeUrls) + .user("root") + .password("root") + .maxSize(3) + .build(); + } - default void onFailure(final Throwable e) { - // Do nothing - } + public static void insertRecordsExample() throws IoTDBConnectionException, StatementExecutionException { + String deviceId = "root.sg1.d1"; + List measurements = new ArrayList<>(); + measurements.add("s1"); + measurements.add("s2"); + measurements.add("s3"); + List deviceIds = new ArrayList<>(); + List> measurementsList = new ArrayList<>(); + List> valuesList = new ArrayList<>(); + List timestamps = new ArrayList<>(); + List> typesList = new ArrayList<>(); + + for (long time = 0; time < 500; time++) { + List values = new ArrayList<>(); + List types = new ArrayList<>(); + values.add(1L); + values.add(2L); + values.add(3L); + types.add(TSDataType.INT64); + types.add(TSDataType.INT64); + types.add(TSDataType.INT64); + + deviceIds.add(deviceId); + measurementsList.add(measurements); + valuesList.add(values); + typesList.add(types); + timestamps.add(time); + if (time != 0 && time % 100 == 0) { + try { + sessionPool.insertRecords(deviceIds, timestamps, measurementsList, typesList, valuesList); + } catch (IoTDBConnectionException | StatementExecutionException e) { + // solve exception + } + deviceIds.clear(); + measurementsList.clear(); + valuesList.clear(); + typesList.clear(); + timestamps.clear(); + } + } + try { + sessionPool.insertRecords(deviceIds, timestamps, measurementsList, typesList, valuesList); + } catch (IoTDBConnectionException | StatementExecutionException e) { + // solve exception + } + } + + public static void closeSessionPool(){ + sessionPool.close(); + } } ``` +##### 单设备多行数据写入接口 +接口说明:支持一次写入单个设备的多行数据,每一行对应一个时间戳的多个测点值。 + +接口列表: + +| 接口名称 | 功能描述 | +|-----------------------------------------------------------------------------------------|----------------------------| +| `insertTablet(Tablet tablet)` | 插入单个设备的多行数据,适用于不同测点独立采集的场景 | + +代码案例: +```java +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.apache.iotdb.session.pool.SessionPool; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.write.record.Tablet; +import org.apache.tsfile.write.schema.MeasurementSchema; + +public class SessionPoolExample { + private static SessionPool sessionPool; + public static void main(String[] args) throws IoTDBConnectionException, StatementExecutionException { + // 1. init SessionPool + constructSessionPool(); + // 2. execute insert data + insertTabletExample(); + // 3. close SessionPool + closeSessionPool(); + } + + private static void constructSessionPool() { + // Using nodeUrls ensures that when one node goes down, other nodes are automatically connected to retry + List nodeUrls = new ArrayList<>(); + nodeUrls.add("127.0.0.1:6667"); + nodeUrls.add("127.0.0.1:6668"); + sessionPool = + new SessionPool.Builder() + .nodeUrls(nodeUrls) + .user("root") + .password("root") + .maxSize(3) + .build(); + } -#### 6 取消订阅 - -`SubscriptionPushConsumer` 和 `SubscriptionPullConsumer` 提供了下述 JAVA 原生接口用于取消订阅并关闭 consumer: - -```Java -// 取消订阅 topics -void unsubscribe(String topic) throws Exception; -void unsubscribe(List topics) throws Exception; - -// 关闭 consumer -void close(); -``` - -- 在 topic 存在的情况下,如果一个 consumer 在没有订阅了某个 topic 的情况下取消订阅某个 topic,不会报错 -- consumer close 时会退出对应的 consumer group,同时自动 unsubscribe 该 consumer 现存订阅的所有 topics -- consumer 在 close 后生命周期即结束,无法再重新 open 订阅并消费数据 - -#### 7 代码示例 - -##### 7.1 单 Pull Consumer 消费 SessionDataSetsHandler 形式的数据 - -```Java -// Create topics -try (final SubscriptionSession session = new SubscriptionSession(HOST, PORT)) { - session.open(); - final Properties config = new Properties(); - config.put(TopicConstant.PATH_KEY, "root.db.**"); - session.createTopic(TOPIC_1, config); -} - -// Subscription: property-style ctor -final Properties config = new Properties(); -config.put(ConsumerConstant.CONSUMER_ID_KEY, "c1"); -config.put(ConsumerConstant.CONSUMER_GROUP_ID_KEY, "cg1"); - -final SubscriptionPullConsumer consumer1 = new SubscriptionPullConsumer(config); -consumer1.open(); -consumer1.subscribe(TOPIC_1); -while (true) { - LockSupport.parkNanos(SLEEP_NS); // wait some time - final List messages = consumer1.poll(POLL_TIMEOUT_MS); - for (final SubscriptionMessage message : messages) { - for (final SubscriptionSessionDataSet dataSet : message.getSessionDataSetsHandler()) { - System.out.println(dataSet.getColumnNames()); - System.out.println(dataSet.getColumnTypes()); - while (dataSet.hasNext()) { - System.out.println(dataSet.next()); - } + private static void insertTabletExample() throws IoTDBConnectionException, StatementExecutionException { + /* + * A Tablet example: + * device1 + * time s1, s2, s3 + * 1, 1, 1, 1 + * 2, 2, 2, 2 + * 3, 3, 3, 3 + */ + // The schema of measurements of one device + // only measurementId and data type in MeasurementSchema take effects in Tablet + List schemaList = new ArrayList<>(); + schemaList.add(new MeasurementSchema("s1", TSDataType.INT64)); + schemaList.add(new MeasurementSchema("s2", TSDataType.INT64)); + schemaList.add(new MeasurementSchema("s3", TSDataType.INT64)); + + Tablet tablet = new Tablet("root.sg.d1", schemaList, 100); + + // Method 1 to add tablet data + long timestamp = System.currentTimeMillis(); + + Random random = new Random(); + for (long row = 0; row < 100; row++) { + int rowIndex = tablet.rowSize++; + tablet.addTimestamp(rowIndex, timestamp); + for (int s = 0; s < 3; s++) { + long value = random.nextLong(); + tablet.addValue(schemaList.get(s).getMeasurementId(), rowIndex, value); + } + if (tablet.rowSize == tablet.getMaxRowNumber()) { + sessionPool.insertTablet(tablet); + tablet.reset(); + } + timestamp++; + } + if (tablet.rowSize != 0) { + sessionPool.insertTablet(tablet); + tablet.reset(); + } } - } - // Auto commit -} -// Show topics and subscriptions -try (final SubscriptionSession session = new SubscriptionSession(HOST, PORT)) { - session.open(); - session.getTopics().forEach((System.out::println)); - session.getSubscriptions().forEach((System.out::println)); + public static void closeSessionPool(){ + sessionPool.close(); + } } - -consumer1.unsubscribe(TOPIC_1); -consumer1.close(); ``` -##### 7.2 多 Push Consumer 消费 TsFileHandler 形式的数据 - -```Java -// Create topics -try (final SubscriptionSession subscriptionSession = new SubscriptionSession(HOST, PORT)) { - subscriptionSession.open(); - final Properties config = new Properties(); - config.put(TopicConstant.FORMAT_KEY, TopicConstant.FORMAT_TS_FILE_HANDLER_VALUE); - subscriptionSession.createTopic(TOPIC_2, config); -} +#### 2.3.2 SQL操作 + +SQL操作分为查询和非查询两类操作,对应的接口为`executeQuery`和`executeNonQuery`操作,其区别为前者执行的是具体的查询语句,会返回一个结果集,后者是执行的是增、删、改操作,不返回结果集。 + +```java +import java.util.ArrayList; +import java.util.List; +import org.apache.iotdb.isession.pool.SessionDataSetWrapper; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.apache.iotdb.session.pool.SessionPool; + +public class SessionPoolExample { + private static SessionPool sessionPool; + public static void main(String[] args) throws IoTDBConnectionException, StatementExecutionException { + // 1. init SessionPool + constructSessionPool(); + // 2. executes a non-query SQL statement, such as a DDL or DML command. + executeQueryExample(); + // 3. executes a query SQL statement and returns the result set. + executeNonQueryExample(); + // 4. close SessionPool + closeSessionPool(); + } -final List threads = new ArrayList<>(); -for (int i = 0; i < 8; ++i) { - final int idx = i; - final Thread thread = - new Thread( - () -> { - // Subscription: builder-style ctor - try (final SubscriptionPushConsumer consumer2 = - new SubscriptionPushConsumer.Builder() - .consumerId("c" + idx) - .consumerGroupId("cg2") - .fileSaveDir(System.getProperty("java.io.tmpdir")) - .ackStrategy(AckStrategy.AFTER_CONSUME) - .consumeListener( - message -> { - doSomething(message.getTsFileHandler()); - return ConsumeResult.SUCCESS; - }) - .buildPushConsumer()) { - consumer2.open(); - consumer2.subscribe(TOPIC_2); - // block the consumer main thread - Thread.sleep(Long.MAX_VALUE); - } catch (final IOException | InterruptedException e) { - throw new RuntimeException(e); + private static void executeNonQueryExample() throws IoTDBConnectionException, StatementExecutionException { + // 1. create a nonAligned time series + sessionPool.executeNonQueryStatement("create timeseries root.test.d1.s1 with dataType = int32"); + // 2. set ttl + sessionPool.executeNonQueryStatement("set TTL to root.test.** 10000"); + // 3. delete time series + sessionPool.executeNonQueryStatement("delete timeseries root.test.d1.s1"); + private static void executeQueryExample() throws IoTDBConnectionException, StatementExecutionException { + // 1. execute normal query + try(SessionDataSetWrapper wrapper = sessionPool.executeQueryStatement("select s1 from root.sg1.d1 limit 10")) { + while (wrapper.hasNext()) { + System.out.println(wrapper.next()); } - }); - thread.start(); - threads.add(thread); -} + } + // 2. execute aggregate query + try(SessionDataSetWrapper wrapper = sessionPool.executeQueryStatement("select count(s1) from root.sg1.d1 group by ([0, 40), 5ms) ")) { + while (wrapper.hasNext()) { + System.out.println(wrapper.next()); + } + } + } + + private static void constructSessionPool() { + // Using nodeUrls ensures that when one node goes down, other nodes are automatically connected to retry + List nodeUrls = new ArrayList<>(); + nodeUrls.add("127.0.0.1:6667"); + nodeUrls.add("127.0.0.1:6668"); + sessionPool = + new SessionPool.Builder() + .nodeUrls(nodeUrls) + .user("root") + .password("root") + .maxSize(3) + .build(); + } -for (final Thread thread : threads) { - thread.join(); + public static void closeSessionPool(){ + sessionPool.close(); + } } ``` - -### 其他功能(直接执行SQL语句) - -``` java -void executeNonQueryStatement(String sql) -``` - -### 写入测试接口 (用于分析网络带宽) - -不实际写入数据,只将数据传输到 server 即返回 - -* 测试 insertRecord - -``` java -void testInsertRecord(String deviceId, long time, List measurements, List values) - -void testInsertRecord(String deviceId, long time, List measurements, - List types, List values) -``` - -* 测试 testInsertRecords - -``` java -void testInsertRecords(List deviceIds, List times, - List> measurementsList, List> valuesList) - -void testInsertRecords(List deviceIds, List times, - List> measurementsList, List> typesList, - List> valuesList) -``` - -* 测试 insertTablet - -``` java -void testInsertTablet(Tablet tablet) -``` - -* 测试 insertTablets - -``` java -void testInsertTablets(Map tablets) -``` - -### 示例代码 - -浏览上述接口的详细信息,请参阅代码 ```session/src/main/java/org/apache/iotdb/session/Session.java``` - -使用上述接口的示例代码在 ```example/session/src/main/java/org/apache/iotdb/SessionExample.java``` - -使用对齐时间序列和元数据模板的示例可以参见 `example/session/src/main/java/org/apache/iotdb/AlignedTimeseriesSessionExample.java` \ No newline at end of file +### 3 全量接口说明 + +#### 3.1 参数列表 +Session具有如下的字段,可以通过构造函数或Session.Builder方式设置如下参数 + +| 字段名 | 类型 | 说明 | +|--------------------------------|-------------------------------|----------------------------------------------------------------------| +| `nodeUrls` | `List` | 数据库节点的 URL 列表,支持多节点连接 | +| `username` | `String` | 用户名 | +| `password` | `String` | 密码 | +| `fetchSize` | `int` | 查询结果的默认批量返回大小 | +| `useSSL` | `boolean` | 是否启用 SSL | +| `trustStore` | `String` | 信任库路径 | +| `trustStorePwd` | `String` | 信任库密码 | +| `queryTimeoutInMs` | `long` | 查询的超时时间,单位毫秒 | +| `enableRPCCompression` | `boolean` | 是否启用 RPC 压缩 | +| `connectionTimeoutInMs` | `int` | 连接超时时间,单位毫秒 | +| `zoneId` | `ZoneId` | 会话的时区设置 | +| `thriftDefaultBufferSize` | `int` | Thrift 默认缓冲区大小 | +| `thriftMaxFrameSize` | `int` | Thrift 最大帧大小 | +| `defaultEndPoint` | `TEndPoint` | 默认的数据库端点信息 | +| `defaultSessionConnection` | `SessionConnection` | 默认的会话连接对象 | +| `isClosed` | `boolean` | 当前会话是否已关闭 | +| `enableRedirection` | `boolean` | 是否启用重定向功能 | +| `enableRecordsAutoConvertTablet` | `boolean` | 是否启用记录自动转换为 Tablet 的功能 | +| `deviceIdToEndpoint` | `Map` | 设备 ID 和数据库端点的映射关系 | +| `endPointToSessionConnection` | `Map` | 数据库端点和会话连接的映射关系 | +| `executorService` | `ScheduledExecutorService` | 用于定期更新节点列表的线程池 | +| `availableNodes` | `INodeSupplier` | 可用节点的供应器 | +| `enableQueryRedirection` | `boolean` | 是否启用查询重定向功能 | +| `version` | `Version` | 客户端的版本号,用于与服务端的兼容性判断 | +| `enableAutoFetch` | `boolean` | 是否启用自动获取功能 | +| `maxRetryCount` | `int` | 最大重试次数 | +| `retryIntervalInMs` | `long` | 重试的间隔时间,单位毫秒 | + + + +#### 3.2 接口列表 + +##### 3.2.1 元数据管理 + +| 方法名 | 功能描述 | 参数解释 | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `createDatabase(String database)` | 创建数据库 | `database`: 数据库名称 | +| `deleteDatabase(String database)` | 删除指定数据库 | `database`: 要删除的数据库名称 | +| `deleteDatabases(List databases)` | 批量删除数据库 | `databases`: 要删除的数据库名称列表 | +| `createTimeseries(String path, TSDataType dataType, TSEncoding encoding, CompressionType compressor)` | 创建单个时间序列 | `path`: 时间序列路径,`dataType`: 数据类型,`encoding`: 编码类型,`compressor`: 压缩类型 | +| `createAlignedTimeseries(...)` | 创建对齐时间序列 | 设备ID、测点列表、数据类型列表、编码列表、压缩类型列表 | +| `createMultiTimeseries(...)` | 批量创建时间序列 | 多个路径、数据类型、编码、压缩类型、属性、标签、别名等 | +| `deleteTimeseries(String path)` | 删除时间序列 | `path`: 要删除的时间序列路径 | +| `deleteTimeseries(List paths)` | 批量删除时间序列 | `paths`: 要删除的时间序列路径列表 | +| `setSchemaTemplate(String templateName, String prefixPath)` | 设置模式模板 | `templateName`: 模板名称,`prefixPath`: 应用模板的路径 | +| `createSchemaTemplate(Template template)` | 创建模式模板 | `template`: 模板对象 | +| `dropSchemaTemplate(String templateName)` | 删除模式模板 | `templateName`: 要删除的模板名称 | +| `addAlignedMeasurementsInTemplate(...)` | 添加对齐测点到模板 | 模板名称、测点路径列表、数据类型、编码类型、压缩类型 | +| `addUnalignedMeasurementsInTemplate(...)` | 添加非对齐测点到模板 | 同上 | +| `deleteNodeInTemplate(String templateName, String path)` | 删除模板中的节点 | `templateName`: 模板名称,`path`: 要删除的路径 | +| `countMeasurementsInTemplate(String name)` | 统计模板中测点数量 | `name`: 模板名称 | +| `isMeasurementInTemplate(String templateName, String path)` | 检查模板中是否存在某测点 | `templateName`: 模板名称,`path`: 测点路径 | +| `isPathExistInTemplate(String templateName, String path)` | 检查模板中路径是否存在 | 同上 | +| `showMeasurementsInTemplate(String templateName)` | 显示模板中的测点 | `templateName`: 模板名称 | +| `showMeasurementsInTemplate(String templateName, String pattern)` | 按模式显示模板中的测点 | `templateName`: 模板名称,`pattern`: 匹配模式 | +| `showAllTemplates()` | 显示所有模板 | 无参数 | +| `showPathsTemplateSetOn(String templateName)` | 显示模板应用的路径 | `templateName`: 模板名称 | +| `showPathsTemplateUsingOn(String templateName)` | 显示模板实际使用的路径 | 同上 | +| `unsetSchemaTemplate(String prefixPath, String templateName)` | 取消路径的模板设置 | `prefixPath`: 路径,`templateName`: 模板名称 | + + +##### 3.2.2 数据写入 +| 方法名 | 功能描述 | 参数解释 | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `insertRecord(String deviceId, long time, List measurements, List types, Object... values)` | 插入单条记录 | `deviceId`: 设备ID,`time`: 时间戳,`measurements`: 测点列表,`types`: 数据类型列表,`values`: 值列表 | +| `insertRecord(String deviceId, long time, List measurements, List values)` | 插入单条记录 | `deviceId`: 设备ID,`time`: 时间戳,`measurements`: 测点列表,`values`: 值列表 | +| `insertRecords(List deviceIds, List times, List> measurementsList, List> valuesList)` | 插入多条记录 | `deviceIds`: 设备ID列表,`times`: 时间戳列表,`measurementsList`: 测点列表列表,`valuesList`: 值列表 | +| `insertRecords(List deviceIds, List times, List> measurementsList, List> typesList, List> valuesList)` | 插入多条记录 | 同上,增加 `typesList`: 数据类型列表 | +| `insertRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> typesList, List> valuesList)` | 插入单设备的多条记录 | `deviceId`: 设备ID,`times`: 时间戳列表,`measurementsList`: 测点列表列表,`typesList`: 类型列表,`valuesList`: 值列表 | +| `insertRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> typesList, List> valuesList, boolean haveSorted)` | 插入排序后的单设备多条记录 | 同上,增加 `haveSorted`: 数据是否已排序 | +| `insertStringRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> valuesList)` | 插入字符串格式的单设备记录 | `deviceId`: 设备ID,`times`: 时间戳列表,`measurementsList`: 测点列表,`valuesList`: 值列表 | +| `insertStringRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> valuesList, boolean haveSorted)` | 插入排序的字符串格式单设备记录 | 同上,增加 `haveSorted`: 数据是否已排序 | +| `insertAlignedRecord(String deviceId, long time, List measurements, List types, List values)` | 插入单条对齐记录 | `deviceId`: 设备ID,`time`: 时间戳,`measurements`: 测点列表,`types`: 类型列表,`values`: 值列表 | +| `insertAlignedRecord(String deviceId, long time, List measurements, List values)` | 插入字符串格式的单条对齐记录 | `deviceId`: 设备ID,`time`: 时间戳,`measurements`: 测点列表,`values`: 值列表 | +| `insertAlignedRecords(List deviceIds, List times, List> measurementsList, List> valuesList)` | 插入多条对齐记录 | `deviceIds`: 设备ID列表,`times`: 时间戳列表,`measurementsList`: 测点列表,`valuesList`: 值列表 | +| `insertAlignedRecords(List deviceIds, List times, List> measurementsList, List> typesList, List> valuesList)` | 插入多条对齐记录 | 同上,增加 `typesList`: 数据类型列表 | +| `insertAlignedRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> typesList, List> valuesList)` | 插入单设备的多条对齐记录 | 同上 | +| `insertAlignedRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> typesList, List> valuesList, boolean haveSorted)` | 插入排序的单设备多条对齐记录 | 同上,增加 `haveSorted`: 数据是否已排序 | +| `insertAlignedStringRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> valuesList)` | 插入字符串格式的单设备对齐记录 | `deviceId`: 设备ID,`times`: 时间戳列表,`measurementsList`: 测点列表,`valuesList`: 值列表 | +| `insertAlignedStringRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> valuesList, boolean haveSorted)` | 插入排序的字符串格式单设备对齐记录 | 同上,增加 `haveSorted`: 数据是否已排序 | +| `insertTablet(Tablet tablet)` | 插入单个Tablet数据 | `tablet`: 要插入的Tablet数据 | +| `insertTablet(Tablet tablet, boolean sorted)` | 插入排序的Tablet数据 | 同上,增加 `sorted`: 数据是否已排序 | +| `insertAlignedTablet(Tablet tablet)` | 插入对齐的Tablet数据 | `tablet`: 要插入的Tablet数据 | +| `insertAlignedTablet(Tablet tablet, boolean sorted)` | 插入排序的对齐Tablet数据 | 同上,增加 `sorted`: 数据是否已排序 | +| `insertTablets(Map tablets)` | 批量插入多个Tablet数据 | `tablets`: 设备ID到Tablet的映射表 | +| `insertTablets(Map tablets, boolean sorted)` | 批量插入排序的多个Tablet数据 | 同上,增加 `sorted`: 数据是否已排序 | +| `insertAlignedTablets(Map tablets)` | 批量插入多个对齐Tablet数据 | `tablets`: 设备ID到Tablet的映射表 | +| `insertAlignedTablets(Map tablets, boolean sorted)` | 批量插入排序的多个对齐Tablet数据 | 同上,增加 `sorted`: 数据是否已排序 | + +##### 3.2.3 数据删除 + +| 方法名 | 功能描述 | 参数解释 | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `deleteTimeseries(String path)` | 删除单个时间序列 | `path`: 时间序列路径 | +| `deleteTimeseries(List paths)` | 批量删除时间序列 | `paths`: 时间序列路径列表 | +| `deleteData(String path, long endTime)` | 删除指定路径的历史数据 | `path`: 路径,`endTime`: 结束时间戳 | +| `deleteData(List paths, long endTime)` | 批量删除路径的历史数据 | `paths`: 路径列表,`endTime`: 结束时间戳 | +| `deleteData(List paths, long startTime, long endTime)` | 删除路径时间范围内的历史数据 | 同上,增加 `startTime`: 起始时间戳 | + + +##### 3.2.4 数据查询 +| 方法名 | 功能描述 | 参数解释 | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `executeQueryStatement(String sql)` | 执行查询语句 | `sql`: 查询SQL语句 | +| `executeQueryStatement(String sql, long timeoutInMs)` | 执行带超时的查询语句 | `sql`: 查询SQL语句,`timeoutInMs`: 查询超时时间(毫秒) | +| `executeRawDataQuery(List paths, long startTime, long endTime)` | 查询指定路径的原始数据 | `paths`: 查询路径列表,`startTime`: 起始时间戳,`endTime`: 结束时间戳 | +| `executeRawDataQuery(List paths, long startTime, long endTime, long timeOut)` | 查询指定路径的原始数据(带超时) | 同上,增加 `timeOut`: 超时时间 | +| `executeLastDataQuery(List paths)` | 查询最新数据 | `paths`: 查询路径列表 | +| `executeLastDataQuery(List paths, long lastTime)` | 查询指定时间的最新数据 | `paths`: 查询路径列表,`lastTime`: 指定的时间戳 | +| `executeLastDataQuery(List paths, long lastTime, long timeOut)` | 查询指定时间的最新数据(带超时) | 同上,增加 `timeOut`: 超时时间 | +| `executeLastDataQueryForOneDevice(String db, String device, List sensors, boolean isLegalPathNodes)` | 查询单个设备的最新数据 | `db`: 数据库名,`device`: 设备名,`sensors`: 传感器列表,`isLegalPathNodes`: 是否合法路径节点 | +| `executeAggregationQuery(List paths, List aggregations)` | 执行聚合查询 | `paths`: 查询路径列表,`aggregations`: 聚合类型列表 | +| `executeAggregationQuery(List paths, List aggregations, long startTime, long endTime)` | 执行带时间范围的聚合查询 | 同上,增加 `startTime`: 起始时间戳,`endTime`: 结束时间戳 | +| `executeAggregationQuery(List paths, List aggregations, long startTime, long endTime, long interval)` | 执行带时间间隔的聚合查询 | 同上,增加 `interval`: 时间间隔 | +| `executeAggregationQuery(List paths, List aggregations, long startTime, long endTime, long interval, long slidingStep)` | 执行滑动窗口聚合查询 | 同上,增加 `slidingStep`: 滑动步长 | +| `fetchAllConnections()` | 获取所有活动连接信息 | 无参数 | + +##### 3.2.5 系统状态与备份 +| 方法名 | 功能描述 | 参数解释 | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `getBackupConfiguration()` | 获取备份配置信息 | 无参数 | +| `fetchAllConnections()` | 获取所有活动的连接信息 | 无参数 | +| `getSystemStatus()` | 获取系统状态 | 已废弃,默认返回 `SystemStatus.NORMAL` | \ No newline at end of file diff --git a/src/zh/UserGuide/V2.0.1/Tree/User-Manual/Data-Sync.md b/src/zh/UserGuide/V2.0.1/Tree/User-Manual/Data-Sync.md new file mode 100644 index 000000000..0dc62fe47 --- /dev/null +++ b/src/zh/UserGuide/V2.0.1/Tree/User-Manual/Data-Sync.md @@ -0,0 +1,23 @@ +--- +redirectTo: Data-Sync_apache.html +--- + diff --git a/src/zh/UserGuide/latest/API/Programming-Data-Subscription.md b/src/zh/UserGuide/latest/API/Programming-Data-Subscription.md new file mode 100644 index 000000000..c8afac5be --- /dev/null +++ b/src/zh/UserGuide/latest/API/Programming-Data-Subscription.md @@ -0,0 +1,244 @@ + + +# 数据订阅API +IoTDB 提供了强大的数据订阅功能,允许用户通过订阅SDK实时获取IoTDB新增的数据。详细的功能定义及介绍:[数据订阅](../User-Manual/Data-Sync.md) + +## 1 核心步骤 + +1. 创建Topic:创建一个Topic,Topic中包含希望订阅的测点。 +2. 订阅Topic:在 consumer 订阅 topic 前,topic 必须已经被创建,否则订阅会失败。同一个 consumer group 下的 consumers 会均分数据。 +3. 消费数据:只有显式订阅了某个 topic,才会收到对应 topic 的数据。 +4. 取消订阅: consumer close 时会退出对应的 consumer group,同时取消现存的所有订阅。 + + +## 2 详细步骤 +本章节用于说明开发的核心流程,并未演示所有的参数和接口,如需了解全部功能及参数请参见: [全量接口说明](../API/Programming-Java-Native-API.md#3-全量接口说明) + + +### 2.1 创建maven项目 +创建一个maven项目,并导入以下依赖(JDK >= 1.8, Maven >= 3.6) + +```xml + + + org.apache.iotdb + iotdb-session + + ${project.version} + + +``` + +### 2.2 代码案例 +#### 2.2.1 Topic操作 +```java +import java.util.Optional; +import java.util.Properties; +import java.util.Set; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.apache.iotdb.rpc.subscription.config.TopicConstant; +import org.apache.iotdb.session.subscription.SubscriptionSession; +import org.apache.iotdb.session.subscription.model.Topic; + +public class DataConsumerExample { + + public static void main(String[] args) throws IoTDBConnectionException, StatementExecutionException { + try (SubscriptionSession session = new SubscriptionSession("127.0.0.1", 6667)) { + // 1. open session + session.open(); + + // 2. create a topic of all data + Properties sessionConfig = new Properties(); + sessionConfig.put(TopicConstant.PATH_KEY, "root.**"); + + session.createTopic("allData", sessionConfig); + + // 3. show all topics + Set topics = session.getTopics(); + System.out.println(topics); + + // 4. show a specific topic + Optional allData = session.getTopic("allData"); + System.out.println(allData.get()); + } + } +} +``` +#### 2.2.2 数据消费 + +##### 场景-1: 订阅IoTDB中新增的实时数据(大屏或组态展示的场景) + +```java +import java.io.IOException; +import java.util.List; +import java.util.Properties; +import org.apache.iotdb.rpc.subscription.config.ConsumerConstant; +import org.apache.iotdb.rpc.subscription.config.TopicConstant; +import org.apache.iotdb.session.subscription.consumer.SubscriptionPullConsumer; +import org.apache.iotdb.session.subscription.payload.SubscriptionMessage; +import org.apache.iotdb.session.subscription.payload.SubscriptionMessageType; +import org.apache.iotdb.session.subscription.payload.SubscriptionSessionDataSet; +import org.apache.tsfile.read.common.RowRecord; + +public class DataConsumerExample { + + public static void main(String[] args) throws IOException { + + // 5. create a pull consumer, the subscription is automatically cancelled when the logic in the try resources is completed + Properties consumerConfig = new Properties(); + consumerConfig.put(ConsumerConstant.CONSUMER_ID_KEY, "c1"); + consumerConfig.put(ConsumerConstant.CONSUMER_GROUP_ID_KEY, "cg1"); + consumerConfig.put(ConsumerConstant.CONSUME_LISTENER_KEY, TopicConstant.FORMAT_SESSION_DATA_SETS_HANDLER_VALUE); + try (SubscriptionPullConsumer pullConsumer = new SubscriptionPullConsumer(consumerConfig)) { + pullConsumer.open(); + pullConsumer.subscribe("topic_all"); + while (true) { + List messages = pullConsumer.poll(10000); + for (final SubscriptionMessage message : messages) { + final short messageType = message.getMessageType(); + if (SubscriptionMessageType.isValidatedMessageType(messageType)) { + for (final SubscriptionSessionDataSet dataSet : message.getSessionDataSetsHandler()) { + while (dataSet.hasNext()) { + final RowRecord record = dataSet.next(); + System.out.println(record); + } + } + } + } + } + } + } +} + + +``` +##### 场景-2:订阅新增的 TsFile(定期数据备份的场景) + +前提:需要被消费的topic的格式为TsfileHandler类型,举例:`create topic topic_all_tsfile with ('path'='root.**','format'='TsFileHandler')` + +```java +import java.io.IOException; +import java.util.List; +import java.util.Properties; +import org.apache.iotdb.rpc.subscription.config.ConsumerConstant; +import org.apache.iotdb.rpc.subscription.config.TopicConstant; +import org.apache.iotdb.session.subscription.consumer.SubscriptionPullConsumer; +import org.apache.iotdb.session.subscription.payload.SubscriptionMessage; + + +public class DataConsumerExample { + + public static void main(String[] args) throws IOException { + // 1. create a pull consumer, the subscription is automatically cancelled when the logic in the try resources is completed + Properties consumerConfig = new Properties(); + consumerConfig.put(ConsumerConstant.CONSUMER_ID_KEY, "c1"); + consumerConfig.put(ConsumerConstant.CONSUMER_GROUP_ID_KEY, "cg1"); + // 2. Specify the consumption type as the tsfile type + consumerConfig.put(TopicConstant.FORMAT_KEY, TopicConstant.FORMAT_TS_FILE_HANDLER_VALUE); + consumerConfig.put(ConsumerConstant.FILE_SAVE_DIR_KEY, "/Users/iotdb/Downloads"); + try (SubscriptionPullConsumer pullConsumer = new SubscriptionPullConsumer(consumerConfig)) { + pullConsumer.open(); + pullConsumer.subscribe("topic_all_tsfile"); + while (true) { + List messages = pullConsumer.poll(10000); + for (final SubscriptionMessage message : messages) { + message.getTsFileHandler().copyFile("/Users/iotdb/Downloads/1.tsfile"); + } + } + } + } +} +``` + + + + +## 2 全量接口说明 + +### 2.1 参数列表 +可通过Properties参数对象设置消费者相关参数,具体参数如下。 + +#### 2.1.1 SubscriptionConsumer + + +| 参数 | 是否必填(默认值) | 参数含义 | +| :-------------------------------------------- | :--------------------------------- | :----------------------------------------------------------- | +| host | optional: 127.0.0.1 | `String`: IoTDB 中某 DataNode 的 RPC host | +| port | optional: 6667 | `Integer`: IoTDB 中某 DataNode 的 RPC port | +| node-urls | optional: 127.0.0.1:6667 | `List`: IoTDB 中所有 DataNode 的 RPC 地址,可以是多个;host:port 和 node-urls 选填一个即可。当 host:port 和 node-urls 都填写了,则取 host:port 和 node-urls 的**并集**构成新的 node-urls 应用 | +| username | optional: root | `String`: IoTDB 中 DataNode 的用户名 | +| password | optional: root | `String`: IoTDB 中 DataNode 的密码 | +| groupId | optional | `String`: consumer group id,若未指定则随机分配(新的 consumer group),保证不同的 consumer group 对应的 consumer group id 均不相同 | +| consumerId | optional | `String`: consumer client id,若未指定则随机分配,保证同一个 consumer group 中每一个 consumer client id 均不相同 | +| heartbeatIntervalMs | optional: 30000 (min: 1000) | `Long`: consumer 向 IoTDB DataNode 定期发送心跳请求的间隔 | +| endpointsSyncIntervalMs | optional: 120000 (min: 5000) | `Long`: consumer 探测 IoTDB 集群节点扩缩容情况调整订阅连接的间隔 | +| fileSaveDir | optional: Paths.get(System.getProperty("user.dir"), "iotdb-subscription").toString() | `String`: consumer 订阅出的 TsFile 文件临时存放的目录路径 | +| fileSaveFsync | optional: false | `Boolean`: consumer 订阅 TsFile 的过程中是否主动调用 fsync | + +`SubscriptionPushConsumer` 中的特殊配置: + +| 参数 | 是否必填(默认值) | 参数含义 | +| :-------------------------------------------- | :--------------------------------- | :----------------------------------------------------------- | +| ackStrategy | optional: `ACKStrategy.AFTER_CONSUME` | 消费进度的确认机制包含以下选项:`ACKStrategy.BEFORE_CONSUME`(当 consumer 收到数据时立刻提交消费进度,`onReceive` 前)`ACKStrategy.AFTER_CONSUME`(当 consumer 消费完数据再去提交消费进度,`onReceive` 后) | +| consumeListener | optional | 消费数据的回调函数,需实现 `ConsumeListener` 接口,定义消费 `SessionDataSetsHandler` 和 `TsFileHandler` 形式数据的处理逻辑 | +| autoPollIntervalMs | optional: 5000 (min: 500) | Long: consumer 自动拉取数据的时间间隔,单位为**毫秒** | +| autoPollTimeoutMs | optional: 10000 (min: 1000) | Long: consumer 每次拉取数据的超时时间,单位为**毫秒** | + +`SubscriptionPullConsumer` 中的特殊配置: + +| 参数 | 是否必填(默认值) | 参数含义 | +| :-------------------------------------------- | :--------------------------------- | :----------------------------------------------------------- | +| autoCommit | optional: true | Boolean: 是否自动提交消费进度如果此参数设置为 false,则需要调用 `commit` 方法来手动提交消费进度 | +| autoCommitInterval | optional: 5000 (min: 500) | Long: 自动提交消费进度的时间间隔,单位为**毫秒**仅当 autoCommit 参数为 true 的时候才会生效 | + + +### 函数列表 +#### 数据订阅 +##### SubscriptionPullConsumer + +| **函数名** | **说明** | **参数** | +|-------------------------------------|--------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `open()` | 打开消费者连接,启动消息消费。如果 `autoCommit` 启用,会启动自动提交工作器。 | 无 | +| `close()` | 关闭消费者连接。如果 `autoCommit` 启用,会在关闭前提交所有未提交的消息。 | 无 | +| `poll(final Duration timeout)` | 拉取消息,指定超时时间。 | `timeout` : 拉取的超时时间。 | +| `poll(final long timeoutMs)` | 拉取消息,指定超时时间(毫秒)。 | `timeoutMs` : 超时时间,单位为毫秒。 | +| `poll(final Set topicNames, final Duration timeout)` | 拉取指定主题的消息,指定超时时间。 | `topicNames` : 要拉取的主题集合。`timeout`: 超时时间。 | +| `poll(final Set topicNames, final long timeoutMs)` | 拉取指定主题的消息,指定超时时间(毫秒)。 | `topicNames` : 要拉取的主题集合。`timeoutMs`: 超时时间,单位为毫秒。 | +| `commitSync(final SubscriptionMessage message)` | 同步提交单条消息。 | `message` : 需要提交的消息对象。 | +| `commitSync(final Iterable messages)` | 同步提交多条消息。 | `messages` : 需要提交的消息集合。 | +| `commitAsync(final SubscriptionMessage message)` | 异步提交单条消息。 | `message` : 需要提交的消息对象。 | +| `commitAsync(final Iterable messages)` | 异步提交多条消息。 | `messages` : 需要提交的消息集合。 | +| `commitAsync(final SubscriptionMessage message, final AsyncCommitCallback callback)` | 异步提交单条消息并指定回调函数。 | `message` : 需要提交的消息对象。`callback` : 异步提交完成后的回调函数。 | +| `commitAsync(final Iterable messages, final AsyncCommitCallback callback)` | 异步提交多条消息并指定回调函数。 | `messages` : 需要提交的消息集合。`callback` : 异步提交完成后的回调函数。 | + +##### SubscriptionPushConsumer + +| **函数名** | **说明** | **参数** | +|-------------------------------------|----------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `open()` | 打开消费者连接,启动消息消费,提交自动轮询工作器。 | 无 | +| `close()` | 关闭消费者连接,停止消息消费。 | 无 | +| `toString()` | 返回消费者对象的核心配置信息。 | 无 | +| `coreReportMessage()` | 获取消费者核心配置的键值对表示形式。 | 无 | +| `allReportMessage()` | 获取消费者所有配置的键值对表示形式。 | 无 | +| `buildPushConsumer()` | 通过 `Builder` 构建 `SubscriptionPushConsumer` 实例。 | 无 | +| `ackStrategy(final AckStrategy ackStrategy)` | 配置消费者的消息确认策略。 | `ackStrategy`: 指定的消息确认策略。 | +| `consumeListener(final ConsumeListener consumeListener)` | 配置消费者的消息消费逻辑。 | `consumeListener`: 消费者接收消息时的处理逻辑。 | +| `autoPollIntervalMs(final long autoPollIntervalMs)` | 配置自动轮询的时间间隔。 | `autoPollIntervalMs` : 自动轮询的间隔时间,单位为毫秒。 | +| `autoPollTimeoutMs(final long autoPollTimeoutMs)` | 配置自动轮询的超时时间。 | `autoPollTimeoutMs`: 自动轮询的超时时间,单位为毫秒。 | diff --git a/src/zh/UserGuide/latest/API/Programming-Java-Native-API.md b/src/zh/UserGuide/latest/API/Programming-Java-Native-API.md index 6b6f99aa2..26ec9720d 100644 --- a/src/zh/UserGuide/latest/API/Programming-Java-Native-API.md +++ b/src/zh/UserGuide/latest/API/Programming-Java-Native-API.md @@ -19,774 +19,447 @@ --> -# Java 原生接口 +# Session原生API -## 安装 +IoTDB 原生 API 中的 Session 是实现与数据库交互的核心接口,它集成了丰富的方法,支持数据写入、查询以及元数据操作等功能。通过实例化 Session,能够建立与 IoTDB 服务器的连接,在该连接所构建的环境中执行各类数据库操作。Session为非线程安全,不能被多线程同时调用。 -### 依赖 +SessionPool 是 Session 的连接池,推荐使用SessionPool编程。在多线程并发的情形下,SessionPool 能够合理地管理和分配连接资源,以提升系统性能与资源利用效率。 -* JDK >= 1.8 -* Maven >= 3.6 +## 1 步骤概览 +1. 创建连接池实例:初始化一个SessionPool对象,用于管理多个Session实例。 +2. 执行操作:直接从SessionPool中获取Session实例,并执行数据库操作,无需每次都打开和关闭连接。 +3. 关闭连接池资源:在不再需要进行数据库操作时,关闭SessionPool,释放所有相关资源。 -### 在 MAVEN 中使用原生接口 +## 2 详细步骤 +本章节用于说明开发的核心流程,并未演示所有的参数和接口,如需了解全部功能及参数请参见: [全量接口说明](./Programming-Java-Native-API.md#3-全量接口说明) 或 查阅: [源码](https://github.com/apache/iotdb/tree/master/example/session/src/main/java/org/apache/iotdb) + +### 2.1 创建maven项目 +创建一个maven项目,并在pom.xml文件中添加以下依赖(JDK >= 1.8, Maven >= 3.6) ```xml org.apache.iotdb iotdb-session + ${project.version} ``` -## 语法说明 - - - 对于 IoTDB-SQL 接口:传入的 SQL 参数需要符合 [语法规范](../Reference/Syntax-Rule.md#字面值常量) ,并且针对 JAVA 字符串进行反转义,如双引号前需要加反斜杠。(即:经 JAVA 转义之后与命令行执行的 SQL 语句一致。) - - 对于其他接口: - - 经参数传入的路径或路径前缀中的节点: 在 SQL 语句中需要使用反引号(`)进行转义的,此处均需要进行转义。 - - 经参数传入的标识符(如模板名):在 SQL 语句中需要使用反引号(`)进行转义的,均可以不用进行转义。 - - 语法说明相关代码示例可以参考:`example/session/src/main/java/org/apache/iotdb/SyntaxConventionRelatedExample.java` - -## 基本接口说明 - -下面将给出 Session 对应的接口的简要介绍和对应参数: - -### Session管理 - -* 初始化 Session - -``` java -// 全部使用默认配置 -session = new Session.Builder.build(); - -// 指定一个可连接节点 -session = - new Session.Builder() - .host(String host) - .port(int port) - .build(); - -// 指定多个可连接节点 -session = - new Session.Builder() - .nodeUrls(List nodeUrls) - .build(); - -// 其他配置项 -session = - new Session.Builder() - .fetchSize(int fetchSize) - .username(String username) - .password(String password) - .thriftDefaultBufferSize(int thriftDefaultBufferSize) - .thriftMaxFrameSize(int thriftMaxFrameSize) - .enableRedirection(boolean enableRedirection) - .version(Version version) - .build(); -``` - -其中,version 表示客户端使用的 SQL 语义版本,用于升级 0.13 时兼容 0.12 的 SQL 语义,可能取值有:`V_0_12`、`V_0_13`、`V_1_0`等。 - - -* 开启 Session - -``` java -void open() -``` - -* 开启 Session,并决定是否开启 RPC 压缩 - -``` java -void open(boolean enableRPCCompression) -``` - -注意: 客户端的 RPC 压缩开启状态需和服务端一致 - -* 关闭 Session - -``` java -void close() -``` - -* SessionPool - -我们提供了一个针对原生接口的连接池 (`SessionPool`),使用该接口时,你只需要指定连接池的大小,就可以在使用时从池中获取连接。 -如果超过 60s 都没得到一个连接的话,那么会打印一条警告日志,但是程序仍将继续等待。 - -当一个连接被用完后,他会自动返回池中等待下次被使用; -当一个连接损坏后,他会从池中被删除,并重建一个连接重新执行用户的操作; -你还可以像创建 Session 那样在创建 SessionPool 时指定多个可连接节点的 url,以保证分布式集群中客户端的高可用性。 - -对于查询操作: - -1. 使用 SessionPool 进行查询时,得到的结果集是`SessionDataSet`的封装类`SessionDataSetWrapper`; -2. 若对于一个查询的结果集,用户并没有遍历完且不再想继续遍历时,需要手动调用释放连接的操作`closeResultSet`; -3. 若对一个查询的结果集遍历时出现异常,也需要手动调用释放连接的操作`closeResultSet`. -4. 可以调用 `SessionDataSetWrapper` 的 `getColumnNames()` 方法得到结果集列名 - -使用示例可以参见 `session/src/test/java/org/apache/iotdb/session/pool/SessionPoolTest.java` - -或 `example/session/src/main/java/org/apache/iotdb/SessionPoolExample.java` - - -### 测点管理接口 - -#### Database 管理 - -* 设置 database - -``` java -void setStorageGroup(String storageGroupId) -``` - -* 删除单个或多个 database - -``` java -void deleteStorageGroup(String storageGroup) -void deleteStorageGroups(List storageGroups) -``` -#### 时间序列管理 - -* 创建单个或多个时间序列 - -``` java -void createTimeseries(String path, TSDataType dataType, - TSEncoding encoding, CompressionType compressor, Map props, - Map tags, Map attributes, String measurementAlias) - -void createMultiTimeseries(List paths, List dataTypes, - List encodings, List compressors, - List> propsList, List> tagsList, - List> attributesList, List measurementAliasList) -``` - -* 创建对齐时间序列 - -``` -void createAlignedTimeseries(String prefixPath, List measurements, - List dataTypes, List encodings, - List compressors, List measurementAliasList); -``` - -注意:目前**暂不支持**使用传感器别名。 - -* 删除一个或多个时间序列 - -``` java -void deleteTimeseries(String path) -void deleteTimeseries(List paths) -``` - -* 检测时间序列是否存在 - -``` java -boolean checkTimeseriesExists(String path) -``` - -#### 元数据模版 - -* 创建元数据模板,可以通过先后创建 Template、MeasurementNode 的对象,描述模板内物理量结构与类型、编码方式、压缩方式等信息,并通过以下接口创建模板 - -``` java -public void createSchemaTemplate(Template template); - -Class Template { - private String name; - private boolean directShareTime; - Map children; - public Template(String name, boolean isShareTime); - - public void addToTemplate(Node node); - public void deleteFromTemplate(String name); - public void setShareTime(boolean shareTime); -} - -Abstract Class Node { - private String name; - public void addChild(Node node); - public void deleteChild(Node node); -} - -Class MeasurementNode extends Node { - TSDataType dataType; - TSEncoding encoding; - CompressionType compressor; - public MeasurementNode(String name, - TSDataType dataType, - TSEncoding encoding, - CompressionType compressor); -} -``` - -通过上述类的实例描述模板时,Template 内应当仅能包含单层的 MeasurementNode,具体可以参见如下示例: - -``` java -MeasurementNode nodeX = new MeasurementNode("x", TSDataType.FLOAT, TSEncoding.RLE, CompressionType.SNAPPY); -MeasurementNode nodeY = new MeasurementNode("y", TSDataType.FLOAT, TSEncoding.RLE, CompressionType.SNAPPY); -MeasurementNode nodeSpeed = new MeasurementNode("speed", TSDataType.DOUBLE, TSEncoding.GORILLA, CompressionType.SNAPPY); - -// This is the template we suggest to implement -Template flatTemplate = new Template("flatTemplate"); -template.addToTemplate(nodeX); -template.addToTemplate(nodeY); -template.addToTemplate(nodeSpeed); - -createSchemaTemplate(flatTemplate); -``` - -* 完成模板挂载操作后,可以通过如下的接口在给定的设备上使用模板注册序列,或者也可以直接向相应的设备写入数据以自动使用模板注册序列。 - -``` java -void createTimeseriesUsingSchemaTemplate(List devicePathList) -``` - -* 将名为'templateName'的元数据模板挂载到'prefixPath'路径下,在执行这一步之前,你需要创建名为'templateName'的元数据模板 -* **请注意,我们强烈建议您将模板设置在 database 或 database 下层的节点中,以更好地适配未来版本更新及各模块的协作** - -``` java -void setSchemaTemplate(String templateName, String prefixPath) -``` - -- 将模板挂载到 MTree 上之后,你可以随时查询所有模板的名称、某模板被设置到 MTree 的所有路径、所有正在使用某模板的所有路径,即如下接口: - -``` java -/** @return All template names. */ -public List showAllTemplates(); - -/** @return All paths have been set to designated template. */ -public List showPathsTemplateSetOn(String templateName); - -/** @return All paths are using designated template. */ -public List showPathsTemplateUsingOn(String templateName) -``` - -- 如果你需要删除某一个模板,请确保在进行删除之前,MTree 上已经没有节点被挂载了模板,对于已经被挂载模板的节点,可以用如下接口卸载模板; - - -``` java -void unsetSchemaTemplate(String prefixPath, String templateName); -public void dropSchemaTemplate(String templateName); -``` - -* 请注意,如果一个子树中有多个孩子节点需要使用模板,可以在其共同父母节点上使用 setSchemaTemplate 。而只有在已有数据点插入模板对应的物理量时,模板才会被设置为激活状态,进而被 show timeseries 等查询检测到。 -* 卸载'prefixPath'路径下的名为'templateName'的元数据模板。你需要保证给定的路径'prefixPath'下需要有名为'templateName'的元数据模板。 - -注意:目前不支持从曾经在'prefixPath'路径及其后代节点使用模板插入数据后(即使数据已被删除)卸载模板。 - - -### 数据写入接口 - -推荐使用 insertTablet 帮助提高写入效率 - -* 插入一个 Tablet,Tablet 是一个设备若干行数据块,每一行的列都相同 - * **写入效率高** - * **支持批量写入** - * **支持写入空值**:空值处可以填入任意值,然后通过 BitMap 标记空值 - -``` java -void insertTablet(Tablet tablet) - -public class Tablet { - /** deviceId of this tablet */ - public String prefixPath; - /** the list of measurement schemas for creating the tablet */ - private List schemas; - /** timestamps in this tablet */ - public long[] timestamps; - /** each object is a primitive type array, which represents values of one measurement */ - public Object[] values; - /** each bitmap represents the existence of each value in the current column. */ - public BitMap[] bitMaps; - /** the number of rows to include in this tablet */ - public int rowSize; - /** the maximum number of rows for this tablet */ - private int maxRowNumber; - /** whether this tablet store data of aligned timeseries or not */ - private boolean isAligned; -} -``` - -* 插入多个 Tablet - -``` java -void insertTablets(Map tablets) -``` - -* 插入一个 Record,一个 Record 是一个设备一个时间戳下多个测点的数据。这里的 value 是 Object 类型,相当于提供了一个公用接口,后面可以通过 TSDataType 将 value 强转为原类型 - - 其中,Object 类型与 TSDataType 类型的对应关系如下表所示: - - | TSDataType | Object | - |------------|--------------| - | BOOLEAN | Boolean | - | INT32 | Integer | - | DATE | LocalDate | - | INT64 | Long | - | TIMESTAMP | Long | - | FLOAT | Float | - | DOUBLE | Double | - | TEXT | String, Binary | - | STRING | String, Binary | - | BLOB | Binary | - -``` java -void insertRecord(String prefixPath, long time, List measurements, - List types, List values) -``` - -* 插入多个 Record - -``` java -void insertRecords(List deviceIds, - List times, - List> measurementsList, - List> typesList, - List> valuesList) -``` - -* 插入同属于一个 device 的多个 Record - -``` java -void insertRecordsOfOneDevice(String deviceId, List times, - List> measurementsList, List> typesList, - List> valuesList) -``` - -#### 带有类型推断的写入 - -当数据均是 String 类型时,我们可以使用如下接口,根据 value 的值进行类型推断。例如:value 为 "true" ,就可以自动推断为布尔类型。value 为 "3.2" ,就可以自动推断为数值类型。服务器需要做类型推断,可能会有额外耗时,速度较无需类型推断的写入慢 - -* 插入一个 Record,一个 Record 是一个设备一个时间戳下多个测点的数据 - -``` java -void insertRecord(String prefixPath, long time, List measurements, List values) -``` - -* 插入多个 Record - -``` java -void insertRecords(List deviceIds, List times, - List> measurementsList, List> valuesList) -``` - -* 插入同属于一个 device 的多个 Record - -``` java -void insertStringRecordsOfOneDevice(String deviceId, List times, - List> measurementsList, List> valuesList) -``` - -#### 对齐时间序列的写入 - -对齐时间序列的写入使用 insertAlignedXXX 接口,其余与上述接口类似: - -* insertAlignedRecord -* insertAlignedRecords -* insertAlignedRecordsOfOneDevice -* insertAlignedStringRecordsOfOneDevice -* insertAlignedTablet -* insertAlignedTablets - -### 数据删除接口 - -* 删除一个或多个时间序列在某个时间点前或这个时间点的数据 - -``` java -void deleteData(String path, long endTime) -void deleteData(List paths, long endTime) -``` - -### 数据查询接口 - -* 时间序列原始数据范围查询: - - 指定的查询时间范围为左闭右开区间,包含开始时间但不包含结束时间。 - -``` java -SessionDataSet executeRawDataQuery(List paths, long startTime, long endTime); -``` - -* 最新点查询: - - 查询最后一条时间戳大于等于某个时间点的数据。 - ``` java - SessionDataSet executeLastDataQuery(List paths, long lastTime); - ``` - - 快速查询单设备下指定序列最新点,支持重定向;如果您确认使用的查询路径是合法的,可将`isLegalPathNodes`置为true以避免路径校验带来的性能损失。 - ``` java - SessionDataSet executeLastDataQueryForOneDevice( - String db, String device, List sensors, boolean isLegalPathNodes); - ``` - -* 聚合查询: - - 支持指定查询时间范围。指定的查询时间范围为左闭右开区间,包含开始时间但不包含结束时间。 - - 支持按照时间区间分段查询。 - -``` java -SessionDataSet executeAggregationQuery(List paths, List aggregations); - -SessionDataSet executeAggregationQuery( - List paths, List aggregations, long startTime, long endTime); - -SessionDataSet executeAggregationQuery( - List paths, - List aggregations, - long startTime, - long endTime, - long interval); - -SessionDataSet executeAggregationQuery( - List paths, - List aggregations, - long startTime, - long endTime, - long interval, - long slidingStep); -``` - -* 直接执行查询语句 - -``` java -SessionDataSet executeQueryStatement(String sql) -``` - -### 数据订阅 - -#### 1 Topic 管理 - -IoTDB 订阅客户端中的 `SubscriptionSession` 类提供了 Topic 管理的相关接口。Topic状态变化如下图所示: - -
- -
- -##### 1.1 创建 Topic - -```Java - void createTopicIfNotExists(String topicName, Properties properties) throws Exception; -``` - -示例: - -```Java -try (final SubscriptionSession session = new SubscriptionSession(host, port)) { - session.open(); - final Properties config = new Properties(); - config.put(TopicConstant.PATH_KEY, "root.db.**"); - session.createTopic(topicName, config); +### 2.2 创建连接池实例 + +```java +import java.util.ArrayList; +import java.util.List; +import org.apache.iotdb.session.pool.SessionPool; + +public class IoTDBSessionPoolExample { + private static SessionPool sessionPool; + + public static void main(String[] args) { + // Using nodeUrls ensures that when one node goes down, other nodes are automatically connected to retry + List nodeUrls = new ArrayList<>(); + nodeUrls.add("127.0.0.1:6667"); + nodeUrls.add("127.0.0.1:6668"); + sessionPool = + new SessionPool.Builder() + .nodeUrls(nodeUrls) + .user("root") + .password("root") + .maxSize(3) + .build(); + } } ``` -##### 1.2 删除 Topic - -```Java -void dropTopicIfExists(String topicName) throws Exception; -``` - -##### 1.3 查看 Topic - -```Java -// 获取所有 topics -Set getTopics() throws Exception; - -// 获取单个 topic -Optional getTopic(String topicName) throws Exception; -``` - -#### 2 查看订阅状态 - -IoTDB 订阅客户端中的 `SubscriptionSession` 类提供了获取订阅状态的相关接口: - -```Java -Set getSubscriptions() throws Exception; -Set getSubscriptions(final String topicName) throws Exception; -``` - -#### 3 创建 Consumer - -在使用 JAVA 原生接口创建 consumer 时,需要指定 consumer 所应用的参数。 +### 2.3 执行数据库操作 +#### 2.3.1 数据写入 +在工业场景中,数据写入可分为以下几类:多行数据写入、单设备多行数据写入,下面按不同场景对写入接口进行介绍。 -对于 `SubscriptionPullConsumer` 和 `SubscriptionPushConsumer` 而言,有以下公共配置: +##### 多行数据写入接口 +接口说明:支持一次写入多行数据,每一行对应一个设备一个时间戳的多个测点值。 -| 参数 | 是否必填(默认值) | 参数含义 | -| :-------------------------------------------- | :--------------------------------- | :----------------------------------------------------------- | -| host | optional: 127.0.0.1 | `String`: IoTDB 中某 DataNode 的 RPC host | -| port | optional: 6667 | `Integer`: IoTDB 中某 DataNode 的 RPC port | -| node-urls | optional: 127.0.0.1:6667 | `List`: IoTDB 中所有 DataNode 的 RPC 地址,可以是多个;host:port 和 node-urls 选填一个即可。当 host:port 和 node-urls 都填写了,则取 host:port 和 node-urls 的**并集**构成新的 node-urls 应用 | -| username | optional: root | `String`: IoTDB 中 DataNode 的用户名 | -| password | optional: root | `String`: IoTDB 中 DataNode 的密码 | -| groupId | optional | `String`: consumer group id,若未指定则随机分配(新的 consumer group),保证不同的 consumer group 对应的 consumer group id 均不相同 | -| consumerId | optional | `String`: consumer client id,若未指定则随机分配,保证同一个 consumer group 中每一个 consumer client id 均不相同 | -| heartbeatIntervalMs | optional: 30000 (min: 1000) | `Long`: consumer 向 IoTDB DataNode 定期发送心跳请求的间隔 | -| endpointsSyncIntervalMs | optional: 120000 (min: 5000) | `Long`: consumer 探测 IoTDB 集群节点扩缩容情况调整订阅连接的间隔 | -| fileSaveDir | optional: Paths.get(System.getProperty("user.dir"), "iotdb-subscription").toString() | `String`: consumer 订阅出的 TsFile 文件临时存放的目录路径 | -| fileSaveFsync | optional: false | `Boolean`: consumer 订阅 TsFile 的过程中是否主动调用 fsync | +接口列表: -##### 3.1 SubscriptionPushConsumer +| 接口名称 | 功能描述 | +|--------------------------------------------------------|-----------------------| +| `insertRecords(List deviceIds, List times, List> measurementsList, List> typesList, List> valuesList)` | 插入多行数据,适用于不同测点独立采集的场景 | -以下为 `SubscriptionPushConsumer` 中的特殊配置: -| 参数 | 是否必填(默认值) | 参数含义 | -| :-------------------------------------------- | :--------------------------------- | :----------------------------------------------------------- | -| ackStrategy | optional: `ACKStrategy.AFTER_CONSUME` | 消费进度的确认机制包含以下选项:`ACKStrategy.BEFORE_CONSUME`(当 consumer 收到数据时立刻提交消费进度,`onReceive` 前)`ACKStrategy.AFTER_CONSUME`(当 consumer 消费完数据再去提交消费进度,`onReceive` 后) | -| consumeListener | optional | 消费数据的回调函数,需实现 `ConsumeListener` 接口,定义消费 `SessionDataSetsHandler` 和 `TsFileHandler` 形式数据的处理逻辑 | -| autoPollIntervalMs | optional: 5000 (min: 500) | Long: consumer 自动拉取数据的时间间隔,单位为**毫秒** | -| autoPollTimeoutMs | optional: 10000 (min: 1000) | Long: consumer 每次拉取数据的超时时间,单位为**毫秒** | +代码案例: +```java +import java.util.ArrayList; +import java.util.List; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.apache.iotdb.session.pool.SessionPool; +import org.apache.tsfile.enums.TSDataType; -其中,`ConsumerListener` 接口定义如下: - -```Java -@FunctionInterface -interface ConsumeListener { - default ConsumeResult onReceive(Message message) { - return ConsumeResult.SUCCESS; +public class SessionPoolExample { + private static SessionPool sessionPool; + public static void main(String[] args) throws IoTDBConnectionException, StatementExecutionException { + // 1. init SessionPool + constructSessionPool(); + // 2. execute insert data + insertRecordsExample(); + // 3. close SessionPool + closeSessionPool(); } -} - -enum ConsumeResult { - SUCCESS, - FAILURE, -} -``` - -##### 3.2 SubscriptionPullConsumer - -以下为 `SubscriptionPullConsumer` 中的特殊配置: - -| 参数 | 是否必填(默认值) | 参数含义 | -| :-------------------------------------------- | :--------------------------------- | :----------------------------------------------------------- | -| autoCommit | optional: true | Boolean: 是否自动提交消费进度如果此参数设置为 false,则需要调用 `commit` 方法来手动提交消费进度 | -| autoCommitInterval | optional: 5000 (min: 500) | Long: 自动提交消费进度的时间间隔,单位为**毫秒**仅当 autoCommit 参数为 true 的时候才会生效 | - -在创建 consumer 后,需要手动调用 consumer 的 open 方法: - -```Java -void open() throws Exception; -``` - -此时,IoTDB 订阅客户端才会校验 consumer 的配置正确性,在校验成功后 consumer 就会加入对应的 consumer group。也就是说,在打开 consumer 后,才可以使用返回的 consumer 对象进行订阅 Topic,消费数据等操作。 - -#### 4 订阅 Topic - -`SubscriptionPushConsumer` 和 `SubscriptionPullConsumer` 提供了下述 JAVA 原生接口用于订阅 Topics: - -```Java -// 订阅 topics -void subscribe(String topic) throws Exception; -void subscribe(List topics) throws Exception; -``` - -- 在 consumer 订阅 topic 前,topic 必须已经被创建,否则订阅会失败 -- 一个 consumer 在已经订阅了某个 topic 的情况下再次订阅这个 topic,不会报错 -- 如果该 consumer 所在的 consumer group 中已经有 consumers 订阅了相同的 topics,那么该 consumer 将会复用对应的消费进度 - -#### 5 消费数据 - -无论是 push 模式还是 pull 模式的 consumer: - -- 只有显式订阅了某个 topic,才会收到对应 topic 的数据 -- 若在创建后没有订阅任何 topics,此时该 consumer 无法消费到任何数据,即使该 consumer 所在的 consumer group 中其它的 consumers 订阅了一些 topics - -##### 5.1 SubscriptionPushConsumer - -SubscriptionPushConsumer 在订阅 topics 后,无需手动拉取数据,其消费数据的逻辑在创建 SubscriptionPushConsumer 指定的 `consumeListener` 配置中。 - -##### 5.2 SubscriptionPullConsumer - -SubscriptionPullConsumer 在订阅 topics 后,需要主动调用 `poll` 方法拉取数据: - -```Java -List poll(final Duration timeout) throws Exception; -List poll(final long timeoutMs) throws Exception; -List poll(final Set topicNames, final Duration timeout) throws Exception; -List poll(final Set topicNames, final long timeoutMs) throws Exception; -``` - -在 poll 方法中可以指定需要拉取的 topic 名称(如果不指定则默认拉取该 consumer 已订阅的所有 topics)和超时时间。 - -当 SubscriptionPullConsumer 配置 autoCommit 参数为 false 时,需要手动调用 commitSync 和 commitAsync 方法同步或异步提交某批数据的消费进度: - -```Java -void commitSync(final SubscriptionMessage message) throws Exception; -void commitSync(final Iterable messages) throws Exception; - -CompletableFuture commitAsync(final SubscriptionMessage message); -CompletableFuture commitAsync(final Iterable messages); -void commitAsync(final SubscriptionMessage message, final AsyncCommitCallback callback); -void commitAsync(final Iterable messages, final AsyncCommitCallback callback); -``` -AsyncCommitCallback 类定义如下: - -```Java -public interface AsyncCommitCallback { - default void onComplete() { - // Do nothing - } + private static void constructSessionPool() { + // Using nodeUrls ensures that when one node goes down, other nodes are automatically connected to retry + List nodeUrls = new ArrayList<>(); + nodeUrls.add("127.0.0.1:6667"); + nodeUrls.add("127.0.0.1:6668"); + sessionPool = + new SessionPool.Builder() + .nodeUrls(nodeUrls) + .user("root") + .password("root") + .maxSize(3) + .build(); + } - default void onFailure(final Throwable e) { - // Do nothing - } + public static void insertRecordsExample() throws IoTDBConnectionException, StatementExecutionException { + String deviceId = "root.sg1.d1"; + List measurements = new ArrayList<>(); + measurements.add("s1"); + measurements.add("s2"); + measurements.add("s3"); + List deviceIds = new ArrayList<>(); + List> measurementsList = new ArrayList<>(); + List> valuesList = new ArrayList<>(); + List timestamps = new ArrayList<>(); + List> typesList = new ArrayList<>(); + + for (long time = 0; time < 500; time++) { + List values = new ArrayList<>(); + List types = new ArrayList<>(); + values.add(1L); + values.add(2L); + values.add(3L); + types.add(TSDataType.INT64); + types.add(TSDataType.INT64); + types.add(TSDataType.INT64); + + deviceIds.add(deviceId); + measurementsList.add(measurements); + valuesList.add(values); + typesList.add(types); + timestamps.add(time); + if (time != 0 && time % 100 == 0) { + try { + sessionPool.insertRecords(deviceIds, timestamps, measurementsList, typesList, valuesList); + } catch (IoTDBConnectionException | StatementExecutionException e) { + // solve exception + } + deviceIds.clear(); + measurementsList.clear(); + valuesList.clear(); + typesList.clear(); + timestamps.clear(); + } + } + try { + sessionPool.insertRecords(deviceIds, timestamps, measurementsList, typesList, valuesList); + } catch (IoTDBConnectionException | StatementExecutionException e) { + // solve exception + } + } + + public static void closeSessionPool(){ + sessionPool.close(); + } } ``` +##### 单设备多行数据写入接口 +接口说明:支持一次写入单个设备的多行数据,每一行对应一个时间戳的多个测点值。 + +接口列表: + +| 接口名称 | 功能描述 | +|-----------------------------------------------------------------------------------------|----------------------------| +| `insertTablet(Tablet tablet)` | 插入单个设备的多行数据,适用于不同测点独立采集的场景 | + +代码案例: +```java +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.apache.iotdb.session.pool.SessionPool; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.write.record.Tablet; +import org.apache.tsfile.write.schema.MeasurementSchema; + +public class SessionPoolExample { + private static SessionPool sessionPool; + public static void main(String[] args) throws IoTDBConnectionException, StatementExecutionException { + // 1. init SessionPool + constructSessionPool(); + // 2. execute insert data + insertTabletExample(); + // 3. close SessionPool + closeSessionPool(); + } + + private static void constructSessionPool() { + // Using nodeUrls ensures that when one node goes down, other nodes are automatically connected to retry + List nodeUrls = new ArrayList<>(); + nodeUrls.add("127.0.0.1:6667"); + nodeUrls.add("127.0.0.1:6668"); + sessionPool = + new SessionPool.Builder() + .nodeUrls(nodeUrls) + .user("root") + .password("root") + .maxSize(3) + .build(); + } -#### 6 取消订阅 - -`SubscriptionPushConsumer` 和 `SubscriptionPullConsumer` 提供了下述 JAVA 原生接口用于取消订阅并关闭 consumer: - -```Java -// 取消订阅 topics -void unsubscribe(String topic) throws Exception; -void unsubscribe(List topics) throws Exception; - -// 关闭 consumer -void close(); -``` - -- 在 topic 存在的情况下,如果一个 consumer 在没有订阅了某个 topic 的情况下取消订阅某个 topic,不会报错 -- consumer close 时会退出对应的 consumer group,同时自动 unsubscribe 该 consumer 现存订阅的所有 topics -- consumer 在 close 后生命周期即结束,无法再重新 open 订阅并消费数据 - -#### 7 代码示例 - -##### 7.1 单 Pull Consumer 消费 SessionDataSetsHandler 形式的数据 - -```Java -// Create topics -try (final SubscriptionSession session = new SubscriptionSession(HOST, PORT)) { - session.open(); - final Properties config = new Properties(); - config.put(TopicConstant.PATH_KEY, "root.db.**"); - session.createTopic(TOPIC_1, config); -} - -// Subscription: property-style ctor -final Properties config = new Properties(); -config.put(ConsumerConstant.CONSUMER_ID_KEY, "c1"); -config.put(ConsumerConstant.CONSUMER_GROUP_ID_KEY, "cg1"); - -final SubscriptionPullConsumer consumer1 = new SubscriptionPullConsumer(config); -consumer1.open(); -consumer1.subscribe(TOPIC_1); -while (true) { - LockSupport.parkNanos(SLEEP_NS); // wait some time - final List messages = consumer1.poll(POLL_TIMEOUT_MS); - for (final SubscriptionMessage message : messages) { - for (final SubscriptionSessionDataSet dataSet : message.getSessionDataSetsHandler()) { - System.out.println(dataSet.getColumnNames()); - System.out.println(dataSet.getColumnTypes()); - while (dataSet.hasNext()) { - System.out.println(dataSet.next()); - } + private static void insertTabletExample() throws IoTDBConnectionException, StatementExecutionException { + /* + * A Tablet example: + * device1 + * time s1, s2, s3 + * 1, 1, 1, 1 + * 2, 2, 2, 2 + * 3, 3, 3, 3 + */ + // The schema of measurements of one device + // only measurementId and data type in MeasurementSchema take effects in Tablet + List schemaList = new ArrayList<>(); + schemaList.add(new MeasurementSchema("s1", TSDataType.INT64)); + schemaList.add(new MeasurementSchema("s2", TSDataType.INT64)); + schemaList.add(new MeasurementSchema("s3", TSDataType.INT64)); + + Tablet tablet = new Tablet("root.sg.d1", schemaList, 100); + + // Method 1 to add tablet data + long timestamp = System.currentTimeMillis(); + + Random random = new Random(); + for (long row = 0; row < 100; row++) { + int rowIndex = tablet.rowSize++; + tablet.addTimestamp(rowIndex, timestamp); + for (int s = 0; s < 3; s++) { + long value = random.nextLong(); + tablet.addValue(schemaList.get(s).getMeasurementId(), rowIndex, value); + } + if (tablet.rowSize == tablet.getMaxRowNumber()) { + sessionPool.insertTablet(tablet); + tablet.reset(); + } + timestamp++; + } + if (tablet.rowSize != 0) { + sessionPool.insertTablet(tablet); + tablet.reset(); + } } - } - // Auto commit -} -// Show topics and subscriptions -try (final SubscriptionSession session = new SubscriptionSession(HOST, PORT)) { - session.open(); - session.getTopics().forEach((System.out::println)); - session.getSubscriptions().forEach((System.out::println)); + public static void closeSessionPool(){ + sessionPool.close(); + } } - -consumer1.unsubscribe(TOPIC_1); -consumer1.close(); ``` -##### 7.2 多 Push Consumer 消费 TsFileHandler 形式的数据 - -```Java -// Create topics -try (final SubscriptionSession subscriptionSession = new SubscriptionSession(HOST, PORT)) { - subscriptionSession.open(); - final Properties config = new Properties(); - config.put(TopicConstant.FORMAT_KEY, TopicConstant.FORMAT_TS_FILE_HANDLER_VALUE); - subscriptionSession.createTopic(TOPIC_2, config); -} +#### 2.3.2 SQL操作 + +SQL操作分为查询和非查询两类操作,对应的接口为`executeQuery`和`executeNonQuery`操作,其区别为前者执行的是具体的查询语句,会返回一个结果集,后者是执行的是增、删、改操作,不返回结果集。 + +```java +import java.util.ArrayList; +import java.util.List; +import org.apache.iotdb.isession.pool.SessionDataSetWrapper; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.apache.iotdb.session.pool.SessionPool; + +public class SessionPoolExample { + private static SessionPool sessionPool; + public static void main(String[] args) throws IoTDBConnectionException, StatementExecutionException { + // 1. init SessionPool + constructSessionPool(); + // 2. executes a non-query SQL statement, such as a DDL or DML command. + executeQueryExample(); + // 3. executes a query SQL statement and returns the result set. + executeNonQueryExample(); + // 4. close SessionPool + closeSessionPool(); + } -final List threads = new ArrayList<>(); -for (int i = 0; i < 8; ++i) { - final int idx = i; - final Thread thread = - new Thread( - () -> { - // Subscription: builder-style ctor - try (final SubscriptionPushConsumer consumer2 = - new SubscriptionPushConsumer.Builder() - .consumerId("c" + idx) - .consumerGroupId("cg2") - .fileSaveDir(System.getProperty("java.io.tmpdir")) - .ackStrategy(AckStrategy.AFTER_CONSUME) - .consumeListener( - message -> { - doSomething(message.getTsFileHandler()); - return ConsumeResult.SUCCESS; - }) - .buildPushConsumer()) { - consumer2.open(); - consumer2.subscribe(TOPIC_2); - // block the consumer main thread - Thread.sleep(Long.MAX_VALUE); - } catch (final IOException | InterruptedException e) { - throw new RuntimeException(e); + private static void executeNonQueryExample() throws IoTDBConnectionException, StatementExecutionException { + // 1. create a nonAligned time series + sessionPool.executeNonQueryStatement("create timeseries root.test.d1.s1 with dataType = int32"); + // 2. set ttl + sessionPool.executeNonQueryStatement("set TTL to root.test.** 10000"); + // 3. delete time series + sessionPool.executeNonQueryStatement("delete timeseries root.test.d1.s1"); + private static void executeQueryExample() throws IoTDBConnectionException, StatementExecutionException { + // 1. execute normal query + try(SessionDataSetWrapper wrapper = sessionPool.executeQueryStatement("select s1 from root.sg1.d1 limit 10")) { + while (wrapper.hasNext()) { + System.out.println(wrapper.next()); } - }); - thread.start(); - threads.add(thread); -} + } + // 2. execute aggregate query + try(SessionDataSetWrapper wrapper = sessionPool.executeQueryStatement("select count(s1) from root.sg1.d1 group by ([0, 40), 5ms) ")) { + while (wrapper.hasNext()) { + System.out.println(wrapper.next()); + } + } + } + + private static void constructSessionPool() { + // Using nodeUrls ensures that when one node goes down, other nodes are automatically connected to retry + List nodeUrls = new ArrayList<>(); + nodeUrls.add("127.0.0.1:6667"); + nodeUrls.add("127.0.0.1:6668"); + sessionPool = + new SessionPool.Builder() + .nodeUrls(nodeUrls) + .user("root") + .password("root") + .maxSize(3) + .build(); + } -for (final Thread thread : threads) { - thread.join(); + public static void closeSessionPool(){ + sessionPool.close(); + } } ``` - -### 其他功能(直接执行SQL语句) - -``` java -void executeNonQueryStatement(String sql) -``` - -### 写入测试接口 (用于分析网络带宽) - -不实际写入数据,只将数据传输到 server 即返回 - -* 测试 insertRecord - -``` java -void testInsertRecord(String deviceId, long time, List measurements, List values) - -void testInsertRecord(String deviceId, long time, List measurements, - List types, List values) -``` - -* 测试 testInsertRecords - -``` java -void testInsertRecords(List deviceIds, List times, - List> measurementsList, List> valuesList) - -void testInsertRecords(List deviceIds, List times, - List> measurementsList, List> typesList, - List> valuesList) -``` - -* 测试 insertTablet - -``` java -void testInsertTablet(Tablet tablet) -``` - -* 测试 insertTablets - -``` java -void testInsertTablets(Map tablets) -``` - -### 示例代码 - -浏览上述接口的详细信息,请参阅代码 ```session/src/main/java/org/apache/iotdb/session/Session.java``` - -使用上述接口的示例代码在 ```example/session/src/main/java/org/apache/iotdb/SessionExample.java``` - -使用对齐时间序列和元数据模板的示例可以参见 `example/session/src/main/java/org/apache/iotdb/AlignedTimeseriesSessionExample.java` \ No newline at end of file +### 3 全量接口说明 + +#### 3.1 参数列表 +Session具有如下的字段,可以通过构造函数或Session.Builder方式设置如下参数 + +| 字段名 | 类型 | 说明 | +|--------------------------------|-------------------------------|----------------------------------------------------------------------| +| `nodeUrls` | `List` | 数据库节点的 URL 列表,支持多节点连接 | +| `username` | `String` | 用户名 | +| `password` | `String` | 密码 | +| `fetchSize` | `int` | 查询结果的默认批量返回大小 | +| `useSSL` | `boolean` | 是否启用 SSL | +| `trustStore` | `String` | 信任库路径 | +| `trustStorePwd` | `String` | 信任库密码 | +| `queryTimeoutInMs` | `long` | 查询的超时时间,单位毫秒 | +| `enableRPCCompression` | `boolean` | 是否启用 RPC 压缩 | +| `connectionTimeoutInMs` | `int` | 连接超时时间,单位毫秒 | +| `zoneId` | `ZoneId` | 会话的时区设置 | +| `thriftDefaultBufferSize` | `int` | Thrift 默认缓冲区大小 | +| `thriftMaxFrameSize` | `int` | Thrift 最大帧大小 | +| `defaultEndPoint` | `TEndPoint` | 默认的数据库端点信息 | +| `defaultSessionConnection` | `SessionConnection` | 默认的会话连接对象 | +| `isClosed` | `boolean` | 当前会话是否已关闭 | +| `enableRedirection` | `boolean` | 是否启用重定向功能 | +| `enableRecordsAutoConvertTablet` | `boolean` | 是否启用记录自动转换为 Tablet 的功能 | +| `deviceIdToEndpoint` | `Map` | 设备 ID 和数据库端点的映射关系 | +| `endPointToSessionConnection` | `Map` | 数据库端点和会话连接的映射关系 | +| `executorService` | `ScheduledExecutorService` | 用于定期更新节点列表的线程池 | +| `availableNodes` | `INodeSupplier` | 可用节点的供应器 | +| `enableQueryRedirection` | `boolean` | 是否启用查询重定向功能 | +| `version` | `Version` | 客户端的版本号,用于与服务端的兼容性判断 | +| `enableAutoFetch` | `boolean` | 是否启用自动获取功能 | +| `maxRetryCount` | `int` | 最大重试次数 | +| `retryIntervalInMs` | `long` | 重试的间隔时间,单位毫秒 | + + + +#### 3.2 接口列表 + +##### 3.2.1 元数据管理 + +| 方法名 | 功能描述 | 参数解释 | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `createDatabase(String database)` | 创建数据库 | `database`: 数据库名称 | +| `deleteDatabase(String database)` | 删除指定数据库 | `database`: 要删除的数据库名称 | +| `deleteDatabases(List databases)` | 批量删除数据库 | `databases`: 要删除的数据库名称列表 | +| `createTimeseries(String path, TSDataType dataType, TSEncoding encoding, CompressionType compressor)` | 创建单个时间序列 | `path`: 时间序列路径,`dataType`: 数据类型,`encoding`: 编码类型,`compressor`: 压缩类型 | +| `createAlignedTimeseries(...)` | 创建对齐时间序列 | 设备ID、测点列表、数据类型列表、编码列表、压缩类型列表 | +| `createMultiTimeseries(...)` | 批量创建时间序列 | 多个路径、数据类型、编码、压缩类型、属性、标签、别名等 | +| `deleteTimeseries(String path)` | 删除时间序列 | `path`: 要删除的时间序列路径 | +| `deleteTimeseries(List paths)` | 批量删除时间序列 | `paths`: 要删除的时间序列路径列表 | +| `setSchemaTemplate(String templateName, String prefixPath)` | 设置模式模板 | `templateName`: 模板名称,`prefixPath`: 应用模板的路径 | +| `createSchemaTemplate(Template template)` | 创建模式模板 | `template`: 模板对象 | +| `dropSchemaTemplate(String templateName)` | 删除模式模板 | `templateName`: 要删除的模板名称 | +| `addAlignedMeasurementsInTemplate(...)` | 添加对齐测点到模板 | 模板名称、测点路径列表、数据类型、编码类型、压缩类型 | +| `addUnalignedMeasurementsInTemplate(...)` | 添加非对齐测点到模板 | 同上 | +| `deleteNodeInTemplate(String templateName, String path)` | 删除模板中的节点 | `templateName`: 模板名称,`path`: 要删除的路径 | +| `countMeasurementsInTemplate(String name)` | 统计模板中测点数量 | `name`: 模板名称 | +| `isMeasurementInTemplate(String templateName, String path)` | 检查模板中是否存在某测点 | `templateName`: 模板名称,`path`: 测点路径 | +| `isPathExistInTemplate(String templateName, String path)` | 检查模板中路径是否存在 | 同上 | +| `showMeasurementsInTemplate(String templateName)` | 显示模板中的测点 | `templateName`: 模板名称 | +| `showMeasurementsInTemplate(String templateName, String pattern)` | 按模式显示模板中的测点 | `templateName`: 模板名称,`pattern`: 匹配模式 | +| `showAllTemplates()` | 显示所有模板 | 无参数 | +| `showPathsTemplateSetOn(String templateName)` | 显示模板应用的路径 | `templateName`: 模板名称 | +| `showPathsTemplateUsingOn(String templateName)` | 显示模板实际使用的路径 | 同上 | +| `unsetSchemaTemplate(String prefixPath, String templateName)` | 取消路径的模板设置 | `prefixPath`: 路径,`templateName`: 模板名称 | + + +##### 3.2.2 数据写入 +| 方法名 | 功能描述 | 参数解释 | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `insertRecord(String deviceId, long time, List measurements, List types, Object... values)` | 插入单条记录 | `deviceId`: 设备ID,`time`: 时间戳,`measurements`: 测点列表,`types`: 数据类型列表,`values`: 值列表 | +| `insertRecord(String deviceId, long time, List measurements, List values)` | 插入单条记录 | `deviceId`: 设备ID,`time`: 时间戳,`measurements`: 测点列表,`values`: 值列表 | +| `insertRecords(List deviceIds, List times, List> measurementsList, List> valuesList)` | 插入多条记录 | `deviceIds`: 设备ID列表,`times`: 时间戳列表,`measurementsList`: 测点列表列表,`valuesList`: 值列表 | +| `insertRecords(List deviceIds, List times, List> measurementsList, List> typesList, List> valuesList)` | 插入多条记录 | 同上,增加 `typesList`: 数据类型列表 | +| `insertRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> typesList, List> valuesList)` | 插入单设备的多条记录 | `deviceId`: 设备ID,`times`: 时间戳列表,`measurementsList`: 测点列表列表,`typesList`: 类型列表,`valuesList`: 值列表 | +| `insertRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> typesList, List> valuesList, boolean haveSorted)` | 插入排序后的单设备多条记录 | 同上,增加 `haveSorted`: 数据是否已排序 | +| `insertStringRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> valuesList)` | 插入字符串格式的单设备记录 | `deviceId`: 设备ID,`times`: 时间戳列表,`measurementsList`: 测点列表,`valuesList`: 值列表 | +| `insertStringRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> valuesList, boolean haveSorted)` | 插入排序的字符串格式单设备记录 | 同上,增加 `haveSorted`: 数据是否已排序 | +| `insertAlignedRecord(String deviceId, long time, List measurements, List types, List values)` | 插入单条对齐记录 | `deviceId`: 设备ID,`time`: 时间戳,`measurements`: 测点列表,`types`: 类型列表,`values`: 值列表 | +| `insertAlignedRecord(String deviceId, long time, List measurements, List values)` | 插入字符串格式的单条对齐记录 | `deviceId`: 设备ID,`time`: 时间戳,`measurements`: 测点列表,`values`: 值列表 | +| `insertAlignedRecords(List deviceIds, List times, List> measurementsList, List> valuesList)` | 插入多条对齐记录 | `deviceIds`: 设备ID列表,`times`: 时间戳列表,`measurementsList`: 测点列表,`valuesList`: 值列表 | +| `insertAlignedRecords(List deviceIds, List times, List> measurementsList, List> typesList, List> valuesList)` | 插入多条对齐记录 | 同上,增加 `typesList`: 数据类型列表 | +| `insertAlignedRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> typesList, List> valuesList)` | 插入单设备的多条对齐记录 | 同上 | +| `insertAlignedRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> typesList, List> valuesList, boolean haveSorted)` | 插入排序的单设备多条对齐记录 | 同上,增加 `haveSorted`: 数据是否已排序 | +| `insertAlignedStringRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> valuesList)` | 插入字符串格式的单设备对齐记录 | `deviceId`: 设备ID,`times`: 时间戳列表,`measurementsList`: 测点列表,`valuesList`: 值列表 | +| `insertAlignedStringRecordsOfOneDevice(String deviceId, List times, List> measurementsList, List> valuesList, boolean haveSorted)` | 插入排序的字符串格式单设备对齐记录 | 同上,增加 `haveSorted`: 数据是否已排序 | +| `insertTablet(Tablet tablet)` | 插入单个Tablet数据 | `tablet`: 要插入的Tablet数据 | +| `insertTablet(Tablet tablet, boolean sorted)` | 插入排序的Tablet数据 | 同上,增加 `sorted`: 数据是否已排序 | +| `insertAlignedTablet(Tablet tablet)` | 插入对齐的Tablet数据 | `tablet`: 要插入的Tablet数据 | +| `insertAlignedTablet(Tablet tablet, boolean sorted)` | 插入排序的对齐Tablet数据 | 同上,增加 `sorted`: 数据是否已排序 | +| `insertTablets(Map tablets)` | 批量插入多个Tablet数据 | `tablets`: 设备ID到Tablet的映射表 | +| `insertTablets(Map tablets, boolean sorted)` | 批量插入排序的多个Tablet数据 | 同上,增加 `sorted`: 数据是否已排序 | +| `insertAlignedTablets(Map tablets)` | 批量插入多个对齐Tablet数据 | `tablets`: 设备ID到Tablet的映射表 | +| `insertAlignedTablets(Map tablets, boolean sorted)` | 批量插入排序的多个对齐Tablet数据 | 同上,增加 `sorted`: 数据是否已排序 | + +##### 3.2.3 数据删除 + +| 方法名 | 功能描述 | 参数解释 | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `deleteTimeseries(String path)` | 删除单个时间序列 | `path`: 时间序列路径 | +| `deleteTimeseries(List paths)` | 批量删除时间序列 | `paths`: 时间序列路径列表 | +| `deleteData(String path, long endTime)` | 删除指定路径的历史数据 | `path`: 路径,`endTime`: 结束时间戳 | +| `deleteData(List paths, long endTime)` | 批量删除路径的历史数据 | `paths`: 路径列表,`endTime`: 结束时间戳 | +| `deleteData(List paths, long startTime, long endTime)` | 删除路径时间范围内的历史数据 | 同上,增加 `startTime`: 起始时间戳 | + + +##### 3.2.4 数据查询 +| 方法名 | 功能描述 | 参数解释 | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `executeQueryStatement(String sql)` | 执行查询语句 | `sql`: 查询SQL语句 | +| `executeQueryStatement(String sql, long timeoutInMs)` | 执行带超时的查询语句 | `sql`: 查询SQL语句,`timeoutInMs`: 查询超时时间(毫秒) | +| `executeRawDataQuery(List paths, long startTime, long endTime)` | 查询指定路径的原始数据 | `paths`: 查询路径列表,`startTime`: 起始时间戳,`endTime`: 结束时间戳 | +| `executeRawDataQuery(List paths, long startTime, long endTime, long timeOut)` | 查询指定路径的原始数据(带超时) | 同上,增加 `timeOut`: 超时时间 | +| `executeLastDataQuery(List paths)` | 查询最新数据 | `paths`: 查询路径列表 | +| `executeLastDataQuery(List paths, long lastTime)` | 查询指定时间的最新数据 | `paths`: 查询路径列表,`lastTime`: 指定的时间戳 | +| `executeLastDataQuery(List paths, long lastTime, long timeOut)` | 查询指定时间的最新数据(带超时) | 同上,增加 `timeOut`: 超时时间 | +| `executeLastDataQueryForOneDevice(String db, String device, List sensors, boolean isLegalPathNodes)` | 查询单个设备的最新数据 | `db`: 数据库名,`device`: 设备名,`sensors`: 传感器列表,`isLegalPathNodes`: 是否合法路径节点 | +| `executeAggregationQuery(List paths, List aggregations)` | 执行聚合查询 | `paths`: 查询路径列表,`aggregations`: 聚合类型列表 | +| `executeAggregationQuery(List paths, List aggregations, long startTime, long endTime)` | 执行带时间范围的聚合查询 | 同上,增加 `startTime`: 起始时间戳,`endTime`: 结束时间戳 | +| `executeAggregationQuery(List paths, List aggregations, long startTime, long endTime, long interval)` | 执行带时间间隔的聚合查询 | 同上,增加 `interval`: 时间间隔 | +| `executeAggregationQuery(List paths, List aggregations, long startTime, long endTime, long interval, long slidingStep)` | 执行滑动窗口聚合查询 | 同上,增加 `slidingStep`: 滑动步长 | +| `fetchAllConnections()` | 获取所有活动连接信息 | 无参数 | + +##### 3.2.5 系统状态与备份 +| 方法名 | 功能描述 | 参数解释 | +|-----------------------------------------------------------------------------------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------| +| `getBackupConfiguration()` | 获取备份配置信息 | 无参数 | +| `fetchAllConnections()` | 获取所有活动的连接信息 | 无参数 | +| `getSystemStatus()` | 获取系统状态 | 已废弃,默认返回 `SystemStatus.NORMAL` | \ No newline at end of file diff --git a/src/zh/UserGuide/latest/User-Manual/Data-Sync.md b/src/zh/UserGuide/latest/User-Manual/Data-Sync.md new file mode 100644 index 000000000..0dc62fe47 --- /dev/null +++ b/src/zh/UserGuide/latest/User-Manual/Data-Sync.md @@ -0,0 +1,23 @@ +--- +redirectTo: Data-Sync_apache.html +--- +