应用负载的相关术语与模拟工具 #一文读懂 IO #
沈大公子  2024-11-25 14:46  发布于中国

虽然计算节点与存储系统通过一根线缆就可以建立连接,这个看似简单的连接却涉及非常多的内容。这种复杂性不仅仅涉及通信协议,即使传输的数据本身也有很多内容可以介绍。本节我们就传输数据本身的内容进行简要的介绍,希望大家对数据特征能够有一个比较全面的了解。

什么是IO?

我们在学习计算机原理的时候都接触过I/O(Input/Output,也即输入输出,读作eye ops)的概念。计算机系统与外围设备交互数据称为输入输出,比如我们敲击键盘,此时键盘向计算机输入数据。再比如我们打字的时候,在显示器显示内容就是计算机在输出数据。

在存储领域沿用上述I/O的概念,无疑存储系统相对于计算机来说是一个外围设备,所以计算机与存储系统交互也称为I/O。在存储相关概念中,通常将I/O简写为IO,表示计算节点与存储系统传输的一个数据块。如果这个数据包是计算机读取的数据,也就是从存储系统到计算节点,称为读IO;如果这个数据包时计算机写入的数据,也就是从计算节点到存储系统的,称为写IO。

IO是如何产生的?

存储的IO产生自应用对存储设备的读写请求。在Linux操作系统中可以通过读写文件的API读写文件或者块设备,此时会产生IO。如下是读文件的函数原型和写文件的函数原型。

ssize_t read(int fd, void *buf, size_t count);

ssize_t write(int fd, const void *buf, size_t count);

上述函数,read函数会产生一个读IO,write函数会产生一个写IO。IO的大小与上述函数的第3个参数有关。

什么是IO的大小

既然IO是一个数据块,那么就有大小之说,一次传输的数据块的大小就是IO大小(IO Size)。IO的大小与硬盘容量的单位一致,常用如字节(Bytes)、KB(Kilobytes)和MB(Megabytes)等描述。IO大小通常与应用程序相关,比如关系型数据库以页(8KB或者16KB等)为粒度读写硬盘,Linux内核以4KB为粒度缓存数据。

存储领域经常会听到大IO和小IO的概念,其实并没有一个具体的界限来限定大IO和小IO,但是我们可以轻易的分辨出1MB的IO一定是大IO,而512字节的IO一定是小IO。为什么要了解大IO和小IO这些概念?

因为IO大小不同,其传输特性会有差异。比如小IO可以传输的很快,而大IO则会传输的比较慢。我们可以举一个简单的例子,比如你要搬家,并且家里有很多书要搬(比如100本)。此时,你需要将这些书搬到外面的货车上。你可以一本一本(小IO)的搬到货车上,这个时候你需要跑100趟;你也可以同一个大箱子装这100本书(大IO),但是你自己可能搬不动,即使两个人抬估计移动的也会非常慢。对比而言,拿一本书,你可以很快的在书柜和货车之间穿梭,但是如果是100本书,你需要先整理到箱子里,然后还需要人帮你慢慢的抬到货车上。

什么是IOPS

IOPS的全称是Input/Output operations (or requests) Per Second,也就是每秒钟IO的数量。那么IOPS与什么因素有关系呢?关于IOPS的应用因素,我们可以借助高速公路上车辆通过收费站进行类比。比如大卡车通常拉的货物比较多,其长度也比较长,可以理解为大IO;小卡车拉的货物少,而其长度比较短,可以理解为小IO。如果以相同的速度经过收费站,那么单位时间(比如一小时)内通过的小卡车的数量一定比大卡车多。

640.jpg

高速公路的收费站通常是双向的,因此可以通过不同的方向描述单位时间内通过收费站车辆的数量。如下图所示,假设在一秒钟中内,下图中的4辆卡车都通过了收费站,我们可以认为IOPS是4。如果从单个方向上来看,从左往右的IOPS是3,而从右往左的IOPS是1。

640.jpg

计算节点访问存储系统与此类似,计算节点对存储的请求包含读请求和写请求。其中1秒钟内完成读请求的数量称为读IOPS,1秒钟内完成写请求的数量称为写IOPS,而两者的总和称为IOPS。

什么是吞吐量

IOPS是描述的每秒钟IO的数量,而吞吐量则是描述的单位时间(通常为1秒)内数据量。以上图中双向通过收费站的常量为例,IOPS为4,而吞吐量则为64KB + 3 * 4KB,也就是76KB/s。

什么是响应时间(Response Time)

响应时间是一个请求经过发送,处理,到最后获得应答的所消耗的时间。以读请求为例,其响应时间是发送读取命令,存储端检索数据,最终到计算节点收到实际数据所消耗的时间总会。

通常而言,相同大小IO的读取请求的响应时间要比写请求的响应时间长。主要是存储系统当中,写请求需要更多的处理过程。对于不同大小的IO,大IO的响应时间要比小IO的响应时间长。

由于请求的响应时间涉及发送、处理和应答,也就是涉及网络传输和存储系统处理两方面的内容,因此网络设备与存储介质是响应时间的直接影响因素。当然,也与存储系统软件算法有着非常大的关系。

什么是读写比率

大多数应用对于存储的访问既有读请求又有写请求,其中读写请求的占比称为读写比率,比如70%读,30%写。读写比率是应用程序负载的一个非常重要的指标,我们知道写请求通常比读请求慢,所以读写比率不同,对存储系统的承载能力要求就不同。

