GlusterFS复制卷迁移至分布式复制卷

GlusterFS是广泛使用的去中心化的分布式文件系统,本文主要介绍GlusterFS里创建不同类型卷的方法,以及如何对已有卷转换类型。

GlusterFS没有提供专门的命令行选项指定卷类型,而是根据根据副本(replica)数量与块(brick)数量关系自动进行判断。

环境信息

CentOS 7.9
GlusterFS 9.4

创建复制卷(Replicate)

GlusterFS的复制卷可以类比于RAID1。当一个volume的replica是brick的整数倍(包含1倍)时创建出来的卷就是复制模式:

> gluster volume create volume1 replica 2 server1:/exp1 server2:/exp2
volume create: volume1: success: please start the volume to access data

> gluster volume start volume1
volume start: volume1: success

> gluster volume info
Volume Name: volume1
Type: Replicate
...

文件在复制卷上的分布,图片来自GlusterFS官方文档:

file

创建分布式复制卷(Distributed-Replicate)

GlusterFS的分布式复制卷可以类比于RAID10当brick数量是replica的整数倍(不含1倍)时,创建出来的卷是分布式复制模式:

> gluster volume create volume1 replica 2 server1:/exp1 server2:/exp2 server3:/exp3 server4:/exp4
volume create: volume1: success: please start the volume to access data

> gluster volume start volume1
volume start: volume1: success

> gluster volume info
Volume Name: volume1
Type: Distributed-Replicate
...

文件在分布式复制卷上的分布,图片来自GlusterFS官方文档:

file

将复制卷转为分布式复制卷

假设现在的复制卷是4节点4副本,要将其转换为4节点2副本。转换方法是先收缩位2节点2副本,再扩展为4节点2副本。

注意:收缩副本后,确保不用的旧目录(前面例子中/exp3和/exp4)一定要手工删除,不要直接使用这些目录进行扩容,否则会出现重复文件问题。

> gluster volume info
Volume Name: volume1
Type: Replicate
Volume ID: 38f39495-6678-40c1-9aa7-36399f6285cc
Status: Started
Snapshot Count: 0
Number of Bricks: 1 x 4 = 4
...

# 收缩(对replicate卷需要加force参数)
> gluster volume remove-brick volume1 replica 2 server3:/exp3 server4:/exp4 force

> gluster volume info
Volume Name: volume1
Type: Replicate
Volume ID: 38f39495-6678-40c1-9aa7-36399f6285cc
Status: Started
Snapshot Count: 0
Number of Bricks: 1 x 2 = 2
...

# 扩展(对replicate卷需要加force参数)
> gluster volume add-brick volume1 replica 2 server3:/exp3 server4:/exp4 force

> gluster volume info
Volume Name: volume1
Type: Distributed-Replicate
Volume ID: 38f39495-6678-40c1-9aa7-36399f6285cc
Status: Started
Snapshot Count: 0
Number of Bricks: 1 x 2 = 2
...

# 平衡
> gluster volume rebalance volume1 start
volume rebalance: volume1: success: Rebalance on volume1 has been started successfully. Use rebalance status command to check status of the rebalance process.
ID: 58799899-5f18-4cfb-98d9-96ab91ce6739

> gluster volume rebalance volume1 status
Node     ebalanced-files          size       scanned      failures       skipped               status  run time in h:m:s
---------      -----------   -----------   -----------   -----------   -----------         ------------     --------------
server2                2        0Bytes             5             0             0            completed        0:00:00
server3                0        0Bytes             5             0             0            completed        0:00:00
server4                0        0Bytes             5             0             0            completed        0:00:00
localhost              0        0Bytes             5             0             0            completed        0:00:00

当文件数量较多时,平衡所需的时间可能会很长,但这并不影响新文件写入glusterfs。下面是某个生产环境做平衡的过程,大约需要半个月:

         Node Rebalanced-files          size       scanned      failures       skipped               status  run time in h:m:s
    ---------      -----------   -----------   -----------   -----------   -----------         ------------     --------------
 10.102.9.127           496771         7.2TB       3340719             0             0          in progress       68:05:36
 10.102.9.128           403741         1.2TB       3433837             0             0          in progress       68:05:36
 10.102.9.132                1       28Bytes        675030            11         34918          in progress       68:05:31
 10.102.9.133                0        0Bytes        673981             0         12583          in progress       68:05:32
 10.102.9.134               12        93.2MB        681215             0         15036          in progress       68:05:34
    localhost           362702       677.4GB       3143169             0             0          in progress       68:05:36
Estimated time left for rebalance to complete :      322:37:43

遇到的问题

