简介:面向存储性能测试与系统调优场景,这份vdbench I/O性能测试工具资源包适合运维、开发及测试人员使用,能够帮助在Linux、Windows、Solaris、AIX、macOS等平台评估磁盘、SSD与存储阵列的读写能力,定位I/O瓶颈。压缩包共61个文件,体积约2.91MB,内容覆盖txt示例配置、so/dll动态库、sh/bat启动脚本、vdbench.jar核心程序及PDF说明文档;txt多用于参数定义,sh/bat负责启动执行,so/dll支撑跨平台运行,并附带顺序读写、随机读写、混合读写、读写比例曲线等多种工作负载示例。借助资源中的多平台可执行组件与实验样例,读者可以掌握vdbench配置文件的编写、数据生成器使用、报告指标解读,以及并发、分布式测试的具体落地方法,为系统性能优化提供量化依据。已有763人学习下载,对于需要快速搭建跨平台存储性能压测环境并理解I/O行为的工程师而言,是一份紧凑而实用的参考资料。
1. vdbench 是什么:一次存储验收的困惑与可复现的解法
某次存储选型,厂商报告里 4K 随机读 32 万 IOPS,自己拿同一批盘在机房实测只有 17 万。中间差的不是盘,是负载模型。vdbench 就是用来把“负载”这件事讲清楚的基准测试工具——Oracle 出品、Java 实现,用一份由 sd、wd、rd 三种定义组成的 workfile 描述存储系统的访问模式,输出 IOPS、带宽和响应时间分布。它解决的是存储到底能扛多大压力这个老问题,既能在一块盘上做最小验证,也能在多机多 LUN 上模拟真实业务。适合做存储选型、容量规划、性能调优的人,不需要会 Java,把 workfile 写对就行。
2. 拆开 workfile:sd / wd / rd 三件套,先跑通一块盘的 4K 随机写
2.1 主控与 slave:理解 vdbench 的进程模型
第一次用 vdbench 的人常问:为什么它能压出比普通脚本高得多的并发?答案不在工具本身多“猛”,而在它的进程模型。vdbench 启动后,主控进程(master)负责读 workfile、编排负载、汇总结果,真正的 IO 由一个个 slave 进程执行。单机测试时 master 和 slave 在同一台机器上,不需要额外配置;多主机压测时,每台目标机器上都要有 slave 进程,主控通过 ssh 或 rsh 把它们拉起来。
这个模型决定了你在 workfile 里写的每个参数最终落到哪里:sd 定义存储对象,wd 定义怎么访问这些对象,rd 定义什么时候跑、跑多久。三层各管各的,理解了这个结构,后面所有配置都不会乱。还有一个实际影响:每个 slave 对应一个独立 JVM,JVM 内的线程数由 wd 里的 threads 参数控制。所以“并发深度”不是单个参数能直接设置的,而是由 slave 数、wd 绑定关系、threads 数共同决定的,这一点后面专门讲。
2.2 最小 workfile:单块裸盘压 4K 随机写
先给一份能直接落地的最小配置。目标是一台 Linux 服务器,一块整盘 /dev/sdb,做 60 秒 4K 随机写压测。把下面内容存成 vdb_test.conf:
# 单机 SSD 上跑 4K 随机写,压 60 秒 sd=sd1, # 存储定义:给这台盘起个名字 lun=/dev/sdb, # 裸设备路径,整个块设备 openflags=o_direct # 绕过操作系统缓存,直接读写设备 wd=wd1, # 工作负载定义:怎么访问 sd1 sd=sd1, # 绑到上面定义的 sd xfersize=4k, # 每次 IO 大小 4KB rdpct=0, # 0% 读 = 100% 写 seekpct=100, # 100% 随机寻址 threads=8 # 这个 wd 在 slave 里开 8 个线程 rd=run1, # 运行定义:什么时候跑、跑多久 wd=wd1, # 启动 wd1 elapsed=60, # 跑 60 秒 warmup=10 # 先预热 10 秒,再开始统计运行命令:
chmod +x vdbench ./vdbench -c vdb_test.conf -o /tmp/vdb_out-c 指定配置文件,-o 指定输出目录。跑完后屏幕上会按秒打印 IOPS、带宽和响应时间,输出目录里会生成 flatfile.csv,这是后面做数据分析的原料。
这里有几个值得说清楚的参数。lun 指向的是整个块设备,也可以写成 /dev/sdb1 这种分区路径,但注意指向分区时,vdbench 只会压这个分区。openflags=o_direct 表示绕过文件系统缓存直接下发 IO,测写性能时少了页缓存这层干扰,数字更接近设备真实水平。rdpct=0 是“全写”,这个特别容易搞反,rdpct 表示读百分比,0 就是完全没读。seekpct=100 表示每次 IO 都随机寻址,0 则是纯顺序,默认值其实是 100,但我习惯显式写出来,避免后面改参数时漏掉。warmup 的作用是让 SSD 缓存、垃圾回收、JVM 的 JIT 编译都稳定下来再开始计数,短时间测试尤其需要。
2.3 五个必须显式写死的参数
用 vdbench 这些年,我总结出五个每次都会确认一遍的参数,它们直接决定负载长什么样。列成一张表方便对照:
| 参数 | 常见取值 | 语义 | 典型业务映射 |
|---|---|---|---|
| xfersize | 4k、8k、64k、1m | 单次 IO 大小 | 4k 数据库随机读写,64k 日志顺序,1m 备份流 |
| rdpct | 0、100、70 | 读占比 | 0=全写,100=全读,70≈OLTP 混合 |
| seekpct | 0、100、50 | 寻址随机度 | 0=纯顺序,100=纯随机,50=半随机半顺序 |
| threads | 1~64 | 单 wd 并发线程数 | 数值越大,IO 队列越深 |
| elapsed | 60~3600 | 压测秒数 | 低于 60 秒结果噪声很大,建议至少 120 秒 |
这五个参数不是拍脑袋填的,每个都要能回答“为什么是这个值”。比如 xfersize 用 4k 是因为数据库页大小常见 8k,但索引扫描常是 4k;用 64k 是因为顺序日志写的典型块大小。rdpct 和 seekpct 组合起来,才真正模拟出业务负载的形态,只改一个参数,结果可能差好几倍。把参数和业务对应起来,给出的测试结论才有人信。
2.4 跑完看什么:stdout、flatfile.csv 与 messages
跑完压测,输出目录里主要看三个东西。第一个是 stdout,也就是屏幕输出,每行是一个采样间隔的汇总,包含间隔秒数、IOPS、MB/s、平均响应时间和最大响应时间。第二个是 flatfile.csv,每个间隔一行,数据更全,适合导入 pandas 或 Excel 做后续分析。第三个是 messages 文件,这个容易被忽略,里面记录的是 IO 错误信息,比如某个 LUN 在压测中段出现读写失败,屏幕上不一定看得到,但 messages 里一定有。
我一般跑完第一件事是去 messages 里扫一眼有没有 “error” 字样,确认没有 IO 错误,再去看性能数字。先看错没错,再看快不快,这个习惯帮我避免了很多次“压测成绩很好看但数据其实没写进去”的尴尬。
3. 压真实存储阵列:多 LUN、多主机与并发深度的正确写法
3.1 从单机到多机:hd 定义与远程 slave 配置
单盘跑通之后,真实存储阵列通常有多台主机、每台挂多块 LUN。把负载分散到这些主机上,靠的是 hd(host definition)段。看下面这个例子:4 台主机,每台 2 个 slave,每台各自压自己的 /dev/sdb。
# 4 台主机的存储阵列压测:每台 2 个 slave,每台压一块独立 LUN hd=default,vdbench=/opt/vdbench,shell=ssh # 公共参数:工具路径、远程 shell hd=h1,system=192.168.10.11,users=2,slaves=2 hd=h2,system=192.168.10.12,users=2,slaves=2 hd=h3,system=192.168.10.13,users=2,slaves=2 hd=h4,system=192.168.10.14,users=2,slaves=2 # 每台主机一块独立 LUN sd=sd1,host=h1,lun=/dev/sdb,openflags=o_direct sd=sd2,host=h2,lun=/dev/sdb,openflags=o_direct sd=sd3,host=h3,lun=/dev/sdb,openflags=o_direct sd=sd4,host=h4,lun=/dev/sdb,openflags=o_direct # 所有 wd 指向所有 sd:让每台机器都压到自己的盘 wd=wd_all,sd=sd1,sd2,sd3,sd4,xfersize=8k,rdpct=70,seekpct=100,threads=8 rd=run_all,wd=wd_all,elapsed=300,warmup=30这段配置里,hd=default 定义了公共项,vdbench 指定远程工具目录,shell=ssh 指定远程启动方式。每个 hd 里的 system 是目标主机 IP,users 表示同时打开的远程通道数,slaves 表示在这台主机上启动的 slave 进程数。我一般让 users 和 slaves 相等,避免通道数不够导致启动排队。
sd 定义里加了 host=h1 这类绑定,意思是这块 LUN 只会被对应主机上的 slave 访问。wd 里的 sd=sd1,sd2,sd3,sd4 表示一个 wd 同时驱动四块盘。注意,一个 wd 绑多个 sd,vdbench 会为每个 sd 各建一条独立的 IO 流,配合 threads 参数,并发量会成倍增长,这是后面调深度的基础。
第一次跑多机之前,先确认每台机器都能 ssh 免密登录,而且要能直接跑 java -version。vdbench 远程启动时会通过 ssh 执行 Java 命令,如果目标机的 JDK 环境有问题,卡在 “Starting slave” 是很常见的。
3.2 队列深度顶不上去:threads 叠加、多 wd 与 fwdrate
用过 fio 的人习惯直接写 iodepth=32,但 vdbench 没有这个参数,刚上手很容易不知道怎么把压力加上去。vdbench 的队列深度等于 slave 数、wd 数、threads 数三者的乘积效应。比如一个 slave 里有一个 wd,threads=8,那这块盘上的并发 IO 就是 8 个线程在发。如果这个 wd 绑了 4 块 sd,每块 sd 都会有 8 个线程的独立 IO 流,总并发就是 32。
想把单盘深度顶得更高,常见做法是对同一块 sd 定义多个 wd,同时跑。看这个例子:
sd=sd1,lun=/dev/sdb,openflags=o_direct # 三个 wd 指向同一块盘,每个 16 线程,叠加深度 wd=wd_d1,sd=sd1,xfersize=4k,rdpct=100,seekpct=100,threads=16 wd=wd_d2,sd=sd1,xfersize=4k,rdpct=100,seekpct=100,threads=16 wd=wd_d3,sd=sd1,xfersize=4k,rdpct=100,seekpct=100,threads=16 rd=deep,wd=wd_d1,wd=wd_d2,wd=wd_d3,elapsed=120,warmup=20三个 wd 各开 16 线程,同一时刻有 48 个线程在打同一块盘,这比单个 wd 的 threads=48 效果更均匀,因为每个 wd 的调度相对独立,不容易出现单线程内部排队。把 threads 提得很高时,比如超过 64,反而会因为线程切换开销拉高响应时间,数字看起来“变慢了”,这不一定是存储的问题,可能是压测工具自身的瓶颈。
fwdrate 是另一个值得知道的参数,它表示这个 wd 每秒钟最多发出多少个 IO。比如 fwdrate=1000 就是限量每秒 1000 次 IO,常用于模拟低优先级业务流。调 fwdrate 不会改变 IO 的大小和随机度,只是给这个 wd 加了个速度上限,对比限速前后的性能差异,能看出存储在高压力下的排队效应有多明显。
3.3 format=yes 是全盘清零:分批跑更安全
sd 段里有一个 format 参数,默认不开启,但一旦设了 format=yes,vdbench 在正式跑测试前会对这块 lun 做“格式化”。注意,这里的格式化对裸设备来说就是全盘清零,不是创建文件系统那种温和操作。很多悲剧就是这么发生的:连着数据盘的 sd 里误加了 format=yes,跑完发现分区表都没了。
我自己的习惯是,凡是 sd 里写 lun,一律不写 format。如果确实需要干净盘状态,那就先确认这块盘是专用测试盘,再把 format=yes 加上。另外,阵列测试不要一个 rd 里把所有 LUN 全压上,先拿一块盘跑基线,确认 IOPS 和延迟在预期范围,再分批把更多 LUN 加进来。这样出了问题能快速定位是某块盘的问题,还是配置的问题。
3.4 混合负载写法:用两个 wd 模拟业务叠加
真实业务很少有单一 IO 模式,数据库通常是小块随机读加大块顺序写。vdbench 可以在一个 rd 里跑多个 wd,各自独立地按自己的参数工作。看这个配置:
wd=wd_oltp,sd=sd1,xfersize=4k,rdpct=90,seekpct=100,threads=16 wd=wd_log,sd=sd1,xfersize=64k,rdpct=0,seekpct=0,threads=4 rd=mix,wd=wd_oltp,wd_log,fwdrate=wd_log:500,elapsed=300,warmup=30wd_oltp 模拟在线交易,4k 随机读占 90%;wd_log 模拟日志写,64k 顺序写,并用 fwdrate=wd_log:500 把日志流限速到每秒 500 次 IO,避免大块写把整个盘的资源全抢走。这种叠加方式比单独跑两种负载再算术平均要真实得多,因为存储内部的缓存策略、垃圾回收、写放大都会在混合压力下显形。
4. 避坑:三次翻车换来的 vdbench 注意清单
4.1 rdpct=0 是全写不是全读
现象:想测全读,随手在 wd 里写了 rdpct=0,跑完发现测试盘被写满了,性能数字还奇低。
原因:vdbench 的 rdpct 是读百分比,0 表示 0% 读,也就是 100% 写。这个语义和很多人的直觉相反,偏偏它又是必填参数,填错一次就是一次无效测试。
解决:写 workfile 时给 rdpct 和 seekpct 都加上注释,哪怕只是rdpct=100 # 全读这种程度。我的习惯是每个 wd 下面都写一行注释说明这个负载模拟什么场景,改参数的人不会误伤。
4.2 format=yes 清掉了数据盘
现象:测试完成后发现某块盘上的数据全没了,分区表也消失,确认是 vdbench 干的。
原因:sd 定义里加了 format=yes,vdbench 对裸设备执行的是全盘清零,不是文件系统级的格式化,更不是“意思一下”。这个参数一旦配上,跑之前就会清盘。
解决:默认不写 format 参数。只有确认是新测试盘、数据不心疼时,才在 sd 上加 format=yes。跑之前用 lsblk 确认盘符没有指向系统盘或数据盘,这个检查不能省。
4.3 flatfile.csv 最后一行不等于整场结果
现象:压测 300 秒,打开 flatfile.csv 看最后一行,数值跟倒数第 30 行差很多,拿最后一行写报告,结果被质疑结论不稳定。
原因:flatfile.csv 每一行是一个采样间隔的瞬时值,默认每秒一行。最后一秒的抖动高或低,都不能代表整场表现。
解决:看整场结果时,要么看 stdout 最后打印的汇总,要么自己把 csv 读进 pandas,取稳定段的均值。我一般会跳过 warmup 期间和结束前最后几秒的数据再算均值,这样更能反映稳态性能。
4.4 远程 slave 卡在 Starting slave
现象:加了远程主机配置后,vdbench 一直卡在 “Starting slave”,最后超时报错。
原因:两类高频问题。一是目标机 JDK 版本太高,vdbench 用的启动参数在高版本 JDK 上不兼容;二是 Linux 的随机数生成器阻塞,Java 进程启动时等 /dev/random 熵池,迟迟起不来。
解决:目标机装 JDK 8 或相近的稳定版本;同时修改目标机 Java 的 securerandom 配置,把随机源指到 urandom。具体做法是改$JAVA_HOME/jre/lib/security/java.security里的securerandom.source=file:/dev/urandom。还有一个笨但有效的思路:先在本地把整套 workfile 跑通,再加一台远程主机试跑,通过之后再上全部节点,不要一次配四台然后一起失败。
5. 把 vdbench 小报表读成可用数字:csv 的区间与变量控制
5.1 用 pandas 取稳定段均值
vdbench 输出的 flatfile.csv 是原始数据,直接交给别人看不懂,也不该把最后一行的数字当最终结果。我一般用一段很短的 pandas 脚本把稳定段抠出来。注意列名以你实际导出的 csv 为准,不同版本有细微差异:
import pandas as pd df = pd.read_csv("flatfile.csv", skipinitialspace=True) # 看有哪些列,确认列名 print(df.columns.tolist()) # 跳过预热段,去掉结尾抖动,取中间稳定段 max_elapsed = df["elapsed"].max() stable = df[(df["elapsed"] >= 30) & (df["elapsed"] <= max_elapsed - 10)] print(stable["IOPS"].mean()) # 平均 IOPS print(stable["MB/sec"].mean()) # 平均带宽 print(stable["Resp_time"].mean()) # 平均响应时间这里的逻辑是:elapsed 列是运行秒数,IOPS 和 MB/sec 是这一秒的瞬时值,Resp_time 是这一秒的平均响应时间。稳定段过滤把 warmup 拉高的部分和测试结束前资源回收的抖动都剔掉。响应时间分布列在 csv 里是一串百分位数字,它是采样统计,不是全量统计,短跑测试时尾部百分位会很飘,想要 p99 稳定,就把 elapsed 拉长到至少 300 秒。
5.2 控制变量法:一个 workfile 只改一个参数
结论可信的前提是控制变量。对比两块盘或者两种配置时,我一般只改一个参数,比如只改 xfersize,其余全部保持原样,输出目录用不同的 -o 路径隔开。跑完后把两次的稳定段均值放在一起对比。如果同时改了块大小又改了并发线程数,测出来的差异就没法归因,报告写出来也经不起追问。多跑几次取稳态值也很有必要,存储设备在不同时间点的负载状态会波动,单次跑数不准。
做存储测试这几年,我最大的教训是:先写死参数,再动手敲盘。所有看起来像玄学的性能波动,最后都能在 workfile 里找到原因。希望帮到你。
本文还有配套的精品资源,点击获取