时钟导致TDengine偶尔连接失败问题排查和解决

一、环境信息

CentOS 7.9
TDengine 3.1.0.0
OpenJDK 11

二、问题现象

一个数据接入程序,定时将数据从指定文件导入到TDengine,发现每天执行的约1000次导入作业里,有1%~2%是失败的,查询失败作业的日志可以看到抛出如下异常:

[2023-08-07 08:35:00.370] [INFO] [INFO ][2023-08-07 08:33:36] 导入中 2001637/QiJia003/1650038400000/Sec_20220416.csv, 已成功导入 51000 行, 182 MB
[2023-08-07 08:35:06.980] [INFO] [INFO ][2023-08-07 08:33:34] 导入中 2001637/QiJia001/1650038400000/Sec_20220416.csv, 已成功导入 57000 行, 183 MB
[2023-08-07 08:35:00.875] [ERROR] create connection SQLException, url: jdbc:TAOS://:/repo_QiJia_1sec, errorCode -2147483637, state 
java.sql.SQLException: TDengine ERROR (8000000b): Unable to establish connection
    at com.taosdata.jdbc.TSDBError.createSQLException(TSDBError.java:76) ~[k2box-storage-taos3-2.2.5.jar:?]
    at com.taosdata.jdbc.TSDBJNIConnector.executeQuery(TSDBJNIConnector.java:119) ~[k2box-storage-taos3-2.2.5.jar:?]
    at com.taosdata.jdbc.TSDBStatement.executeQuery(TSDBStatement.java:47) ~[k2box-storage-taos3-2.2.5.jar:?]
    at com.alibaba.druid.pool.DruidAbstractDataSource.validateConnection(DruidAbstractDataSource.java:1419) ~[k2box-storage-taos3-2.2.5.jar:?]
    at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1719) ~[k2box-storage-taos3-2.2.5.jar:?]
    at com.alibaba.druid.pool.DruidDataSource$CreateConnectionThread.run(DruidDataSource.java:2813) ~[k2box-storage-taos3-2.2.5.jar:?]