1、ls时发现有部分文件和目录名重复:

> ls -l /my/glusterfs
drwxr-xr-x 188 k2data k2data  4096 1月  22 10:14 ALaS
drwxr-xr-x 517 k2data k2data  4096 1月  24 15:19 AoF
drwxr-xr-x 517 k2data k2data  4096 1月  24 15:19 AoF
drwxr-xr-x 358 k2data k2data  4096 1月  22 09:23 BaiGL
drwxr-xr-x 328 k2data k2data  4096 1月  21 20:40 BaiS
drwxr-xr-x 328 k2data k2data  4096 1月  21 20:40 BaiS
...

此问题是由于有一个节点的brick目录没有清除,之前认为只要重命名即可(保留文件以便万一需要时恢复),但发现glusterfs似乎会识别到重命名后的文件,观察其下面的.glusterfs隐藏目录里的内容,修改时间一直在变。

后用gluster replace-brick命令从glusterfs里剔除此节点替换为另一个新服务器后问题消失。

2、有些目录权限丢失
例如下面的目录,一些目录权限变成了000,并且owner从k2data变成了root,导致相关程序报错Permission denied

> ls -l /my/glusterfs
drwxr-xr-x 273 k2data k2data  4096 1月  21 21:44 DiL
d--------- 315 root   root    4096 1月  21 21:46 FanC
drwxr-xr-x 338 k2data k2data  4096 1月  27 10:15 FengDS
...

出现原因不明,解决方法是手工将这些目录权限改过来(从000改为755):

ls -l | grep d--- | awk '{print $9}' | xargs chown k2data:k2data -R
ls -l | grep d--- | awk '{print $9}' | xargs chmod 755 -R

3、文件信息显示为问号

ls -l /my/glusterfs/data/172.17.23.230/20230629
ls: 无法访问/my/gluster/repo_FengDS_hfqfile/data/172.17.23.230/20230629/_zipped.zip: 没有那个文件或目录
总用量 0
-????????? ? ? ? ?            ? _zipped.zip

经排查是因为这个文件所在的物理目录不正确,例如文件本应在brick1、2、3,但实际被放在了brick4、5、6,如果手工移动到正确位置则问题消失。

产生这个问题的原因暂时未知,猜测1)可能是扩容过程中跨子volume的节点没有清空导致 2) rebalance过程导致(由于文件很多,需要半个月时间才能完成)。

4、部分文件脑裂

> gluster volume heal volumn1 info
Brick server1:/my/glusterfs
<gfid:c22fa080-f917-4862-b9d9-a12aff40d392> - Is in split-brain
Status: Connected
Number of entries: 1

Brick server2:/my/glusterfs
<gfid:a60abad3-e42a-49ba-8b8e-463ea656c9cb>
<gfid:12c6e0ae-638c-4d59-96ff-c35a3bda3e07> - Is in split-brain
<gfid:f429f4ad-d935-48a0-bbfa-23407d433e8c> - Is in split-brain
<gfid:6e99bf54-07ab-4c55-92c1-0f5b297079a5> - Is in split-brain
/k2box/apps/com.my.import03/job - Is in split-brain
<gfid:5235e32e-4a7c-457a-ba08-3b7ef61a77e7> - Is in split-brain
<gfid:ba80b4bf-cff3-4dd2-b6a5-cba656431641> - Is in split-brain
/k2box/apps/com.my.import03/job/3471565
<gfid:493df16c-6c91-485a-b6f1-87208395b8f4>
<gfid:afd49274-d9e4-48b2-8814-53fd76fce327> - Is in split-brain
Status: Connected
Number of entries: 10
...

自动修复:

> gluster volume heal volumn1

5、有一个brick的统计信息异常
gluster volume top命令查询当前各brick打开文件数量,有一个brick的数量明显异常(18446744073709534192个):

> gluster volume top volume1 open | grep fds -B 2
Brick: 10.102.9.126:/my/glusterfs
Current open fds: 0, Max open fds: 1767, Max openfd time: 2023-08-16 04:08:46.556300 +0000
--
186883          /k2box/repos/repo_XuTuan_1sec/.repo/meta.settings
Brick: 10.102.9.134:/my/glusterfs
Current open fds: 18446744073709534192, Max open fds: 18446744073709551615, Max openfd time: 2024-02-01 20:44:34.245111 +0000
--
882             /k2box/apps/com.acme.import01/job/3527701/output/log/stats.log
Brick: 10.102.9.133:/my/glusterfs
Current open fds: 0, Max open fds: 400, Max openfd time: 2024-02-01 13:19:06.438338 +0000
--
2124            /k2box/repos/repo_ZhengXBQ_hive/.repo/batch.settings
Brick: 10.102.9.132:/my/glusterfs
Current open fds: 0, Max open fds: 400, Max openfd time: 2024-02-01 13:19:06.439036 +0000
...

