Linux IO Scheduler(Linux IO 调度器)

     每一个块设备或者块设备的分区,都对应有自身的请求队列(request_queue),而每一个请求队列均可以选择一个I/O调度器来协调所递交的requestI/O调度器的基本目的是将请求按照它们对应在块设备上的扇区号进行排列,以减小磁头的移动,提升效率。每一个设备的请求队列里的请求将按顺序被响应。实际上,除了这个队列,每一个调度器自身都维护有不一样数量的队列,用来对递交上来的request进行处理,而排在队列最前面的request将适时被移动到请求队列中等待响应。html

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

      

      内核中实现的IO调度器主要有四种--Noop,Deadline,CFG, Anticipatory。
linux

1,Noop算法

     Noop调度算法是内核中最简单的IO调度算法。Noop调度算法也叫做电梯调度算法,它将IO请求放入到一个FIFO队列中,而后逐个执行这些IO请求,固然对于一些在磁盘上连续的IO请求,Noop算法会适当作一些合并。这个调度算法特别适合那些不但愿调度器从新组织IO请求顺序的应用。ios

     这种调度算法在如下场景中优点比较明显:算法

     1)在IO调度器下方有更加智能的IO调度设备。若是您的Block Device Drivers是Raid,或者SAN,NAS等存储设备,这些设备会更好地组织IO请求,不用IO调度器去作额外的调度工做;数据库

     2)上层的应用程序比IO调度器更懂底层设备。或者说上层应用程序到达IO调度器的IO请求已是它通过精心优化的,那么IO调度器就不须要多此一举,只须要按序执行上层传达下来的IO请求便可。api

     3)对于一些非旋转磁头氏的存储设备,使用Noop的效果更好。由于对于旋转磁头式的磁盘来讲,IO调度器的请求重组要花费必定的CPU时间,可是对于SSD磁盘来讲,这些重组IO请求的CPU时间能够节省下来,由于SSD提供了更智能的请求调度算法,不须要内核去多此一举。这篇文章说起了SSD中使用Noop效果会更好。服务器

2,Deadline算法

     Deadline算法的核心在于保证每一个IO请求在必定的时间内必定要被服务到,以此来避免某个请求饥饿。多线程

     Deadline算法中引入了四个队列,这四个队列能够分为两类,每一类都由读和写两类队列组成,一类队列用来对请求按起始扇区序号进行排序,经过红黑树来组织,称为sort_list;另外一类对请求按它们的生成时间进行排序,由链表来组织,称为fifo_list。每当肯定了一个传输方向(读或写),那么将会从相应的sort_list中将一批连续请求dispatch到requst_queue的请求队列里,具体的数目由fifo_batch来肯定。只有下面三种状况才会致使一次批量传输的结束:异步

    1)对应的sort_list中已经没有请求了

    2)下一个请求的扇区不知足递增的要求

    3)上一个请求已是批量传输的最后一个请求了。

     全部的请求在生成时都会被赋上一个期限值(根据jiffies),并定期限值排序在fifo_list中,读请求的期限时长默认为为500ms,写请求的期限时长默认为5s,能够看出内核对读请求是十分偏爱的,其实不只如此,在deadline调度器中,还定义了一个starved和writes_starved,writes_starved默认为2,能够理解为写请求的饥饿线,内核老是优先处理读请求,starved代表当前处理的读请求批数,只有starved超过了writes_starved后,才会去考虑写请求。所以,假如一个写请求的期限已经超过,该请求也不必定会被马上响应,由于读请求的batch还没处理完,即便处理完,也必须等到starved超过writes_starved才有机会被响应。为何内核会偏袒读请求?这是从总体性能上进行考虑的。读请求和应用程序的关系是同步的,由于应用程序要等待读取的内容完毕,才能进行下一步工做,所以读请求会阻塞进程,而写请求则不同,应用程序发出写请求后,内存的内容什么时候写入块设备对程序的影响并不大,因此调度器会优先处理读请求。

     默认状况下,读请求的超时时间是500ms,写请求的超时时间是5s。

     这篇文章说在一些多线程应用下,Deadline算法比CFQ算法好。这篇文章说在一些数据库应用下,Deadline算法比CFQ算法好。

3,Anticipatory算法

    Anticipatory算法的核心是局部性原理,它指望一个进程昨晚一次IO请求后还会继续在此处作IO请求。在IO操做中,有一种现象叫“假空闲”(Deceptive idleness),它的意思是一个进程在刚刚作完一波读操做后,看似是空闲了,不读了,可是实际上它是在处理这些数据,处理完这些数据以后,它还会接着读,这个时候若是IO调度器去处理另一个进程的请求,那么当原来的假空闲进程的下一个请求来的时候,磁头又得seek到刚才的位置,这样大大增长了寻道时间和磁头旋转时间。因此,Anticipatory算法会在一个读请求作完后,再等待必定时间t(一般是6ms),若是6ms内,这个进程上还有读请求过来,那么我继续服务,不然,处理下一个进程的读写请求。

     在一些场景下,Antocipatory算法会有很是有效的性能提高。这篇文章有说,这篇文章也有一份评测。

      值得一提的是,Anticipatory算法从Linux 2.6.33版本后,就被移除了,由于CFQ经过配置也能达到Anticipatory算法的效果。

