BenchmarkSQL-5.0测试Postgre数据库

  金牌会员 | 2025-1-22 19:56:31 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 855|帖子 855|积分 2565

参考文章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
  1. CREATE USER benchmarksql WITH ENCRYPTED PASSWORD '123123';
  2. CREATE DATABASE benchmarksql OWNER benchmarksql;
复制代码
(2)编译 BenchmarkSQL 源代码

在BenchmarkSQL源代码目录中包含有一个build文件,利用 ant 来编译:
  1. cd benchmarksql
  2. ant
复制代码
这会生成须要的 JAR 文件 (BenchmarkSQL-5.0.jar),另有一个build文件夹包含了生成的各种类,后面需要配置这个路径。
(3)创建配置文件

切换到run文件夹,在这里需要配置一些东西,我们测试的事Postgre数据库,所以只需要修改props.pg即可,可以在bash中利用vim功能进行编辑,也可以直接在记事本中编辑。
  1. cd run
  2. cp props.pg my_postgres.properties
  3. vi my_postgres.properties
复制代码
在这个文件里需要修改一下连接,用户另有暗码,确保连接我们的测试数据库。
  1. db=postgres
  2. driver=org.postgresql.Driver
  3. conn=jdbc:postgresql://localhost:5432/benchmarksql
  4. user=benchmarksql
  5. password=123123
复制代码
注意: 提供的 props.pg 配置仅用于功能测试。如果要进行基准测试,可以根据需要调整系统配置参数(如数据库巨细、并发连接数等),在后边我们会跑两次测试,一次是基于事件数的测试,另有一次是基于时间的测试。
(4)创建数据库模式和加载初始数据

利用 runDatabaseBuild.sh 脚本来创建数据库模式并加载初始数据:
  1. ./runDatabaseBuild.sh my_postgres.properties