重启glusterd服务或重新mount都无效。

参考资料

https://docs.gluster.org/en/latest/Administrator-Guide/Setting-Up-Volumes

NFS挂载意外断开导致java启动docker容器失败

一、环境信息

NFS客户端:
银河麒麟V10,ARM架构
Linux version 4.19.90-24.4.v2101.ky10.aarch64

NFS服务器:
Windows Server 2022 Standard

二、问题现象

通过java api启动docker容器时发现容器没有起来,查询后台日志信息如下:

[2024-01-14 15:10:01.141] [INFO] Starting docker container
[2024-01-14 15:10:02.117] [INFO] Recoverable I/O exception (java.net.SocketException) caught when processing request to {}->http://192.168.101.80:22375
[2024-01-14 15:10:02.152] [INFO] Docker client connected successfully
[2024-01-14 15:10:07.154] [INFO] Recoverable I/O exception (java.net.SocketException) caught when processing request to {}->http://192.168.101.80:22375
[2024-01-14 15:10:07.220] [INFO] Docker client connected successfully
[2024-01-14 15:10:12.221] [INFO] Recoverable I/O exception (java.net.SocketException) caught when processing request to {}->http://192.168.101.80:22375
[2024-01-14 15:10:12.282] [INFO] Docker client connected successfully
[2024-01-14 15:10:17.284] [INFO] Recoverable I/O exception (java.net.SocketException) caught when processing request to {}->http://192.168.101.80:22375
[2024-01-14 15:10:17.354] [INFO] Docker client connected successfully
[2024-01-14 15:10:22.356] [INFO] Recoverable I/O exception (java.net.SocketException) caught when processing request to {}->http://192.168.101.80:22375
[2024-01-14 15:10:22.391] [INFO] Docker client connected successfully

初步怀疑磁盘空间被占满导致,执行df命令时发现没有响应:

[root@192 storage]# df -h
(卡死在这里没有任何输出,只能按ctrl+c退出)

三、问题解决

由于前几天出现过df命令无响应的情况,所以马上意识到可能是远程nfs挂载的远程磁盘有问题,经过询问果然是有人改了远程nfs服务器的ip地址。

让运维恢复了nfs服务器的地址,然后重启一下rpcbind服务:

systemctl restart rpcbind

如果以上命令无效,可以尝试强制卸载nfs路径:

umount -f -l /mnt/hdfs/k2repos/CMSFTPServer

此时df命令恢复正常:

[root@192 storage]# df -h | grep -v docker
Filesystem                    Size  Used Avail Use% Mounted on
devtmpfs                       32G     0   32G   0% /dev
tmpfs                          32G     0   32G   0% /dev/shm
tmpfs                          32G  546M   31G   2% /run
tmpfs                          32G     0   32G   0% /sys/fs/cgroup
/dev/mapper/klas-root         4.0T  207G  3.8T   6% /
tmpfs                          32G     0   32G   0% /tmp
/dev/vda2                    1014M  218M  797M  22% /boot
/dev/vda1                     599M  6.5M  593M   2% /boot/efi
192.168.101.251:/home/data   1022G   15G 1007G   2% /mnt/hdfs/k2repos
10.144.136.169:/CMSFTPServer  7.3T  6.1T  1.3T  83% /mnt/hdfs/k2repos/CMSFTPServer
tmpfs                         6.3G     0  6.3G   0% /run/user/0

日志显示java启动docker容器也恢复正常:

[2024-01-14 16:00:01.149] [INFO] Starting docker container
[2024-01-14 16:00:13.955] [INFO] Container started, status code: 1

四、预防措施

为了预防类似情况发生时严重影响业务,可以考虑以soft模式挂载nfs服务(默认为hard模式),这样当nfs服务不可用时进程不会hang住,而是返回一个错误:

mount -o soft -t nfs 10.144.136.169:/CMSFTPServer /mnt/hdfs/k2repos/CMSFTPServer

实测以soft模式启动后,若nfs服务不可用,docker容器能够正常启动,但此时df命令仍然无响应,此时用df -x nfs4命令可以查看除nfs以外的磁盘。

但soft模式也有代价:

Since nfsv4 performs open/lock operations that have their ordering strictly enforced by the server, the options intr and soft cannot be safely used. hard nfsv4 mounts are strongly recommended.

五、参考链接

时钟导致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的性能对比(链接)。