一个很常见的需求,就是需要对部署之后的组件进行压测,验证其默认参数/调整后的参数,是否符合预期,是否满足交付条件 今天就介绍一个很常见的组件,sysbench!
sysbench 是怎么样一个东西?
它是一个模块化的、跨平台的基准测试工具,主要用于评估系统性能和数据库性能。它最初设计用于 CPU、内存、线程和文件 I/O 的基准测试,但后来扩展了对数据库操作的支持,成为评估数据库性能的强大工具。它可以支持多种数据库管理系统(DBMS),如 MySQL、PostgreSQL 和 MariaDB,并且可以通过编写自定义 Lua 脚本来支持其他类型的数据库。
上面介绍到的,比如服务器CPU、服务器内存、服务器I/O都是日常十分关注的基础指标。
内置模块有:
- CPU 测试:评估 CPU 性能。
- 内存测试:评估内存读写速度。
- 线程测试:评估线程创建和切换性能。
- 文件 I/O 测试:评估磁盘读写性能。
- OLTP 测试:评估数据库的在线事务处理(OLTP)性能。
- 可扩展性:用户可以编写自定义 Lua 脚本,以执行特定的测试逻辑或模拟复杂的业务场景。
- 测试报告:提供详细的性能统计数据,帮助识别性能瓶颈。
一个工具在github上面累积了 6.2k 的star数,它的功能一定长在了很多人的心坎上。
安装一个 sysbench
由于这个工具是如此常用,安装也是非常方便 ,直接从官方的安装包一键安装:
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
Arch Linux
sudo pacman -Suy sysbench
macOS
brew install sysbench
如何使用?
基础语法: sysbench [options]... [testname] [command]
sysbench --help
可以帮你了解它的所有参数和用法
sysbench --help
Usage:
sysbench [options]... [testname] [command]
Commands implemented by most tests: prepare run cleanup help
General options:
--threads=N number of threads to use [1]
--events=N limit for total number of events [0]
--time=N limit for total execution time in seconds [10]
--forced-shutdown=STRING number of seconds to wait after the --time limit before forcing shutdown, or 'off' to disable [off]
--thread-stack-size=SIZE size of stack per thread [64K]
--rate=N average transactions rate. 0 for unlimited rate [0]
--report-interval=N periodically report intermediate statistics with a specified interval in seconds. 0 disables intermediate reports [0]
--report-checkpoints=[LIST,...] dump full statistics and reset all counters at specified points in time. The argument is a list of comma-separated values representing the amount of time in seconds elapsed from start of test when report checkpoint(s) must be performed. Report checkpoints are off by default. []
--debug[=on|off] print more debugging info [off]
--validate[=on|off] perform validation checks where possible [off]
--help[=on|off] print help and exit [off]
--version[=on|off] print version and exit [off]
--config-file=FILENAME File containing command line options
--tx-rate=N deprecated alias for --rate [0]
--max-requests=N deprecated alias for --events [0]
--max-time=N deprecated alias for --time [0]
--num-threads=N deprecated alias for --threads [1]
Pseudo-Random Numbers Generator options:
--rand-type=STRING random numbers distribution {uniform,gaussian,special,pareto} [special]
--rand-spec-iter=N number of iterations used for numbers generation [12]
--rand-spec-pct=N percentage of values to be treated as 'special' (for special distribution) [1]
--rand-spec-res=N percentage of 'special' values to use (for special distribution) [75]
--rand-seed=N seed for random number generator. When 0, the current time is used as a RNG seed. [0]
--rand-pareto-h=N parameter h for pareto distribution [0.2]
Log options:
--verbosity=N verbosity level {5 - debug, 0 - only critical messages} [3]
--percentile=N percentile to calculate in latency statistics (1-100). Use the special value of 0 to disable percentile calculations [95]
--histogram[=on|off] print latency histogram in report [off]
General database options:
--db-driver=STRING specifies database driver to use ('help' to get list of available drivers) [mysql]
--db-ps-mode=STRING prepared statements usage mode {auto, disable} [auto]
--db-debug[=on|off] print database-specific debug information [off]
Compiled-in database drivers:
mysql - MySQL driver
pgsql - PostgreSQL driver
mysql options:
--mysql-host=[LIST,...] MySQL server host [localhost]
--mysql-port=[LIST,...] MySQL server port [3306]
--mysql-socket=[LIST,...] MySQL socket
--mysql-user=STRING MySQL user [sbtest]
--mysql-password=STRING MySQL password []
--mysql-db=STRING MySQL database name [sbtest]
--mysql-ssl[=on|off] use SSL connections, if available in the client library [off]
--mysql-ssl-cipher=STRING use specific cipher for SSL connections []
--mysql-compression[=on|off] use compression, if available in the client library [off]
--mysql-debug[=on|off] trace all client library calls [off]
--mysql-ignore-errors=[LIST,...] list of errors to ignore, or "all" [1213,1020,1205]
--mysql-dry-run[=on|off] Dry run, pretend that all MySQL client API calls are successful without executing them [off]
pgsql options:
--pgsql-host=STRING PostgreSQL server host [localhost]
--pgsql-port=N PostgreSQL server port [5432]
--pgsql-user=STRING PostgreSQL user [sbtest]
--pgsql-password=STRING PostgreSQL password []
--pgsql-db=STRING PostgreSQL database name [sbtest]
Compiled-in tests:
fileio - File I/O test
cpu - CPU performance test
memory - Memory functions speed test
threads - Threads subsystem performance test
mutex - Mutex performance test
See 'sysbench <testname> help' for a list of options for each test.
那么哪些参数是日常使用,比较重要的?
--threads
: 执行压测脚本的工作线程数,默认1个,但是如果要更好地模拟业务使用,肯定是一定数量的多线程数量,具体合适的值可以与开发人员确定。--events
: 请求总数限制。0 (默认值) 表示无限制。--time
: 整体执行的秒数,0代表无限制,默认10秒,通常这是代表一个观察周期,具体合适的值也需要与开发人员沟通--warmup-time
: 在启用统计信息的实际基准测试运行之前,在禁用统计信息的情况下执行事件数秒。当您想从统计数据中排除基准测试运行的初始周期时,这非常有用。在许多基准测试中,初始阶段不具有代表性,因为 CPU/数据库/页面和其他缓存需要一些时间来预热。默认是0。--rate
: 平均交易率。该数字指定所有线程平均每秒应执行多少个事件 (事务)。0(默认)表示无限制速率,即事件以最快的速度执行。--thread-init-timeout
: 等待工作线程初始化的时间(以秒为单位),默认30秒。--thread-stack-size
: 每个线程的堆栈大小,默认32K。--report-interval
: 以指定的时间间隔(以秒为单位)定期报告中间统计信息。请注意,此选项生成的统计信息是按间隔生成的,而不是累积的。0 代表禁用。通常设置一个合理的值,过于密集可能影响测试结果。--debug
: 打印更多调试信息,默认关闭。--validate
: 尽可能对测试结果进行验证,默认关闭。--help
--verbosity
: 详细级别(0 - 仅关键消息,5 - 调试),默认是4。--percentile
: 测量所有已处理请求的执行时间,默认是95,通常还有99,999,9999需求的测试,根据实际情况可调整。--luajit-cmd
: 执行LuaJIT控制指令。
除了执行指令,剩下就是后续的指令: prepare run cleanup help
help
就是展示常用的帮助页面prepare
就是执行脚本压测之前的准备工作,例如数据库就需要创建数据库、创建数据库表等run
就是真实开启执行压测cleanup
是执行压测后,对数据库表进行一些清理,便于下一次压测的开展
压测一下 MySQL的性能
脚本准备
我们在sysbench的目录下:
[@ sysbench]# ls -l
total 2097284
-rwxr-xr-x 1 root root 1452 Mar 16 2019 bulk_insert.lua
-rw-r--r-- 1 root root 14369 Mar 16 2019 oltp_common.lua
-rwxr-xr-x 1 root root 1290 Mar 16 2019 oltp_delete.lua
-rwxr-xr-x 1 root root 2415 Mar 16 2019 oltp_insert.lua
-rwxr-xr-x 1 root root 1265 Mar 16 2019 oltp_point_select.lua
-rwxr-xr-x 1 root root 1649 Mar 16 2019 oltp_read_only.lua
-rwxr-xr-x 1 root root 1824 Mar 16 2019 oltp_read_write.lua
-rwxr-xr-x 1 root root 1118 Mar 16 2019 oltp_update_index.lua
-rwxr-xr-x 1 root root 1127 Mar 16 2019 oltp_update_non_index.lua
-rwxr-xr-x 1 root root 1440 Mar 16 2019 oltp_write_only.lua
-rwxr-xr-x 1 root root 1919 Mar 16 2019 select_random_points.lua
-rwxr-xr-x 1 root root 2118 Mar 16 2019 select_random_ranges.lua
-rw------- 1 root root 429506560 Jan 8 09:55 test_file.0
-rw------- 1 root root 429506560 Jan 8 09:55 test_file.1
-rw------- 1 root root 429506560 Jan 8 09:55 test_file.2
-rw------- 1 root root 429506560 Jan 8 09:55 test_file.3
-rw------- 1 root root 429506560 Jan 8 09:55 test_file.4
drwxr-xr-x 4 root root 49 Jan 8 09:02 tests
能看到这些脚本,分别都是一些针对数据库压测的lua脚本,用于不同阶段,比如common可能代表压测前的准备和压测后的清理,有insert\delete\update\bulk insert等脚本,可以用于数据库读写的测试。
默认执行的sbtest表,结构十分简单
CREATE TABLE sbtest%d(
id %s,
k INTEGER DEFAULT '0' NOT NULL,
c CHAR(120) DEFAULT '' NOT NULL,
pad CHAR(60) DEFAULT '' NOT NULL,
%s (id)
)
通常我们的测试场景为了尽可能准确,可能会使用一个更为接近的表字段、字段数量,以期望测出业务情况下更为准确的性能情况。 这个时候就需要我们自己来撰写压测脚本,就是修改其中的lua脚本,步骤包括:
- 需要自定义表结构,例如修改common中的建表语句
CREATE TABLE sbtest%d (
id %s,
method char(16) NOT NULL DEFAULT '',
callid char(64) NOT NULL DEFAULT '',
time datetime NOT NULL,
duration int unsigned NOT NULL DEFAULT '0',
ms_duration int unsigned NOT NULL DEFAULT '0',
setuptime int NOT NULL DEFAULT '0',
created datetime DEFAULT CURRENT_TIMESTAMP,
src_ip varchar(25) DEFAULT NULL,
dst_ip varchar(25) DEFAULT NULL,
caller varchar(255) DEFAULT NULL,
callee varchar(255) DEFAULT NULL,
call_start_time datetime(3) DEFAULT CURRENT_TIMESTAMP(3),
call_end_time datetime(3) DEFAULT CURRENT_TIMESTAMP(3),
calllevel int unsigned NOT NULL DEFAULT '0',
routinglevel int unsigned NOT NULL DEFAULT '0',
calleraccount varchar(255) SET utf8mb4 DEFAULT NULL,
calleeaccount varchar(255) SET utf8mb4 DEFAULT NULL,
caller_callid char(100) NOT NULL DEFAULT '',
callee_callid char(100) NOT NULL DEFAULT '',
area varchar(10) DEFAULT NULL,
real_duration bigint unsigned DEFAULT '0',
%s (id)
)
替换默认的sbtest,当然名称都可以修改
- 对于增删改查的SQL,根据自己的情况定义,修改字符和模拟值:
con:query(string.format("INSERT INTO %s (method, from_tag, to_tag, callid, sip_code, sip_reason, time, duration, ms_duration, setuptime, created, src_ip, dst_ip, caller, callee, call_start_time, call_end_time, caller_in, callee_in, caller_out, callee_out, callergateway, calleegateway, calllevel, routinglevel, calleraccount, calleeaccount, caller_callid, callee_callid, area, end_side, end_code, end_reason, real_duration) VALUES " ..
"('%s', '%s', '%s','%s',%d,'%s','%s',%d,%d,'%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s',%d,%d,'%s','%s','%s','%s','%s',%d,%d,'%s',%d)",
table_name, c_val, pad_val, c_val, c_val,0, c_val, os.date("%Y-%m-%d %H:%M:%S",os.time()),0,0, os.date("%Y-%m-%d %H:%M:%S",os.time()), os.date("%Y-%m-%d %H:%M:%S",os.time()), c_val, c_val, c_val, c_val, os.date("%Y-%m-%d %H:%M:%S",os.time()), os.date("%Y-%m-%d %H:%M:%S",os.time()), c_val, c_val, c_val, c_val, c_val, c_val, 0,0,c_val, c_val, c_val, c_val, c_val,0,0, c_val,0))
执行测试
脚本一切准备完毕就可以开始使用命令开始测试:
prepare
阶段
[@ sysbench]# sysbench /usr/share/sysbench/oltp_insert_new.lua --mysql-host=ip --mysql-port=port --mysql-user=user --mysql-password='pass' --mysql-db=dbname --db-driver=mysql --tables=1 --table-size=1 --report-interval=10 --threads=1 --time=60 prepare
sysbench 1.0.17 (using system LuaJIT 2.0.4)
Creating table 'sbtest1'...
Inserting 1 records into 'sbtest1'
Creating a secondary index on 'sbtest1'...
run
阶段
[@ sysbench]# sysbench /usr/share/sysbench/oltp_insert_new.lua --mysql-host=ip --mysql-port=port --mysql-user=user --mysql-password='pass' --mysql-db=dbname --db-driver=mysql --tables=1 --table-size=1 --report-interval=10 --threads=1 --time=60 run
sysbench 1.0.17 (using system LuaJIT 2.0.4)
Running the test with following options:
Number of threads: 1
Report intermediate results every 10 second(s)
Initializing random number generator from current time
Initializing worker threads...
Threads started!
[ 10s ] thds: 1 tps: 506.41 qps: 506.41 (r/w/o: 0.00/506.41/0.00) lat (ms,95%): 2.35 err/s: 0.00 reconn/s: 0.00
[ 20s ] thds: 1 tps: 472.72 qps: 472.72 (r/w/o: 0.00/472.72/0.00) lat (ms,95%): 2.76 err/s: 0.00 reconn/s: 0.00
[ 30s ] thds: 1 tps: 430.74 qps: 430.74 (r/w/o: 0.00/430.74/0.00) lat (ms,95%): 2.97 err/s: 0.00 reconn/s: 0.00
[ 40s ] thds: 1 tps: 489.78 qps: 489.78 (r/w/o: 0.00/489.78/0.00) lat (ms,95%): 2.39 err/s: 0.00 reconn/s: 0.00
[ 50s ] thds: 1 tps: 514.12 qps: 514.12 (r/w/o: 0.00/514.12/0.00) lat (ms,95%): 2.26 err/s: 0.00 reconn/s: 0.00
SQL statistics:
queries performed:
read: 0
write: 29224
other: 0
total: 29224
transactions: 29224 (487.02 per sec.)
queries: 29224 (487.02 per sec.)
ignored errors: 0 (0.00 per sec.)
reconnects: 0 (0.00 per sec.)
General statistics:
total time: 60.0038s
total number of events: 29224
Latency (ms):
min: 1.15
avg: 2.05
max: 30.09
95th percentile: 2.61
sum: 59930.19
Threads fairness:
events (avg/stddev): 29224.0000/0.00
execution time (avg/stddev): 59.9302/0.00
压测后上面这个报告就是最终说明问题的证据,满足/不满足就依靠上面的数据描述。
cleanup
阶段
[@ sysbench]# sysbench /usr/share/sysbench/oltp_insert_new.lua --mysql-host=ip --mysql-port=port --mysql-user=user --mysql-password='pass' --mysql-db=dbname --db-driver=mysql --tables=1 --table-size=1 --report-interval=10 --threads=1 --time=60 cleanup
sysbench 1.0.17 (using system LuaJIT 2.0.4)
Dropping table 'sbtest1'...