はじめに
CentOS 6とCentOS 7とAmazon LinuxではデフォルトのI/Oスケジューラに違いがあるため、ionice
コマンドを実行するときやパフォーマンスチューニングをするときに注意が必要だ。
各OSでのデフォルトI/Oスケジューラ
CentOS 6ではI/Oスケジューラはcfq
になっている。CentOS 7ではdeadline
に変更された。(https://access.redhat.com/solutions/32376)
In RHEL 7, the deadline IO scheduler is the default IO scheduler for all block devices except SATA disks. For SATA disk, the default IO scheduler is cfq.
Amazon Linuxではnoop
になっている。
CentOS 6
$ cat /etc/redhat-release && uname -r CentOS release 6.7 (Final) 2.6.32-279.el6.x86_64 $ cat /sys/block/sda/queue/scheduler noop anticipatory deadline [cfq]
CentOS 7
$ cat /etc/redhat-release && uname -r CentOS Linux release 7.3.1611 (Core) 3.10.0-229.el7.x86_64 # SATAディスクではないので、デフォルトI/Oスケジューラはcfqではなくdeadlineになっているはず $ dmesg | grep sd[a-z] | grep SCSI [ 1.029523] sd 2:0:0:0: [sda] Attached SCSI disk $ cat /sys/block/sda/queue/scheduler noop [deadline] cfq
Amazon Linux
$ uname -r 4.4.23-31.54.amzn1.x86_64 $ cat /sys/block/xvda/queue/scheduler [noop]
I/Oスケジューラ
http://tfcenturion.hatenablog.com/entry/2015/11/07/091307にI/Oスケジューラの各種について、簡潔にまとめられているので引用する。
noop
最もシンプルなスケジューラで、I/O要求を単純に要求順に処理する。
noopはスケジューリング負荷が小さく、ランダムアクセスが高速なハードウェアに適していると考えられている。deadline
ディスク上で位置が近いI/O要求を優先して処理を行うことで、HDDヘッドの移動量を削減する。位置が近いI/O要求を優先するため、HDDヘッドから遠くにあるI/O要求は後回しされるが、待ち時間の限界値(deadline)より長く待たされているI/O要求が発生した際には、そのI/O要求を優先する。
deadlineの設定によりレイテンシの上限が保証されるため、リアルタイムアプリケーションやデータベース管理に適していると言われている。anticipatory(AS)
I/O要求を予測し、その予測に基づいて処理を行う。位置が近い場所へのI/Oがすぐ後に発行されると予測 した場合、I/O要求の処理を遅延させ、近隣のI/O要求が発行されるのを待ってから、I/O要求の処理を行う。つまり、いくつかのI/O要求を貯めてから処理を行う性質がある。
ただしDeadlineと同様に、待ち時間の長いI/O要求が発生した場合にはI/O待ちを中断し、待ち時間の長いI/O要求を優先して処理する。
anticipatoryは、データにシーケンシャルにアクセスするWebサーバなどに有効とされている。cfq(Completely Fair Queuing)
スケジューラは内部に多数のキューを保持しておき、プロセス単位でI/O要求をそれらキューに割り振っていく。処理対象のキューを一定間隔で切り替えることで、プロセス間でI/O要求は公平に処理されていく。
また、Deadlineやanticipatoryと同様に待ち時間の長いI/O要求が存在した場合、それを優先的に処理していく。
I/Oスケジューラとパフォーマンス
クラウドと仮想化
Linux 2.6 カーネル ベースの仮想マシンでディスク I/O パフォーマンスが遅い (2094615)に書かれている通り、「仮想化された環境では多くの場合、I/O をホスト レイヤーとゲスト レイヤーの両方でスケジュールすることは有益ではなく」、ゲストが頑張るよりもホストに任せた方がいい。NOOP または Deadline のパフォーマンスが仮想化された Linux ゲストに対し向上したことが示されたとのことなので、クラウド全盛の現在は、デフォルトがNOOP または DeadlineになっているCentOS 7あるいはAmazon Linuxは、この観点では適切なのだろう。
noop
でもdeadline
でもVMwareのテストでは効果がでたとのことだが、ホストに極力任せるという観点からはnoop
の方がよさそう。
CentOS 6のEOLは2020/11/30だが、現時点でもCentOS 6のサーバをクラウド上で動かし、I/Oをヘビーユースするのであれば、I/Oスケジューラを変えた方がパフォーマンスがあがるかもしれない。
ディスク/dev/sda
のI/Oスケジューラを変更するのであれば、以下のように変更する。
$ sudo su -
# cat /sys/block/sda/queue/scheduler
noop anticipatory deadline [cfq]
# echo noop > /sys/block/sda/queue/scheduler
# cat /sys/block/sda/queue/scheduler
[noop] anticipatory deadline cfq
サーバを再起動したら元に戻ってしまうので、/etc/rc.local
に記入したり、initスクリプト化してchkconfig onにしたりするといい。
/dev/sda
だけのように各ディスク単位ではなく、サーバ全体でI/Oスケジューラを変更し、さらに永続化したければ、/etc/grub.conf
を編集する。/etc/grub.conf
のkernel
行にelevator=deadline
やelevator=noop
を追記して再起動すればいい。
CentOS 7であれば/etc/grub.conf
ではなく/etc/default/grub
のGRUB_CMDLINE_LINUX
行にelevator=noop
を書いてから、/boot/grub2/grub.cfg
をrebuildするためにgrub2-mkconfig -o /boot/grub2/grub.cfg
を実行する。
参考:https://access.redhat.com/solutions/32376
SSD
現在はクラウドとともにSSDを使うことも多くなっている。ランダムアクセスが高速なハードウェアに適しているnoop
を選択することでパフォーマンス向上するかもしれない。
deadline
もanticipatory
もHDDヘッドの移動量が少なくなるようにするロジックなので、HDDヘッドがないSSDには適切なロジックではない。
ionice
ioniceはcfqじゃないと意味がない
パフォーマンス観点でcfq
からnoop
に変えた方がいいという話をしてきたが、パフォーマンスとは真逆のI/O負荷をできるだけ抑えながら処理をさせるionice
についても考えなければならない。
ionice
が効く場合は限られていて、I/Oスケジューラがcfq
でないと効果がない。
ioniceが効く条件
- I/Oスケジューラが
cfq
である Read()
であるWrite()
の場合は、-o sync
でマウントされているかO_DIRECT,O_SYNC
フラグを立てている
参考:
その ionice、ほんとに効いてますか?
【続編】その ionice、ほんとに効いてますか?
またLVMやソフトウェアRAIDを組んでいる場合も、ionice
は無意味となる。
CFQ is the default for scheduling actual physical disks, but small things like software RAID and LVM do not have disk schedulers at all and as far as I can tell ionice is completely ineffectual on them (for both read and write IO)
参考:Some notes on Linux's ionice
ioniceの代わり
コピーのときのio負荷を減らしたい場合
rsync
の--bwlimit
で帯域制限を行う。R/W両方とも負荷が減る。
巨大なファイルを削除する場合
大量・巨大なファイル操作を低負荷で行う方法でtruncate
を使った方法が紹介されていた。
#/bin/bash
set -euo pipefail
filepath=$1
# 10MBytes/sec
filesize=$(du -m ${filepath} | awk '{print $1}')
sleep_time=0.1
for num in $(seq 1 ${filesize});
do
truncate -s $((${filesize}-${num}))M ${filepath}
sleep ${sleep_time}
done
\rm ${filepath}