复制代码
在这里会出现疯狂报错,大部分会提示无法加载主类ExecJDBC大概是LoadData,这里是由于在第二步编译的class类会默认放在build文件夹中,而runDatabaseBuild.sh会调用runSQL.sh和runLoader.sh。问题就出在后两个文件里边,由于在路径中不含有包含类的文件夹,所以需要我们自己来配置。
下边直接放上修改后的文件,文件里实在也会提示,如setmyCP大概利用exit 1这种来进行留白。
  1. runSQL.sh
  2. #!/usr/bin/env bash
  3. # ----
  4. # Check command line usage
  5. # ----
  6. if [ $# -ne 2 ] ; then
  7.     echo "usage: $(basename $0) PROPS_FILE SQL_FILE" >&2
  8.     exit 2
  9. fi
  10. # ----
  11. # Load common functions
  12. # ----
  13. source funcs.sh $1
  14. # ----
  15. # Determine which SQL file to use.
  16. #
  17. # 1) If $2 specifies a file that ends in .sql, we use that.
  18. # 2) If a file ./sql.<dbtype>/$2.sql exists, we use that.
  19. # 3) If none of the above, use ./sql.common/$2.sql.
  20. # ----
  21. if echo "$2" | grep -q -e '\.sql$' ; then
  22.     ENDS_WITH_SQL=1
  23. else
  24.     ENDS_WITH_SQL=0
  25. fi
  26. if [ -f "${2}" -a $ENDS_WITH_SQL -eq 1 ] ; then
  27.     SQL_FILE="$2"
  28. else
  29.     if [ -f "./sql.$(getProp db)/${2}.sql" ] ; then
  30.         SQL_FILE="./sql.$(getProp db)/${2}.sql"
  31.     else
  32.             SQL_FILE="./sql.common/${2}.sql"
  33.         if [ ! -f "${SQL_FILE}" ] ; then
  34.            echo "ERROR: Cannot locate SQL file for ${2}" >&2
  35.            exit 1
  36.         fi
  37.     fi
  38. fi
  39. # ----
  40. # Set myCP according to the database type.
  41. # ----
  42. 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"
  43. echo "# ------------------------------------------------------------"
  44. echo "# Loading SQL file ${SQL_FILE}"
  45. echo "# ------------------------------------------------------------"
  46. myOPTS="-Dprop=$1"
  47. myOPTS="$myOPTS -DcommandFile=${SQL_FILE}"
  48. java -cp "$myCP" $myOPTS ExecJDBC
复制代码
  1. runLoader.sh
  2. #!/usr/bin/env bash
  3. if [ $# -lt 1 ] ; then
  4.     echo "usage: $(basename $0) PROPS_FILE [ARGS]" >&2
  5.     exit 2
  6. fi
  7. source funcs.sh $1
  8. shift
  9. # 手动设置类路径
  10. 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"
  11. # 如果 funcs.sh 中有设置类路径的函数,可以保留它
  12. # setCP || exit 1
  13. 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 文件,给这个文件也加上路径。
  1. runDatabaseBuild.sh
  2. #!/bin/sh
  3. if [ $# -lt 1 ] ; then
  4.     echo "usage: $(basename $0) PROPS [OPT VAL [...]]" >&2
  5.     exit 2
  6. fi
  7. PROPS="$1"
  8. shift
  9. if [ ! -f "${PROPS}" ] ; then
  10.     echo "${PROPS}: no such file or directory" >&2
  11.     exit 1
  12. fi
  13. DB="$(grep '^db=' $PROPS | sed -e 's/^db=//')"
  14. # 设置 PostgreSQL JDBC 驱动路径
  15. POSTGRES_JDBC="D:/360MoveData/Users/HUAWEI/Desktop/benchmarksql-5.0/lib/postgres/postgresql-9.3-1102.jdbc41.jar"
  16. BUILD_DIR="D:/360MoveData/Users/HUAWEI/Desktop/benchmarksql-5.0/build"
  17. # 包含所有 SQL 文件的路径
  18. BEFORE_LOAD="tableCreates"
  19. AFTER_LOAD="indexCreates foreignKeys extraHistID buildFinish"
  20. # 添加类路径
  21. CLASSPATH="$BUILD_DIR;$POSTGRES_JDBC"
  22. # 执行 SQL 加载步骤
  23. for step in ${BEFORE_LOAD} ; do
  24.     ./runSQL.sh "${PROPS}" $step
  25. done
  26. # 执行数据加载器脚本
  27. java -cp "$CLASSPATH" -Dprop=$PROPS LoadData $*
  28. # 执行后续的 SQL 步骤
  29. for step in ${AFTER_LOAD} ; do
  30.     ./runSQL.sh "${PROPS}" $step
  31. done
复制代码
如许三个文件改完,这一步就应该是可以正常运行了。在这一步中会在数据库中创建相应的表、数据和各种约束,方便下一步进行测试。可以利用navicat来进行查看。

(5)运行基准测试

到这一步终于是可以运行基准测试了,还是在run文件夹中。
  1. ./runBenchmark.sh my_postgres.properties
复制代码
这里大概率会出现雷同的问题,即无法加载类另有jdbc。同时还会跳出一个新的问题,即无法调用log4j,这里需要先去下载这个文件,然后配置好环境,接下来把log4j的bin文件路径也附加到myCP中去(这里只加给runBenchmark.sh一个文件就行了)。下边是我的runBenchmark.sh,在myCP里边设置了三个路径。
  1. runBenchmark.sh
  2. #!/usr/bin/env bash
  3. if [ $# -ne 1 ] ; then
  4.     echo "usage: $(basename $0) PROPS_FILE" >&2
  5.     exit 2
  6. fi
  7. SEQ_FILE="./.jTPCC_run_seq.dat"
  8. if [ ! -f "${SEQ_FILE}" ] ; then
  9.     echo "0" > "${SEQ_FILE}"
  10. fi
  11. SEQ=$(expr $(cat "${SEQ_FILE}") + 1) || exit 1
  12. echo "${SEQ}" > "${SEQ_FILE}"
  13. source funcs.sh $1
  14. 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"
  15. myOPTS="-Dprop=$1 -DrunID=${SEQ}"
  16. java -cp "$myCP" $myOPTS jTPCC
复制代码
接下来这一步就可以正常运行了,运行效果大概是如许的:

(6)调整基准测试配置

完成初次测试后,可以通过修改 props.pg 文件来扩大基准测试的规模(增长仓库数、终端数和持续时间等)。
要从基于事件数的测试切换到基于时间的测试,可以修改以下参数:
  1. runTxnsPerTerminal=0 runMins=3
复制代码
重复上一步的操纵,会完成又一次的测试,每次测试生成的数据会包含在my_result_测试时间如许的一个文件夹中。
(7)效果报告

基准测试完成后,可以利用 generateReport.sh 脚本生成报告:
  1. ./generateReport.sh my_result_directory
复制代码
这里把directory改成具体的时间。该脚本会生成HTML报告,并包含基准测试效果的图表。
我们以上一步runTxnsPerTerminal=0 runMins=3,生成的效果为例,下图是生成的文件:

report里边是如许的:

这里会发现下边还是少了一些东西,这里对应着系统的资源利用。
打开generateReport.sh发现调用的是misc中的os_collector_linux的文件,由于我用的是Windows系统,所以这个文件并不适用,也导致下边的数据另有图片无法正常生成。
这里我也写了一个os_collector_windows,但是这里边涉及要改的东西有点多,由于代码自己就比较“屎”所以就没有继续测试,改完这个还要把props.pg再改一下,仅供参考:
  1. #props.pg
  2. osCollectorScript=./misc/os_collector_windows.py
  3. osCollectorInterval=1
  4. //osCollectorSSHAddr=user@dbhost
  5. osCollectorDevices=cpu memory
复制代码
  1. #os_collector_windows
  2. import psutil
  3. import time
  4. import csv
  5. import sys
  6. import os
  7. # ----
  8. # main
  9. # ----
  10. def main(argv):
  11.     global deviceFDs
  12.     global lastDeviceData
  13.     # ----
  14.     # 获取 runID 和采集间隔
  15.     # ----
  16.     runID = int(argv[0])
  17.     interval = float(argv[1])
  18.     # ----
  19.     # 初始化系统信息收集和输出CSV表头
  20.     # ----
  21.     sysInfo = ['run', 'elapsed', ]
  22.     sysInfo += initSystemUsage()
  23.     print(",".join([str(x) for x in sysInfo]))
  24.     # ----
  25.     # 获取指定的设备,设备格式为 net_<devname> 或 blk_<devname>
  26.     # ----
  27.     devices = []
  28.     deviceFDs = {}
  29.     lastDeviceData = {}
  30.     for dev in argv[2:]:
  31.         if dev.startswith('blk_'):
  32.             devices.append(dev)
  33.         elif dev.startswith('net_'):
  34.             devices.append(dev)
  35.         else:
  36.             raise Exception("unknown device type '" + dev + "'")
  37.     # ----
  38.     # 初始化每个设备的使用情况
  39.     # ----
  40.     for dev in devices:
  41.         if dev.startswith('blk_'):
  42.             devInfo = ['run', 'elapsed', 'device', ]
  43.             devInfo += initBlockDevice(dev)
  44.             print(",".join([str(x) for x in devInfo]))
  45.         elif dev.startswith('net_'):
  46.             devInfo = ['run', 'elapsed', 'device', ]
  47.             devInfo += initNetDevice(dev)
  48.             print(",".join([str(x) for x in devInfo]))
  49.     # ----
  50.     # 输出表头
  51.     # ----
  52.     sys.stdout.flush()
  53.     try:
  54.         while True:
  55.             # ----
  56.             # 等待下一个采集间隔,并计算经过的时间(毫秒)
  57.             # ----
  58.             now = time.time()
  59.             elapsed = int((now - float(argv[1])) * 1000.0)
  60.             # ----
  61.             # 收集 CPU 和内存使用信息
  62.             # ----
  63.             sysInfo = [runID, elapsed, ]
  64.             sysInfo += getSystemUsage()
  65.             print(",".join([str(x) for x in sysInfo]))
  66.             # ----
  67.             # 收集每个设备的使用情况
  68.             # ----
  69.             for dev in devices:
  70.                 if dev.startswith('blk_'):
  71.                     devInfo = [runID, elapsed, dev, ]
  72.                     devInfo += getBlockUsage(dev, interval)
  73.                     print(",".join([str(x) for x in devInfo]))
  74.                 elif dev.startswith('net_'):
  75.                     devInfo = [runID, elapsed, dev, ]
  76.                     devInfo += getNetUsage(dev, interval)
  77.                     print(",".join([str(x) for x in devInfo]))
  78.             # ----
  79.             # 更新下一次采集的时间
  80.             # ----
  81.             time.sleep(interval)
  82.             sys.stdout.flush()
  83.     except KeyboardInterrupt:
  84.         print("")
  85.         return 0
  86.     except IOError as e:
  87.         if e.errno == errno.EPIPE:
  88.             return 0
  89.         else:
  90.             raise e
  91. def initSystemUsage():
  92.     return ['cpu_percent', 'memory_percent']
  93. def getSystemUsage():
  94.     # 获取 CPU 和内存使用率
  95.     cpu_percent = psutil.cpu_percent(interval=1)
  96.     memory_percent = psutil.virtual_memory().percent
  97.     return [cpu_percent, memory_percent]
  98. def initBlockDevice(dev):
  99.     # Windows 没有类似 Linux 的 /sys/block 路径,因此需要使用 psutil 获取磁盘 I/O 信息
  100.     return ['rdiops', 'rdkbps', 'wriops', 'wrkbps']
  101. def getBlockUsage(dev, interval):
  102.     # 使用 psutil 获取磁盘 I/O 数据
  103.     disk_io = psutil.disk_io_counters()
  104.     rdiops = disk_io.read_count
  105.     wriops = disk_io.write_count
  106.     rd_bytes = disk_io.read_bytes
  107.     wr_bytes = disk_io.write_bytes
  108.     # 计算每秒的读写次数和数据量
  109.     return [rdiops / interval, rd_bytes / interval / 1024.0, wriops / interval, wr_bytes / interval / 1024.0]
  110. def initNetDevice(dev):
  111.     # 获取网络接口的初始数据
  112.     return ['rxpktsps', 'rxkbps', 'txpktsps', 'txkbps']
  113. def getNetUsage(dev, interval):
  114.     # 获取网络接口的流量数据
  115.     net_io = psutil.net_io_counters()
  116.     rx_pkts = net_io.bytes_recv
  117.     tx_pkts = net_io.bytes_sent
  118.     # 计算每秒的接收和发送流量
  119.     return [
  120.         rx_pkts / interval,
  121.         rx_pkts / interval / 1024.0,
  122.         tx_pkts / interval,
  123.         tx_pkts / interval / 1024.0
  124.     ]
  125. if __name__ == '__main__':
  126.     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企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表