如何使用 taosdemo 对 TDengine 进行性能测试?这里有一份详细教程

自 2019 年 7 月开源以来,TDengine 凭借创新的数据建模设计、快捷的安装方式、易用的编程接口和强大的数据写入查询性能博得了大量用户的青睐,其中写入和查询性能往往令刚接触 TDengine 的用户称叹不已。为了便于用户在最短时间内就体验到 TDengine 高性能这个特点,我们专门开发了一个名为 taosdemo 的应用程序,用于对 TDengine 进行写入和查询的性能测试。

用户可以通过 taosdemo 轻松模拟大量设备产生海量数据的场景,还可以通过 taosdemo 参数灵活控制表的个数、表的列数、数据类型、一次插入的记录条数、记录的时间间隔、乱序比例、客户端接口类型以及并发线程数量等等。同时,taosdemo也支持查询和订阅的测试。

运行 taosdemo 很简单,通过下载 TDengine 安装包或者自行下载 TDengine 代码进行编译,都可以在安装目录或者编译结果目录中找到并运行。

接下来本文为大家讲解 taosdemo 的使用介绍及注意事项。

如何使用 taosdemo 进行写入测试

在不使用任何参数的情况下执行 taosdemo 命令,输出如下:

$ taosdemo

taosdemo is simulating data generated by power equipment monitoring...

host:                       127.0.0.1:6030
user:                       root
password:                   taosdata
configDir:
resultFile:                 ./output.txt
thread num of insert data:  8
thread num of create table: 8
top insert interval:        0
number of records per req:  30000
max sql length:             1048576
database count:             1
database[0]:
  database[0] name:      test
  drop:                  yes
  replica:               1
  precision:             ms
  super table count:     1
  super table[0]:
      stbName:           meters
      autoCreateTable:   no
      childTblExists:    no
      childTblCount:     10000
      childTblPrefix:    d
      dataSource:        rand
      iface:             taosc
      insertRows:        10000
      interlaceRows:     0
      disorderRange:     1000
      disorderRatio:     0
      maxSqlLen:         1048576
      timeStampStep:     1
      startTimestamp:    2017-07-14 10:40:00.000
      sampleFormat:
      sampleFile:
      tagsFile:
      columnCount:       3
column[0]:FLOAT column[1]:INT column[2]:FLOAT
      tagCount:            2
        tag[0]:INT tag[1]:BINARY(16)

         Press enter key to continue or Ctrl-C to stop

这里显示的是接下来 taosdemo 进行数据写入的各项参数。默认不输入任何命令行参数的情况下, taosdemo 将模拟生成一个电力行业典型应用的电表数据采集场景数据,即建立一个名为 test 的数据库,并创建一个名为 meters 的超级表,超级表的结构为:

taos> describe test.meters;
             Field              |         Type         |   Length    |   Note   |
=================================================================================
 ts                             | TIMESTAMP            |           8 |          |
 current                        | FLOAT                |           4 |          |
 voltage                        | INT                  |           4 |          |
 phase                          | FLOAT                |           4 |          |
 groupid                        | INT                  |           4 | TAG      |
 location                       | BINARY               |          64 | TAG      |
Query OK, 6 row(s) in set (0.002972s)

按回车键后 taosdemo 将建立数据库 test 和超级表 meters,并按照 TDengine 数据建模的最佳实践,以 meters 超级表为模板生成一万个子表,代表一万个独立上报数据的电表设备。

taos> use test;
Database changed.

taos> show stables;
              name              |      created_time       | columns |  tags  |   tables    |
============================================================================================
 meters                         | 2021-08-27 11:21:01.209 |       4 |      2 |       10000 |
Query OK, 1 row(s) in set (0.001740s)

然后 taosdemo 为每个电表设备模拟生成一万条记录:

...
====thread[3] completed total inserted rows: 6250000, total affected rows: 6250000. 347626.22 records/second====
[1]:100%
====thread[1] completed total inserted rows: 6250000, total affected rows: 6250000. 347481.98 records/second====
[4]:100%
====thread[4] completed total inserted rows: 6250000, total affected rows: 6250000. 347149.44 records/second====
[8]:100%
====thread[8] completed total inserted rows: 6250000, total affected rows: 6250000. 347082.43 records/second====
[6]:99%
[6]:100%
====thread[6] completed total inserted rows: 6250000, total affected rows: 6250000. 345586.35 records/second====
Spent 18.0863 seconds to insert rows: 100000000, affected rows: 100000000 with 16 thread(s) into test.meters. 5529049.90 records/second

insert delay, avg:      28.64ms, max:     112.92ms, min:       9.35ms

以上信息是在一台具备 8 个 CPU 64G 内存的普通 PC 服务器上进行实测的结果。数据显示 taosdemo 用了 18 秒的时间插入了 100000000 (不用数了,一亿条)记录,平均每秒钟插入 552 万 9 千零 49 条记录。

此外,TDengine 还能提供性能更好的参数绑定接口,在同样的硬件上使用其参数绑定接口 (taosdemo -I stmt )进行相同数据量的写入时,结果如下:

...


====thread[0] completed total inserted rows: 12500000, total affected rows: 12500000. 2162660.81 records/second====
[1]:100%
====thread[1] completed total inserted rows: 12500000, total affected rows: 12500000. 2156510.94 records/second====
[7]:96%
[5]:96%
[7]:97%
[5]:97%
[7]:98%
[5]:98%
[7]:99%
[5]:99%
[7]:100%
====thread[7] completed total inserted rows: 12500000, total affected rows: 12500000. 2106514.83 records/second====
[5]:100%
====thread[5] completed total inserted rows: 12500000, total affected rows: 12500000. 2104144.73 records/second====
[4]:96%
[2]:96%
[4]:97%
[2]:97%
[4]:98%
[2]:98%
[4]:99%
[2]:99%
[4]:100%
====thread[4] completed total inserted rows: 12500000, total affected rows: 12500000. 2044114.28 records/second====
[2]:100%
====thread[2] completed total inserted rows: 12500000, total affected rows: 12500000. 2039911.44 records/second====
Spent 6.1404 seconds to insert rows: 100000000, affected rows: 100000000 with 8 thread(s) into test.meters. 16285462.00 records/second


insert delay, avg: 4.68ms, max: 662.29ms, min: 2.00ms

结果显示 taosdemo 用了 6 秒的时间插入了一亿条记录,每秒钟插入性能高达 1628 万 5462 条记录。

由于 taosdemo 使用起来非常方便,我们又对其做了更多的功能扩充,使其能够支持更复杂的参数设置,便于进行快速原型开发的样例数据准备和验证工作。

完整的 taosdemo 命令行参数列表通过 taosdemo –help 显示如下:

$ taosdemo --help
-f, --file=FILE The meta file to the execution procedure.
-u, --user=USER The user name to use when connecting to the server.
-p, --password The password to use when connecting to the server.
-c, --config-dir=CONFIG_DIR Configuration directory.
-h, --host=HOST TDengine server FQDN to connect. The default host is localhost.
-P, --port=PORT The TCP/IP port number to use for the connection.
-I, --interface=INTERFACE The interface (taosc, rest, and stmt) taosdemo uses. By default use 'taosc'.
-d, --database=DATABASE Destination database. By default is 'test'.
-a, --replica=REPLICA Set the replica parameters of the database, By default use 1, min: 1, max: 3.
-m, --table-prefix=TABLEPREFIX Table prefix name. By default use 'd'.
-s, --sql-file=FILE The select sql file.
-N, --normal-table Use normal table flag.
-o, --output=FILE Direct output to the named file. By default use './output.txt'.
-q, --query-mode=MODE Query mode -- 0: SYNC, 1: ASYNC. By default use SYNC.
-b, --data-type=DATATYPE The data_type of columns, By default use: FLOAT, INT, FLOAT.
-w, --binwidth=WIDTH The width of data_type 'BINARY' or 'NCHAR'. By default use 64
-l, --columns=COLUMNS The number of columns per record. Demo mode by default is 1 (float, int, float). Max values is 4095
All of the new column(s) type is INT. If use -b to specify column type, -l will be ignored.
-T, --threads=NUMBER The number of threads. By default use 8.
-i, --insert-interval=NUMBER The sleep time (ms) between insertion. By default is 0.
-S, --time-step=TIME_STEP The timestamp step between insertion. By default is 1.
-B, --interlace-rows=NUMBER The interlace rows of insertion. By default is 0.
-r, --rec-per-req=NUMBER The number of records per request. By default is 30000.
-t, --tables=NUMBER The number of tables. By default is 10000.
-n, --records=NUMBER The number of records per table. By default is 10000.
-M, --random The value of records generated are totally random.
By default to simulate power equipment scenario.
-x, --aggr-func Test aggregation functions after insertion.
-y, --answer-yes Input yes for prompt.
-O, --disorder=NUMBER Insert order mode--0: In order, 1 ~ 50: disorder ratio. By default is in order.
-R, --disorder-range=NUMBER Out of order data's range. Unit is ms. By default is 1000.
-g, --debug Print debug info.
-?, --help Give this help list
--usage Give a short usage message
-V, --version Print program version.

Mandatory or optional arguments to long options are also mandatory or optional
for any corresponding short options.

Report bugs to <support@taosdata.com>.

下面介绍 taosdemo 的参数,其设计初衷是为了满足数据模拟的需求。接下来先介绍几个常用的参数:

-I, --interface=INTERFACE     The interface (taosc, rest, and stmt) taosdemo uses. Default is 'taosc'.

前面在介绍 taosdemo 不同接口的性能差异时已经提到, -I 参数为选择不同的接口,目前支持 taosc、stmt 和 rest 三种。其中 taosc 为使用 SQL 语句方式进行数据写入;stmt 为使用参数绑定接口进行数据写入;rest 为使用 RESTful 协议进行数据写入。

-T, --threads=NUMBER          The number of threads. Default is 8.

通过-T 参数设置 taosdemo 使用几个线程进行数据同步写入,多线程写入可以尽最大可能压榨硬件的处理能力。

-b, --data-type=DATATYPE      The data_type of columns, default: FLOAT, INT, FLOAT.

-w, --binwidth=WIDTH          The width of data_type 'BINARY' or 'NCHAR'. Default is 64
  
-l, --columns=COLUMNS         The number of columns per record. Demo mode by default is 3 (float, int, float). Max values is 4095

前文提到,taosdemo 默认创建一个典型电表数据采集应用场景,每个设备包含电流电压相位3个采集量。对于需要定义不同采集量的场景,可以使用 -b 参数,TDengine 支持 BOOL、TINYINT、SMALLINT、INT、BIGINT、FLOAT、DOUBLE、BINARY、NCHAR、TIMESTAMP 等多种数据类型。通过 -b 加上以“ , ”(英文逗号)分割定制类型的列表,可以使 taosdemo 建立对应的超级表和子表,并插入相应模拟数据;通过 -w 参数可以指定 BINARY 和 NCHAR 数据类型的列的宽度(默认为 64 );-l 参数可以在 -b 参数指定数据类型的几列之后补充以 INT 型的总的列数,特别多列的情况下可以减少手工输入的过程,最多支持到 4095 列。

-r, --rec-per-req=NUMBER      The number of records per request. Default is 30000.

为了尽最大可能利用 TDengine 性能,可以使用多客户端、多线程以及一次插入多条数据来进行数据写入。 -r 参数为设置一次写入请求可以拼接的记录条数,默认为30000条。有效的拼接记录条数还和客户端缓冲区大小有关,目前的缓冲区为 1M Bytes,如果记录的列宽度比较大,最大有效拼接记录条数可以通过 1M 除以列宽(以字节为单位)计算得出。

