From 14de96766b7b6010b44637a34e3c3b899318aaae Mon Sep 17 00:00:00 2001 From: dingbo Date: Fri, 15 Jul 2022 03:01:03 +0800 Subject: [PATCH] docs: high-volume.md --- .../example/highvolume/DataBaseMonitor.java | 47 ++ .../example/highvolume/FastWriteExample.java | 47 +- .../example/highvolume/MockDataSource.java | 53 +++ .../com/taos/example/highvolume/ReadTask.java | 55 +-- .../taos/example/highvolume/SQLWriter.java | 5 +- .../taos/example/highvolume/StmtWriter.java | 4 + .../taos/example/highvolume/WriteTask.java | 4 +- .../src/main/resources/highvolume2.drawio | 12 +- .../python/highvolume_faster_queue.py | 2 +- .../{mockdatasoruce.py => mockdatasource.py} | 0 .../03-insert-data/05-high-volume.md | 429 ++++++++++++++++++ .../03-insert-data/05-high-volume.mdx | 366 --------------- docs/zh/07-develop/03-insert-data/high.png | Bin 0 -> 26418 bytes .../07-develop/03-insert-data/highvolume.webp | Bin 6746 -> 7308 bytes 14 files changed, 544 insertions(+), 480 deletions(-) create mode 100644 docs/examples/java/src/main/java/com/taos/example/highvolume/DataBaseMonitor.java create mode 100644 docs/examples/java/src/main/java/com/taos/example/highvolume/MockDataSource.java create mode 100644 docs/examples/java/src/main/java/com/taos/example/highvolume/StmtWriter.java rename docs/examples/python/{mockdatasoruce.py => mockdatasource.py} (100%) create mode 100644 docs/zh/07-develop/03-insert-data/05-high-volume.md delete mode 100644 docs/zh/07-develop/03-insert-data/05-high-volume.mdx create mode 100644 docs/zh/07-develop/03-insert-data/high.png diff --git a/docs/examples/java/src/main/java/com/taos/example/highvolume/DataBaseMonitor.java b/docs/examples/java/src/main/java/com/taos/example/highvolume/DataBaseMonitor.java new file mode 100644 index 0000000000..5c513ec282 --- /dev/null +++ b/docs/examples/java/src/main/java/com/taos/example/highvolume/DataBaseMonitor.java @@ -0,0 +1,47 @@ +package com.taos.example.highvolume; + +import java.sql.*; + +/** + * Prepare target database. + * Count total records in database periodically so that we can estimate the writing speed. + */ +public class DataBaseMonitor { + private Connection conn; + private Statement stmt; + + public DataBaseMonitor init() throws SQLException { + if (conn == null) { + String jdbcURL = System.getenv("TDENGINE_JDBC_URL"); + conn = DriverManager.getConnection(jdbcURL); + stmt = conn.createStatement(); + } + return this; + } + + public void close() { + try { + stmt.close(); + } catch (SQLException e) { + } + try { + conn.close(); + } catch (SQLException e) { + } + } + + public void prepareDatabase() throws SQLException { + stmt.execute("DROP DATABASE IF EXISTS test"); + stmt.execute("CREATE DATABASE test"); + stmt.execute("CREATE STABLE test.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)"); + } + + public Long count() throws SQLException { + if (!stmt.isClosed()) { + ResultSet result = stmt.executeQuery("SELECT count(*) from test.meters"); + result.next(); + return result.getLong(1); + } + return null; + } +} \ No newline at end of file diff --git a/docs/examples/java/src/main/java/com/taos/example/highvolume/FastWriteExample.java b/docs/examples/java/src/main/java/com/taos/example/highvolume/FastWriteExample.java index e8af1a68ea..15672dddd9 100644 --- a/docs/examples/java/src/main/java/com/taos/example/highvolume/FastWriteExample.java +++ b/docs/examples/java/src/main/java/com/taos/example/highvolume/FastWriteExample.java @@ -9,51 +9,7 @@ import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; -/** - * Prepare target database. - * Count total records in database periodically so that we can estimate the writing speed. - */ -class DataBaseMonitor { - private Connection conn; - private Statement stmt; - public DataBaseMonitor init() throws SQLException { - if (conn == null) { - String jdbcURL = System.getenv("TDENGINE_JDBC_URL"); - conn = DriverManager.getConnection(jdbcURL); - stmt = conn.createStatement(); - } - return this; - } - - public void close() { - try { - stmt.close(); - } catch (SQLException e) { - } - try { - conn.close(); - } catch (SQLException e) { - } - } - - public void prepareDatabase() throws SQLException { - stmt.execute("DROP DATABASE IF EXISTS test"); - stmt.execute("CREATE DATABASE test"); - stmt.execute("CREATE STABLE test.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)"); - } - - public Long count() throws SQLException { - if (!stmt.isClosed()) { - ResultSet result = stmt.executeQuery("SELECT count(*) from test.meters"); - result.next(); - return result.getLong(1); - } - return null; - } -} - -// ANCHOR: main public class FastWriteExample { final static Logger logger = LoggerFactory.getLogger(FastWriteExample.class); @@ -110,5 +66,4 @@ public class FastWriteExample { lastCount = count; } } -} -// ANCHOR_END: main \ No newline at end of file +} \ No newline at end of file diff --git a/docs/examples/java/src/main/java/com/taos/example/highvolume/MockDataSource.java b/docs/examples/java/src/main/java/com/taos/example/highvolume/MockDataSource.java new file mode 100644 index 0000000000..6fe83f002e --- /dev/null +++ b/docs/examples/java/src/main/java/com/taos/example/highvolume/MockDataSource.java @@ -0,0 +1,53 @@ +package com.taos.example.highvolume; + +import java.util.Iterator; + +/** + * Generate test data + */ +class MockDataSource implements Iterator { + private String tbNamePrefix; + private int tableCount; + private long maxRowsPerTable = 1000000000L; + + // 100 milliseconds between two neighbouring rows. + long startMs = System.currentTimeMillis() - maxRowsPerTable * 100; + private int currentRow = 0; + private int currentTbId = -1; + + // mock values + String[] location = {"LosAngeles", "SanDiego", "Hollywood", "Compton", "San Francisco"}; + float[] current = {8.8f, 10.7f, 9.9f, 8.9f, 9.4f}; + int[] voltage = {119, 116, 111, 113, 118}; + float[] phase = {0.32f, 0.34f, 0.33f, 0.329f, 0.141f}; + + public MockDataSource(String tbNamePrefix, int tableCount) { + this.tbNamePrefix = tbNamePrefix; + this.tableCount = tableCount; + } + + @Override + public boolean hasNext() { + currentTbId += 1; + if (currentTbId == tableCount) { + currentTbId = 0; + currentRow += 1; + } + return currentRow < maxRowsPerTable; + } + + @Override + public String next() { + long ts = startMs + 100 * currentRow; + int groupId = currentTbId % 5 == 0 ? currentTbId / 5 : currentTbId / 5 + 1; + StringBuilder sb = new StringBuilder(tbNamePrefix + "_" + currentTbId + ","); // tbName + sb.append(ts).append(','); // ts + sb.append(current[currentRow % 5]).append(','); // current + sb.append(voltage[currentRow % 5]).append(','); // voltage + sb.append(phase[currentRow % 5]).append(','); // phase + sb.append(location[currentRow % 5]).append(','); // location + sb.append(groupId); // groupID + + return sb.toString(); + } +} \ No newline at end of file diff --git a/docs/examples/java/src/main/java/com/taos/example/highvolume/ReadTask.java b/docs/examples/java/src/main/java/com/taos/example/highvolume/ReadTask.java index 94cde899f4..a6fcfed1d2 100644 --- a/docs/examples/java/src/main/java/com/taos/example/highvolume/ReadTask.java +++ b/docs/examples/java/src/main/java/com/taos/example/highvolume/ReadTask.java @@ -7,57 +7,6 @@ import java.util.Iterator; import java.util.List; import java.util.concurrent.BlockingQueue; -/** - * Generate test data - */ -class MockDataSource implements Iterator { - private String tbNamePrefix; - private int tableCount; - private long maxRowsPerTable = 1000000000L; - - // 100 milliseconds between two neighbouring rows. - long startMs = System.currentTimeMillis() - maxRowsPerTable * 100; - private int currentRow = 0; - private int currentTbId = -1; - - // mock values - String[] location = {"LosAngeles", "SanDiego", "Hollywood", "Compton", "San Francisco"}; - float[] current = {8.8f, 10.7f, 9.9f, 8.9f, 9.4f}; - int[] voltage = {119, 116, 111, 113, 118}; - float[] phase = {0.32f, 0.34f, 0.33f, 0.329f, 0.141f}; - - public MockDataSource(String tbNamePrefix, int tableCount) { - this.tbNamePrefix = tbNamePrefix; - this.tableCount = tableCount; - } - - @Override - public boolean hasNext() { - currentTbId += 1; - if (currentTbId == tableCount) { - currentTbId = 0; - currentRow += 1; - } - return currentRow < maxRowsPerTable; - } - - @Override - public String next() { - long ts = startMs + 100 * currentRow; - int groupId = currentTbId % 5 == 0 ? currentTbId / 5 : currentTbId / 5 + 1; - StringBuilder sb = new StringBuilder(tbNamePrefix + "_" + currentTbId + ","); // tbName - sb.append(ts).append(','); // ts - sb.append(current[currentRow % 5]).append(','); // current - sb.append(voltage[currentRow % 5]).append(','); // voltage - sb.append(phase[currentRow % 5]).append(','); // phase - sb.append(location[currentRow % 5]).append(','); // location - sb.append(groupId); // groupID - - return sb.toString(); - } -} - -// ANCHOR: ReadTask class ReadTask implements Runnable { private final static Logger logger = LoggerFactory.getLogger(ReadTask.class); private final int taskId; @@ -106,6 +55,4 @@ class ReadTask implements Runnable { logger.info("stop"); this.active = false; } -} - -// ANCHOR_END: ReadTask \ No newline at end of file +} \ No newline at end of file diff --git a/docs/examples/java/src/main/java/com/taos/example/highvolume/SQLWriter.java b/docs/examples/java/src/main/java/com/taos/example/highvolume/SQLWriter.java index 772913bb84..68b759f2e4 100644 --- a/docs/examples/java/src/main/java/com/taos/example/highvolume/SQLWriter.java +++ b/docs/examples/java/src/main/java/com/taos/example/highvolume/SQLWriter.java @@ -7,8 +7,6 @@ import java.sql.*; import java.util.HashMap; import java.util.Map; -// ANCHOR: SQLWriter - /** * A helper class encapsulate the logic of writing using SQL. *