[2023-08-07 08:35:00.883] [ERROR] create connection SQLException, url: jdbc:TAOS://:/repo_QiJia_1sec, errorCode -2147483637, state 
java.sql.SQLException: TDengine ERROR (8000000b): Unable to establish connection
    at com.taosdata.jdbc.TSDBError.createSQLException(TSDBError.java:76) ~[k2box-storage-taos3-2.2.5.jar:?]
    at com.taosdata.jdbc.TSDBJNIConnector.executeQuery(TSDBJNIConnector.java:119) ~[k2box-storage-taos3-2.2.5.jar:?]
    at com.taosdata.jdbc.TSDBStatement.executeQuery(TSDBStatem

三、问题排查

由于此环境的TDengine是不久前从2.6.0升级到3.1.0.0的,升级之前没有这个问题,所以一开始怀疑是TDengine的新版本有问题,或者是新版本的默认参数与旧版本不同导致的。但尝试修改了服务端和客户端(连接池)的各种参数后问题依旧。用ulimit -nlsof -p查询打开的文件数量和文件上限也在正常范围内。

后来考虑是否作业(docker容器)消耗资源有异常,查看资源占用图表时发现折线图有向反方向走的情况(下图红色箭头指向的位置),说明服务器系统时间有倒流的情况。

file

仔细查看前面的失败作业日志,其实也有时间戳倒流的现象,并且连接失败现象与时间戳倒流是同时发生的:

file

因此将问题嫌疑定位到与系统时钟有关。经过进一步排查发现,此服务器使用ntpdate定期从两台ntp服务器分别进行周期性的对时:

1、每5分钟从服务器10.102.9.31同步时间:

[root@taos-1 ~]# crontab -l
*/5 * * * * /usr/sbin/ntpdate 10.102.9.31 >/dev/null 2>&1

2、每64秒从服务器10.102.9.32同步时间:

[root@taos-1 ~]# ntpstat
synchronised to NTP server (10.102.9.32) at stratum 3
   time correct to within 115 ms
   polling server every 64 s
[root@taos-1 ~]#

并且服务器10.102.9.32的时钟比10.102.9.31慢7秒左右,这就造成了这台服务器的时钟周期性的向前和向后跳动。当TDengine服务端的时钟改变时,连接池里原有的连接会失效无法连接。

[root@taos-1 ~]# /usr/sbin/ntpdate 10.102.9.32
 7 Aug 18:51:23 ntpdate[49651]: step time server 10.102.9.32 offset 7.341666 sec

[root@taos-1 ~]# /usr/sbin/ntpdate 10.102.9.31
 7 Aug 18:54:31 ntpdate[1798]: step time server 10.102.9.31 offset -7.200571 sec

四、问题解决

从两台服务器分别对时显然是没有必要的,应该是运维人员不了解情况的情况下操作导致的,去掉其中一个对时的任务后即可。这里我们去掉的是ntpstat里配置的对时服务,我们发现它是用chrony服务实现的,因此执行systemctl stop chronydsystemctl disable chronyd

使用SysBench测试PostgreSQL并发访问性能

本文使用SysBench测试PostgreSQL服务在不同并发线程数下的性能表现。

一、环境信息

被测服务器(192.168.132.167):

  • 4核8线程,32GB,1TB 7200转机械硬盘
  • CentOS 7.9
  • PostgreSQL 9.6.2

测试客户端(192.168.130.152):

  • 4核8线程,32GB,2TB 7200转机械硬盘
  • CentOS 7.9
  • SysBench 1.0.17

网络:

  • 千兆以太局域网

二、测试综合性能

为了测试不同参数下数据库的性能表现,我们通常会将测试过程写成shell脚本循环执行。由于性能测试的时间可能很长,我们用tee命令将每次测试的结果保存到文件以免丢失数据。

我们用下面的脚本测试在不同线程数下的综合性能(oltp_read_write)。

#!/bin/sh
pg_host=192.168.132.167
pg_port=5432
pg_user=sbtest
pg_password=password
pg_db=sbtest

for ((threads=5; threads<=160; threads=threads+5));do
    sysbench oltp_read_write \
    --pgsql-host=$pg_host --pgsql-port=$pg_port \
    --pgsql-user=$pg_user --pgsql-password=$pg_password \
    --pgsql-db=$pg_db \
    --table_size=100000 --tables=12 \
    prepare

    sysbench oltp_read_write \
    --pgsql-host=$pg_host --pgsql-port=$pg_port \
    --pgsql-user=$pg_user --pgsql-password=$pg_password \
    --pgsql-db=$pg_db \
    --report-interval=5 \
    --table_size=100000 --tables=12 \
    --threads=$threads \
    --time=30 \
    run | tee -a result.txt 2>&1

    sysbench oltp_read_write \
    --pgsql-host=$pg_host --pgsql-port=$pg_port \
    --pgsql-user=$pg_user --pgsql-password=$pg_password \
    --pgsql-db=$pg_db \
    --tables=12 \
    cleanup
done

上面测试脚本的结果保存在result.txt文件里,使用下面的命令可以获取到所需的数据项,方便粘贴到excel表格里画图:

获取平均QPS值(考虑到超过100000的情况因此用两个cut实现):
cat result.txt |grep queries:|cut -c 40-57|cut -d '(' -f 2
获取平均Latency值:
cat result_read.txt |grep avg:|cut -c 48-65

为减小误差,我们把测试脚本执行了2次,将每次得到的各线程数下的QPS数值画成一条曲线并叠加,得到下面的统计图,其中横轴是并发线程数,纵轴是QPS均值:

file

测试结果里各个线程数下TPS数值与QPS都十分接近20:1的关系,所以这里不单独画图展示TPS了。

从图中可以看出,两次测试结果比较接近,随着线程数增加QPS指标也同步增加,在120线程左右达到峰值。

两次测试的平均请求延迟(latency,毫秒)统计如下图所示,其中横轴仍然是并发线程数,纵轴是延迟时间:

file

延迟基本与线程数正相关,并且在140线程以上斜率有所增加。

三、测试只读性能

我们使用sysbench oltp_read_only命令测试纯读取场景下,不同线程数的QPS表现:

#!/bin/sh
pg_host=192.168.132.167
pg_port=5432
pg_user=sbtest
pg_password=password
pg_db=sbtest

sysbench oltp_read_only \
--db-driver=pgsql \
--pgsql-host=$pg_host --pgsql-port=$pg_port \
--pgsql-user=$pg_user --pgsql-password=$pg_password \
--pgsql-db=$pg_db \
--table_size=100000 \
--tables=12 \
prepare

for ((threads=5; threads<=120; threads=threads+5));do
    sysbench oltp_read_only \
    --db-driver=pgsql \
    --report-interval=5 \
    --table-size=100000 \
    --tables=12 \
    --threads=$threads \
    --time=30 \
    --pgsql-host=$pg_host --pgsql-port=$pg_port \
    --pgsql-user=$pg_user --pgsql-password=$pg_password \
    --pgsql-db=$pg_db \
    run | tee -a result.txt 2>&1
done

sysbench oltp_read_only \
--db-driver=pgsql \
--pgsql-host=$pg_host --pgsql-port=$pg_port \
--pgsql-user=$pg_user --pgsql-password=$pg_password \
--pgsql-db=$pg_db \
--tables=12 \
cleanup

为减小误差,我们把测试脚本执行了2次,将每次得到的各线程数下的QPS数值画成一条曲线并叠加,得到下面的统计图,其中横轴是并发线程数,纵轴是QPS均值:

file

测试结果里各个线程数下,TPS数值与QPS都十分接近16:1的关系,所以这里不单独画图展示TPS了。

从图中可以看出,两次测试结果比较接近,都是在50个并发线程时达到QPS峰值(大约35000请求/秒)。

两次测试的平均请求延迟(latency,毫秒)统计如下图所示,其中横轴仍然是并发线程数,纵轴是延迟时间:

file

从图中可以看出,两次测试结果比较接近,随着线程数量的增加,平均延迟是单调上升的,即使在并发50个线程QPS已经达到峰值后,平均延迟也是上涨的。因此从这两项测试结果来看,在50线程以上继续增加线程数量是没有意义的。

启动Docker容器超过100个后报错问题

环境信息

AMD EPYC 7551 32-Core Processor 64bit / 452GB / 4.0TB
Kylin Linux Advanced Server V10 (Sword)
Linux-419.90-24.4.v2101.ky10.x86 64-x86 64-with-kylin-10-Sword
Docker 19.03.9

问题现象

Docker容器超过一定数量后无法启动新的容器。

排查和解决

先检查目前正常运行的容器数量,有134个:

> docker ps| wc-l
134

查看了系统可用CPU和内存资源,发现可用16个CPU线程和24GB内存,可用资源还比较宽裕。

考虑到目前运行的容器已经比较多,首先怀疑是打开文件数量(open files)达到上限,检查系统设置值:

> ulimit -n
1024

因为其他类似环境里一般都设置为65535,所以用ulimit -n 65536将打开文件数上限改大,但问题并没有解决。

既然启动新的容器失败,可能docker daemon日志会有线索,查了一下果然发现有错误信息如下:

>  journalctl -ru docker.service
...
04月 26 14:39:35 k2box-211 dockerd[3016]: time="2023-04-26T14:39:35.212798766+08:00" level=error msg="stream copy error: reading from a closed fifo"
04月 26 14:39:35 k2box-211 dockerd[3016]: time="2023-04-26T14:39:35.212674036+08:00" level=error msg="stream copy error: reading from a closed fifo"
04月 26 14:39:26 k2box-211 dockerd[3016]: time="2023-04-26T14:39:26.731792766+08:00" level=warning msg="Health check for container 443a24419f9864ca13a6772c35bb7fe9eabcf317d670d7c73c0383e5473cb397 error: context deadline exceeded"
04月 26 14:39:02 k2box-211 dockerd[3016]: time="2023-04-26T14:39:02.706135654+08:00" level=warning msg="Health check for container d18b85908134d2c45869f68daf825d0c7f6541e77673e8c83a03d34a1c0a3ad9 error: OCI runtime exec failed: exec failed: container_linux.go:318: starting container process caused \"exec: \\\"curl\\\": executable file not found in $PATH\": unknown"
04月 26 14:39:02 k2box-211 dockerd[3016]: time="2023-04-26T14:39:02.699741119+08:00" level=error msg="stream copy error: reading from a closed fifo"
04月 26 14:39:02 k2box-211 dockerd[3016]: time="2023-04-26T14:39:02.699578169+08:00" level=error msg="stream copy error: reading from a closed fifo"
...

日志提示context deadline exceeded,这个错误信息虽然比较模糊,但从时间上看与尝试创建新容器的时间是吻合的,并且应该是某个阈值的限制。在网上google了一下没有找到相同的问题。

尝试停止了一个正在运行的容器,此时启动新容器能正常成功1个,再启动则会失败,因此确认了是容器数量达到了某个阈值,但为什么是134个?

经过仔细分析,发现这134个容器的启动方式有区别,其中34个是通过docker run命令行启动的,另外100个是通过docker-java以程序方式启动的(http接口),查看相关代码发现后者果然有限制:

DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
    .dockerHost(config.getDockerHost())
    .sslConfig(config.getSSLConfig())
    .maxConnections(100)  // 这里对连接数量做了限制
    .connectionTimeout(Duration.ofSeconds(30))
    .responseTimeout(Duration.ofSeconds(45))
    .build();

至此定位到问题原因,解决方法是将maxConnections(100)改为maxConnections(1024)并打包更新到问题环境。

使用Docker部署PostgreSql高可用集群(RepMgr方案)

本文记录了某项目中使用docker部署PostgreSQL集群的步骤和注意事项,使用的镜像是bitnami/postgresql-repmgr,其中与用户身份有关的内容在其他bitnami打包的镜像中也适用。

一、环境信息

CentOS 7.9
PostgreSQL 16

二、部署方案

PostgreSQL集群有多种方案,这里我们使用在项目中多次使用比较稳定的repmgr方案,repmgr能够在集群环境管理每个PostgreSQL节点的主从状态,官网介绍如下:

repmgr是一个开源的工具套件,用于管理PostgreSQL服务器集群中的复制和故障转移。它通过提供设置备用服务器、监控复制和执行故障转移或手动切换操作等管理任务的工具,增强了PostgreSQL内置的热备功能。
repmgr自从PostgreSQL 9.0引入内置的复制机制后,就提供了高级的支持。当前的repmgr系列,repmgr 5,支持了PostgreSQL 9.3引入的最新的复制功能,如级联复制、时间线切换和通过复制协议进行基础备份。

bitnami提供的postgresql-repmgr镜像是将PostgreSQL与repmgr打包在一起,形成一个集成的解决方案,以便用户能够快速搭建PostgreSQL集群服务,用户可以从dockerhub获取到这个镜像。

这个PostgreSQL集群解决方案包括PostgreSQL复制管理器,一个用于管理PostgreSQL集群上的复制和故障转移的开源工具。

三、部署步骤

首先确认集群环境各个服务器节点的状态:

关闭防火墙(如果docker已启动需要重启docker服务,否则关闭防火墙后启动容器会报iptables命令错):

systemctl stop firewalld 
systemctl disable firewalld

确保服务器时钟准确:

ntpdate cn.ntp.org.cn

安装和启动docker服务和docker-compose工具:

yum install -y epel-release
yum install -y docker
yum install -y docker-compose
systemctl enable docker
systemctl start docker

安装postgresql-repmgr镜像:

docker pull bitnami/postgresql-repmgr

将各个服务器名称写入hosts文件,vi /etc/hosts添加下面内容:

10.102.9.80 pg-0
10.102.9.81 pg-1
10.102.9.82 pg-2

四、配置

数据目录

bitnami的镜像使用非root用户身份,即容器里的root用户映射到宿主机的非root用户,此用户是ID为1001的无名称用户。要让容器的数据能够持久化到宿主机,需要准备一个数据目录(此例中为/mnt/sda/bitnami/postgresql)并映射到容器内,此目录的owner是1001:

mkdir /mnt/sda/bitnami
mkdir /mnt/sda/bitnami/postgresql
chown 1001:root /mnt/sda/bitnami -R

网络

创建docker网络以便节点间能够通信:

docker network create --subnet=172.25.0.0/24 --gateway=172.25.0.1 pg-network

配置文件

在任意目录创建pg.yml文件,内容如下:

version: '2'
networks:
  default:
    external:
      name: pg-network
services:
  pg:
    container_name: "pg"
    image: bitnami/postgresql-repmgr:latest
    networks:
      default:
        ipv4_address: 172.25.0.110
    ports:
      - "5432:5432"
    restart: always
    volumes:
      - /mnt/sda/bitnami/postgresql:/bitnami/postgresql
      - /etc/hosts:/etc/hosts
    environment:
      - POSTGRESQL_POSTGRES_PASSWORD=adminpassword
      - POSTGRESQL_USERNAME=myuser
      - POSTGRESQL_PASSWORD=mypassword
      - POSTGRESQL_DATABASE=mydatabase
      - REPMGR_PASSWORD=adminpassword
      - REPMGR_PRIMARY_HOST=pg-0
      - REPMGR_PRIMARY_PORT=5432
      - REPMGR_PARTNER_NODES=pg-0,pg-1,pg-2:5432
      - REPMGR_NODE_NAME=pg-0
      - REPMGR_NODE_NETWORK_NAME=pg-0
      - REPMGR_PORT_NUMBER=5432

在第二个节点类似创建pg.yml文件,修改其中的部分内容(ip地址、以及2处节点名称,见代码中的标注)如下所示:

version: '2'
networks:
  default:
    external:
      name: pg-network
services:
  pg:
    container_name: "pg"
    image: bitnami/postgresql-repmgr:latest
    networks:
      default:
        ipv4_address: 172.25.0.111    <-- 修改了这里
    ports:
      - "5432:5432"
    restart: always
    volumes:
      - /mnt/sda/bitnami/postgresql:/bitnami/postgresql
      - /etc/hosts:/etc/hosts
    environment:
      - POSTGRESQL_POSTGRES_PASSWORD=adminpassword
      - POSTGRESQL_USERNAME=myuser
      - POSTGRESQL_PASSWORD=mypassword
      - POSTGRESQL_DATABASE=mydatabase
      - REPMGR_PASSWORD=adminpassword
      - REPMGR_PRIMARY_HOST=pg-0
      - REPMGR_PRIMARY_PORT=5432
      - REPMGR_PARTNER_NODES=pg-0,pg-1,pg-2:5432
      - REPMGR_NODE_NAME=pg-1    <-- 修改了这里
      - REPMGR_NODE_NETWORK_NAME=pg-1    <-- 修改了这里
      - REPMGR_PORT_NUMBER=5432

第三个节点的情况类似,为节约篇幅这里不再贴配置文件内容。

启动服务

在每个节点分别使用docker-compose命令启动服务:

docker-compose -f /root/pg-ha/pg.yml up -d

查看repmgr状态,例如当前primary节点是哪一个:

docker exec -ti pg /opt/bitnami/scripts/postgresql-repmgr/entrypoint.sh repmgr -f /opt/bitnami/repmgr/conf/repmgr.conf service status

 ID | Name | Role    | Status    | Upstream | repmgrd | PID | Paused? | Upstream last seen
----+------+---------+-----------+----------+---------+-----+---------+--------------------
 1000 | pg-0 | standby |   running | pg-1     | running | 1   | no      | 1 second(s) ago
 1001 | pg-1 | primary | * running |          | running | 1   | no      | n/a
 1002 | pg-2 | standby |   running | pg-1     | running | 1   | no      | 0 second(s) ago

尝试连接数据库,验证服务是否正常(-U参数很重要):

docker exec -ti pg psql -U myuser -d mydatabase

若需要手工切换standby节点为primary执行下面的命令,需要节点之间配置过免密:

docker exec -it pg /opt/bitnami/scripts/postgresql-repmgr/entrypoint.sh repmgr -f /opt/bitnami/repmgr/conf/repmgr.conf standby switchover

使用SysBench进行数据库性能测试

SysBench是一个基于LuaJIT的可脚本化多线程基准测试工具。它最常用于数据库基准测试,但也可用于创建不涉及数据库服务器的任意复杂工作负载。本文以一个典型测试为例,介绍SysBench的安装和使用。

一、环境信息

被测服务器(192.168.132.167):

  • 4核8线程,32GB,1TB 7200转机械硬盘
  • CentOS 7.9
  • PostgreSQL 9.6.2

测试客户端(192.168.130.152):

  • 4核8线程,32GB,2TB 7200转机械硬盘
  • CentOS 7.9
  • SysBench 1.0.17

网络:

  • 千兆以太局域网

二、准备工作

安装SysBench

不同发行版的Linux按照官网上的说明安装即可:

Debian/Ubuntu

curl -s https://packagecloud.io/install/repositories/akopytov/sysbench/script.deb.sh | sudo bash
sudo apt -y install sysbench

RHEL/CentOS:

curl -s https://packagecloud.io/install/repositories/akopytov/sysbench/script.rpm.sh | sudo bash
sudo yum -y install sysbench

Fedora:

curl -s https://packagecloud.io/install/repositories/akopytov/sysbench/script.rpm.sh | sudo bash
sudo dnf -y install sysbench

验证安装成功:

sysbench --version
sysbench 1.0.17

创建测试用DB

在被测服务器上,创建一个空的专门用于性能测试的库(sbtest),以及相应的用户:

psql -h 192.168.132.167 -U postgres -W
> CREATE USER sbtest WITH PASSWORD 'password';
> CREATE DATABASE sbtest;
> GRANT ALL PRIVILEGES ON DATABASE sbtest TO sbtest;

验证从客户端能够访问到此数据库:

psql -h 192.168.132.167 -U sbtest -W -d sbtest

三、开始测试

SysBench基本用法

SysBench自带了多种数据库性能测试的场景,这些测试场景的名称(testname)列表可以在/usr/share/sysbench/目录下找到,包括bulk_insert, oltp_delete, oltp_insert, oltp_point_select, oltp_read_only, oltp_read_write, oltp_update_index, oltp_update_non_index, oltp_write_only, select_random_pointsselect_random_ranges,从名字可以大致猜测出所代表的场景,例如oltp_read_write代表综合读写的场景,oltp_write_only代表只读的场景。

SysBench执行命令的统一格式是:

sysbench [options]... [testname] [command]

其中testname就是上面列出的测试名称,每个不同的testname有自己的command,但大多数支持的command都是prepareruncleanup。当然我们一般还需要填写options来指定如数据库地址、数据库密码等信息。

下面以oltp_read_write场景为例,介绍最经常使用的几个命令。

准备测试数据(prepare)

prepare的作用是向目标数据库里插入一些随机数据,作为后面真正的测试的数据环境。下面的命令在目标数据库里创建12张表,每张表里添加10万行随机数据:

sysbench oltp_read_write \
    --db-driver=pgsql --pgsql-host=192.168.132.167 --pgsql-port=5432 \
    --pgsql-user=sbtest --pgsql-password=password --pgsql-db=sbtest \
    --table_size=100000 --tables=12 \
    prepare

执行测试(run)

下面的命令执行实际的测试:

sysbench oltp_read_write \
    --db-driver=pgsql --pgsql-host=192.168.132.167 --pgsql-port=5432 \
    --pgsql-user=sbtest --pgsql-password=password --pgsql-db=sbtest \
    --report-interval=5 \
    --table_size=100000 --tables=12 \
    --threads=32 \
    --time=30 --warmup-time=10 \
    run

其中time参数规定了测试执行的时长(30秒),warmup-time参数规定了测试前预热阶段的时长(10秒),threads参数规定了客户端并发请求的线程数量(32线程)。

测试启动后,屏幕上会按指定时间间隔输出当前性能指标:

[ 5s ] thds: 32 tps: 217.41 qps: 4476.39 (r/w/o: 3138.93/895.24/442.22) lat (ms,95%): 467.30 err/s: 0.40 reconn/s: 0.00
[ 10s ] thds: 32 tps: 221.21 qps: 4380.99 (r/w/o: 3076.74/861.64/442.62) lat (ms,95%): 530.08 err/s: 0.00 reconn/s: 0.00
[ 15s ] thds: 32 tps: 301.40 qps: 6071.88 (r/w/o: 4239.86/1229.02/603.01) lat (ms,95%): 297.92 err/s: 0.00 reconn/s: 0.00
[ 20s ] thds: 32 tps: 262.78 qps: 5180.49 (r/w/o: 3626.58/1027.94/525.97) lat (ms,95%): 344.08 err/s: 0.20 reconn/s: 0.00
[ 25s ] thds: 32 tps: 186.81 qps: 3813.27 (r/w/o: 2673.39/765.05/374.83) lat (ms,95%): 590.56 err/s: 0.40 reconn/s: 0.00
[ 30s ] thds: 32 tps: 318.79 qps: 6382.66 (r/w/o: 4466.10/1278.17/638.39) lat (ms,95%): 227.40 err/s: 0.20 reconn/s: 0.00

测试结束后,会输出汇总指标报告:

SQL statistics:
    queries performed:
        read:                            106134
        write:                           30305
        other:                           15169
        total:                           151608
    transactions:                        7575   (252.30 per sec.)
    queries:                             151608 (5049.62 per sec.)
    ignored errors:                      6      (0.20 per sec.)
    reconnects:                          0      (0.00 per sec.)

General statistics:
    total time:                          30.0216s
    total number of events:              7575

Latency (ms):
         min:                                    9.55
         avg:                                  126.79
         max:                                 1051.62
         95th percentile:                      369.77
         sum:                               960450.42

Threads fairness:
    events (avg/stddev):           236.7188/3.16
    execution time (avg/stddev):   30.0141/0.00

清除测试数据

要清除前面执行测试产生的数据,执行下面的命令:

sysbench oltp_read_write \
    --db-driver=pgsql --pgsql-host=192.168.132.167 --pgsql-port=5432 \
    --pgsql-user=sbtest --pgsql-password=password --pgsql-db=sbtest \
    --tables=12 \

注意tables参数需要手工指定并且与prepare时一致,否则下次prepare可能会报错table已存在。

四、参考资料

HBase建表超时问题和解决

问题描述

尝试用下面的命令在hbase shell里创建启用SNAPPY压缩的表,建表命令一直没有返回,直到过了10分钟左右提示错误信息如下:

hbase:012:0> create 'hb_data_2',{NAME=>'DATA', 'COMPRESSION' => 'SNAPPY'}, {NUMREGIONS => 64, SPLITALGO => 'HexStringSplit' }

2022-12-05 19:54:03,386 INFO  [ReadOnlyZKClient-taos-1:2181,taos-2:2181,taos-3:2181@0x0e0d9e3f] zookeeper.ZooKeeper (ZooKeeper.java:close(1422)) - Session: 0x284b286aa820049 closed
2022-12-05 19:54:03,387 INFO  [ReadOnlyZKClient-taos-1:2181,taos-2:2181,taos-3:2181@0x0e0d9e3f-EventThread] zookeeper.ClientCnxn (ClientCnxn.java:run(524)) - EventThread shut down for session: 0x284b286aa820049
ERROR: The procedure 844 is still running
For usage try 'help "create"'
Took 608.2809 seconds
hbase:013:0>

在web-ui里看到:

file

查看日志:

> tail /usr/local/hbase-2.5.1/logs/hbase-root-regionserver-taos-1.log -n200
...
2022-12-05T22:51:11,801 WARN  [RS_OPEN_REGION-regionserver/zookeeper-1:16020-0] handler.AssignRegionHandler: Failed to open region hb_data_2,f0000000,1670241238417.d5ced9638670a54fb4172157ee539d34., will report to master
org.apache.hadoop.hbase.DoNotRetryIOException: Compression algorithm 'snappy' previously failed test. Set hbase.table.sanity.checks to false at conf or table descriptor if you want to bypass sanity checks
        at org.apache.hadoop.hbase.util.TableDescriptorChecker.warnOrThrowExceptionForFailure(TableDescriptorChecker.java:339) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.util.TableDescriptorChecker.checkCompression(TableDescriptorChecker.java:306) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.regionserver.HRegion.openHRegion(HRegion.java:7220) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.regionserver.HRegion.openHRegionFromTableDir(HRegion.java:7183) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.regionserver.HRegion.openHRegion(HRegion.java:7159) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.regionserver.HRegion.openHRegion(HRegion.java:7118) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.regionserver.HRegion.openHRegion(HRegion.java:7074) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.regionserver.handler.AssignRegionHandler.process(AssignRegionHandler.java:147) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.executor.EventHandler.run(EventHandler.java:100) ~[hbase-server-2.5.1.jar:2.5.1]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?]
        at java.lang.Thread.run(Thread.java:829) ~[?:?]