-t, --tables=NUMBER           The number of tables. Default is 10000.
-n, --records=NUMBER          The number of records per table. Default is 10000.
-M, --random                  The value of records generated are totally random. The default is to simulate power equipment senario.

前文还提到 taosdemo 默认创建 10000 个表,每个表写入 10000 条记录,可以通过 -t 和 -n 设置表的数量和每个表记录的数量。这里默认无参数生成的数据为模拟真实场景,模拟生成的数据为电流、电压和相位值增加一定的抖动,可以更真实表现 TDengine 高效的数据压缩能力。如果需要模拟生成完全随机数据,可以通过 -M 参数实现。

-O, --disorder=NUMBER         Insert order mode--0: In order, 1 ~ 50: disorder ratio. Default is in order.
-R, --disorder-range=NUMBER   Out of order data's range, ms, default is 1000.

在某些场景,接收到的数据并不是完全按时间顺序排列,而是包含一定比例的乱序数据,对此TDengine 也能进行很好的处理。为了模拟乱序数据的写入,taosdemo 提供 -O 和 -R 参数进行设置。-O 参数为 0 和不使用 -O 参数两者相同,为完全有序数据写入,参数 1 到 50 为数据中包含乱序数据的比例。-R 参数为乱序数据时间戳偏移的范围,默认为 1000 毫秒。另外注意,时序数据以时间戳为唯一标识,所以乱序数据可能会生成和之前已经写入的数据完全相同的时间戳,这样的数据会根据数据库创建的 update 设置值或者被丢弃(update 0)或者覆盖已有数据(update 1 或 2),所以可能会发生总的写入数据条数和期待的条数不一致的情况。

 -g, --debug                   Print debug info.

如果对 taosdemo 写入数据过程感兴趣或者觉得数据写入结果不符合预期,可以使用 -g 参数使 taosdemo 把执行过程的中间调试信息输出到屏幕上,也可以通过 Linux 重定向命令导入到另外一个文件,方便找到发生问题的原因。另外 taosdemo 在执行失败后也会把相应执行的语句和调试原因输出到屏幕,可以搜索输出文字中的 “reason” 来找到 TDengine 服务端返回的错误原因信息,这里要注意使用 -g 参数进行调试信息输出会影响性能。

-y, --answer-yes              Default input yes for prompt.

前面我们可以看到 taosdemo 默认在进行创建数据库或插入数据之前,输出将要进行操作的参数列表并等待用户按回车键再继续执行写入操作,方便使用者在插入之前了解即将进行的数据写入的内容。为了方便进行自动测试,-y 参数可以使 taosdemo 输出参数后立刻进行数据写入操作。


-x, --aggr-func Test aggregation funtions after insertion.

TDengine 不仅插入性能非常强大,其先进的数据库引擎设计使得查询性能也异常强大。taosdemo 提供了一个 -x 参数,可以在插入数据结束后进行常用查询操作并输出查询消耗时间。以下为在前述相同服务器上插入一亿条记录后进行常用查询的结果。

可以看到 select * 取出一亿条记录(不输出到屏幕)操作仅消耗1.26秒。而对一亿条记录进行常用的聚合函数操作通常仅需要二十几毫秒,时间最长的 count 函数也不到四十毫秒。

taosdemo -I stmt -T 48 -y -x
...
...
select          * took 1.266835 second(s)
...
select   count(*) took 0.039684 second(s)
...
Where condition: groupid = 1
select avg(current) took 0.025897 second(s)
...
select sum(current) took 0.025622 second(s)
...
select max(current) took 0.026124 second(s)
...
...
select min(current) took 0.025812 second(s)
...
select first(current) took 0.024105 second(s)
...

除了命令行方式, taosdemo 还支持接受指定一个 JSON 文件做为传入参数的方式。一个典型的 JSON 文件内容如下:

{
    "filetype": "insert",
    "cfgdir": "/etc/taos",
    "host": "127.0.0.1",
    "port": 6030,
    "user": "root",
    "password": "taosdata",
    "thread_count": 4,                   
    "thread_count_create_tbl": 4,        
    "result_file": "./insert_res.txt",   
    "confirm_parameter_prompt": "no",    
    "insert_interval": 0,         
    "interlace_rows": 100,        
    "num_of_records_per_req": 100,
    "databases": [{
        "dbinfo": {
            "name": "db",
            "drop": "yes",                
            "replica": 1,
            "days": 10,
            "cache": 16,
            "blocks": 8,
            "precision": "ms",
            "keep": 3650,
            "minRows": 100,
            "maxRows": 4096,
            "comp":2,
            "walLevel":1,
            "cachelast":0,
            "quorum":1,
            "fsync":3000,
            "update": 0
        },
        "super_tables": [{
            "name": "stb",
            "child_table_exists":"no",   
            "childtable_count": 100,   
            "childtable_prefix": "stb_", 
            "auto_create_table": "no",   
            "batch_create_tbl_num": 5,   
            "data_source": "rand",       
            "insert_mode": "taosc",      
            "insert_rows": 100000,       
            "childtable_limit": 10,      
            "childtable_offset":100,     
            "interlace_rows": 0,         
            "insert_interval":0,         
            "max_sql_len": 1024000,      
            "disorder_ratio": 0,         
            "disorder_range": 1000,      
            "timestamp_step": 10,         
            "start_timestamp": "2020-10-01 00:00:00.000",  
            "sample_format": "csv",       
            "sample_file": "./sample.csv",   
            "tags_file": "",              
            "columns": [{"type": "INT"}, {"type": "DOUBLE", "count":10}, {"type": "BINARY", "len": 16, "count":3}, {"type": "BINARY", "len": 32, "count":6}],
            "tags": [{"type": "TINYINT", "count":2}, {"type": "BINARY", "len": 16, "count":5}]
        }]
    }]
}

JSON 文件还可以提供更丰富的设置项。例如:我们可以通过 “thread_count” 和 “thread_count_create_tbl” 来为建表和插入数据指定不同数量的线程;可以通过 “child_table_exists”、”childtable_limit” 和 “childtable_offset” 的组合来使用多个 taosdemo 进程;甚至可以在不同的电脑上对同一个超级表的不同范围子表进行同时写入;还可以通过 “data_source” 和 “sample_file” 来指定数据来源为 csv 文件,来实现导入已有数据的功能。

如何使用 taosdemo 进行查询和订阅测试

taosdemo 不仅可以进行数据写入,还拥有查询和订阅功能。但一个 taosdemo 实例只能支持其中的一种功能,不能同时支持三种功能,我们可以通过配置文件来指定进行哪种功能的测试。

以下为一个典型查询 JSON 示例文件内容:

{
  "filetype": "query",
  "cfgdir": "/etc/taos",
  "host": "127.0.0.1",
  "port": 6030,
  "user": "root",
  "password": "taosdata",
  "confirm_parameter_prompt": "no",
  "databases": "db",
  "query_times": 2,
  "query_mode": "taosc",
  "specified_table_query": {
    "query_interval": 1,
    "concurrent": 3,
    "sqls": [
      {
        "sql": "select last_row(*) from stb0 ",
        "result": "./query_res0.txt"
      },
      {
        "sql": "select count(*) from stb00_1",
        "result": "./query_res1.txt"
      }
    ]
  },
  "super_table_query": {
    "stblname": "stb1",
    "query_interval": 1,
    "threads": 3,
    "sqls": [
      {
        "sql": "select last_row(ts) from xxxx",
        "result": "./query_res2.txt"
      }
    ]
  }
}

以下为 JSON 文件中和查询相关的特有参数含义:

“query_times”: 每种查询类型的查询次数
“query_mode”: 查询数据接口。”taosc”:调用TDengine的c接口;“resetful”:使用restful接口。可选项。缺省是“taosc”。
“specified_table_query”: { 指定表的查询
“query_interval”: 执行sqls的间隔,单位是秒。可选项,缺省是0。
“concurrent”: 并发执行sqls的线程数,可选项,缺省是1。每个线程都执行所有的sqls。
“sqls”: 可以添加多个sql语句,最多支持100条。
“sql”: 查询语句。必选项。
“result”: 查询结果写入的文件名。可选项,缺省是空,表示查询结果不写入文件。
“super_table_query”: { 对超级表中所有子表的查询
“stblname”: 超级表名称。必选项。
“query_interval”: 执行sqls的间隔,单位是秒。可选项,缺省是0。
“threads”: 并发执行sqls的线程数,可选项,缺省是1。每个线程负责一部分子表,执行所有的sqls。
“sql”: “select count(*) from xxxx”。查询超级表内所有子表的查询语句,其中表名必须写成 “xxxx”,实例会自动替换成子表名。
“result”: 查询结果写入的文件名。可选项,缺省是空,表示查询结果不写入文件。

以下为一个典型订阅 JSON 示例文件内容:

{
    "filetype":"subscribe",
    "cfgdir": "/etc/taos",
    "host": "127.0.0.1",
    "port": 6030,
    "user": "root",
    "password": "taosdata",
    "databases": "db",
    "confirm_parameter_prompt": "no",
    "specified_table_query":
      {
       "concurrent":1, 
       "mode":"sync", 
       "interval":0, 
       "restart":"yes", 
       "keepProgress":"yes",
       "sqls": [
        {
          "sql": "select * from stb00_0 ;", 
          "result": "./subscribe_res0.txt"
        }]
      },
    "super_table_query": 
      {
       "stblname": "stb0",
       "threads":1, 
       "mode":"sync", 
       "interval":10000, 
       "restart":"yes", 
       "keepProgress":"yes",
       "sqls": [
        {
          "sql": "select * from xxxx where ts > '2021-02-25 11:35:00.000' ;", 
          "result": "./subscribe_res1.txt"
        }]
      }
  }

以下为订阅功能相关的特有参数含义:

“interval”: 执行订阅的间隔,单位是秒。可选项,缺省是0。
“restart”: 订阅重启。”yes”:如果订阅已经存在,重新开始;”no”: 继续之前的订阅。(请注意执行用户需要对 dataDir 目录有读写权限)
“keepProgress”: 保留订阅信息进度。yes表示保留订阅信息,no表示不保留。该值为yes,restart为no时,才能继续之前的订阅。
“resubAfterConsume”: 配合 keepProgress 使用,在订阅消费了相应次数后调用 unsubscribe 取消订阅并再次订阅。
“result”: 查询结果写入的文件名。可选项,缺省是空,表示查询结果不写入文件。 注意:每条sql语句后保存结果的文件不能重名,且生成结果文件时,文件名会附加线程号。

结语

TDengine是涛思数据专为物联网、车联网、工业互联网、IT运维等场景设计和优化的大数据平台,由于数据库内核中创新的数据存储和查询引擎设计,其展现出了远超同类产品的高效性能。同时,TDengine还支持 SQL 语法和多种编程语言的连接器(目前支持 Java、Python、Go、C#、NodeJS 以及 Rust 等),易用性极强,学习成本几乎为零。为了便于运维需求,其还提供数据迁移和监控功能等相关生态工具软件。

为了方便刚接触 TDengine 的使用者进行技术评估和压力测试,我们为 taosdemo 开发了丰富的特性,本文即为对 taosdemo 的一个简单介绍。相信随着 TDengine 新功能的不断增加,taosdemo 也会继续演化和改进。

当下,作为 TDengine 的一部分,taosdemo 的源代码在 GitHub 上已经完全开源,欢迎大家就 taosdemo 或 TDengine 的使用或实现在 GitHub 或者涛思数据的用户群提出建议或反馈。