顺序IO与随机IO

顺序IO是指一系列的IO,并且这一系列的IO是首尾相连的,如下图上半部分所示。随机IO是这一系列的IO完全没有规律,甚至一会儿前,一会儿后。

640.jpg

顺序IO与随机IO的性能差异与存储介质的类型有个。存储介质从访问性方面可以分为顺序介质、旋转介质和随机介质。其中磁带是典型的顺序介质;机械硬盘是典型的旋转介质;内存和SSD硬盘是典型的随机介质。

内存和SSD硬盘可以很好的处理随机IO,但磁带和机械硬盘的随机IO访问则会产生很大的访问延迟。以磁带为例,如果需要访问磁带旋转顺序相反的数据需要一个倒带的过程,这个过程非常耗时。机械硬盘与磁带类似,因为硬盘访问数据是通过磁臂摆动和盘片旋转定位数据的,非连续IO将导致磁臂的大幅摆动,且需要等待盘片旋转到指定位置,这些机械动作都是非常耗时的。

IO队列与队列深度

从概念上来说,IO队列是一种组织IO请求的数据结构,用于管理多个IO请求。队列深度则是指队列中IO请求的数量。我们依然可以通过高速公路收费站的例子理解一下队列的作用。如下图所示,假设收费站一分钟可完成2个收费任务。没有队列或者队列深度为1的情况下,收费任务可以马上完成,但是一分钟只能完成一个。因为在这个任务完成后,后面的卡车还要好久才能过来。

第二个实例,如果两辆开车同时过来,排队过收费站,那么在一分钟中就可以完成2个收费任务,每一辆车都可以在1分钟内完成缴费,也就是等待时间不会超过一分钟。但是如果排队的卡车数量达到6辆或者更多,那么由于收费站每分钟只能完成两个收费任务,所以第6辆车需要3分钟后才能完成缴费。对应于存储系统,如果计算节点队列深度与存储系统的处理能力匹配,那么可以达到最大IOPS,如果队列深度过大,虽然IOPS可以达到比较大的值,但是IO延时将会增加。

640.jpg

上面概念基本上涵盖了计算节点负载特征和存储系统处理能力的相关描述。在一个实际的业务当中,只有对上述内容有一个清晰的了解才能明确业务对存储系统的性能要求,及当前存储系统是否可以满足业务的性能指标要求。

负载模拟工具fio

在实际生产中我们经常需要检测存储系统的承载能力,但是搭建一个实际的应用环境会比较复杂,一般会借助一些工具测试存储系统的承载能力,比如fio或者vdbench等。以fio为例,我们可以在Linux中通过如下一个命令行启动一个测试任务。

fio --filename=/dev/sdd --direct=1 --rw=randwrite --bs=16K --ioengine=libaio --iodepth=256 --numjobs=8 --time_based --group_reporting --name=iops-test-job

在上述命令行中,各个选项的含义如下:

filename: 要写入数据的位置,上例中目的是测试硬盘sdd。我们可以同时指定多个设备,比如/dev/sdd:/dev/sde则表示同时测试sdd和sde两个硬盘。

direct: 表示是否经过缓存,如果为1则表示不经过缓存直接访问块设备。在Linux操作系统中与O_DIRECT旗标对应,而有些操作系统并不支持直接IO,此时该选项失效。

rw: rw是readwrite的缩写,表示访问模式,可以是read、write、trim、readwrite、randread、randwrite和randrw等。

bs: bs是blocksize的缩写,表示IO的大小,该选项的缺省值是4096。由于rw可以指定同时读写,对应的bs可以分别指定读写的不同IO大小,比如8k,32k表示读为8K,写为32K。

ioengine: 表示访问存储设备的接口模式,常用的如sync(表示同步接口,与调用read和write函数类似)、libaio(Linux原生异步接口)和io_uring等。总数量达到几十种,而且每种操作系统平台略有差异。

iodepth:队列深度

numjobs:测试任务的数量,每个任务对应着一个进程或者一个线程。通过多个job可以提高业务压力。

当然,我们这里只是简单的介绍了一下fio的常用选项,实际可用的选项要比我们介绍的多得多。命令行方式非常直接,简单,但是如果想模拟更加复杂的负载的话,可用借助fio的配置文件。如下是一个fio配置文件的实例,在这里实例中我们配置了两个不同的任务,一个用于测试sdd的写性能,另外一个用于测试sdc的读性能。

[global]

runtime=20

time_based

;–start job file —

[random-write]

filename=/dev/sdd

ioengine=libaio

iodepth=4

rw=randwrite

bs=32k

direct=0

size=64m

numjobs=4

;–end job file–

;–start job file —

[random-read]

filename=/dev/sdc

ioengine=libaio

iodepth=4

rw=randread

bs=32k

direct=0

size=64m

numjobs=4

;–end job file–

当测试完成后,会生成一个测试报告,报告中会包含IOPS和带宽等信息。同时还会包含关于IO延时的统计信息,具体如下图所示。

640.jpg

至此,我们介绍了应用负载相关的术语及负载模拟工具。通过对相关术语的理解能够对计算机存储技术能够有更加全面的认识。

文章来源:数据存储张 公众号

全部回复(
回复
回复