【银河麒麟V10】【服务器】IO调度算法与性能分析

一、IO调度算法介绍

1、IO调度器(IO Scheduler)概念

     IO调度器(IO Scheduler)是操作系统用来决定块设备上IO操作提交顺序的方法。存在的目的有两个,一是提高IO吞吐量,二是降低IO响应时间。然而IO吞吐量和IO响应时间往往是矛盾的,为了尽量平衡这两者,IO调度器提供了多种调度算法来适应不同的IO请求场景。如:对数据库这种随机读写的场景最有利的算法是DEANLINE。

IO调度器在内核栈中所处位置如下:

【银河麒麟V10】【服务器】IO调度算法与性能分析_第1张图片

 【银河麒麟V10】【服务器】IO调度算法与性能分析_第2张图片

                                              【银河麒麟V10】【服务器】IO调度算法与性能分析_第3张图片

IO scheduler 的作用主要是为了减少磁盘转动的需求。主要通过2中方式实现:

1.合并
2.排序

每个设备都会自己对应请求队列,所有的请求在被处理之前都会在请求队列上。 在新来一个请求的时候如果发现这个请求和前面的某个请求请求的位置是相邻的,那么就可以合并为一个请求。如果不能找到合并的,就会按照磁盘的转动方向进行排序。通常IO scheduler 的作用就是为了在进行合并和排序的同时,也不会太影响单个请求的处理时间。

2、常用IO算法介绍:

2.1、NOOP算法(电梯式调度程序)

【银河麒麟V10】【服务器】IO调度算法与性能分析_第4张图片

noop是什么?
     noop是一种输入输出调度算法 . NOOP, No Operation. 什么都不做,请求来一个处理一个。这种方式事实起来简单,也更有效。问题就是disk seek 太多,对于传统磁盘,这是不能接受的。 但对于SSD 磁盘就可以,因为SSD 磁盘不需要转动。
noop的别称:
     又称为电梯调度算法。
noop原理是怎样的?
     将输入输出请求放到一个FIFO队列中,然后按次序执行队列中的输入输出请求:

当来一个新请求时:

     如果能合并就合并
     如果不能合并,就会尝试排序。 如果队列上的请求都已经很老了,这个新的请求就不能插队,只能放到最后面。否则就插到合适的位置
     如果既不能合并,有没有合适的位置插入,就放到请求队列的最后。
适用场景:
     4.1 在不希望修改输入输出请求先后顺序的场景下;
  4.2 在输入输出之下具有更加智能调度算法的设备,如NAS存储设备;
  4.3 上层应用程序已经精心优化过的输入输出请求;
  4.4 非旋转磁头式的磁盘设备,如SSD磁盘

2.2、CFQ(Completely Fair Queuing, 完全公平排队)

     CFQ(Completely Fair Queuing)算法,顾名思义,绝对公平算法。它试图为竞争块设备使用权的所有进程分配一个请求队列和一个时间片,在调度器分配给进程的时间片内,进程可以将其读写请求发送给底层块设备,当进程的时间片消耗完,进程的请求队列将被挂起,等待调度。

每个进程的时间片和每个进程的队列长度取决于进程的IO优先级,每个进程都会有一个IO优先级,CFQ调度器将会将其作为考虑的因素之一,来确定该进程的请求队列何时可以获取块设备的使用权。

IO优先级从高到低可以分为三大类:

 RT(real time)
 BE(best try)
 IDLE(idle)

其中RT和BE又可以再划分为8个子优先级。可以通过ionice 去查看和修改。 优先级越高,被处理的越早,用于这个进程的时间片也越多,一次处理的请求数也会越多。

实际上,我们已经知道CFQ调度器的公平是针对于进程而言的,而只有同步请求(read或syn write)才是针对进程而存在的,他们会放入进程自身的请求队列,而所有同优先级的异步请求,无论来自于哪个进程,都会被放入公共的队列,异步请求的队列总共有8(RT)+8(BE)+1(IDLE)=17个。

从Linux 2.6.18起,CFQ作为默认的IO调度算法。对于通用的服务器来说,CFQ是较好的选择。具体使用哪种调度算法还是要根据具体的业务场景去做足benchmark来选择,不能仅靠别人的文字来决定。

2.3、DEADLINE算法(截止时间调度程序)

     DEADLINE在CFQ的基础上,解决了IO请求饿死的极端情况

     除了CFQ本身具有的IO排序队列之外,DEADLINE额外分别为读IO和写IO提供了FIFO队列。

【银河麒麟V10】【服务器】IO调度算法与性能分析_第5张图片

读FIFO队列的最大等待时间为500ms,写FIFO队列的最大等待时间为5s(当然这些参数都是可以手动设置的)。

FIFO队列内的IO请求优先级要比CFQ队列中的高,,而读FIFO队列的优先级又比写FIFO队列的优先级高。优先级可以表示如下:

 FIFO(Read) > FIFO(Write) > CFQ
 

deadline 算法保证对于既定的 IO 请求以最小的延迟时间,从这一点理解,对于 DSS 应用应该会是很适合的。

deadline 实际上是对Elevator 的一种改进:
 1 避免有些请求太长时间不能被处理。
 2 区分对待读操作和写操作。

deadline IO 维护3个队列。第一个队列和Elevator 一样, 尽量按照物理位置排序。 第二个队列和第三个队列都是按照时间排序,不同的是一个是读操作一个是写操作。

deadline IO 之所以区分读和写是因为设计者认为如果应用程序发了一个读请求,一般就会阻塞到那里,一直等到的结果返回。 而写请求则不是通常是应用请求写到内存即可,由后台进程再写回磁盘。应用程序一般不等写完成就继续往下走。 所以读请求应该比写请求有更高的优先级。

在这种设计下,每个新增请求都会先放到第一个队列,算法和Elevator的方式一样,同时也会增加到读或者写队列的尾端。这样首先处理一些第一队列的请求,同时检测第二/三队列前几个请求是否等了太长时间,如果已经超过一个阀值,就会去处理一下。 这个阀值对于读请求时 5ms, 对于写请求时5s.

个人认为对于记录数据库变更日志的分区,例如oracle 的online log, mysql 的binlog 等等,最好不要使用这种分区。 因为这类写请求通常是调用fsync 的。 如果写完不成,也会很影响应用性能的。

2.4、ANTICIPATORY算法

     CFQ和DEADLINE考虑的焦点在于满足零散IO请求上。对于连续的IO请求,比如顺序读,并没有做优化。

为了满足随机IO和顺序IO混合的场景,Linux还支持ANTICIPATORY调度算法。ANTICIPATORY的在DEADLINE的基础上,为每个读IO都设置了6ms的等待时间窗口。如果在这6ms内OS收到了相邻位置的读IO请求,就可以立即满足。

2.5、集中调度算法之间的比较

     mq-deadline调度器跟单队列的deadline调度器发挥的功能很相似。它有个insert_request()函数,不会使用多个staging队列,而是把请求放到两个全局的基于时间的队列中 - 一个放读请求,一个放写请求,先尝试把该新请求与已经存在的请求合并,如果合并不了,则把这个新请求放到队列尾部。dispatch_request()函数会从这些队列中返回第一个请求:基于时间的队列,基于请求批大小,以及避免写饥饿的队列。

     bfq调度器,即Budget Fair Queueing, 一定程度上是借鉴cfq实现的。内核里有介绍bfq的文档(Documentation/block/bfq-iosched.txt),几年前lwn上有篇讲bfq的文章(https://lwn.net/Articles/601799/),后来又出了一篇文章(https://lwn.net/Articles/709202/), 跟进了bfq被吸纳进multi-queue的情况。bfq有一点比较像mq-deadline,没有使用多个per-CPU staging队列。bfq有多个队列,每一个队列由一把自旋锁保护。

     与mq-deadline和bfq不一样,kyber IO调度器,在这篇文章中(https://lwn.net/Articles/720675/)有简单介绍,它使用了per-CPU的staging队列,也没有实现自己的insert_request()函数,而用的是默认行为。dispatch_request()函数为每一个硬件上下文都维护着各种内部队列,如果这些队列是空的,它就会从给定的硬件上下文所对应的所有staging队列来收集请求,然后在内部将请求做个分布,如果硬件队列不是空的,它就直接从对应的内部队列里下发请求。关于kyber调度器的这几个方面内核没有相应的注释和文档:策略解释,请求分布,以及处理顺序。

3、单队列与多队列

3.1、单队列与多队列介绍

     说明:"request层"并存着两种模型:单队列(single-queue) 和 多队列(multi-queue)。多队列的出现也就是近几年的事情,也许总有一天会完全取代单队列的,但是目前来看两者在内核的使用都相当活跃。

     单队列(single-queue) 框架

     早期的存储设备是磁盘,特点是机械运动寻址、且不支持多硬件队列并发处理 io,所以代码逻辑自然地设计了一个软件分发队列,这种软件逻辑上只有一个分发队列的架构称作 single-queue 架构,由于软件本身的开销(多核访问 request queue 需要获取 request_queue->queue_lock 等原因),single-queue 的 IOPS 能达到百万到千万级别的数据量,由于早期存储器件速度慢,百万的 IOPS 已经完全能够满足需求。

     多队列(multi-queue) 架构

     当支持多队列的高速存储器件出现后,器件端处理的时间变短,single-queue 引入的软件开销变得突出,软件成为性能瓶颈,导致性能瓶颈的因素有 3 个:

1) 所有 cpu 共享一个 request queue,对 request_queue->queue_lock 的竞争比较多。

2) 大多数情况下,完成一次 io 需要两次中断,一个是硬件中断,一个是 IPI 核间中断用于触发其他 cpu 上的软中断。

3) 如果提交 io 请求的 cpu 不是接收到硬件中断的 cpu,还存在远端内存访问的问题。

Jens Axboe(block maintainer) 针对 single-queue 存在的问题,提出了 multi-queue 架构,这种架构为每个 cpu 分配一个软件队列(称为 soft context dispatch q),又根据存储器件的硬件队列(hardware q)数量分配了相同数量的硬件上下文分发队列(hard context dispatch q,这是软件逻辑上的队列),通过固定的映射关系,将 1 个或多个 soft context dispatch q 映射到 1 个 hard context dispatch q,再将 hard context dispatch q 与存储器件的 hardware q 一一对应起来,达到并发处理的效果,提升 IO 性能。

     常用IO调度算法分类:

【银河麒麟V10】【服务器】IO调度算法与性能分析_第6张图片

3.2、单队列与多队列的区别

【银河麒麟V10】【服务器】IO调度算法与性能分析_第7张图片

4、如何选择I/O调度器

说明:为什么需要IO调度呢?在最开始的时候,Linux存储在磁盘上。磁盘盘片高速旋转,通过磁臂的移动读取数据。磁臂的移动是物理上的机械上的移动,它无法瞬移,这速度是很慢的。如果我们读取的数据位置很随机,一会在A地点,一会在隔着老远的B地点,移动的时间就全做了无用功,这也就是我们说的随机读写性能慢的原因。如果读取的数据地址是连续的,即使不是连续的也是地址接近的,那么移动磁臂的时间损耗就少了。在最开始,IO调度的作用就是为了合并相近的IO请求,减少磁臂的移动损耗。

 noop推荐场景:闪存等存储介质(如SSD)、个人PC;

     a、闪存介质适合noop原因:noop先来先处理的做法对磁盘来说时间损耗非常大,大量浪费了磁盘磁臂移动的时间。但是对闪存设备,例如mmc、nand等,却是最好的选择,因为闪存设备的物理结构跟磁盘完全不同,其通过一些规范的命令即可读取数据,没有磁臂这东西。此时IO调度算法里的排序、合并其实没太大意义,反而浪费了CPU和内存。

     b、个人PC适合noop原因:在个人PC的场景上,往往需要打开大量的程序,创建大量的进程。每个进程都可能有IO的请求。在这场景下,我们需要的是如何确保不同进程或进程组间IO资源使用的公平性。总不能因为A进程要拷贝电影,独占了IO资源,导致B进程无法打开文档不是?
cfq调度算法是以进程之间公平享用IO资源为出发点设计的,所以,个人PC建议使用cfq调度算法,但cfq调度算法不仅仅用于个人PC,准确来说,cfq调度算法适用于有大量进程的多用户系统。

deadline推荐场景:对IO压力比较重,且功能比较单一的场景,例如数据库服务器

     a、数据库服务器适合deadline原因:deadline是一种以提高机械硬盘吞吐量为思考出发点的调度算法,所以准确来说,deadline调度算法适用于IO压力比较重,且业务功能单一的场景,而数据库毫无疑问是最为匹配的场景了。

5、I/O调度方法的查看与设置

 1、查看当前麒麟系统的I/O调度方法

# cat /sys/block/sda/queue/scheduler

如上图表示当前算法是bfq算法,也是默认麒麟服务器默认算法

 2、查看麒麟系统当前支持的I/O调度算法

# dmesg | grep -i scheduler

【银河麒麟V10】【服务器】IO调度算法与性能分析_第8张图片

 3、临时更改I/O调度方法

# echo mq-deadline > /sys/block/sda/queue/scheduler      //将I/O调度算法临时改为mq-deadline,sda为盘符名称,根据实际情况修改

 4、永久更改I/O调度算法

# vim  /etc/udev/rules.d/99io_scheduler.rules       //改文件如需新建,然后添加以下内容:

ACTION=="add|change",KERNEL=="sda",ATTR{queue/scheduler}="mq-deadline"

 重启测试可以发现调度算法仍然为更改后的调度算法

二、iostat 工具

三、iotop工具

四、IO测试工具之fio

五、IO测试工具之iozone

5.1、资源链接

iozone源码包和使用手册下载链接:

链接:https://pan.baidu.com/s/1qIV2p2RbQZ2DT9PAJJcsqQ 
提取码:1234 
 

iozone官网(含最新安装包下载地址):

Iozone Filesystem Benchmarkhttps://www.iozone.org/

5.2、iozone安装

将源码tar包正常解压后开始编译安装:

# cd  iozone3_424/src/current
# make clean
# make           //后会出现编译的目标平台


X86平台:
# make linux-AMD64
arm平台:
# make linux-arm

5.3、运行测试

后续待更......

你可能感兴趣的:(#,服务器操作系统)