参考文章benchmarksql 测试Postgres-CSDN博客,由于原文中缺少对具体文件的配置,所以加以增补。
1.BenchmarkSQL介绍
BenchmarkSQL是一个开源数据库测试工具,内嵌了TPCC测试脚本,可以对EnterpriseDB、PostgreSQL、MySQL、Oracle以及SQL Server等多种数据库直接进行测试。
BenchmarkSQL的测试场景:
TPC-C(Transaction Processing Performance Council-C)是一个用于评估在线事件处理(OLTP)系统性能的基准测试标准,模拟了一个贸易订单处理系统。它包罗五种重要事件类型:新订单、支付、订单状态查询、库存状态查询和交货。TPC-C 测试评估数据库在处理高并发事件时的吞吐量(tpmC)、响应时间和扩展性,广泛用于数据库性能评测、硬件和软件选择及系统调优。
TPC-C 会模拟一个批发商的货品管理环境。该批发公司有N个仓库,每个仓库供应10个地区,
其中每个地区为3000 名顾客服务。在每个仓库中有10 个终端,每个终端用于一个地区。
在运行的时间,10*N 个终端操纵员会向公司的数据库发送5类请求。由于一个仓库中不可能
存储公司中所有的货品,有一些请求必须发往其他的仓库,因此数据库在逻辑上是分布的。
N 是一个可变的参数值,测试则可以将 N 作为传入参数。
2.BenchmarkSQL测试所需环境
利用 BenchmarkSQL 需要以下环境:
- Java JDK:通常需要 JDK 7 或更高版本。
- PostgreSQL 数据库(或其他支持的数据库,如 MySQL、Oracle):需要安装并配置数据库。
- JDBC 驱动:根据所利用的数据库,BenchmarkSQL 需要相应的 JDBC 驱动(例如 PostgreSQL、MySQL 等)。
- 配置文件:需要根据目的数据库配置 BenchmarkSQL 的配置文件,确保数据库连接正常。
- ant:Ant是Java的生成工具,是Apache的核心项目;需要在安装好JDK后安装。
- R:需要安装R来生成最后的报告。
- benchmarksql-5.0下载地址:https://sourceforge.net/projects/benchmarksql/?source=typ_redirect,该项目利用java编写。
3.如何运行BenchmarkSQL(Windows系统)
在下载好的zip文件里,包含有一个HOW-TO-RUN,我们按照这个来运行。
(1)创建benchmarksql用户和数据库
利用SQL shell
- CREATE USER benchmarksql WITH ENCRYPTED PASSWORD '123123';
- CREATE DATABASE benchmarksql OWNER benchmarksql;
复制代码 (2)编译 BenchmarkSQL 源代码
在BenchmarkSQL源代码目录中包含有一个build文件,利用 ant 来编译:
这会生成须要的 JAR 文件 (BenchmarkSQL-5.0.jar),另有一个build文件夹包含了生成的各种类,后面需要配置这个路径。
(3)创建配置文件
切换到run文件夹,在这里需要配置一些东西,我们测试的事Postgre数据库,所以只需要修改props.pg即可,可以在bash中利用vim功能进行编辑,也可以直接在记事本中编辑。
- cd run
- cp props.pg my_postgres.properties
- vi my_postgres.properties
复制代码 在这个文件里需要修改一下连接,用户另有暗码,确保连接我们的测试数据库。
- db=postgres
- driver=org.postgresql.Driver
- conn=jdbc:postgresql://localhost:5432/benchmarksql
- user=benchmarksql
- password=123123
复制代码 注意: 提供的 props.pg 配置仅用于功能测试。如果要进行基准测试,可以根据需要调整系统配置参数(如数据库巨细、并发连接数等),在后边我们会跑两次测试,一次是基于事件数的测试,另有一次是基于时间的测试。
(4)创建数据库模式和加载初始数据
利用 runDatabaseBuild.sh 脚本来创建数据库模式并加载初始数据:
- ./runDatabaseBuild.sh my_postgres.properties
复制代码 在这里会出现疯狂报错,大部分会提示无法加载主类ExecJDBC大概是LoadData,这里是由于在第二步编译的class类会默认放在build文件夹中,而runDatabaseBuild.sh会调用runSQL.sh和runLoader.sh。问题就出在后两个文件里边,由于在路径中不含有包含类的文件夹,所以需要我们自己来配置。
下边直接放上修改后的文件,文件里实在也会提示,如setmyCP大概利用exit 1这种来进行留白。
- runSQL.sh
- #!/usr/bin/env bash
- # ----
- # Check command line usage
- # ----
- if [ $# -ne 2 ] ; then
- echo "usage: $(basename $0) PROPS_FILE SQL_FILE" >&2
- exit 2
- fi
- # ----
- # Load common functions
- # ----
- source funcs.sh $1
- # ----
- # Determine which SQL file to use.
- #
- # 1) If $2 specifies a file that ends in .sql, we use that.
- # 2) If a file ./sql.<dbtype>/$2.sql exists, we use that.
- # 3) If none of the above, use ./sql.common/$2.sql.
- # ----
- if echo "$2" | grep -q -e '\.sql$' ; then
- ENDS_WITH_SQL=1
- else
- ENDS_WITH_SQL=0
- fi
- if [ -f "${2}" -a $ENDS_WITH_SQL -eq 1 ] ; then
- SQL_FILE="$2"
- else
- if [ -f "./sql.$(getProp db)/${2}.sql" ] ; then
- SQL_FILE="./sql.$(getProp db)/${2}.sql"
- else
- SQL_FILE="./sql.common/${2}.sql"
- if [ ! -f "${SQL_FILE}" ] ; then
- echo "ERROR: Cannot locate SQL file for ${2}" >&2
- exit 1
- fi
- fi
- fi
- # ----
- # Set myCP according to the database type.
- # ----
- myCP="D:/360MoveData/Users/HUAWEI/Desktop/benchmarksql-5.0/build;D:/360MoveData/Users/HUAWEI/Desktop/benchmarksql-5.0/lib/postgres/postgresql-9.3-1102.jdbc41.jar"
- echo "# ------------------------------------------------------------"
- echo "# Loading SQL file ${SQL_FILE}"
- echo "# ------------------------------------------------------------"
- myOPTS="-Dprop=$1"
- myOPTS="$myOPTS -DcommandFile=${SQL_FILE}"
- java -cp "$myCP" $myOPTS ExecJDBC
复制代码- runLoader.sh
- #!/usr/bin/env bash
- if [ $# -lt 1 ] ; then
- echo "usage: $(basename $0) PROPS_FILE [ARGS]" >&2
- exit 2
- fi
- source funcs.sh $1
- shift
- # 手动设置类路径
- myCP="D:/360MoveData/Users/HUAWEI/Desktop/benchmarksql-5.0/build;D:/360MoveData/Users/HUAWEI/Desktop/benchmarksql-5.0/lib/postgres/postgresql-9.3-1102.jdbc41.jar"
- # 如果 funcs.sh 中有设置类路径的函数,可以保留它
- # setCP || exit 1
- java -cp "$myCP" -Dprop=$PROPS LoadData $*
复制代码 这里实在就是设置了myCP,我这里利用的是绝对路径确保程序可以找到文件,实在这里加了两条路径(根据我的文件存储位置),分别是D:/360MoveData/Users/HUAWEI/Desktop/benchmarksql-5.0/build和D:/360MoveData/Users/HUAWEI/Desktop/benchmarksql-5.0/lib/postgres/postgresql-9.3-1102.jdbc41.jar,那么前一个路径用来解决类无法加载的问题,后边的路径设计别的一条报错,即无法调用jdbc,所以在这里把benchmarksql自带的jar文件附加到myCP中,确保可以正常调用。
改完之后可以实行运行一下runDatabaseBuild.sh ,这里可能还是会出现报错,缘故原由还是上边两个,于是我又重写了runDatabaseBuild.sh 文件,给这个文件也加上路径。
- runDatabaseBuild.sh
- #!/bin/sh
- if [ $# -lt 1 ] ; then
- echo "usage: $(basename $0) PROPS [OPT VAL [...]]" >&2
- exit 2
- fi
- PROPS="$1"
- shift
- if [ ! -f "${PROPS}" ] ; then
- echo "${PROPS}: no such file or directory" >&2
- exit 1
- fi
- DB="$(grep '^db=' $PROPS | sed -e 's/^db=//')"
- # 设置 PostgreSQL JDBC 驱动路径
- POSTGRES_JDBC="D:/360MoveData/Users/HUAWEI/Desktop/benchmarksql-5.0/lib/postgres/postgresql-9.3-1102.jdbc41.jar"
- BUILD_DIR="D:/360MoveData/Users/HUAWEI/Desktop/benchmarksql-5.0/build"
- # 包含所有 SQL 文件的路径
- BEFORE_LOAD="tableCreates"
- AFTER_LOAD="indexCreates foreignKeys extraHistID buildFinish"
- # 添加类路径
- CLASSPATH="$BUILD_DIR;$POSTGRES_JDBC"
- # 执行 SQL 加载步骤
- for step in ${BEFORE_LOAD} ; do
- ./runSQL.sh "${PROPS}" $step
- done
- # 执行数据加载器脚本
- java -cp "$CLASSPATH" -Dprop=$PROPS LoadData $*
- # 执行后续的 SQL 步骤
- for step in ${AFTER_LOAD} ; do
- ./runSQL.sh "${PROPS}" $step
- done
复制代码 如许三个文件改完,这一步就应该是可以正常运行了。在这一步中会在数据库中创建相应的表、数据和各种约束,方便下一步进行测试。可以利用navicat来进行查看。
(5)运行基准测试
到这一步终于是可以运行基准测试了,还是在run文件夹中。
- ./runBenchmark.sh my_postgres.properties
复制代码 这里大概率会出现雷同的问题,即无法加载类另有jdbc。同时还会跳出一个新的问题,即无法调用log4j,这里需要先去下载这个文件,然后配置好环境,接下来把log4j的bin文件路径也附加到myCP中去(这里只加给runBenchmark.sh一个文件就行了)。下边是我的runBenchmark.sh,在myCP里边设置了三个路径。
- runBenchmark.sh
- #!/usr/bin/env bash
- if [ $# -ne 1 ] ; then
- echo "usage: $(basename $0) PROPS_FILE" >&2
- exit 2
- fi
- SEQ_FILE="./.jTPCC_run_seq.dat"
- if [ ! -f "${SEQ_FILE}" ] ; then
- echo "0" > "${SEQ_FILE}"
- fi
- SEQ=$(expr $(cat "${SEQ_FILE}") + 1) || exit 1
- echo "${SEQ}" > "${SEQ_FILE}"
- source funcs.sh $1
- myCP="D:/360MoveData/Users/HUAWEI/Desktop/benchmarksql-5.0/build;D:/360MoveData/Users/HUAWEI/Desktop/benchmarksql-5.0/lib/postgres/postgresql-9.3-1102.jdbc41.jar;D:/360MoveData/Users/HUAWEI/Desktop/log4j-1.2.17/apache-log4j-1.2.17/log4j-1.2.17.jar"
- myOPTS="-Dprop=$1 -DrunID=${SEQ}"
- java -cp "$myCP" $myOPTS jTPCC
复制代码 接下来这一步就可以正常运行了,运行效果大概是如许的:
(6)调整基准测试配置
完成初次测试后,可以通过修改 props.pg 文件来扩大基准测试的规模(增长仓库数、终端数和持续时间等)。
要从基于事件数的测试切换到基于时间的测试,可以修改以下参数:
- runTxnsPerTerminal=0 runMins=3
复制代码 重复上一步的操纵,会完成又一次的测试,每次测试生成的数据会包含在my_result_测试时间如许的一个文件夹中。
(7)效果报告
基准测试完成后,可以利用 generateReport.sh 脚本生成报告:
- ./generateReport.sh my_result_directory
复制代码 这里把directory改成具体的时间。该脚本会生成HTML报告,并包含基准测试效果的图表。
我们以上一步runTxnsPerTerminal=0 runMins=3,生成的效果为例,下图是生成的文件:
report里边是如许的:
这里会发现下边还是少了一些东西,这里对应着系统的资源利用。
打开generateReport.sh发现调用的是misc中的os_collector_linux的文件,由于我用的是Windows系统,所以这个文件并不适用,也导致下边的数据另有图片无法正常生成。
这里我也写了一个os_collector_windows,但是这里边涉及要改的东西有点多,由于代码自己就比较“屎”所以就没有继续测试,改完这个还要把props.pg再改一下,仅供参考:
- #props.pg
- osCollectorScript=./misc/os_collector_windows.py
- osCollectorInterval=1
- //osCollectorSSHAddr=user@dbhost
- osCollectorDevices=cpu memory
复制代码- #os_collector_windows
- import psutil
- import time
- import csv
- import sys
- import os
- # ----
- # main
- # ----
- def main(argv):
- global deviceFDs
- global lastDeviceData
- # ----
- # 获取 runID 和采集间隔
- # ----
- runID = int(argv[0])
- interval = float(argv[1])
- # ----
- # 初始化系统信息收集和输出CSV表头
- # ----
- sysInfo = ['run', 'elapsed', ]
- sysInfo += initSystemUsage()
- print(",".join([str(x) for x in sysInfo]))
- # ----
- # 获取指定的设备,设备格式为 net_<devname> 或 blk_<devname>
- # ----
- devices = []
- deviceFDs = {}
- lastDeviceData = {}
- for dev in argv[2:]:
- if dev.startswith('blk_'):
- devices.append(dev)
- elif dev.startswith('net_'):
- devices.append(dev)
- else:
- raise Exception("unknown device type '" + dev + "'")
- # ----
- # 初始化每个设备的使用情况
- # ----
- for dev in devices:
- if dev.startswith('blk_'):
- devInfo = ['run', 'elapsed', 'device', ]
- devInfo += initBlockDevice(dev)
- print(",".join([str(x) for x in devInfo]))
- elif dev.startswith('net_'):
- devInfo = ['run', 'elapsed', 'device', ]
- devInfo += initNetDevice(dev)
- print(",".join([str(x) for x in devInfo]))
- # ----
- # 输出表头
- # ----
- sys.stdout.flush()
- try:
- while True:
- # ----
- # 等待下一个采集间隔,并计算经过的时间(毫秒)
- # ----
- now = time.time()
- elapsed = int((now - float(argv[1])) * 1000.0)
- # ----
- # 收集 CPU 和内存使用信息
- # ----
- sysInfo = [runID, elapsed, ]
- sysInfo += getSystemUsage()
- print(",".join([str(x) for x in sysInfo]))
- # ----
- # 收集每个设备的使用情况
- # ----
- for dev in devices:
- if dev.startswith('blk_'):
- devInfo = [runID, elapsed, dev, ]
- devInfo += getBlockUsage(dev, interval)
- print(",".join([str(x) for x in devInfo]))
- elif dev.startswith('net_'):
- devInfo = [runID, elapsed, dev, ]
- devInfo += getNetUsage(dev, interval)
- print(",".join([str(x) for x in devInfo]))
- # ----
- # 更新下一次采集的时间
- # ----
- time.sleep(interval)
- sys.stdout.flush()
- except KeyboardInterrupt:
- print("")
- return 0
- except IOError as e:
- if e.errno == errno.EPIPE:
- return 0
- else:
- raise e
- def initSystemUsage():
- return ['cpu_percent', 'memory_percent']
- def getSystemUsage():
- # 获取 CPU 和内存使用率
- cpu_percent = psutil.cpu_percent(interval=1)
- memory_percent = psutil.virtual_memory().percent
- return [cpu_percent, memory_percent]
- def initBlockDevice(dev):
- # Windows 没有类似 Linux 的 /sys/block 路径,因此需要使用 psutil 获取磁盘 I/O 信息
- return ['rdiops', 'rdkbps', 'wriops', 'wrkbps']
- def getBlockUsage(dev, interval):
- # 使用 psutil 获取磁盘 I/O 数据
- disk_io = psutil.disk_io_counters()
- rdiops = disk_io.read_count
- wriops = disk_io.write_count
- rd_bytes = disk_io.read_bytes
- wr_bytes = disk_io.write_bytes
- # 计算每秒的读写次数和数据量
- return [rdiops / interval, rd_bytes / interval / 1024.0, wriops / interval, wr_bytes / interval / 1024.0]
- def initNetDevice(dev):
- # 获取网络接口的初始数据
- return ['rxpktsps', 'rxkbps', 'txpktsps', 'txkbps']
- def getNetUsage(dev, interval):
- # 获取网络接口的流量数据
- net_io = psutil.net_io_counters()
- rx_pkts = net_io.bytes_recv
- tx_pkts = net_io.bytes_sent
- # 计算每秒的接收和发送流量
- return [
- rx_pkts / interval,
- rx_pkts / interval / 1024.0,
- tx_pkts / interval,
- tx_pkts / interval / 1024.0
- ]
- if __name__ == '__main__':
- sys.exit(main(sys.argv[1:]))
复制代码 4.报告内容分析
最后来看看报告里边写了什么,我这份报告是以时间为基准测试的(3分钟)。
(1)整体性能:
- Overall tpmC: 124.67
- Overall tpmTotal: 280.67
- tpmC 是核心事件(NEW_ORDER)每分钟处理的事件数。
- tpmTotal 是所有类型的事件(包罗NEW_ORDER、Payment、Order Status、Delivery和Stock Level)每分钟的总事件数。
这里的tpmC为124.67,意味着每分钟大约处理124.67个NEW_ORDER事件,这是TPC-C基准测试中最为关键的性能指标。
(2)理论最大值:
- 理论最大值:12.86 NEW_ORDER 事件/分钟/仓库
TPC-C规范指出,在理想的条件下,每个仓库的NEW_ORDER事件理论最大值为12.86。要到达这个最大值,需要:
- 完善的负载分配(45%的事件是NEW_ORDER)。
- 系统在零响应时间下运行。
现实上,由于系统的延迟和负载不平衡,很难到达这个理论最大值。
(3)现实性能 vs 理论性能:
- 124.67 tpmC 是理论最大值的 969.414%
这意味着系统的现实性能是理论最大值的约9.7倍。也就是说,相较于理想状态,当前的系统性能远超预期,表明该数据库配置在特定负载下的表现非常好。思量到系统的现实响应时间和负载,这种超出理论最大值的表现通常表明系统在某些方面(如硬件配置、数据库优化、并行处理等)非常高效。
(4)生成的两张图:
每分钟事件数量。
事件延迟。
说点题外话:
跑这个测试从十月就开始搞,由于中间有考试另有大作业,一直也没有什么进展,而且中间一直报错也是相称的烦人,终于在十二月初算上搞通了。相称于是成功复现了一篇论文,纵然是最新的benchmarksql也是2016年的版本了,而且说实话也并不好用,各种东西都需要改才能跑的动,当然要让我写一个数据库测试工具,估计也得到猴年马月了,接下来还是要继续努力。
记于2024年12月7日
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |