SSD 性能测试
Posted coredump
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SSD 性能测试相关的知识,希望对你有一定的参考价值。
如何描述 SSD 的性能
SSD 的性能指标主要包括:IOPS、带宽和延迟。
-
IOPS (Input/Output Operations per Second)是指每秒能处理的 I/O 个数。一般 OLTP 的数据库应用需要关注 IOPS 性能。 -
带宽是指单位时间内可以成功传输的数据量(MB/s)。大量顺序读写的应用,例如离线计算任务,需要关注带宽。 -
延迟是指 SSD 处理一个 I/O 需要的时间(us)。一般情况下,在线业务对高延迟比较敏感,离线任务对高延迟比较不敏感。
dd 命令
dd 是 Linux 下的一个命令,其作用是用指定大小的块拷贝一个文件,并在拷贝的同时进行指定的转换。
dd 的几个主要参数(命令的详细参数、用法可以参考 Linux man page[1] ):
-
if: 输入文件(input file) -
of: 输出文件(output file) -
ibs: 输入块大小(input block size) -
obs: 输出块大小(output block size) -
bs: 输入和输出的块大小(input and output block size) -
iflag: 输入 flag(input flag) -
oflag: 输出 flag(outout flag) -
count: 拷贝的 block 数量
几个常用的 flag 参数:
-
direct: 使用 O_DIRECT 读写文件 -
dsync:写入文件块后,调用 fdatasync -
sync: 写入文件块后,调用 fsync -
nonblock: 采用非阻塞的方式读写文件。在 Linux 下,只有 O_DIRECT 的文件 I/O 支持非阻塞
/dev/zero 是 Linux 提供的一个特殊文件,可以无限产生 0x00 字符,从这个文件读取不产生 I/O,可以用来作为输入文件测试 SSD 的纯写速度。
/dev/null 也会 Linux 提供的一个特殊文件,提供一种“无底洞”的特性,往这个文件写入任何数据都不会产生 I/O,可以作为输出文件测试 SSD 的纯读速度。
用于测试 SSD 的命令:
dd if=/dev/zero of=test.data bs=4k oflag=direct,nonblock count=1000000
dd if=/dev/zero of=test.data bs=4k oflag=direct,nonblock,dsync count=1000000
dd if=/dev/zero of=test.data bs=4k oflag=direct,nonblock,sync count=1000000
dd 命令行工具的优点是使用简单,并且 Linux 操作系统一般都有自带,不需要额外安装。缺点也是因为太简单了:
-
单进程单线程运行,无法充分发挥 SSD 的并行特性。 -
只能测试顺序 I/O,不能测试随机 I/O。 -
只能测试纯读或纯写,不能按比例测试读写混合。 -
测试结果只能看出带宽,没有延迟、IOPS。
pg_test_fsync
pg_test_fsync 是 PostgreSQL 提供的一个测试工具,主要作用是让用户方便快捷地了解特定系统上最快的 wal_sync_method。pg_test_fsync 的执行结果报告了每个 wal_sync_method 的平均文件同步操作时间。
简单说,pg_test_fsync 可以用来测试 fsync/fdatasync 的延迟。
pg_test_fsync 可以从 PostgreSQL 的官网[2]下载源代码进行编译。比如:
-
我下载的是 postgresql-13.1.tar.bz2 [3]。 -
解压后,开始编译: -
切换带源代码的根目录下: cd postgresql-13.1
-
执行 ./configure
-
切换带 pg_test_fsync 的代码目录下: cd src/bin/pg_test_fsync
-
编译 pg_test_fsync: make
运行 pg_test_fsync 时遇到一个小问题:
8 * 2kB open_sync writes pg_test_fsync: error: write failed: Invalid argument
原因是使用 O_DIRECT 对写入的数据有对齐要求:
Under Linux 2.4, transfer sizes, and the alignment of the user buffer and the file offset must all be multiples of the logical block size of the filesystem. Since Linux 2.6.0, alignment to the logical block size of the underlying storage (typically 512 bytes) suffices. The logical block size can be determined using the ioctl(2) BLKSSZGET** **operation or from the shell using the command: blockdev --getss
在我的测试机器上,这个 block size 是 4096 而 pg_test_fsync 这个工具认为是 512:
cat /sys/block/nvme0n1/queue/physical_block_size 4096
最简单的做法是将不符合这个要求的两行代码注释掉:
static void
test_open_syncs(void)
{
printf(_("\nCompare open_sync with different write sizes:\n"));
printf(_("(This is designed to compare the cost of writing 16kB in different write\n"
"open_sync sizes.)\n"));
test_open_sync(_(" 1 * 16kB open_sync write"), 16);
test_open_sync(_(" 2 * 8kB open_sync writes"), 8);
test_open_sync(_(" 4 * 4kB open_sync writes"), 4);
//test_open_sync(_(" 8 * 2kB open_sync writes"), 2);
//test_open_sync(_("16 * 1kB open_sync writes"), 1);
}
执行结果示例:
5 seconds per test
O_DIRECT supported on this platform for open_datasync and open_sync.
Compare file sync methods using one 8kB write:
(in wal_sync_method preference order, except fdatasync is Linux's default)
open_datasync 63591.342 ops/sec 16 usecs/op
fdatasync 52872.641 ops/sec 19 usecs/op
fsync 52138.296 ops/sec 19 usecs/op
fsync_writethrough n/a
open_sync 65479.469 ops/sec 15 usecs/op
Compare file sync methods using two 8kB writes:
(in wal_sync_method preference order, except fdatasync is Linux's default)
open_datasync 32794.672 ops/sec 30 usecs/op
fdatasync 38832.670 ops/sec 26 usecs/op
fsync 36112.149 ops/sec 28 usecs/op
fsync_writethrough n/a
open_sync 33255.280 ops/sec 30 usecs/op
Compare open_sync with different write sizes:
(This is designed to compare the cost of writing 16kB in different write
open_sync sizes.)
1 * 16kB open_sync write 50401.319 ops/sec 20 usecs/op
2 * 8kB open_sync writes 32680.378 ops/sec 31 usecs/op
4 * 4kB open_sync writes 19378.457 ops/sec 52 usecs/op
Test if fsync on non-write file descriptor is honored:
(If the times are similar, fsync() can sync data written on a different
descriptor.)
write, fsync, close 47270.649 ops/sec 21 usecs/op
write, close, fsync 45598.608 ops/sec 22 usecs/op
Non-sync'ed 8kB writes:
write 380100.992 ops/sec 3 usecs/op
这里简单解释一下执行结果:
-
指定写入数据后同步到 SSD 有几种方式: -
open_datasync: open 文件的时候指定 O_DSYNC。 -
fdatasync: write 之后调用 fdatasync。 -
fsync: write 之后调用 fsync。 -
fsync_writethrough: 是 Windows 的,这里忽略。 -
open_sync: open 文件的时候指定 O_SYNC。 -
测试结果: -
8kB 的 sync 延迟不到 20us,16kB 的 sync 延迟大约 30us。 -
O_SYNC 和 O_DSYNC 延迟差别不大;fsync 和 fdatasync 延迟差别不大。 -
O_SYNC/O_DSYNC 的延迟看起来略优于 fsync/fdatasync。 -
直接写 page cache 不进行 sync 的延迟大概是 3us => 可以看出,sync 非常影响数据库应用的延迟/性能。
fio
fio[4] 是一个多线程 I/O 生成工具,可以生成多种 I/O 模式,可以用来测试 SSD 的性能,特别是 IOPS。
fio 是个功能挺复杂的工具,这里先简单介绍几个常用的命令参数:
-
--name=fio-test # 任务名称 -
--filename=/dev/sda # 读写的文件或者设备 -
--direct=1 # 使用 O_DIRECT 读写数据(绕过 page cache) -
--rw=randread # 随机读 -
--rw=randwrite # 随机写 -
--rw=randrw # 随机读写混合 -
--rw=read # 顺序读 -
--rw=write # 顺序写 -
--rw=rw # 顺序读写混合 -
--rwmixwrite=30 # 混合读写的模式下,写占30% -
--bs=4k # 单次 I/O 的数据块大小为 4k -
--bsrange=512-2048 # 单机 I/O 的数据块大小范围 -
--size=5G # 测试文件大小为 5GB -
--numjobs=30 # 30 个线程 -
--time_based # 基于时间运行,运行 --runtime 时间后结束 -
--runtime=600 # 测试时间为 600 秒,如果不写则一直将 --size 大小的文件写完为止 -
--ioengine=libaio # I/O 引擎,Linux 下一般用 libaio -
--group_reporting # 显示结果汇总每个进程的信息 -
--lockmem=1G # 只使用 1GB 内存进行测试 -
--iodepth=32 # I/O 队列深度,可以认为是使用异步 I/O 时同时提交的 I/O 数。
这里直接用几个实际例子来说明。
-
测试随机写 IOPS:4K 块、队列深度 32
fio \
--name=fio-test \
--filename=test.data \
--size=50G \
--bs=4k \
--rw=randwrite \
--ioengine=libaio \
--direct=1 \
--iodepth=32 \
--time_based \
--runtime=600 \
--group_reporting
-
测试随机读 IOPS:4K 块、队列深度 32
fio \
--name=fio-test \
--filename=test.data \
--size=50G \
--bs=4k \
--rw=randread \
--ioengine=libaio \
--direct=1 \
--iodepth=32 \
--time_based \
--runtime=600 \
--group_reporting
-
测试顺序写带宽:128K 块、队列深度 32
fio \
--name=fio-test \
--filename=test.data \
--size=50G \
--bs=128k \
--rw=write \
--ioengine=libaio \
--direct=1 \
--iodepth=32 \
--time_based \
--runtime=600 \
--group_reporting
-
测试顺序读带宽:128K 块、队列深度 32
fio \
--name=fio-test \
--filename=test.data \
--size=50G \
--bs=128k \
--rw=read \
--ioengine=libaio \
--direct=1 \
--iodepth=32 \
--time_based \
--runtime=600 \
--group_reporting
fio 的输出示例:
fio-test: (groupid=0, jobs=1): err= 0: pid=41759: Fri Dec 4 16:05:04 2020
write: IOPS=245k, BW=956MiB/s (1003MB/s)(560GiB/600001msec); 0 zone resets
slat (nsec): min=1251, max=1708.0k, avg=2791.81, stdev=1767.94
clat (usec): min=7, max=2171, avg=126.93, stdev=17.05
lat (usec): min=10, max=2174, avg=129.78, stdev=17.15
clat percentiles (usec):
| 1.00th=[ 85], 5.00th=[ 102], 10.00th=[ 111], 20.00th=[ 119],
| 30.00th=[ 123], 40.00th=[ 125], 50.00th=[ 126], 60.00th=[ 128],
| 70.00th=[ 130], 80.00th=[ 135], 90.00th=[ 145], 95.00th=[ 159],
| 99.00th=[ 182], 99.50th=[ 192], 99.90th=[ 210], 99.95th=[ 223],
| 99.99th=[ 253]
bw ( KiB/s): min=644168, max=999168, per=100.00%, avg=979886.11, stdev=23593.11, samples=1199
iops : min=161042, max=249792, avg=244971.55, stdev=5898.29, samples=1199
lat (usec) : 10=0.01%, 20=0.01%, 50=0.05%, 100=4.22%, 250=95.71%
lat (usec) : 500=0.01%, 750=0.01%, 1000=0.01%
lat (msec) : 2=0.01%, 4=0.01%
cpu : usr=28.70%, sys=71.31%, ctx=47128, majf=0, minf=31
IO depths : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=100.0%, >=64=0.0%
submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.1%, 64=0.0%, >=64=0.0%
issued rwts: total=0,146906790,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=32
Run status group 0 (all jobs):
WRITE: bw=956MiB/s (1003MB/s), 956MiB/s-956MiB/s (1003MB/s-1003MB/s), io=560GiB (602GB), run=600001-600001msec
Disk stats (read/write):
nvme2n1: ios=2/146881125, merge=0/0, ticks=0/2404296, in_queue=2433748, util=100.00%
上面是我在某台测试机器上用 fio 测试随机写 IOPS(4K 块、队列深度 32) 的输出结果。这里简单解释一下:
-
第一行没什么关键信息。 -
第二行总结了本次测试的结果:写操作 IOPS 245k,带宽(BW)1003 MB/s。 -
接下来的 slat、clat、lat 是延迟信息: -
slat: 提交延迟(submission latency)。提交延迟只对异步 I/O 有意义。 -
clat: 完成延迟(completion latency)。I/O 提交成功到 I/O 完成的时间。 -
lat: 总延迟(total latency)。I/O 请求从创建到完成的时间。 -
min 是本次测试的最小值;max 是本次测试的最大值;stdev 是标准差。
总结
本文介绍了 3 个可以用来测试 SSD 的工具:dd、pg_test_fsync、fio:
-
dd 命令功能比较简单,已经不适合用来测试 SSD 了。 -
pg_test_fsync 可以很方便快捷地测试 SSD 的 sync 性能。 -
fio 是个强大的工具,各种工作负载都能模拟,输出信息比较丰富:IOPS、带宽、延迟应有尽有。本文举的几个例子只是里面的九牛一毛(不过也达到了我本次的测试目的)。
一般网上或产品提供商提供的性能数据只能作为一个参考,因为不同的工作负载、不同的型号的 SSD 都会影响实际的使用性能,真实数据还是要自己实际测试才靠谱。
参考资料
Linux man page: https://man7.org/linux/man-pages/man1/dd.1.html
[2]PostgreSQL 的官网: https://www.postgresql.org/
[3]postgresql-13.1.tar.bz2: https://ftp.postgresql.org/pub/source/v13.1/postgresql-13.1.tar.bz2
[4]fio: https://fio.readthedocs.io/en/latest/fio_doc.html
以上是关于SSD 性能测试的主要内容,如果未能解决你的问题,请参考以下文章