Caused by: org.apache.hadoop.hbase.DoNotRetryIOException: Compression algorithm 'snappy' previously failed test.
        at org.apache.hadoop.hbase.util.CompressionTest.testCompression(CompressionTest.java:90) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.util.TableDescriptorChecker.checkCompression(TableDescriptorChecker.java:300) ~[hbase-server-2.5.1.jar:2.5.1]
        ... 10 more
2022-12-05T22:51:11,801 WARN  [RS_OPEN_REGION-regionserver/zookeeper-1:16020-2] handler.AssignRegionHandler: Failed to open region hb_data_2,e4000000,1670241238417.722ed7fb3b36599c9ea0176774e3f91c., will report to master
org.apache.hadoop.hbase.DoNotRetryIOException: Compression algorithm 'snappy' previously failed test. Set hbase.table.sanity.checks to false at conf or table descriptor if you want to bypass sanity checks
        at org.apache.hadoop.hbase.util.TableDescriptorChecker.warnOrThrowExceptionForFailure(TableDescriptorChecker.java:339) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.util.TableDescriptorChecker.checkCompression(TableDescriptorChecker.java:306) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.regionserver.HRegion.openHRegion(HRegion.java:7220) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.regionserver.HRegion.openHRegionFromTableDir(HRegion.java:7183) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.regionserver.HRegion.openHRegion(HRegion.java:7159) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.regionserver.HRegion.openHRegion(HRegion.java:7118) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.regionserver.HRegion.openHRegion(HRegion.java:7074) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.regionserver.handler.AssignRegionHandler.process(AssignRegionHandler.java:147) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.executor.EventHandler.run(EventHandler.java:100) ~[hbase-server-2.5.1.jar:2.5.1]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?]
        at java.lang.Thread.run(Thread.java:829) ~[?:?]