@@ -204,5 +202,4 @@ public class SQLWriter { } catch (SQLException e) { } } -} -// ANCHOR_END: SQLWriter +} \ No newline at end of file diff --git a/docs/examples/java/src/main/java/com/taos/example/highvolume/StmtWriter.java b/docs/examples/java/src/main/java/com/taos/example/highvolume/StmtWriter.java new file mode 100644 index 0000000000..8ade06625d --- /dev/null +++ b/docs/examples/java/src/main/java/com/taos/example/highvolume/StmtWriter.java @@ -0,0 +1,4 @@ +package com.taos.example.highvolume; + +public class StmtWriter { +} diff --git a/docs/examples/java/src/main/java/com/taos/example/highvolume/WriteTask.java b/docs/examples/java/src/main/java/com/taos/example/highvolume/WriteTask.java index bbe9cb0770..de9e5463d7 100644 --- a/docs/examples/java/src/main/java/com/taos/example/highvolume/WriteTask.java +++ b/docs/examples/java/src/main/java/com/taos/example/highvolume/WriteTask.java @@ -5,7 +5,6 @@ import org.slf4j.LoggerFactory; import java.util.concurrent.BlockingQueue; -// ANCHOR: WriteTask class WriteTask implements Runnable { private final static Logger logger = LoggerFactory.getLogger(WriteTask.class); private final int maxBatchSize; @@ -56,5 +55,4 @@ class WriteTask implements Runnable { logger.info("stop"); this.active = false; } -} -// ANCHOR_END: WriteTask \ No newline at end of file +} \ No newline at end of file diff --git a/docs/examples/java/src/main/resources/highvolume2.drawio b/docs/examples/java/src/main/resources/highvolume2.drawio index 65196646df..8c9ae09007 100644 --- a/docs/examples/java/src/main/resources/highvolume2.drawio +++ b/docs/examples/java/src/main/resources/highvolume2.drawio @@ -1,6 +1,6 @@ - + @@ -52,22 +52,22 @@ - + - + - + - + - + diff --git a/docs/examples/python/highvolume_faster_queue.py b/docs/examples/python/highvolume_faster_queue.py index b47646bb9f..c9d606388f 100644 --- a/docs/examples/python/highvolume_faster_queue.py +++ b/docs/examples/python/highvolume_faster_queue.py @@ -10,7 +10,7 @@ import time import os from multiprocessing import Process from faster_fifo import Queue -from mockdatasoruce import MockDataSource +from mockdatasource import MockDataSource from queue import Empty from typing import List diff --git a/docs/examples/python/mockdatasoruce.py b/docs/examples/python/mockdatasource.py similarity index 100% rename from docs/examples/python/mockdatasoruce.py rename to docs/examples/python/mockdatasource.py diff --git a/docs/zh/07-develop/03-insert-data/05-high-volume.md b/docs/zh/07-develop/03-insert-data/05-high-volume.md new file mode 100644 index 0000000000..52b89e8a9b --- /dev/null +++ b/docs/zh/07-develop/03-insert-data/05-high-volume.md @@ -0,0 +1,429 @@ +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +# 高效写入 + +本节介绍如何高效地向 TDengine 写入数据。 + +## 高效写入原理 {#principle} + +### 客户端程序的角度 {#application-view} + +从客户端程序的角度来说,高效写入数据要考虑以下几个因素: + +1. 单次写入的数据量。一般来讲,每批次写入的数据量越大越高效(但超过一定阈值其优势会消失)。使用 SQL 写入 TDengine 时,尽量在一条 SQL 中拼接更多数据。目前,TDengine 支持的一条 SQL 的最大长度为 1,048,576(1M)个字符。可通过配置客户端参数 maxSQLLength(默认值为 65480)进行修改。 +2. 并发连接数。一般来讲,同时写入数据的并发连接数越多写入越高效(但超过一定阈值反而会下降,取决于服务端处理能力)。 +3. 数据在不同表(或子表)之间的分布,即要写入数据的相邻性。一般来说,每批次只向同一张表(或子表)写入数据比向多张表(或子表)写入数据要更高效; +4. 写入方式。一般来讲: + - 参数绑定写入比 SQL 写入更高效。因参数绑定方式避免了 SQL 解析。(但增加了 C 接口的调用次数,对于连接器也有性能损耗)。 + - SQL 写入不自动建表比自动建表更高效。因自动建表要频繁检查表是否存在 + - SQL 写入比无模式写入更高效。因无模式入会自动建表且需要动态更改表结构 + +客户端程序要充分且恰当地利用以上几个因素。在单次写入中尽量只向同一张表(或子表)写入数据,每批次写入的数据量经过测试和调优设定为一个最适合当前系统处理能力的数值,并发写入的连接数同样经过测试和调优后设定为一个最适合当前系统处理能力的数值,以实现在当前系统中的最佳写入速度。 + +### 数据源的角度 {#datasource-view} + +客户端程序通常需要从数据源读数据再写入 TDengine。从数据源角度来说,以下几种情况需要在读线程和写线程之间增加队列: + +1. 有多个数据源,单个数据源生成数据的速度远小于单线程写入的速度,但数据量整体比较大。此时队列的作用是把多个数据源的数据汇聚到一起,增加单次写入的数据量。 +2. 单个数据源生成数据的速度远大于单线程写入的速度。此时队列的作用是增加写入的并发度。 +3. 单张表的数据分散在多个数据源。此时队列的作用是将同一张表的数据提前汇聚到一起,提高写入时数据的相邻性。 + +如果写应用的数据源是 Kafka, 写应用本身即 Kafka 的消费者,则可利用 Kafka 的特性实现高效写入。比如: + +1. 将同一张表的数据写到同一个 Topic 的同一个 Partition,增加数据的相邻性 +2. 通过订阅多个 Topic 实现数据汇聚 +3. 通过增加 Consumer 线程数增加写入的并发度 +4. 通过增加每次 fetch 的最大数据量来增加单次写入的最大数据量 + +### 服务器配置的角度 {#setting-view} + +从服务器配置的角度来说,也有很多优化写入性能的方法。 + +如果无论怎么调节客户端程序,taosd 进程的 CPU 使用率都很低,那很可能需要增加 vgroup 的数量。比如:数据库总表数是 1000 且 minTablesPerVnode 设置的也是 1000,那么这个数据至多有一个 vgroup。此时如果将 minTablesPerVnode 和 tablelncStepPerVnode 都设置成 100, 则这个数据库可能用到 10 个 vgroup。 + +更多调优参数,请参考[性能优化](../../operation/optimize)和[配置参考](../../reference/config)部分。 + +## 高效写入示例 {#sample-code} + +### 场景设计 {#scenario} + +下面的示例程序展示了如何高效写入数据,场景设计如下: + +- TDengine 客户端程序从其它数据源不断读入数据,在示例程序中采用生成模拟数据的方式来模拟读取数据源 +- 单个连接向 TDengine 写入的速度无法与读数据的速度相匹配,因此客户端程序启动多个线程,每个线程都建立了与 TDengine 的连接,每个线程都有一个独占的固定大小的消息队列 +- 客户端程序将接收到的数据根据所属的表名(或子表名)HASH 到不同的线程,即写入该线程所对应的消息队列,以此确保属于某个表(或子表)的数据一定会被一个固定的线程处理 +- 各个子线程在将所关联的消息队列中的数据读空后或者读取数据量达到一个预定的阈值后将该批数据写入 TDengine,并继续处理后面接收到的数据 + +![TDengine 高效写入示例场景的线程模型](highvolume.webp) + +### 示例代码 {#code} + +这一部分是针对以上场景的示例代码。对于其它场景高效写入原理相同,不过代码需要适当修改。 + +本示例代码假设源数据属于同一张超级表(meters)的不同子表。程序在开始写入数据之前已经在 test 库创建了这个超级表。对于子表,将根据收到的数据,由应用程序自动创建。如果实际场景是多个超级表,只需修改写任务自动建表的代码。 + + + + +**程序清单** + +| 类名 | 功能说明 | +| ---------------- | --------------------------------------------------------------------------- | +| FastWriteExample | 主程序 | +| ReadTask | 从模拟源中读取数据,将表名经过 hash 后得到 Queue 的 index,写入对应的 Queue | +| WriteTask | 从 Queue 中获取数据,组成一个 Batch,写入 TDengine | +| MockDataSource | 模拟生成一定数量 meters 子表的数据 | +| SQLWriter | WriteTask 依赖这个类完成 SQL 拼接、自动建表、 SQL 写入、SQL 长度检查 | +| StmtWriter | 实现参数绑定方式批量写入,暂未完成 | +| DataBaseMonitor | 统计写入速度,并每隔 10 秒把当前写入速度打印到控制台 | + + +以下是各类的完整代码和更详细的功能说明。 + +

