IO 调度算法 NOOP NOOP 全称 No Operation,中文名称电梯式调度器,该算法实现了最简单的 FIFO 队列,所有 I/O 请求大致按照先来后到的顺序进行操作。之所以说”大致“,原因是 NOOP 在 FIFO 的基础上还做了相邻 IO 请求的合并,并不是完完全全按照先进先出的规则满足 IO 请求。它是基于先入先出(FIFO)队列概念的 Linux 内核里最简单的 I/O 调度器。此调度程序最适合于固态硬盘。
Anticipatory Anticipatory 的中文含义是”预料的,预想的”,顾名思义有个 I/O 发生的时候,如果又有进程请求 I/O 操作,则将产生一个默认的 6 毫秒猜测时间,猜测下一个进程请求 I/O 是要干什么的。这个 I/O 调度器对读操作优化服务时间,在提供一个 I/O 的时候进行短时间等待,使进程能够提交到另外的 I/O。Anticipatory 算法从 Linux 2.6.33 版本后被删除了,因为使用 CFQ 通过配置也能达到 Anticipatory 的效果。
DeadLine Deadline 翻译成中文是截止时间调度器,它避免有些请求太长时间不能被处理。另外可以区分对待读操作和写操作。DEADLINE 额外分别为读 I/O 和写 I/O 提供了 FIFO 队列。
CFQ CFQ 全称 Completely Fair Scheduler ,中文名称完全公平调度器,它是现在许多 Linux 发行版的默认调度器,CFQ 是内核默认选择的 I/O 调度器。该算法的特点是按照 IO 请求的地址进行排序,而不是按照先来后到的顺序来进行响应。CFQ 为每个进程/线程,单独创建一个队列来管理该进程所产生的请求,也就是说每个进程一个队列,各队列之间的调度使用时间片来调度,
相关命令 查看当前系统支持的I/O调度器 1 2 3 4 5 [sankuai@dx-cloud-climc01 ~]$ dmesg | grep -i scheduler io scheduler noop registered io scheduler anticipatory registered io scheduler deadline registered io scheduler cfq registered (default)
查看一个硬盘使用的I/O调度器 1 2 [sankuai@gh-cloud-mss-store02 ~]$ cat /sys/block/sda/queue/scheduler noop anticipatory deadline [cfq]
查看调度算法参数的含义 1 2 yum -y install kernel-doc 比如:/usr/share/doc/kernel-doc-2.6.32/Documentation/block/deadline-iosched.txt
永久修改系统 IO 调度器 grubby 命令 1 $ grubby --grub --update-kernel=ALL --args="elevator=deadline
配置文件 1 2 3 4 5 6 7 使用vi编辑器修改grub配置文件 # vi cat /etc/default/grub # 修改第五行,在行尾添加 elevator= cfq 然后保存文件,重新编译配置文件, # grub2-mkconfig -o /boot/grub2/grub.cfg
算法调优 CFQ 算法参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 [sankuai@gh-cloud-mss-store02 ~]$ ls -l /sys/block/sda/queue/iosched total 0 -rw-r--r-- 1 root root 4096 Jun 4 11:53 back_seek_max -rw-r--r-- 1 root root 4096 Jun 4 11:53 back_seek_penalty -rw-r--r-- 1 root root 4096 Jun 4 11:53 fifo_expire_async -rw-r--r-- 1 root root 4096 Jun 4 11:53 fifo_expire_sync -rw-r--r-- 1 root root 4096 Jun 4 11:53 group_idle -rw-r--r-- 1 root root 4096 Jun 4 11:53 group_isolation -rw-r--r-- 1 root root 4096 Jun 4 11:53 low_latency -rw-r--r-- 1 root root 4096 May 24 2017 quantum -rw-r--r-- 1 root root 4096 Jun 4 11:53 slice_async -rw-r--r-- 1 root root 4096 Jun 4 11:53 slice_async_rq -rw-r--r-- 1 root root 4096 May 24 2017 slice_idle -rw-r--r-- 1 root root 4096 Jun 4 11:53 slice_sync
back_seek_max
默认 16384。该参数规定了磁头向后寻址的最大范围,默认值是16M。对于请求所访问的扇区号在磁头后方的请求,cfq 会像向前寻址的请求一样调度他。
back_seek_penalty
默认值是 2。该参数用来计算向后寻址的代价。相对于前方查找,后方查找的距离为 1/2(1/back_seek_penalty)时,cfq 调度时就认为这两个请求寻址的代价是相同的。
fifo_expire_async
默认值是250ms。该参数用来控制异步请求的超时时间。如果队列被激活后,则优先检查是否有请求超时,如果有超时的请求,则派发。但是,在队列激活的期间内,只会派发 一个超时的请求,其余的请求按照请求的优先级,以及所访问的扇区号大小来派发。
fifo_expire_sync
默认值是125ms。功能类似于fifo_expire_async参数,该参数用于控制同步请求的超时时间,。
group_idle
默认值是 8ms,该参数为了提高吞吐量。
group_isolation
该参数用来标识应用程序所在的cgroup。
low_latency
该参数用来表示低延迟,默认值是1ms
quantum
该参数用于控制队列派发到设备驱动层所含有的请求数,默认值是8。不管是同步队列还是异步队列, 在时间片内,超过这个限制,则不再派发请求。对于异步队列 而言,请求数的派发个数还取决于参数slice_async_rq。
slice_async
默认值是 40ms。这个参数功能同slice_sync,但是用来计算异步队列的时间片。 异步队列的时间片的计算公式是:time slice=slice_async + (slice_async/5 * (4 - priority));
slice_async_rq
默认值是 2。这个参数用来计算在时间片内异步请求被派发的最大数。同样,最大请求数也依赖于队列的优先级。计算公式是:最大请求数=2 * slice_async_rq( 8 –priority );
slice_idle
默认值是 8ms,这个参数只控制同步队列的idle time。当同步队列当前没有请求派发时,并不切换到其他队列,而是等待 8ms,以便让应用程序产生更多的请求。直到同步队列的时间片用完。
slice_sync
默认值是 100ms。这个参数用来计算同步队列的时间片, 默认值是100ms。时间片还依赖于队列的优先级。同步队列的时间片的计算公式是:time slice=slice_sync + (slice_sync/5 * (4 - priority));
Anticipatory 算法参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 The parameters are: * read_expire Controls how long until a read request becomes "expired". It also controls the interval between which expired requests are served, so set to 50, a request might take anywhere < 100ms to be serviced _if_ it is the next on the expired list. Obviously request expiration strategies won't make the disk go faster. The result basically equates to the timeslice a single reader gets in the presence of other IO. 100*((seek time / read_expire) + 1) is very roughly the % streaming read efficiency your disk should get with multiple readers. * read_batch_expire Controls how much time a batch of reads is given before pending writes are served. A higher value is more efficient. This might be set below read_expire if writes are to be given higher priority than reads, but reads are to be as efficient as possible when there are no writes. Generally though, it should be some multiple of read_expire. * write_expire, and * write_batch_expire are equivalent to the above, for writes. * antic_expire Controls the maximum amount of time we can anticipate a good read (one with a short seek distance from the most recently completed request) before giving up. Many other factors may cause anticipation to be stopped early, or some processes will not be "anticipated" at all. Should be a bit higher for big seek time devices though not a linear correspondence - most processes have only a few ms thinktime. In addition to the tunables above there is a read-only file named est_time which, when read, will show: - The probability of a task exiting without a cooperating task submitting an anticipated IO. - The current mean think time. - The seek distance used to determine if an incoming IO is better.
测试-fio 可以使用专业的工具 fio,或者命令 dd。
fio 格式 fio 可以采用命令行格式,也可以采用 jobfile 格式。
1 2 3 fio [options] [jobfile] ... options --output=filename
jobfile 参数 General
I/O type 1 2 3 4 5 6 7 8 9 direct=bool # 是否使用缓存,true 表示不使用缓存 readwrite=str read # 顺序读 write # 顺序写 randread # 随机读 randwrite # 随即写 rw # 顺序读写 randrw # 随机读写
Block size 1 2 3 4 5 blocksize/bs=int[,int][,int] # read, writes, trimes,默认是 4096 bs=256k blocksize_range/bsrange=irange[,irange][,irange] bsrange=1k-4k,2k-8k bssplit=str
Buffers and memory 1 2 3 4 5 iomem=str, mem=str malloc shm shmhuge mmap
I/O size 1 2 3 size=int # 一个任务中每一个线程的总大小 io_size=int, io_limit=int filesize=irange(int)
I/O engine 1 2 3 4 5 ioengine=str sync # 基本的读写操作 psync # 基本的 pread 和 pwrite vsync # 基本的 readv 和 writev 将通过将相邻的 I/O 合并为一个提交来模拟排队。 libaio # Linux 原生的异步 I/O
I/O depth
I/O rate 1 2 thinktime=time # I/O 发出后,在一段时间内停止,再发出下一个 rate=int[,int][,int] # 限制某一个任务的带宽
I/O latency 1 latency_target=time # 如果设置,fio将尝试查找给定工作负载运行时的最高性能点,同时保持低于此目标的延迟。
I/O replay 1 2 write_iolog=str read_iolog=str
Threads, processes and job synchronization 1 thread # fio 默认是通过 fork 的方式创建任务,但是如果这个参数指定,就会采用 POSIX 的线程函数来创建线程
Verification 1 verify_only # 不指定工作负载,只验证数据是否符合之前的
Steady state 1 2 steadystate=str:float, ss=str:float # 定义评估稳态性能的标准和限制。 iops # 收集 iops 数据 如:iops:2
Measurements and reporting 1 2 write_bw_log=str write_lat_log=str
相关测试用例 随机读 1 2 3 4 5 6 7 8 9 10 11 [Rand_Read_Testing] direct=1 iodepth=128 rw=randread ioengine=libaio bs=4k size=1G numjobs=1 runtime=1000 group_reporting filename=/s3plus/mount/002/randread.test
参考:http://devopslinux.com/2016/07/23/PTE-fio/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # 顺序读 fio -filename=/dev/sda -direct=1 -iodepth 1 -thread -rw=read -ioengine=psync -bs=16k -size=200G -numjobs=30 -runtime=1000 -group_reporting -name=mytest # 顺序写 fio -filename=/dev/sda -direct=1 -iodepth 1 -thread -rw=write -ioengine=psync -bs=16k -size=200G -numjobs=30 -runtime=1000 -group_reporting -name=mytest # 随机读 fio -filename=/dev/sda -direct=1 -iodepth 1 -thread -rw=randread -ioengine=psync -bs=16k -size=200G -numjobs=30 -runtime=1000 -group_reporting -name=mytest # 随机写 fio -filename=/dev/sda -direct=1 -iodepth 1 -thread -rw=randwrite -ioengine=psync -bs=16k -size=200G -numjobs=30 -runtime=1000 -group_reporting -name=mytest # 混合随机读写 fio -filename=/dev/sda -direct=1 -iodepth 1 -thread -rw=randrw -rwmixread=70 -ioengine=psync -bs=16k -size=200G -numjobs=30 -runtime=100 -group_reporting -name=mytest -ioscheduler=noop
混合随机读写 1 2 3 4 5 6 7 8 9 10 11 [randrw] direct=1 iodepth=128 rw=randrw ioengine=libaio bs=4k size=1G numjobs=16 runtime=60 group_reporting filename=/s3plus/mount/002/randrw.file
参考:
https://www.ibm.com/developerworks/cn/linux/l-lo-io-scheduler-optimize-performance/index.html
http://bbs.chinaunix.net/thread-1967733-1-1.html