Caused by: org.apache.hadoop.hbase.DoNotRetryIOException: Compression algorithm 'snappy' previously failed test.
        at org.apache.hadoop.hbase.util.CompressionTest.testCompression(CompressionTest.java:90) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.util.TableDescriptorChecker.checkCompression(TableDescriptorChecker.java:300) ~[hbase-server-2.5.1.jar:2.5.1]
        ... 10 more
2022-12-05T22:51:11,801 INFO  [RS_OPEN_REGION-regionserver/zookeeper-1:16020-1] handler.AssignRegionHandler: Open hb_data_2,,1670241238417.ec86493893d7742525919263abd01c2c.
2022-12-05T22:51:11,802 INFO  [RS_OPEN_REGION-regionserver/zookeeper-1:16020-1] regionserver.HRegion: Closing region hb_data_2,,1670241238417.ec86493893d7742525919263abd01c2c.
2022-12-05T22:51:11,802 INFO  [RS_OPEN_REGION-regionserver/zookeeper-1:16020-1] regionserver.HRegion: Closed hb_data_2,,1670241238417.ec86493893d7742525919263abd01c2c.
...

此时表处于ENABLING状态:

file

尝试删除,但删除命令也会死住直到超时:

hbase:017:0> drop 'hb_data_2'
ERROR: The procedure 2829 is still running
For usage try 'help "drop"'