4,CFQ算法

    CFQ(Completely Fair Queuing)算法,顾名思义,绝对公平算法。它试图为竞争块设备使用权的全部进程分配一个请求队列和一个时间片,在调度器分配给进程的时间片内,进程能够将其读写请求发送给底层块设备,当进程的时间片消耗完,进程的请求队列将被挂起,等待调度。 每一个进程的时间片和每一个进程的队列长度取决于进程的IO优先级,每一个进程都会有一个IO优先级,CFQ调度器将会将其做为考虑的因素之一,来肯定该进程的请求队列什么时候能够获取块设备的使用权。IO优先级从高到低能够分为三大类:RT(real time),BE(best try),IDLE(idle),其中RTBE又能够再划分为8个子优先级。实际上,咱们已经知道CFQ调度器的公平是针对于进程而言的,而只有同步请求(readsyn write)才是针对进程而存在的,他们会放入进程自身的请求队列,而全部同优先级的异步请求,不管来自于哪一个进程,都会被放入公共的队列,异步请求的队列总共有8(RT)+8(BE)+1(IDLE)=17个。

    从Linux 2.6.18起,CFQ做为默认的IO调度算法。

    对于通用的服务器来讲,CFQ是较好的选择。

 

对于使用哪一种调度算法来讲,仍是要根据具体的业务场景去作足benchmark来选择,不能仅靠别人的文字来决定。

5,更改IO调度算法

RHEL5/OEL5以及以后的版本中(好比RHEL6和RHEL7),能够针对每块磁盘制定I/O Scheduler,修改完毕马上生效,好比:

$ cat /sys/block/sda1/queue/scheduler
[noop] anticipatory deadline cfq

#修改成cfq
$ echo 'cfq'>/sys/block/sda1/queue/scheduler

#马上生效
$ cat /sys/block/sda1/queue/scheduler
noop anticipatory deadline [cfq]

 

6,一些磁盘相关的内核参数

/sys/block/sda/queue/nr_requests 磁盘队列长度。默认只有 128 个队列,能够提升到 512 个.会更加占用内存,但能更加多的合并读写操做,速度变慢,但能读写更加多的量


/sys/block/sda/queue/iosched/antic_expire 等待时间 。读取附近产生的新请时等待多长时间

 

/sys/block/sda/queue/read_ahead_kb
    这个参数对顺序读很是有用,意思是,一次提早读多少内容,不管实际须要多少.默认一次读 128kb 远小于要读的,设置大些对读大文件很是有用,能够有效的减小读 seek 的次数,这个参数可使用 blockdev –setra 来设置,setra 设置的是多少个扇区,因此实际的字节是除以2,好比设置 512 ,实际是读 256 个字节.
 
/proc/sys/vm/dirty_ratio

  这个参数控制文件系统的文件系统写缓冲区的大小,单位是百分比,表示系统内存的百分比,表示当写缓冲使用到系统内存多少的时候,开始向磁盘写出数 据.增大之会使用更多系统内存用于磁盘写缓冲,也能够极大提升系统的写性能.可是,当你须要持续、恒定的写入场合时,应该下降其数值,通常启动上缺省是 10.下面是增大的方法: echo ’40’> 

 

/proc/sys/vm/dirty_background_ratio

  这个参数控制文件系统的pdflush进程,在什么时候刷新磁盘.单位是百分比,表示系统内存的百分比,意思是当写缓冲使用到系统内存多少的时候, pdflush开始向磁盘写出数据.增大之会使用更多系统内存用于磁盘写缓冲,也能够极大提升系统的写性能.可是,当你须要持续、恒定的写入场合时,应该下降其数值,通常启动上缺省是 5.下面是增大的方法: echo ’20’ >

 

 /proc/sys/vm/dirty_writeback_centisecs

    这个参数控制内核的脏数据刷新进程pdflush的运行间隔.单位是 1/100 秒.缺省数值是500,也就是 5 秒.若是你的系统是持续地写入动做,那么实际上仍是下降这个数值比较好,这样能够把尖峰的写操做削平成屡次写操做.设置方法以下: echo ‘200’ > /proc/sys/vm/dirty_writeback_centisecs 若是你的系统是短时间地尖峰式的写操做,而且写入数据不大(几十M/次)且内存有比较多富裕,那么应该增大此数值: echo ‘1000’ > /proc/sys/vm/dirty_writeback_centisecs

 

/proc/sys/vm/dirty_expire_centisecs

  这个参数声明Linux内核写缓冲区里面的数据多“旧”了以后,pdflush进程就开始考虑写到磁盘中去.单位是 1/100秒.缺省是 30000,也就是 30 秒的数据就算旧了,将会刷新磁盘.对于特别重载的写操做来讲,这个值适当缩小也是好的,但也不能缩小太多,由于缩小太多也会致使IO提升太快.建议设置为 1500,也就是15秒算旧. echo ‘1500’ > /proc/sys/vm/dirty_expire_centisecs 固然,若是你的系统内存比较大,而且写入模式是间歇式的,而且每次写入的数据不大(好比几十M),那么这个值仍是大些的好.

 

参考文献

1,https://en.wikipedia.org/wiki/Noop_scheduler

2,https://en.wikipedia.org/wiki/Deadline_scheduler

3,https://en.wikipedia.org/wiki/Anticipatory_scheduling

4,https://en.wikipedia.org/wiki/CFQ

5,http://www.redhat.com/magazine/008jun05/features/schedulers/ 这篇文章介绍了四种IO调度算法而且针对他们的应用场景作了一个评测

6,http://www.dbform.com/html/2011/1510.html

7,https://support.rackspace.com/how-to/configure-flash-drives-in-high-io-instances-as-data-drives/ 这篇文章介绍了一些SSD中IO scheduler的配置

8,https://www.percona.com/blog/2009/01/30/linux-schedulers-in-tpcc-like-benchmark/

9,http://www.ibm.com/support/knowledgecenter/api/content/linuxonibm/liaat/liaatbestpractices_pdf.pdf

10,http://dl.acm.org/citation.cfm?id=502046&dl=GUIDE&coll=GUIDE

11,http://www.nuodb.com/techblog/tuning-linux-io-scheduler-ssds