+FastWriteExample +主程序负责: + +1. 创建消息队列 +2. 启动写线程 +3. 启动读线程 +4. 每隔 10 秒统计一次写入速度 + +主程序默认暴露了 4 个参数,每次启动程序都可调节,用于测试和调优: + +1. 读线程个数。默认为 1。 +2. 写线程个数。默认为 3。 +3. 模拟生成的总表数。默认为 1000。将会平分给各个读线程。 +4. 每批最多写入记录数量。默认为 3000。 + +队列容量(taskQueueCapacity)也是与性能有关的参数,可通过修改程序调节。一般来讲,队列容量越大,入队被阻塞的概率越小,队列的吞吐量越大,但是内存占用也会越大。 示例程序默认值已经设置地足够大。 + +```java +{{#include docs/examples/java/src/main/java/com/taos/example/highvolume/FastWriteExample.java}} +``` + +
+ +
+ReadTask + +读任务负责从数据源读数据。每个读任务都关联了一个模拟数据源。每个模拟数据源可生成一点数量表的数据。不同的模拟数据源生成不同表的数据。 + +读任务采用阻塞的方式写消息队列。也就是说,一旦队列满了,写操作就会阻塞。 + +```java +{{#include docs/examples/java/src/main/java/com/taos/example/highvolume/ReadTask.java}} +``` + +
+ +
+WriteTask + +```java +{{#include docs/examples/java/src/main/java/com/taos/example/highvolume/WriteTask.java}} +``` + +
+ +
+ +MockDataSource + +```java +{{#include docs/examples/java/src/main/java/com/taos/example/highvolume/MockDataSource.java}} +``` + +
+ +
+ +SQLWriter + +SQLWriter 类封装了拼 SQL 和写数据的逻辑。注意,所有的表都没有提前创建,而是写入出错的时候,再以超级表为模板批量建表,然后重新执行 INSERT 语句。 + +```java +{{#include docs/examples/java/src/main/java/com/taos/example/highvolume/SQLWriter.java}} +``` + +
+ +
+ +DataBaseMonitor + +```java +{{#include docs/examples/java/src/main/java/com/taos/example/highvolume/DataBaseMonitor.java}} +``` + +
+ +**执行步骤** + +
+执行 Java 示例程序 + +执行程序前需配置环境变量 `TDENGINE_JDBC_URL`。如果 TDengine Server 部署在本机,且用户名、密码和端口都是默认值,那么可配置: + +``` +TDENGINE_JDBC_URL="jdbc:TAOS://localhost:6030?user=root&password=taosdata" +``` + +**本地集成开发环境执行示例程序** + +1. clone TDengine 仓库 + ``` + git clone git@github.com:taosdata/TDengine.git --depth 1 + ``` +2. 用集成开发环境打开 `docs/examples/java` 目录。 +3. 在开发环境中配置环境变量 `TDENGINE_JDBC_URL`。如果已配置了全局的环境变量 `TDENGINE_JDBC_URL` 可跳过这一步。 +4. 运行类 `com.taos.example.highvolume.FastWriteExample`。 + +**远程服务器上执行示例程序** + +若要在服务器上执行示例程序,可按照下面的步骤操作: + +1. 打包示例代码。在目录 TDengine/docs/examples/java 下执行: + ``` + mvn package + ``` +2. 远程服务器上创建 examples 目录: + ``` + mkdir -p examples/java + ``` +3. 复制依赖到服务器指定目录: + - 复制依赖包,只用复制一次 + ``` + scp -r .\target\lib @:~/examples/java + ``` + - 复制本程序的 jar 包,每次更新代码都需要复制 + ``` + scp -r .\target\javaexample-1.0.jar @:~/examples/java + ``` +4. 配置环境变量。 + 编辑 `~/.bash_profile` 或 `~/.bashrc` 添加如下内容例如: + + ``` + export TDENGINE_JDBC_URL="jdbc:TAOS://localhost:6030?user=root&password=taosdata" + ``` + + 以上使用的是本地部署 TDengine Server 时默认的 JDBC URL。你需要根据自己的实际情况更改。 + +5. 用 java 命令启动示例程序,命令模板: + + ``` + java -classpath lib/*:javaexample-1.0.jar com.taos.example.highvolume.FastWriteExample + ``` + +6. 结束测试程序。测试程序不会自动结束,在获取到当前配置下稳定的写入速度后,按 CTRL + C 结束程序。 + 下面是一次实际运行的截图: + + ``` + root@vm85$ java -classpath lib/*:javaexample-1.0.jar com.taos.example.highvolume.FastWriteExample 2 12 + 18:56:35.896 [main] INFO c.t.e.highvolume.FastWriteExample - readTaskCount=2, writeTaskCount=12 tableCount=1000 maxBatchSize=3000 + 18:56:36.011 [WriteThread-0] INFO c.taos.example.highvolume.WriteTask - started + 18:56:36.015 [WriteThread-0] INFO c.taos.example.highvolume.SQLWriter - maxSQLLength=1048576 + 18:56:36.021 [WriteThread-1] INFO c.taos.example.highvolume.WriteTask - started + 18:56:36.022 [WriteThread-1] INFO c.taos.example.highvolume.SQLWriter - maxSQLLength=1048576 + 18:56:36.031 [WriteThread-2] INFO c.taos.example.highvolume.WriteTask - started + 18:56:36.032 [WriteThread-2] INFO c.taos.example.highvolume.SQLWriter - maxSQLLength=1048576 + 18:56:36.041 [WriteThread-3] INFO c.taos.example.highvolume.WriteTask - started + 18:56:36.042 [WriteThread-3] INFO c.taos.example.highvolume.SQLWriter - maxSQLLength=1048576 + 18:56:36.093 [WriteThread-4] INFO c.taos.example.highvolume.WriteTask - started + 18:56:36.094 [WriteThread-4] INFO c.taos.example.highvolume.SQLWriter - maxSQLLength=1048576 + 18:56:36.099 [WriteThread-5] INFO c.taos.example.highvolume.WriteTask - started + 18:56:36.100 [WriteThread-5] INFO c.taos.example.highvolume.SQLWriter - maxSQLLength=1048576 + 18:56:36.100 [WriteThread-6] INFO c.taos.example.highvolume.WriteTask - started + 18:56:36.101 [WriteThread-6] INFO c.taos.example.highvolume.SQLWriter - maxSQLLength=1048576 + 18:56:36.103 [WriteThread-7] INFO c.taos.example.highvolume.WriteTask - started + 18:56:36.104 [WriteThread-7] INFO c.taos.example.highvolume.SQLWriter - maxSQLLength=1048576 + 18:56:36.105 [WriteThread-8] INFO c.taos.example.highvolume.WriteTask - started + 18:56:36.107 [WriteThread-8] INFO c.taos.example.highvolume.SQLWriter - maxSQLLength=1048576 + 18:56:36.108 [WriteThread-9] INFO c.taos.example.highvolume.WriteTask - started + 18:56:36.109 [WriteThread-9] INFO c.taos.example.highvolume.SQLWriter - maxSQLLength=1048576 + 18:56:36.156 [WriteThread-10] INFO c.taos.example.highvolume.WriteTask - started + 18:56:36.157 [WriteThread-11] INFO c.taos.example.highvolume.WriteTask - started + 18:56:36.158 [WriteThread-10] INFO c.taos.example.highvolume.SQLWriter - maxSQLLength=1048576 + 18:56:36.158 [ReadThread-0] INFO com.taos.example.highvolume.ReadTask - started + 18:56:36.158 [ReadThread-1] INFO com.taos.example.highvolume.ReadTask - started + 18:56:36.158 [WriteThread-11] INFO c.taos.example.highvolume.SQLWriter - maxSQLLength=1048576 + 18:56:46.369 [main] INFO c.t.e.highvolume.FastWriteExample - count=18554448 speed=1855444 + 18:56:56.946 [main] INFO c.t.e.highvolume.FastWriteExample - count=39059660 speed=2050521 + 18:57:07.322 [main] INFO c.t.e.highvolume.FastWriteExample - count=59403604 speed=2034394 + 18:57:18.032 [main] INFO c.t.e.highvolume.FastWriteExample - count=80262938 speed=2085933 + 18:57:28.432 [main] INFO c.t.e.highvolume.FastWriteExample - count=101139906 speed=2087696 + 18:57:38.921 [main] INFO c.t.e.highvolume.FastWriteExample - count=121807202 speed=2066729 + 18:57:49.375 [main] INFO c.t.e.highvolume.FastWriteExample - count=142952417 speed=2114521 + 18:58:00.689 [main] INFO c.t.e.highvolume.FastWriteExample - count=163650306 speed=2069788 + 18:58:11.646 [main] INFO c.t.e.highvolume.FastWriteExample - count=185019808 speed=2136950 + ``` + +
+ + + + +**程序清单** + +Python 示例程序中采用了多进程的架构,并使用了跨进程的消息队列。 + +| 函数或类 | 功能说明 | +| ------------------------ | -------------------------------------------------------------------- | +| main 函数 | 程序入口, 创建各个子进程和消息队列 | +| run_monitor_process 函数 | 创建数据库,超级表,统计写入速度并定时打印到控制台 | +| run_read_task 函数 | 读进程主要逻辑,负责从其它数据系统读数据,并分发数据到为之分配的队列 | +| MockDataSource 类 | 模拟数据源, 实现迭代器接口,每次批量返回每张表的接下来 1000 条数据 | +| run_write_task 函数 | 写进程主要逻辑。每次从队列中取出尽量多的数据,并批量写入 | +| SQLWriter类 | SQL 写入和自动建表 | + + +
+main 函数 + +main 函数负责创建消息队列和启动子进程,子进程有 3 类: + +1. 1 个监控进程,负责数据库初始化和统计写入速度 +2. n 个读进程,负责从其它数据系统读数据 +3. m 个写进程,负责写数据库 + +main 函数可以接收 5 个启动参数,依次是: + +1. 读任务(进程)数, 默认为 1 +2. 写任务(进程)数, 默认为 1 +3. 模拟生成的总表数,默认为 1000 +4. 队列大小(单位字节),默认为 1000000 +5. 每批最多写入记录数量, 默认为 3000 + +```python +{{#include docs/examples/python/highvolume_faster_queue.py:main}} +``` + +
+ +
+run_monitor_process + +监控进程负责初始化数据库,并监控当前的写入速度。 + +```python +{{#include docs/examples/python/highvolume_faster_queue.py:monitor}} +``` + +
+ +
+ +run_read_task 函数 + +读进程,负责从其它数据系统读数据,并分发数据到为之分配的队列。 + +```python +{{#include docs/examples/python/highvolume_faster_queue.py:read}} +``` + +
+ +
+ +MockDataSource + +以下是模拟数据源的实现,我们假设数据源生成的每一条数据都带有目标表名信息。实际中你可能需要一定的规则确定目标表名。 + +```python +{{#include docs/examples/python/mockdatasource.py}} +``` + +
+ +
+run_write_task 函数 + +写进程每次从队列中取出尽量多的数据,并批量写入。 + +```python +{{#include docs/examples/python/highvolume_faster_queue.py:write}} +``` + +
+ +
+ +SQLWriter 类封装了拼 SQL 和写数据的逻辑。所有的表都没有提前创建,而是写入出错的时候,再以超级表为模板批量建表,然后重新执行 INSERT 语句。这个类也对 SQL 是否超过最大长度限制做了检查,如果接近 SQL 最大长度限制(maxSQLLength),将会立即执行 SQL。为了减少 SQL 此时,建议将 maxSQLLength 适当调大。 + +SQLWriter + +```python +{{#include docs/examples/python/sql_writer.py}} +``` + +
+ +**执行步骤** + +
+ +执行 Python 示例程序 + +1. 前提条件 + + - 已安装 TDengine 客户端驱动 + - 已安装 Python3, 推荐版本 >= 3.8 + - 已安装 taospy + +2. 安装 faster-fifo 代替 python 内置的 multiprocessing.Queue + + ``` + pip3 install faster-fifo + ``` + +3. 点击上面的“查看源码”链接复制 `highvolume_faster_queue.py` 和 `sql_writer.py` 两个文件。 + +4. 执行示例程序 + + ``` + python3 highvolume_faster_queue.py + ``` + + 下面是一次实际运行的输出: + + ``` + root@vm85$ python3 highvolume_faster_queue.py 8 8 + 2022-07-14 19:13:45,869 [root] - READ_TASK_COUNT=8, WRITE_TASK_COUNT=8, TABLE_COUNT=1000, QUEUE_SIZE=1000000, MAX_BATCH_SIZE=3000 + 2022-07-14 19:13:48,882 [root] - WriteTask-0 started with pid 718347 + 2022-07-14 19:13:48,883 [root] - WriteTask-1 started with pid 718348 + 2022-07-14 19:13:48,884 [root] - WriteTask-2 started with pid 718349 + 2022-07-14 19:13:48,884 [root] - WriteTask-3 started with pid 718350 + 2022-07-14 19:13:48,885 [root] - WriteTask-4 started with pid 718351 + 2022-07-14 19:13:48,885 [root] - WriteTask-5 started with pid 718352 + 2022-07-14 19:13:48,886 [root] - WriteTask-6 started with pid 718353 + 2022-07-14 19:13:48,886 [root] - WriteTask-7 started with pid 718354 + 2022-07-14 19:13:48,887 [root] - ReadTask-0 started with pid 718355 + 2022-07-14 19:13:48,888 [root] - ReadTask-1 started with pid 718356 + 2022-07-14 19:13:48,889 [root] - ReadTask-2 started with pid 718357 + 2022-07-14 19:13:48,889 [root] - ReadTask-3 started with pid 718358 + 2022-07-14 19:13:48,890 [root] - ReadTask-4 started with pid 718359 + 2022-07-14 19:13:48,891 [root] - ReadTask-5 started with pid 718361 + 2022-07-14 19:13:48,892 [root] - ReadTask-6 started with pid 718364 + 2022-07-14 19:13:48,893 [root] - ReadTask-7 started with pid 718365 + 2022-07-14 19:13:56,042 [DataBaseMonitor] - count=6676310 speed=667631.0 + 2022-07-14 19:14:06,196 [DataBaseMonitor] - count=20004310 speed=1332800.0 + 2022-07-14 19:14:16,366 [DataBaseMonitor] - count=32290310 speed=1228600.0 + 2022-07-14 19:14:26,527 [DataBaseMonitor] - count=44438310 speed=1214800.0 + 2022-07-14 19:14:36,673 [DataBaseMonitor] - count=56608310 speed=1217000.0 + 2022-07-14 19:14:46,834 [DataBaseMonitor] - count=68757310 speed=1214900.0 + 2022-07-14 19:14:57,280 [DataBaseMonitor] - count=80992310 speed=1223500.0 + 2022-07-14 19:15:07,689 [DataBaseMonitor] - count=93805310 speed=1281300.0 + 2022-07-14 19:15:18,020 [DataBaseMonitor] - count=106111310 speed=1230600.0 + 2022-07-14 19:15:28,356 [DataBaseMonitor] - count=118394310 speed=1228300.0 + 2022-07-14 19:15:38,690 [DataBaseMonitor] - count=130742310 speed=1234800.0 + 2022-07-14 19:15:49,000 [DataBaseMonitor] - count=143051310 speed=1230900.0 + 2022-07-14 19:15:59,323 [DataBaseMonitor] - count=155276310 speed=1222500.0 + 2022-07-14 19:16:09,649 [DataBaseMonitor] - count=167603310 speed=1232700.0 + 2022-07-14 19:16:19,995 [DataBaseMonitor] - count=179976310 speed=1237300.0 + ``` + +
+ +
+ diff --git a/docs/zh/07-develop/03-insert-data/05-high-volume.mdx b/docs/zh/07-develop/03-insert-data/05-high-volume.mdx deleted file mode 100644 index 23ec979a02..0000000000 --- a/docs/zh/07-develop/03-insert-data/05-high-volume.mdx +++ /dev/null @@ -1,366 +0,0 @@ -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; - -# 高效写入 - -## 高效写入原理 {#principle} - - -本节介绍如何高效地向 TDengine 写入数据。高效写入数据要考虑几个因素:数据在不同表(或子表)之间的分布,即要写入数据的相邻性;单次写入的数据量;并发连接数。一般来说,每批次只向同一张表(或子表)写入数据比向多张表(或子表)写入数据要更高效;每批次写入的数据量越大越高效(但超过一定阈值其优势会消失;同时写入数据的并发连接数越多写入越高效(但超过一定阈值反而会下降,取决于服务端处理能力)。 - -为了更高效地向 TDengine 写入数据,客户端程序要充分且恰当地利用以上几个因素。在单次写入中尽量只向同一张表(或子表)写入数据,每批次写入的数据量经过测试和调优设定为一个最适合当前系统处理能力的数值,并发写入的连接数同样经过测试和调优后设定为一个最适合当前系统处理能力的数值,以实现在当前系统中的最佳写入速度。同时,TDengine 还提供了独特的参数绑定写入,这也是一个有助于实现高效写入的方法。 - -为了使写入最高效,除了客户端程序的设计,服务端的配置也很重要。如果无论怎么调节客户端程序,taosd 进程的 CPU 使用率都很低,那很可能需要增加 vgroup 的数量。比如:数据库总表数是 1000 且 minTablesPerVnode 设置的也是 1000,那么这个数据至多有一个 vgroup。此时如果将 minTablesPerVnode 和 tablelncStepPerVnode 都设置成 100, 则这个数据库有可能用到 10 个 vgroup。更多性能调优参数请参考[配置参考](../../reference/config)性能调优部分。 - -## 场景设计 {#scenario} - -下面的示例程序展示了如何高效写入数据: - -- TDengine 客户端程序从消息队列或者其它数据源不断读入数据,在示例程序中采用生成模拟数据的方式来模拟读取数据源 -- 单个连接向 TDengine 写入的速度无法与读数据的速度相匹配,因此客户端程序启动多个线程,每个线程都建立了与 TDengine 的连接,每个线程都有一个独占的固定大小的消息队列 -- 客户端程序将接收到的数据根据所属的表名(或子表名)HASH 到不同的线程,即写入该线程所对应的消息队列,以此确保属于某个表(或子表)的数据一定会被一个固定的线程处理 -- 各个子线程在将所关联的消息队列中的数据读空后或者读取数据量达到一个预定的阈值后将该批数据写入 TDengine,并继续处理后面接收到的数据 - -![TDengine 高效写入线程模型](highvolume.webp) - -:::note -上图所示架构,每个写任务只负责写特定的表,体现了数据的相邻性原则。但是读任务所读的表,我们假设是随机的。这样一个队列有多个写入线程(或进程),队列内部可能产生锁的消耗。实际场景,如果能做到一个读任务对应一个写任务是最好的。 -::: - -## 示例代码 - -这一部分是针对以上场景的示例代码。建议先阅读此场景的示例代码,对于其它场景高效写入原理相同,不够代码需要适当修改。 - - - - - - - - - - - - - - - -## 其它场景 - -由于写入场景众多,无法一一列举,这一部分描述对于其它常用场景修改示例代码的方法。 - - -## Java 示例程序 {#java-demo} - -在 Java 示例程序中采用拼接 SQL 的写入方式。 - -### 主程序 {#java-demo-main} - -主程序负责: - -1. 创建消息队列 -2. 启动写线程 -3. 启动读线程 -4. 每隔 10 秒统计一次写入速度 - -主程序默认暴露了 4 个参数,每次启动程序都可调节,用于测试和调优: - -1. 读线程个数。默认为 1。 -2. 写线程个数。默认为 3。 -3. 模拟生成的总表数。默认为 1000。将会平分给各个读线程。 -4. 每批最多写入记录数量。默认为 3000。 - -
-主程序 - -```java -{{#include docs/examples/java/src/main/java/com/taos/example/highvolume/FastWriteExample.java:main}} -``` - -
- - -队列容量(taskQueueCapacity)也是与性能有关的参数,可通过修改程序调节。一般来讲,队列容量越大,入队被阻塞的概率越小,队列的吞吐量越大,但是内存占用也会越大。 - -### 读任务的实现 {#java-demo-read} - -读任务负责从数据源读数据。每个读任务都关联了一个模拟数据源。每个模拟数据源可生成一点数量表的数据。不同的模拟数据源生成不同表的数据。 - -读任务采用阻塞的方式写消息队列。也就是说,一旦队列满了,写操作就会阻塞。 - -
-读任务的实现 - -```java -{{#include docs/examples/java/src/main/java/com/taos/example/highvolume/ReadTask.java:ReadTask}} -``` - -
- -### 写任务的实现 {#java-demo-write} - -
-写任务的实现 - -```java -{{#include docs/examples/java/src/main/java/com/taos/example/highvolume/WriteTask.java:WriteTask}} -``` - -
- -### SQLWriter 类的实现 {#java-demo-sql-writer} - -SQLWriter 类封装了拼 SQL 和写数据的逻辑。注意,所有的表都没有提前创建,而是写入出错的时候,再以超级表为模板批量建表,然后重新执行 INSERT 语句。 - -
-SQLWriter 类的实现 - -```java -{{#include docs/examples/java/src/main/java/com/taos/example/highvolume/SQLWriter.java:SQLWriter}} -``` - -
- -### 执行示例程序 {#run-java-demo} - -
-执行 Java 示例程序 - - -执行程序前需配置环境变量 `TDENGINE_JDBC_URL`。如果 TDengine Server 部署在本机,且用户名、密码和端口都是默认值,那么可配置: - -``` -TDENGINE_JDBC_URL="jdbc:TAOS://localhost:6030?user=root&password=taosdata" -``` - -#### 本地集成开发环境执行示例程序 {#java-demo-local-run} - -1. clone TDengine 仓库 - ``` - git clone git@github.com:taosdata/TDengine.git --depth 1 - ``` -2. 用集成开发环境打开 `docs/examples/java` 目录。 -3. 在开发环境中配置环境变量 `TDENGINE_JDBC_URL`。如果已配置了全局的环境变量 `TDENGINE_JDBC_URL` 可跳过这一步。 -4. 运行类 `com.taos.example.highvolume.FastWriteExample`。 - -#### 远程服务器上执行示例程序 {#java-demo-remote-run} - -若要在服务器上执行示例程序,可按照下面的步骤操作: - -1. 打包示例代码。在目录 TDengine/docs/examples/java 下执行: - ``` - mvn package - ``` -2. 远程服务器上创建 examples 目录: - ``` - mkdir -p examples/java - ``` -3. 复制依赖到服务器指定目录: - - 复制依赖包,只用复制一次 - ``` - scp -r .\target\lib @:~/examples/java - ``` - - - 复制本程序的 jar 包,每次更新代码都需要复制 - ``` - scp -r .\target\javaexample-1.0.jar @:~/examples/java - ``` -4. 配置环境变量。 - 编辑 `~/.bash_profile` 或 `~/.bashrc` 添加如下内容例如: - ``` - export TDENGINE_JDBC_URL="jdbc:TAOS://localhost:6030?user=root&password=taosdata" - ``` - 以上使用的是本地部署 TDengine Server 时默认的 JDBC URL。你需要根据自己的实际情况更改。 - -5. 用 java 命令启动示例程序,命令模板: - - ``` - java -classpath lib/*:javaexample-1.0.jar com.taos.example.highvolume.FastWriteExample - ``` - -6. 结束测试程序。测试程序不会自动结束,在获取到当前配置下稳定的写入速度后,按 CTRL + C 结束程序。 - 下面是一次实际运行的截图: - - ``` - [testuser@vm95 java]$ java -classpath lib/*:javaexample-1.0.jar com.taos.example.highvolume.FastWriteExample 1 9 1000 2000 - 17:01:01.131 [main] INFO c.t.e.highvolume.FastWriteExample - readTaskCount=1, writeTaskCount=9 tableCount=1000 maxBatchSize=2000 - 17:01:01.286 [WriteThread-0] INFO c.taos.example.highvolume.WriteTask - started - 17:01:01.354 [WriteThread-1] INFO c.taos.example.highvolume.WriteTask - started - 17:01:01.360 [WriteThread-2] INFO c.taos.example.highvolume.WriteTask - started - 17:01:01.366 [WriteThread-3] INFO c.taos.example.highvolume.WriteTask - started - 17:01:01.433 [WriteThread-4] INFO c.taos.example.highvolume.WriteTask - started - 17:01:01.438 [WriteThread-5] INFO c.taos.example.highvolume.WriteTask - started - 17:01:01.443 [WriteThread-6] INFO c.taos.example.highvolume.WriteTask - started - 17:01:01.448 [WriteThread-7] INFO c.taos.example.highvolume.WriteTask - started - 17:01:01.454 [WriteThread-8] INFO c.taos.example.highvolume.WriteTask - started - 17:01:01.454 [ReadThread-0] INFO com.taos.example.highvolume.ReadTask - started - 17:01:11.615 [main] INFO c.t.e.highvolume.FastWriteExample - count=18766442 speed=1876644 - 17:01:21.775 [main] INFO c.t.e.highvolume.FastWriteExample - count=38947464 speed=2018102 - 17:01:32.428 [main] INFO c.t.e.highvolume.FastWriteExample - count=58649571 speed=1970210 - 17:01:42.577 [main] INFO c.t.e.highvolume.FastWriteExample - count=79264890 speed=2061531 - 17:01:53.265 [main] INFO c.t.e.highvolume.FastWriteExample - count=99097476 speed=1983258 - 17:02:04.209 [main] INFO c.t.e.highvolume.FastWriteExample - count=119546779 speed=2044930 - 17:02:14.935 [main] INFO c.t.e.highvolume.FastWriteExample - count=141078914 speed=2153213 - 17:02:25.617 [main] INFO c.t.e.highvolume.FastWriteExample - count=162183457 speed=2110454 - 17:02:36.718 [main] INFO c.t.e.highvolume.FastWriteExample - count=182735614 speed=2055215 - 17:02:46.988 [main] INFO c.t.e.highvolume.FastWriteExample - count=202895614 speed=2016000 - ``` - -
- -## Python 示例程序 {#python-demo} - -该 Python 示例程序中采用了多进程的架构,并使用了跨进程的队列通信。写任务采用拼装 SQL 的方式写入。 -### main 函数 {#python-demo-main} - -main 函数负责创建消息队列和启动子进程,子进程有 3 类: - -1. 1 个监控进程,负责数据库初始化和统计写入速度 -2. n 个读进程,负责从其它数据系统读数据 -3. m 个写进程,负责写数据库 - -main 函数可以接收 5 个启动参数,依次是: - -1. 读任务(进程)数, 默认为 1 -2. 写任务(进程)数, 默认为 1 -3. 模拟生成的总表数,默认为 1000 -4. 队列大小(单位字节),默认为 1000000 -5. 每批最多写入记录数量, 默认为 3000 - -
- -main 函数 - -```python -{{#include docs/examples/python/highvolume_faster_queue.py:main}} -``` - -
- -### 监控进程 - -监控进程负责初始化数据库,并监控当前的写入速度。 - -
-Monitor Process - -```python -{{#include docs/examples/python/highvolume_faster_queue.py:monitor}} -``` - -
- -### 读进程 {#python-read-process} - -#### 读进程主要逻辑 {#python-run-read-task} - -读进程,负责从其它数据系统读数据,并分发数据到各个写进程。 - -
- -run_read_task 函数 - -```python -{{#include docs/examples/python/highvolume_faster_queue.py:read}} -``` - -
- -#### 模拟数据源 {#python-mock-data-source} - -以下是模拟数据源的实现,我们假设数据源生成的每一条数据都带有目标表名信息。实际中你可能需要一定的规则确定目标表名。 - -
-MockDataSource - -```python -{{#include docs/examples/python/highvolume_faster_queue.py:MockDataSource}} -``` - -
- -### 写进程 {#python-write-process} - -写进程每次从队列中取出尽量多的数据,并批量写入。 - -
-run_write_task 函数 - -```python -{{#include docs/examples/python/highvolume_faster_queue.py:write}} -``` -
- -### SQLWriter 类的实现 {#python-sql-writer} - -SQLWriter 类封装了拼 SQL 和写数据的逻辑。所有的表都没有提前创建,而是写入出错的时候,再以超级表为模板批量建表,然后重新执行 INSERT 语句。这个类也对 SQL 是否超过最大长度限制做了检查,如果接近 SQL 最大长度限制(maxSQLLength),将会立即执行 SQL。为了减少 SQL 此时,建议将 maxSQLLength 适当调大。 - -
- -SQLWriter - -```python -{{#include docs/examples/python/sql_writer.py}} -``` - -
- -### 执行示例程序 {#run-python-demo} - -
- -执行 Python 示例程序 - -1. 前提条件 - - 已安装 TDengine 客户端驱动 - - 已安装 Python3, 推荐版本 >= 3.8 - - 已安装 taospy - -2. 安装 faster-fifo 代替 python 内置的 multiprocessing.Queue - ``` - pip3 install faster-fifo - ``` - -3. 点击上面的“查看源码”链接复制 `highvolume_faster_queue.py` 和 `sql_writer.py` 两个文件。 - -4. 执行示例程序 - - ``` - python3 highvolume_faster_queue.py - ``` - -下面是一次实际运行的输出: - -``` -[testuser@vm95 python]$ python3.6 highvolume_faster_queue.py 9 9 1000 5000000 3000 -2022-07-13 10:05:50,504 [root] - READ_TASK_COUNT=9, WRITE_TASK_COUNT=9, TABLE_COUNT=1000, QUEUE_SIZE=5000000, MAX_BATCH_SIZE=3000 -2022-07-13 10:05:53,542 [root] - WriteTask-0 started with pid 5475 -2022-07-13 10:05:53,542 [root] - WriteTask-1 started with pid 5476 -2022-07-13 10:05:53,543 [root] - WriteTask-2 started with pid 5477 -2022-07-13 10:05:53,543 [root] - WriteTask-3 started with pid 5478 -2022-07-13 10:05:53,544 [root] - WriteTask-4 started with pid 5479 -2022-07-13 10:05:53,544 [root] - WriteTask-5 started with pid 5480 -2022-07-13 10:05:53,545 [root] - WriteTask-6 started with pid 5481 -2022-07-13 10:05:53,546 [root] - WriteTask-7 started with pid 5482 -2022-07-13 10:05:53,546 [root] - WriteTask-8 started with pid 5483 -2022-07-13 10:05:53,547 [root] - ReadTask-0 started with pid 5484 -2022-07-13 10:05:53,548 [root] - ReadTask-1 started with pid 5485 -2022-07-13 10:05:53,549 [root] - ReadTask-2 started with pid 5486 -2022-07-13 10:05:53,550 [root] - ReadTask-3 started with pid 5487 -2022-07-13 10:05:53,551 [root] - ReadTask-4 started with pid 5488 -2022-07-13 10:05:53,552 [root] - ReadTask-5 started with pid 5489 -2022-07-13 10:05:53,552 [root] - ReadTask-6 started with pid 5490 -2022-07-13 10:05:53,553 [root] - ReadTask-7 started with pid 5491 -2022-07-13 10:05:53,554 [root] - ReadTask-8 started with pid 5492 -2022-07-13 10:06:00,842 [DataBaseMonitor] - count=6612939 speed=661293.9 -2022-07-13 10:06:11,151 [DataBaseMonitor] - count=14765739 speed=815280.0 -2022-07-13 10:06:21,677 [DataBaseMonitor] - count=23282163 speed=851642.4 -2022-07-13 10:06:31,985 [DataBaseMonitor] - count=31673139 speed=839097.6 -2022-07-13 10:06:42,343 [DataBaseMonitor] - count=39819439 speed=814630.0 -2022-07-13 10:06:52,830 [DataBaseMonitor] - count=48146339 speed=832690.0 -2022-07-13 10:07:03,396 [DataBaseMonitor] - count=56385039 speed=823870.0 -2022-07-13 10:07:14,341 [DataBaseMonitor] - count=64848739 speed=846370.0 -2022-07-13 10:07:24,877 [DataBaseMonitor] - count=73654566 speed=880582.7 -``` - -
\ No newline at end of file diff --git a/docs/zh/07-develop/03-insert-data/high.png b/docs/zh/07-develop/03-insert-data/high.png new file mode 100644 index 0000000000000000000000000000000000000000..94788f06531853a5ae8e2a0e169429cd84a60731 GIT binary patch literal 26418 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 kJ;OQx^$LQvZIhNi>vh|I9Y91(0N+-{ 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 literal 6746 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 -- GitLab