Took 668.4171 seconds
hbase:018:0>

尝试更换算法为LZ4,仍然提示类似错误:

org.apache.hadoop.hbase.DoNotRetryIOException: Compression algorithm 'lz4' previously failed test. Set hbase.table.sanity.checks to false at conf or table descriptor if you want to bypass sanity checks
        at org.apache.hadoop.hbase.util.TableDescriptorChecker.warnOrThrowExceptionForFailure(TableDescriptorChecker.java:339) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.util.TableDescriptorChecker.checkCompression(TableDescriptorChecker.java:306) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.regionserver.HRegion.openHRegion(HRegion.java:7220) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.regionserver.HRegion.openHRegionFromTableDir(HRegion.java:7183) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.regionserver.HRegion.openHRegion(HRegion.java:7159) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.regionserver.HRegion.openHRegion(HRegion.java:7118) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.regionserver.HRegion.openHRegion(HRegion.java:7074) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.regionserver.handler.AssignRegionHandler.process(AssignRegionHandler.java:147) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.executor.EventHandler.run(EventHandler.java:100) ~[hbase-server-2.5.1.jar:2.5.1]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?]
        at java.lang.Thread.run(Thread.java:829) ~[?:?]
Caused by: org.apache.hadoop.hbase.DoNotRetryIOException: Compression algorithm 'lz4' previously failed test.
        at org.apache.hadoop.hbase.util.CompressionTest.testCompression(CompressionTest.java:90) ~[hbase-server-2.5.1.jar:2.5.1]
        at org.apache.hadoop.hbase.util.TableDescriptorChecker.checkCompression(TableDescriptorChecker.java:300) ~[hbase-server-2.5.1.jar:2.5.1]
        ... 10 more

