type
status
date
slug
summary
tags
category
icon
password
在虚拟机中添加NVMe设备
由于我的虚拟机在创建的时候使用的是默认的设置,而默认设置中只会有一个SCSI硬盘,因此需要再为虚拟机额外添加一个NVMe磁盘设备(在配置之前需要关闭虚拟机)
添加NVMe设备
在需要安装NVMe设备的虚拟机界面中,点击“编辑虚拟机设置”
在弹出的界面中点击“添加”,并在新弹出的窗口中选择“硬盘”(NVMe是一个硬盘设备),然后点击下一步。
在弹出的窗口中选择NVMe
接下来的操作都是用来设定NVMe虚拟磁盘的存储方式的(比如是否需要新建一个虚拟磁盘、是使用一个文件描述该虚拟磁盘还是使用多个文件描述该虚拟磁盘),这个根据具体情况进行配置即可,这里就不再赘述了。
到这一步在/dev中就可以通过下面的指令找到nvme设备了:
NVMe磁盘分区
上面的操作相当于是为虚拟机创建了一个物理意义上的NVMe设备,接下来还需要为该设备创建分区
在终端中输入下面的指令,以使用fdisk为NVMe设备创建新分区
这里的/dev/nvme0n1需要替换为实际的disk路径
在终端引导中输入“n”(new,表示新建分区)后,剩下的部分直接使用默认配置即可。
退出fdisk之后,使用下面的指令查看磁盘分区的情况
我的输出结果为:
挂载分区
在挂载分区之前需要先格式化分区,即将分区格式化为某个文件系统的格式。
在这里我希望NVMe磁盘分区的文件系统与原有虚拟机上的磁盘分区的文件系统一致,因此需要查看一下之前挂载在ubuntu根目录上的磁盘分区的文件系统是什么。可以使用以下指令查看所有磁盘分区的文件系统:
从这里可以发现sda磁盘中的sda3分区(即挂载在根目录上的磁盘分区)使用的文件系统为ext4。因此我也会将NVMe磁盘分区格式化为ext4文件系统。使用以下的指令将NVMe磁盘分区格式化:
格式化磁盘之后就可以再次使用
lsblk -f
检查磁盘使用的文件系统。接下来就是将磁盘挂载到一个挂载点上。
挂载点实际上就是系统中的一个目录
使用以下指令可以将某一个磁盘的分区(即/dev/nvme0n1p1)挂载在/mnt/nvme挂载点上
接下来就可以使用
df -h
指令查看NVMe设备是否挂载成功:后面使用FIO的时候就可以直接在
/mnt/nvme
目录中创建测试文件,即可测试NVMe硬盘的性能。FIO的安装与使用
FIO的安装
这里直接把FIO的代码clone下来,然后按照Readme中Building部分进行安装即可:
FIO的使用
参考:
在使用FIO之前,需要先了解 一下FIO工具中各个参数的含义,以及FIO脚本如何编写
FIO参数
- filename:测试文件名称,通常选择需要测试的盘的data目录(也可以直接指定文件的路径)。
需要注意的是这个参数一定不能直接指定块设备,不然会导致文件系统损坏
- direct:是否使用directIO,测试过程绕过OS自带的buffer,使测试磁盘的结果更真实。
- direct=1:表示使用directIO
- direct=0:表示不使用directIO
Linux读写的时候,内核维护了缓存,数据先写到缓存,后面再后台写到SSD。读的时候也优先读缓存里的数据。这样速度可以加快,但是一旦掉电缓存里的数据就没了。所以有一种模式叫做DirectIO,跳过缓存,直接读写SSD。
- rw:表示测试的读写类型
- rw=randwread:测试随机读的I/O
- rw=randwrite:测试随机写的I/O
- rw=randrw:测试随机混合写和读的I/O
- rw=read:测试顺序读的I/O
- rw=write:测试顺序写的I/O
- rw=rw:测试顺序混合写和读的I/O
- bs:设定单次IO的块文件大小。如bs=4k,即设定单次IO的块文件大小为4k
- bsrange:指定IO数据块的大小范围。如bsrange=512-2048,即指定IO数据块的大小范围为512-2048字节。
- size:设定每个线程读写的数据量。如size=5G,表示每个线程读写的数据量为5G
- numjobs:指定每个任务开多少个线程。如numjobs=1,就表示每个任务开1个线程
- name:用于指定任务的名称。
如果通过fio -name=job1 -name=job2指令,建立了两个任务,那么这两个任务将共享-name=job1之前的参数。-name=job2之后的就是job2任务独有的参数。
- thread:表示使用线程进行测试(即使用pthread_create创建线程以执行测试任务)
另一种是fork创建进程。但是由于进程的开销比线程要大,所以一般都采用thread测试。
- runtime:用于指定测试时间。如runtime=1000表示测试时间为1000秒。
如果不指定测试时间则会一直将size大小的文件分bs(或bsrange)每次写完后停止。
- time_based:使用了这个参数后,如果在runtime指定的时间还没到时文件就被读写完成,将继续重复直到runtime时间结束。
- ioengine:指定io引擎。实际上就是IO的方式
- sync:使用标准的同步 I/O 操作(
read
和write
系统调用)。 - libaio:Linux本地异步I/O。
- mmap:使用内存映射 I/O(
mmap
系统调用)。 - posixaio:使用 POSIX 异步 I/O 接口(
aio_read
和aio_write
)。 - net:使用网络 I/O。
- splice:使用
splice
系统调用进行零拷贝 I/O 操作。
目前我认为这个IO引擎应该不是重点,所以没有更深入地去了解了
- iodepth:指定IO队列的深度。如iodepth=16,就表示IO队列的深度为16。
在异步模式下,CPU不能一直无限的发命令到SSD。比如SSD执行读写如果发生了卡顿,那有可能系统会一直不停的发命令,几千个,甚至几万个,这样一方面SSD扛不住,另一方面这么多命令会很占内存,系统也要挂掉了。这样,就带来一个参数叫做队列深度。这里的队列深度实际上跟NVMe SQ与CQ的队列深度的概念差不多。
- rwmixwrite:用于指定在混合读写的模式下,写操作的占比。如rwmixwrite=30表示在混合读写的模式下,写操作占比为30%
对应的还有一个rwmixread参数用于指示在混合读写模式下,读操作的占比。
- group_reporting:关于显示结果的参数,表示汇总每个进程的信息(也就是各个任务的信息不会单独显示,在后面会遇到这个问题)
- lockmem:设定用于测试的内存大小。lockmem=1g表示只使用1g内存进行测试。
- zero_buffers:表示用0初始化系统buffer
FIO脚本
FIO脚本具有如下的基本格式:
其中各个部分的作用已经在注释中指出了,这里就不再赘述了。
作为一个脚本,在FIO脚本中同样可以使用变量,并且使用变量的方式与在Makefile中使用变量的方式是相同的,直接使用“$”即可使用变量。
编写的FIO脚本已经上传了github仓库:
结果分析
一般测试
使用fio脚本测试的结果也存放在了github仓库中:
参考:
在同步IO下,测试结果为:
ㅤ | IOPS | BW | LAT(AVG) | LAT(STDEV) |
顺序读 | 4668 | 18.2MiB/s | 427.56us | 3350.10us |
顺序写 | 5185 | 20.3MiB/s | 383.72us | 2847.53us |
顺序读写(73) | 3326+1424 | 13.0MiB/s+5699KiB/s | 437.72us/377.62us | 2484.57us/4487.09us |
顺序读写(37) | 1496+3489 | 5987KiB/s+13.6MiB/s | 442.91us/381.59us | 2383.66us/3168.40us |
随机读 | 4408 | 17.2MiB/s | 451.99us | 3023.52us |
随机写 | 5123 | 20.0MiB/s | 387.67us | 2924.31us |
随机读写(73) | 3163+1363 | 12.4MiB/s+5455KiB/s | 477.38us/353.95us | 2490.11us/4344.45us |
随机读写(37) | 1424+3329 | 5697KiB/s+13.0MiB/s | 530.28us/371.11us | 3151.14us/3148.85us |
在异步IO下,测试结果为:
ㅤ | IOPS | BW | LAT(AVG) | LAT(STDEV) |
顺序读 | 16.4k | 64.1MiB/s | 3323.12us | 116024.89us |
顺序写 | 3046 | 11.9MiB/s | 20976.73us | 626750.13us |
顺序读写(73) | 2136+914 | 8544KiB/s+3658KiB/s | 20612.87us/21832.83us | 617780.87us/647541.35us |
顺序读写(37) | 915+2133 | 3663KiB/s+8533KiB/s | 19273.04us/21644.39us | 590345.99us/641811.81us |
随机读 | 21.1k | 82.3MiB/s | 2932.16us | 64796.87us |
随机写 | 3045 | 11.9MiB/s | 20999.45us | 626907.60us |
随机读写(73) | 2125+916 | 8502KiB/s+3665KiB/s | 20949.02us/21164.18us | 629906.01us/632482.49us |
随机读写(37) | 912+2133 | 3650KiB/s+8532KiB/s | 19836.96us/21510.89us | 600530.83us/639234.72us |
随机写测试
进行随机写测试之前需要将磁盘格式化,格式化的过程就是先取消挂载,然后重新格式化文件系统,然后再挂载上去就好了,因此可以使用下面的指令进行格式化:
接下来就是重新编写一个fio脚本来测试随机写测试,脚本同样放在了仓库中:
测试数据如下:
1 | 7983KiB/s | 16 | 7739KiB/s |
2 | 8017KiB/s | 17 | 7756KiB/s |
3 | 7751KiB/s | 18 | 7759KiB/s |
4 | 7843KiB/s | 19 | 7804KiB/s |
5 | 7731KiB/s | 20 | 7774KiB/s |
6 | 7779KiB/s | 21 | 7800KiB/s |
7 | 7738KiB/s | 22 | 7625KiB/s |
8 | 7980KiB/s | 23 | 7793KiB/s |
9 | 7900KiB/s | 24 | 6962KiB/s |
10 | 7730KiB/s | 25 | 7728KiB/s |
11 | 7765KiB/s | 26 | 7742KiB/s |
12 | 7775KiB/s | 27 | 7742KiB/s |
13 | 7841KiB/s | 28 | 7768KiB/s |
14 | 7737KiB/s | 29 | 8062KiB/s |
15 | 7739KiB/s | ㅤ | ㅤ |
杂记
- iodepth与同步的IO引擎不可以同时使用,因为当IO引擎为同步引擎时,不会存在IO指令过多的情况,也就不用设定IO队列的深度了
如果使用同步IO引擎并且iodepth≥1的话,就会弹出下面的信息:
- 在fio中如果使用了group_reporting,并且没有使用new_group创建额外的分组的话,那么所有的任务都会归到同一个任务组中,这样最终输出信息的报告也会只有一个
- 作者:Noah
- 链接:https://imnoah.top/article/NVMe/Task02
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。