写性能测试
1.概述
时序数据应用在传感器的典型场景中时,传感器指标数量往往很大,监控数据还是7*24小时不断的高频写入,因此时序数据库需要能持续支持高性能写入。本文使用基于TSBS(Times Series Benchmark Suite)增加支持Uqbar的Uqbar_benchmark作为性能基准测试平台,在不同的场景下,与同样基于关系模型的主流时序数据库TimescaleDB进行写性能对比。
1.1 Uqbar简介
Uqbar是在openGauss内核基础上开发的时序数据库,同时支持关系模型和时序模型,具备高性能、低成本、稳定可靠和开放易用等优点,是专为物联网场景设计的超融合时序数据库。Uqbar可以用于管理海量时序数据,也可以用于OLTP场景管理关系数据,还支持跨时序数据和关系数据的复杂关联查询,为物联网场景提供一站式数据解决方案。
1.2 TimescaleDB简介
TimescaleDB是一款基于PostgreSQL开发的时序数据库,其以PostgreSQL插件的形式存在,可通过安装插件的方式使PostgreSQL具备时序能力而无需重新安装数据库。TimescaleDB提供丰富的时序功能,优秀的压缩能力,能够使用PostgreSQL数据库丰富的生态,支持完整的SQL。
2. 时序数据库性能基准测评平台Uqbar_benchmark
Uqbar_benchmark是基于TSBS(Time Series Benchmark Suite)时序数据性能基准测评平台修改,增加了对Uqbar时序数据库的性能测试支持。TSBS是由Timescale开源的时序性能基准测评平台,包含DevOps和IoT两个典型应用场景。TSBS提供了丰富的参数用于测试不同指标下时序数据库的性能情况,包括采样时间范围,采样间隔,设备数,是否创建索引,连接数等,能完成从数据生成,数据写入,查询生成,查询执行的完整流程,并提供数据写入,查询执行过程的性能数据。因其开源,功能丰富的特点,跨系统跨架构能力以及完善的接口定义,被众多时序数据库厂商作为基准性能测评平台。
3. 测试环境
3.1 硬件配置
Uqbar和TimescaleDB均部署在同一个服务器,而Uqbar_benchmark客户端部署到另外一台和服务端配置相当的机器上。具体硬件配置如下:
3.1.1 ARM+麒麟测试环境机器配置
ARM+KL(Server端) | ARM+KL(Client端) | |
---|---|---|
CPU | Kunpeng-920 * 64 | Kunpeng-920 * 64 |
内存 | 509GB | 509GB |
硬盘 | NVME 1.8T * 4 | NVME 1.8T * 4 |
OS | Kylin Linux Advanced Server release V10 (Sword) ;Linux version 4.19.90-24.4.v2101.ky10.aarch64 | Kylin Linux Advanced Server release V10 (Sword);Linux version 4.19.90-24.4.v2101.ky10.aarch64 |
文件系统 | XFS | XFS |
3.1.2 x86+CentOS 测试环境机器配置
x86+CentOS 7.9(Server端) | x86+CentOS 7.9(Client端) | |
---|---|---|
CPU | Intel(R) Xeon(R) Gold 5320 * 104 | Intel(R) Xeon(R) Gold 5320 * 104 |
内存 | 503GB | 503GB |
硬盘 | NVME 1.8T * 4 | NVME 1.5T * 5 |
OS | CentOS Linux release 7.9.2009 (Core) Linux version 3.10.0-1160.el7.x86_64 | CentOS Linux release 7.9.2009 (Core) Linux version 3.10.0-1160.el7.x86_64 |
文件系统 | XFS | XFS |
3.2 软件配置
Uqbar版本:Uqbar 2.0
TimescaleDB版本:PostgreSQL12.12+TimescaleDB1.7.0
Uqbar_benchmark版本:Uqbar_benchmark2.0
4. 测试场景
4.1 测试模型
本文采用Uqbar_benchmark的IoT和DevOps两个典型应用场景进行对比,IoT应用场景有2张表:readings、diagnostics;其中readings表记录7个测量值,diagnostics表记录3个测量值,类型均为double precision;DevOps应用场景有9张表:cpu、disk、diskio、kernel、mem、net、nginx、postgresl、redis。
具体的表结构可以参考附件。
Uqbar在写入的时候提供了两种数据模型,写性能对比时需要和TimescaleDB的数据模型保持一致,因此在写数据时,需要将Uqbar_benchmak参数use-tags-id设置为true,即会单独建立一张tags表,用于记录设备信息,并在实际记录metrics的表中使用tags_id唯一确定设备信息。
此外,索引中有time-index和partition-index,测试时,需要保证Uqbar和TimescaleDB的索引数目一致,本次测试使用默认索引数目设置,会将Uqbar_benchmak参数time-index和partition-index均设置为true。
4.2 写性能测试指标
在数据库写入时,使用平均每秒中写入的metrics数(Metrics-per-second),作为时序数据库写性能的测试指标。比如针对IoT用例中的readings 表,表中有7个测量值,因此每上报一次数据,会写入一行数据,每行数据中包括7个测量指标(metrics)。
4.3 写性能测试场景
本测试使用如下三个不同场景,进行写性能对比,每个场景的数据规模见表1所示:
表1.场景数据说明
场景一 | 场景二 | 场景三 | |
---|---|---|---|
设备数目scale | 1000 | 10000 | 100,000 |
持续时间 | 10 天 | 1 天 | 3 小时 |
数据间隔 | 10 秒 | 10 秒 | 10 秒 |
IoT metrics总数 | 864,000,000 | 864,000,000 | 1,080,000,000 |
DevOps metrics总数 | 8,726,400,000 | 8,726,400,000 | 10,908,000,000 |
场景中分别选取设备数为1千,1万,10万三个梯队作为对比,时间间隔均选择10s,对应的时间跨度分别为10天,1天以及3小时。由于IoT类型包括两张表,DevOps包括9张表,因此IoT和DevOps对应的总行数和总metrics数值存在较大差异。由表中记录可知,三个场景中的metrics数均超过了1亿,其中scale为10万的DevOps metrics总数达到了109亿。
各项数据的计算公式及关系如下:
-
IoT metrics总数=持续时间/数据间隔*设备数目*10(持续时间需换算成秒)
-
DevOps metrics总数=持续时间/数据间隔*设备数目*101
Uqbar和TimescaleDB均按照下表2设置Uqbar_benchmark的其他写入参数:
表2.写入参数设置
事项 | 取值 |
---|---|
chunk-time | 1天 |
batch-size | 1000 |
workers | x86 150, arm 70 |
索引 | time-index & partition-index |
写入方式 | insert |
注:本次测试中,麒麟arm环境的CPU核数为64,而X86环境的CPU核数为104,因此不同环境使用了不同的workers
5. 测试步骤
写性能测试步骤主要分成两大步骤:生成数据和写入数据。
5.1 生成数据
Uqbar和TimescaleDB生成数据命令如下(本文仅列出IoT用例的生成命令,DevOps用例可将参数--use-case的值iot替换成devops即可):
/Uqbar_benchmark/bin/tsbs_load_uqbar /tsbs_generate_data --use-case=iot --seed=123 --scale=1000 --timestamp-start=2023-08-01T00:00:00Z --timestamp-end=2023-08-11T00:00:00Z --log-interval=10s --format=uqbar | gzip > /data/tsbsdata/scale1000-logInterval10s-iot-data.gz
注:Uqbar_benchmark中,Uqbar和TimescaleDB写入时使用的数据格式是一致的,因此只需要生成一次数据,可以供Uqbar和TimescaleDB两者同时使用。
5.2 写入数据
Uqbar写入数据命令如下:
cat /data/tsbsdata/scale1000-logInterval10s-iot-data.gz | gunzip | /Uqbar_benchmark/bin/tsbs_load_uqbar --host=127.0.0.1 --port=2626 --user=test002 --password=gauss@123 --db-name=uqbar_iot_10s_1000scale --workers=150 --use-insert=true --batch-size=1000 --partition-index=true --time-index=true --no-flow-control --use-tags-id=true --chunkgroup-duration=1d
TimescaleDB写入数据命令如下:
cat /data/tsbsdata/scale1000-logInterval10s-iot-data.gz | gunzip | /Uqbar_benchmark/bin/tsbs_load_uqbar --host=127.0.0.1 --port=2626 --user=test002 --password=gauss@123 --db-name=uqbar_iot_10s_1000scale --workers=150 --use-insert=true --batch-size=1000 --partition-index=true --time-index=true --no-flow-control --version=1 --chunk-time=24h
写入命令执行完成之后,在写入命令输出中会显示本次的写性能测试数值。
6. 写性能对比结果和分析
6.1 CentOS x86环境下的写性能对比
具体的对比数据如下:
表3. x86环境下写性能对比
注: Uqbar相对于TimescaleDB的增长率计算公式:(Uqbar的写性能数值-TimescaleDB的写性能数值)/ TimescaleDB的写性能数值
图 1. x86环境下不同场景的写性能对比(metrics/sec.数值越大越好)
由表3和图1可以看到,在x86环境上,IoT和DevOps两种典型应用场景中,Uqbar的写性能均优于TimescaleDB,且高于TimescaledB 22%以上,最大达到TimescaleDB的8.55倍。详细如下:
-
IoT应用场景,Uqbar写入性能相比TimescaleDB写入性能的增长率最低为48.6%,最高为 113.87%;
-
DevOps应用场景,Uqbar写入性能相比TimescaleDB写入性能的增长率最低为TimescaleDB 22%,最高为755.34%;
Uqbar在三种场景下的写速率在DevOps用例中最高达到了809万metrics/second,IoT用例中最高为511万metrics/second。
6.2 麒麟arm环境下的写性能对比
具体的对比数据如下:
表4. 麒麟arm环境下写性能对比数据
图 2.arm环境下不同场景的写性能对比(metrics/sec.数值越大越好)
由表4和图3可以看到,在麒麟arm环境中,IoT和DevOps两种典型应用场景中,Uqbar的写性能也均优于TimescaleDB,且高于TimescaledB 4.61%以上,最大达到TimescaleDB的4.6倍。详细如下:
-
IoT应用场景,Uqbar写入性能相比TimescaleDB写入性能的增长率最低为4.61%,最高为 64.07%;
-
DevOps应用场景,Uqbar写入性能相比TimescaleDB写入性能的增长率最低为TimescaleDB 21.8%,最高为461.77%;
其中,Uqbar在三种场景下的写速率在DevOps用例中最高达到了812万metrics/second。
此外由6.1中表3和6.2中表4还可以得知,随着设备数目的增大,从1万增长到10万时,Uqbar和TimescaleDB写性能均有不同幅度的下降,但是TimescaleDB在DevOps场景中下降相对更明显。
7. 总结
和同样基于关系模型的主流时序数据库TimescaleDB相比,Uqbar的写性能在不同平台不同场景中,均有不同程度的领先优势。Uqbar的写性能在DevOps用例中最高能达到每秒写入800多万指标。随着scale的增大,DevOps场景下Uqbar相对于TimescaleDB的增长率最大达到了7.55倍。
附件
Uqbar_benchmark表结构
当数据模型一致,即use-tags-id为true时的表结构如下:
iot类型
perftest_copywrite004=# \d readings;
Table "public.readings"
Column | Type | Modifiers
------------------+--------------------------+---------------
time | timestamp with time zone | default now()
tags_id | integer |
latitude | double precision |
longitude | double precision |
elevation | double precision |
velocity | double precision |
heading | double precision |
grade | double precision |
fuel_consumption | double precision |
additional_tags | text |
Indexes:
"readings_tags_id_time_idx" btree (tags_id, "time" DESC) LOCAL TABLESPACE pg_default
"readings_time_idx" btree ("time" DESC) LOCAL TABLESPACE pg_default
Partition By RANGE(time) Subpartition By HASH(tags_id)
Number of partitions: 3 (View pg_partition to check each partition range.)
Number of subpartitions: 9 (View pg_partition to check each subpartition range.)
perftest_copywrite004=# \d diagnostics
Table "public.diagnostics"
Column | Type | Modifiers
-----------------+--------------------------+---------------
time | timestamp with time zone | default now()
tags_id | integer |
fuel_state | double precision |
current_load | double precision |
status | double precision |
additional_tags | text |
Indexes:
"diagnostics_tags_id_time_idx" btree (tags_id, "time" DESC) LOCAL TABLESPACE pg_default
"diagnostics_time_idx" btree ("time" DESC) LOCAL TABLESPACE pg_default
Partition By RANGE(time) Subpartition By HASH(tags_id)
Number of partitions: 3 (View pg_partition to check each partition range.)
Number of subpartitions: 9 (View pg_partition to check each subpartition range.)
uqbar_database=> \d tags
Table "public.tags"
Column | Type | Modifiers
---------------------+---------+---------------------------------------------------
id | integer | not null default nextval('tags_id_seq'::regclass)
hostname | text |
region | text |
datacenter | text |
rack | text |
os | text |
arch | text |
team | text |
service | text |
service_version | text |
service_environment | text |
Indexes:
"tags_pkey" PRIMARY KEY, btree (id) TABLESPACE pg_default
"uniq1" UNIQUE, btree (hostname, region, datacenter, rack, os, arch, team, service, service_version, service_environment) TABLESPACE pg_default
"tags_hostname_idx" btree (hostname) TABLESPACE pg_default
devops类型
uqbar_database=> \d cpu;
Table "public.cpu"
Column | Type | Modifiers
------------------+--------------------------+---------------
time | timestamp with time zone | default now()
tags_id | integer |
usage_user | double precision |
usage_system | double precision |
usage_idle | double precision |
usage_nice | double precision |
usage_iowait | double precision |
usage_irq | double precision |
usage_softirq | double precision |
usage_steal | double precision |
usage_guest | double precision |
usage_guest_nice | double precision |
additional_tags | text |
Indexes:
"cpu_tags_id_time_idx" btree (tags_id, "time" DESC) LOCAL TABLESPACE pg_default
"cpu_time_idx" btree ("time" DESC) LOCAL TABLESPACE pg_default
Partition By RANGE(time) Subpartition By HASH(tags_id)
Number of partitions: 14 (View pg_partition to check each partition range.)
Number of subpartitions: 42 (View pg_partition to check each subpartition range.)
uqbar_database=> \d disk
Table "public.disk"
Column | Type | Modifiers
-----------------+--------------------------+---------------
time | timestamp with time zone | default now()
tags_id | integer |
total | double precision |
free | double precision |
used | double precision |
used_percent | double precision |
inodes_total | double precision |
inodes_free | double precision |
inodes_used | double precision |
additional_tags | text |
Indexes:
"disk_tags_id_time_idx" btree (tags_id, "time" DESC) LOCAL TABLESPACE pg_default
"disk_time_idx" btree ("time" DESC) LOCAL TABLESPACE pg_default
Partition By RANGE(time) Subpartition By HASH(tags_id)
Number of partitions: 14 (View pg_partition to check each partition range.)
Number of subpartitions: 42 (View pg_partition to check each subpartition range.)
uqbar_database=> \d diskio
Table "public.diskio"
Column | Type | Modifiers
-----------------+--------------------------+---------------
time | timestamp with time zone | default now()
tags_id | integer |
reads | double precision |
writes | double precision |
read_bytes | double precision |
write_bytes | double precision |
read_time | double precision |
write_time | double precision |
io_time | double precision |
additional_tags | text |
Indexes:
"diskio_tags_id_time_idx" btree (tags_id, "time" DESC) LOCAL TABLESPACE pg_default
"diskio_time_idx" btree ("time" DESC) LOCAL TABLESPACE pg_default
Partition By RANGE(time) Subpartition By HASH(tags_id)
Number of partitions: 14 (View pg_partition to check each partition range.)
Number of subpartitions: 42 (View pg_partition to check each subpartition range.)
uqbar_database=> \d mem
Table "public.mem"
Column | Type | Modifiers
-------------------+--------------------------+---------------
time | timestamp with time zone | default now()
tags_id | integer |
total | double precision |
available | double precision |
used | double precision |
free | double precision |
cached | double precision |
buffered | double precision |
used_percent | double precision |
available_percent | double precision |
buffered_percent | double precision |
additional_tags | text |
Indexes:
"mem_tags_id_time_idx" btree (tags_id, "time" DESC) LOCAL TABLESPACE pg_default
"mem_time_idx" btree ("time" DESC) LOCAL TABLESPACE pg_default
Partition By RANGE(time) Subpartition By HASH(tags_id)
Number of partitions: 14 (View pg_partition to check each partition range.)
Number of subpartitions: 42 (View pg_partition to check each subpartition range.)
uqbar_database=> \d nginx
Table "public.nginx"
Column | Type | Modifiers
-----------------+--------------------------+---------------
time | timestamp with time zone | default now()
tags_id | integer |
accepts | double precision |
active | double precision |
handled | double precision |
reading | double precision |
requests | double precision |
waiting | double precision |
writing | double precision |
additional_tags | text |
Indexes:
"nginx_tags_id_time_idx" btree (tags_id, "time" DESC) LOCAL TABLESPACE pg_default
"nginx_time_idx" btree ("time" DESC) LOCAL TABLESPACE pg_default
Partition By RANGE(time) Subpartition By HASH(tags_id)
Number of partitions: 14 (View pg_partition to check each partition range.)
Number of subpartitions: 42 (View pg_partition to check each subpartition range.)
uqbar_database=> \d kernel
Table "public.kernel"
Column | Type | Modifiers
------------------+--------------------------+---------------
time | timestamp with time zone | default now()
tags_id | integer |
boot_time | double precision |
interrupts | double precision |
context_switches | double precision |
processes_forked | double precision |
disk_pages_in | double precision |
disk_pages_out | double precision |
additional_tags | text |
Indexes:
"kernel_tags_id_time_idx" btree (tags_id, "time" DESC) LOCAL TABLESPACE pg_default
"kernel_time_idx" btree ("time" DESC) LOCAL TABLESPACE pg_default
Partition By RANGE(time) Subpartition By HASH(tags_id)
Number of partitions: 14 (View pg_partition to check each partition range.)
Number of subpartitions: 42 (View pg_partition to check each subpartition range.)
uqbar_database=> \d net
Table "public.net"
Column | Type | Modifiers
-----------------+--------------------------+---------------
time | timestamp with time zone | default now()
tags_id | integer |
bytes_sent | double precision |
bytes_recv | double precision |
packets_sent | double precision |
packets_recv | double precision |
err_in | double precision |
err_out | double precision |
drop_in | double precision |
drop_out | double precision |
additional_tags | text |
Indexes:
"net_tags_id_time_idx" btree (tags_id, "time" DESC) LOCAL TABLESPACE pg_default
"net_time_idx" btree ("time" DESC) LOCAL TABLESPACE pg_default
Partition By RANGE(time) Subpartition By HASH(tags_id)
Number of partitions: 14 (View pg_partition to check each partition range.)
Number of subpartitions: 42 (View pg_partition to check each subpartition range.)
uqbar_database=> \d postgresl
Table "public.postgresl"
Column | Type | Modifiers
-----------------+--------------------------+---------------
time | timestamp with time zone | default now()
tags_id | integer |
numbackends | double precision |
xact_commit | double precision |
xact_rollback | double precision |
blks_read | double precision |
blks_hit | double precision |
tup_returned | double precision |
tup_fetched | double precision |
tup_inserted | double precision |
tup_updated | double precision |
tup_deleted | double precision |
conflicts | double precision |
temp_files | double precision |
temp_bytes | double precision |
deadlocks | double precision |
blk_read_time | double precision |
blk_write_time | double precision |
additional_tags | text |
Indexes:
"postgresl_tags_id_time_idx" btree (tags_id, "time" DESC) LOCAL TABLESPACE pg_default
"postgresl_time_idx" btree ("time" DESC) LOCAL TABLESPACE pg_default
Partition By RANGE(time) Subpartition By HASH(tags_id)
Number of partitions: 14 (View pg_partition to check each partition range.)
Number of subpartitions: 42 (View pg_partition to check each subpartition range.)
uqbar_database=> \d redis
Table "public.redis"
Column | Type | Modifiers
-----------------------------+--------------------------+---------------
time | timestamp with time zone | default now()
tags_id | integer |
uptime_in_seconds | double precision |
total_connections_received | double precision |
expired_keys | double precision |
evicted_keys | double precision |
keyspace_hits | double precision |
keyspace_misses | double precision |
instantaneous_ops_per_sec | double precision |
instantaneous_input_kbps | double precision |
instantaneous_output_kbps | double precision |
connected_clients | double precision |
used_memory | double precision |
used_memory_rss | double precision |
used_memory_peak | double precision |
used_memory_lua | double precision |
rdb_changes_since_last_save | double precision |
sync_full | double precision |
sync_partial_ok | double precision |
sync_partial_err | double precision |
pubsub_channels | double precision |
pubsub_patterns | double precision |
latest_fork_usec | double precision |
connected_slaves | double precision |
master_repl_offset | double precision |
repl_backlog_active | double precision |
repl_backlog_size | double precision |
repl_backlog_histlen | double precision |
mem_fragmentation_ratio | double precision |
used_cpu_sys | double precision |
used_cpu_user | double precision |
used_cpu_sys_children | double precision |
used_cpu_user_children | double precision |
additional_tags | text |
Indexes:
"redis_tags_id_time_idx" btree (tags_id, "time" DESC) LOCAL TABLESPACE pg_default
"redis_time_idx" btree ("time" DESC) LOCAL TABLESPACE pg_default
Partition By RANGE(time) Subpartition By HASH(tags_id)
Number of partitions: 14 (View pg_partition to check each partition range.)
Number of subpartitions: 42 (View pg_partition to check each subpartition range.)