解决方案

在每个节点的hbase-site.xml里添加如下配置:

<property>
  </name>hbase.table.sanity.checks</name>
  <value>false</value>
</property>

然后重启hbase集群:

> stop-hbase.sh
> start-hbase.sh

重新尝试建表(要换一个表名):

hbase:008:0> create 'hb_data_5',{NAME=>'DATA', 'COMPRESSION' => 'SNAPPY'}, {NUMREGIONS => 64, SPLITALGO => 'HexStringSplit' }

2022-12-06 15:30:37,106 INFO  [main] client.HBaseAdmin (HBaseAdmin.java:postOperationResult(3591)) - Operation: CREATE, Table Name: default:hb_data_5, procId: 13870 completed
Created table hb_data_5
Took 2.2455 seconds
=> Hbase::Table - hb_data_5
hbase:009:0>

但是返现原先的表无法禁用并删除(HBase里删表必须先disable):

hbase:005:0> disable 'hb_data_2'
2022-12-06 15:28:16,517 INFO  [main] client.HBaseAdmin (HBaseAdmin.java:rpcCall(926)) - Started disable of hb_data_2

ERROR: Table hb_data_2 is disabled!

For usage try 'help "disable"'

Took 0.0529 seconds
hbase:006:0> drop 'hb_data_2'

ERROR: Table org.apache.hadoop.hbase.TableNotDisabledException: Not DISABLED; tableName=hb_data_2, state=ENABLING
        at org.apache.hadoop.hbase.master.HMaster.checkTableModifiable(HMaster.java:2786)
        at org.apache.hadoop.hbase.master.procedure.DeleteTableProcedure.prepareDelete(DeleteTableProcedure.java:241)
        at org.apache.hadoop.hbase.master.procedure.DeleteTableProcedure.executeFromState(DeleteTableProcedure.java:90)
        at org.apache.hadoop.hbase.master.procedure.DeleteTableProcedure.executeFromState(DeleteTableProcedure.java:58)
        at org.apache.hadoop.hbase.procedure2.StateMachineProcedure.execute(StateMachineProcedure.java:188)
        at org.apache.hadoop.hbase.procedure2.Procedure.doExecute(Procedure.java:922)
        at org.apache.hadoop.hbase.procedure2.ProcedureExecutor.execProcedure(ProcedureExecutor.java:1648)
        at org.apache.hadoop.hbase.procedure2.ProcedureExecutor.executeProcedure(ProcedureExecutor.java:1394)
        at org.apache.hadoop.hbase.procedure2.ProcedureExecutor.access$1000(ProcedureExecutor.java:75)
        at org.apache.hadoop.hbase.procedure2.ProcedureExecutor$WorkerThread.runProcedure(ProcedureExecutor.java:1960)
        at org.apache.hadoop.hbase.trace.TraceUtil.trace(TraceUtil.java:216)
        at org.apache.hadoop.hbase.procedure2.ProcedureExecutor$WorkerThread.run(ProcedureExecutor.java:1987)
 should be disabled!

For usage try 'help "drop"'

Took 0.0226 seconds

TDengine与Cassandra集群版性能对比测试

本文记录集群环境下TDengine与Cassandra数据库处理时序数据的性能对比,主要考察数据的写入和读取速度(吞吐率)指标。因为InfluxDB社区版不支持集群,所以没有放在一起进行对比。

一、测试环境

服务器

使用三台物理服务器组成数据库集群,配置如下:

CPU 内存 硬盘 网络
Intel i7 4核8线程 32G 2T 7200转机械 千兆网络
操作系统 TDengine Cassandra
CentOS 7.9 2.6 4.0.6

客户端

CPU 内存 硬盘 网络
Intel i7 4核8线程 32G 256G SSD固态 千兆网络
操作系统 JDK JMeter
Windows10 OpenJDK 11 v4

二、测试结果

数据写入性能

测试数据为10个csv文件,每个文件大小约为10GB,包含下面的内容:

  • 每个文件包含50个设备10天的数据
  • 数据频率为1秒
  • 每行数据包含500列
  • 所有数据均为double类型
  • 每个数据文件包含43200050行
  • 每个数据文件大小为167GB

以不同并发数向数据库写入10个csv文件,多次执行取结果平均值得到的结果见下图,其中横轴是并发线程数,纵轴是每秒写入的数据点数(行x列):

数据库配置为单副本的情况:

file

数据库配置为三副本的情况:

file

三副本时,服务器CPU资源占用率情况,纵轴为CPU占用百分比:

file

总体来看,在相同的条件下TDengine的数据写入速度高于Cassandra数据库,副本数量增加对写入速度没有太大影响,并且TDengine服务器CPU资源占用低于Cassandra。

数据读取性能

在两个数据库中分别建表testdb,并预先存入下面的数据:

  • 50个设备10天的数据
  • 数据频率为1秒
  • 每行数据包含500列
  • 所有数据均为double类型

数据读取测试在三副本配置下进行,测试结果如下图,其中横轴是读取的列数,纵轴是每秒读取到的点数(行x列):

file

从图中可以看到,随着抽取的列数增加两个数据库的数据抽取速度都有增加,TDengine在300列达到峰值,并且TDengine的的读取速度基本保持在Cassandra的4到5倍。

三、测试结论

以上测试结果显示,5并发下TDengine的数据写入性能与Cassandra接近,10并发以上则明显优于Cassandra;在数据读取方面TDengine的性能数倍于Cassandra具有压倒优势。

TDengine、InfluxDB与PostgreSQL单机版性能对比测试

本文记录单机环境下TDengine、InfluxDB和PostgreSQL三种数据库,处理时序数据的性能对比,主要考察数据的写入和读取速度。

一、测试环境

服务器

CPU 内存 硬盘 网络
Intel i7 4核8线程 32G 2T 7200转机械 千兆网络
操作系统 TDengine Influxdb PostgreSQL
CentOS 7.9 2.6 2.0 beta 9.0

客户端

CPU 内存 硬盘 网络
Intel i7 4核8线程 32G 256G SSD固态 千兆网络
操作系统 JDK JMeter
Windows10 OpenJDK 11 v4

二、测试数据

测试数据为10个csv文件,每个文件大小约为10GB,包含下面的内容:

  • 每个文件包含100个设备的数据
  • 数据频率为1秒
  • 每行数据包含1000列
  • 所有数据均为double类型

三、测试结果

数据写入

同时向数据库写入10个csv文件,多次执行取结果平均值:

数据库 数据行导入速率(record/s) 数据点导入速率(points/s)
InfluxDB 235 235000
TDengine 750 750208
PostgreSQL 213 213000

file

数据读取

数据读取性能与读取的列数相关,因此分别测试了不同列数情况下三种数据库的读取速度(单并发):

file

四、测试结论

以上初步测试结果显示,在单并发大多数场景下,TDengine的性能相比InfluxDB和PostgreSQL具有较大优势。由于项目时间所限,此次未能对多并发情况下各个数据库的性能进行对比略显遗憾。作为补充,可参考集群环境多并发条件下TDengine与Cassandra的性能对比(链接)。

Anaconda虚拟环境离线迁移

项目中遇到需要在离线环境下为anaconda安装python 3.8虚拟环境的问题,即服务器不能连接互联网的环境,这里记录一下解决方法。

一、环境信息

> uname -a
Linux taos-1 3.10.0-1160.el7.x86_64 #1 SMP Mon Oct 19 16:18:59 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

> conda --version
conda 4.5.4

二、在线安装虚拟环境

如果已经有一个需要迁移的虚拟环境,则可以跳过这一步。

先找一台在线的服务器,安装anaconda所需的虚拟环境,我这里虚拟环境是python 3.8加上pandasnumpymatplotlib等几个常用软件包。

