IO调度器(IO Scheduler)是操作系统用来决定块设备上IO操作提交顺序的方法。存在的目的有两个,一是提高IO吞吐量,二是降低IO响应时间。然而IO吞吐量和IO响应时间往往是矛盾的,为了尽量平衡这两者,IO调度器提供了多种调度算法来适应不同的IO请求场景。其中,对数据库这种随机读写的场景最有利的算法是DEANLINE。接着我们按照从简单到复杂的顺序,迅速扫一下Linux 2.6内核提供的几种IO调度算法。
1、NOOP
NOOP(No Operation)算法的全写为No Operation。该算法实现了最最简单的FIFO队列,所有IO请求大致按照先来后到的顺序进行操作。之所以说“大致”,原因是NOOP在FIFO的基础上还做了简单的相邻IO请求的合并(注意只是做简单的合并,关键在于简单),并不是完完全全按照先进先出的规则满足IO请求。
2、CFQ
CFQ(Completely Fair Queuing)把进程当成了基本的调度单位,也就是说各个请求现在都是属于进程的,CFQ调度的是进程,当选择了某个进程的时候,他的请求才能够被发送到设备,否则只能在自己的队列里面待着(说进程可能不太准确,这个实际上是一个task_struct里面有一个,所以可能说内核线程更准确一些,各位看官有印象就好了,下面我就不区分了)。
3、DEADLINE
DEADLINE在CFQ的基础上,解决了IO请求饿死的极端情况。除了CFQ本身具有的IO排序队列之外,DEADLINE额外分别为读IO和写IO提供了FIFO队列。读FIFO队列的最大等待时间为500ms,写FIFO队列的最大等待时间为5s(当然这些参数都是可以手动设置的)。FIFO队列内的IO请求优先级要比CFQ队列中的高,,而读FIFO队列的优先级又比写FIFO队列的优先级高。优先级可以表示如下:
FIFO(Read) > FIFO(Write) > CFQ
deadline 算法保证对于既定的 IO 请求以最小的延迟时间,从这一点理解,对于 DSS 应用应该会是很适合的。
4、ANTICIPATORY
CFQ和DEADLINE考虑的焦点在于满足零散IO请求上。对于连续的IO请求,比如顺序读,并没有做优化。为了满足随机IO和顺序IO混合的场景,Linux还支持ANTICIPATORY调度算法。ANTICIPATORY的在DEADLINE的基础上,为每个读IO都设置了6ms的等待时间窗口。如果在这6ms内OS收到了相邻位置的读IO请求,就可以立即满足。
IO调度器算法的选择,既取决于硬件特征,也取决于应用场景。
在传统的SAS盘上,CFQ、DEADLINE、ANTICIPATORY都是不错的选择;对于专属的数据库服务器,DEADLINE的吞吐量和响应时间都表现良好。然而在新兴的固态硬盘比如SSD、Fusion IO上,最简单的NOOP反而可能是最好的算法,因为其他三个算法的优化是基于缩短寻道时间的,而固态硬盘没有所谓的寻道时间且IO响应时间非常短。
查看和修改IO调度器的算法非常简单。如下所示:
Task: View Current Disk scheduler
Assuming that your disk name /dev/sda, type:
# cat /sys/block/{DEVICE-NAME}/queue/scheduler
# cat /sys/block/sda/queue/scheduler
Sample output:
noop anticipatory deadline [cfq]
Task: Set I/O Scheduler For A Hard Disk
To set a specific scheduler, simply type the command as follows:
# echo {SCHEDULER-NAME} > /sys/block/{DEVICE-NAME}/queue/scheduler
For example, set noop scheduler, enter:
# echo noop > /sys/block/hda/queue/scheduler