> conda create -n python38 python=3.8
> conda install -n python38 pandas numpy matplotlib

三、备份虚拟环境

使用conda pack命令完整备份虚拟环境:

> conda install -c conda-pack
> conda pack -n python38 -o env_python38_pack.tar.gz

备份得到的是一个.tar.gz压缩包,如果查看压缩包里的内容,目录结构如下图所示:

file

四、离线恢复虚拟环境

离线恢复不需要conda-pack命令,只需要将打好的虚拟环境包复制到目标环境的临时目录,然后解压缩到anaconda的envs目录下即可:

> mkdir /root/anaconda3/envs/python38
> tar zxvf /tmp/env_python38_pack.tar.gz -C /root/anaconda3/envs/python38

输入conda info -e命令可以看到虚拟环境已经添加上了:

file

五、补充说明

1. 是否可以直接备份env下的目录?

一般不可以,按 conda-pack 官网给出的理由如下:

A tool like conda-pack is necessary because conda environments are not relocatable. Simply moving an environment to a different directory can render it partially or completely inoperable. conda-pack addresses this challenge by building archives from original conda package sources and reproducing conda’s own relocation logic.

网上有些文章提到可以直接备份envs/python38目录然后用下面的命令恢复:

> conda create -n [name] --clone [path] --offline

但经过测试当虚拟环境有依赖包时,离线恢复环境会失败,提示下面的错误,原因可能是虚拟环境的依赖包与目标环境pkgs目录下的包版本不同:

RuntimeError('EnforceUnusedAdapter called with url https://repo.anaconda.com/pkgs/main/linux-64/ca-certificates-2022.10.11-h06a4308_0.tar.bz2\nThis command is using a remote connection in offline mode.\n',)

file

观察源anaconda下的pkgs目录可以发现,conda install安装新的虚拟环境时有一些依赖包是安装在pkgs目录下的。那么如果备份时将pkgs目录也备份下来是否可以呢?我认为有以下缺点:

  1. pkgs目录比较大不方便网络传输(一般2GB~3GB);
  2. 恢复时需要处理envspkgs两个目录比较繁琐;
  3. 对目标环境的侵入性比较高,因为会覆盖pkgs下的一些文件。

如果只备份pkgs下的部分新安装的目录,比如按修改时间过滤,则可能遇到类似下面的错误:

FileNotFoundError: [Errno 2] No such file or directory: '/root/anaconda3/pkgs/libstdcxx-ng-11.2.0-h1234567_1/info/index.json'

2. 是否可以用conda export命令备份?

如果目标环境是在线的可以用conda export命令备份,然后用conda create -f恢复。但对离线的情况不适用。

> conda env export -n python38 > /tmp/python38.yml
> cat /tmp/python38.yml
name: python38
channels:
  - defaults
dependencies:
  - ca-certificates=2022.10.11=h06a4308_0
  - certifi=2022.9.24=py38h06a4308_0
  - ld_impl_linux-64=2.38=h1181459_1
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.3=h5eee18b_3
  - openssl=1.1.1q=h7f8727e_0
  - pip=22.2.2=py38h06a4308_0
  - python=3.8.13=haa1d7c7_1
  - readline=8.2=h5eee18b_0
  - setuptools=65.5.0=py38h06a4308_0
  - sqlite=3.39.3=h5082296_0
  - tk=8.6.12=h1ccaba5_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.2.6=h5eee18b_0
  - zlib=1.2.13=h5eee18b_0
prefix: /disk1/anaconda3/envs/python38

# 恢复环境
> conda env create -f python38.yaml

综上,建议用conda pack命令备份anaconda的虚拟环境。

六、参考链接

https://www.anaconda.com/blog/moving-conda-environments
https://conda.github.io/conda-pack/
https://www.jianshu.com/p/adb927fa0091
https://blog.csdn.net/snowing118102/article/details/84319736
https://blog.csdn.net/CREATOR_Jay_xu/article/details/91971446

使用JMeter测试TDengine数据库性能

TDengine 是一款专为时序数据打造的国产数据库产品,它可对TB量级的时序数据进行存储、分析和分发,常备应用于设备运行状态实时监测和预警等场景。

在前面的文章中我们曾经介绍过使用sysbench进行过数据库性能测试,但sysbench聚焦于mysql和postgresql这两种关系数据库,对其他数据库的支持几乎为零(曾支持过oracle但后来取消了)。因此这里我们使用更通用的测试框架JMeter进行性能测试。

环境信息

TDengine服务器 TDengine驱动 jmeter
3.0.0 3.0.2 5.5

添加依赖

将连接TDengine所需的jar包复制到jmeter安装目录下的lib目录:

taos-jdbcdriver-3.0.2.jar
fastjson-1.2.29.jar

准备数据库

在TDengine数据库里预先创建用于测试的库和表:

create database if not exists test vgroups 10 buffer 10;

测试发现create table既可以创建stable也可以创建table,为避免歧义我们严格用create stable来创建超级表:

create stable test.stable01 (time timestamp, col001 double, col002 double, col003 double, \
    col004 double, col005 double, col006 double, col007 double, col008 double, col009 double, \
    col010 double) tags (device varchar(20));

创建子表:

create table test.table01 using stable01 tags ('device01');

测试计划

在GUI模式下创建测试计划(tdengine_test.jmx):
file

执行测试

执行测试(-n表示不启动GUI,-t参数指定测试计划文件,-l指定测试csv格式的结果文件,-e表示测试结束后生成html格式报告,-o表示指定报告的路径名):

jmeter -n -t tdengine_test.jmx -l log1.jtl -e -o report1

测试机作为客户端,与TDengine服务不在同一个局域网内的情况下测试结果:
file

测试机作为客户端,与TDengine服务在同一个局域网内的情况下测试结果:
file

参考资料

https://jmeter.apache.org/usermanual/build-db-test-plan.html
https://jmeter.apache.org/usermanual/generating-dashboard.html
https://docs.taosdata.com/connector/java/
https://docs.taosdata.com/taos-sql/table/