原因の一つとして見落とされがちなのが、LinuxカーネルのI/Oスケジューラの設定だ。I/Oスケジューラはカーネルがディスクへの読み書き要求をどの順序・方式で処理するかを決める仕組みで、SSDとHDDでは最適解が大きく異なる。カーネル5.0以降ではマルチキューアーキテクチャ(blk-mq)が標準となり、使えるスケジューラの種類も変化している。
この記事では、現在のI/Oスケジューラを確認する方法から始め、none・mq-deadline・BFQを用途別に切り替える手順、udev rulesで設定を永続化する方法、データベース・仮想化サーバー向けの推奨設定まで解説する。RHEL 9系~Rocky Linux 9・Ubuntu 24.04 LTSを想定環境として説明する。
この記事のポイント
・現在のスケジューラは cat /sys/block/<デバイス>/queue/scheduler で確認できる
・SSD/NVMeには none か mq-deadline、HDDには bfq が基本方針
・echo コマンドで即時変更できるが再起動で消えるため udev rules で永続化する
・DBサーバーは mq-deadline(SATA SSD)または none(NVMe)が推奨
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
I/Oスケジューラとは何か、なぜ重要なのか
アプリケーションがファイルを読み書きするとき、I/O要求はすぐにディスクへ送られるわけではない。カーネルはいったん要求をキューに溜め込み、I/Oスケジューラが処理順を決定してからデバイスドライバに渡す。この処理順の決定がパフォーマンスに大きく影響する理由は、HDDとSSDの物理特性の違いにある。
・HDDの場合:物理的なヘッドが移動してデータを読み書きするため、ヘッド移動距離を最小化する「シーク最適化」が効果的だ。バラバラなアドレスのI/O要求をアドレス順に並べ替えることで、スループットが大幅に向上する。
・SSD・NVMeの場合:物理的なヘッドが存在せず、どのアドレスも均一速度でアクセスできる。そのため複雑なスケジューリングは不要で、スケジューラのオーバーヘッド自体がボトルネックになる可能性がある。
カーネル5.0以降では、旧来の
cfq(Completely Fair Queuing)は廃止された。現行の主要スケジューラは none・mq-deadline・bfq・kyber の4種類だ(カーネルのビルド構成によって利用できるスケジューラは異なる)。I/Oスケジューラの設定を意識するのは特に、ストレージを換装したとき・本番サーバーのパフォーマンスチューニングをするとき・新規サーバーを構築するときだ。ディストリビューションのデフォルト設定が用途に合っているとは限らない。
現在のI/Oスケジューラを確認する方法
1. ブロックデバイスの一覧を確認する
まずlsblk でシステムのブロックデバイスを確認する。-d オプションでディスク単体のみ表示し、ROTA 列でHDD(1)かSSD(0)かを判定できる。$ lsblk -d -o NAME,TYPE,SIZE,ROTA
NAME TYPE SIZE ROTA sda disk 500G 1 sdb disk 250G 0 nvme0n1 disk 500G 0
ROTA=1 がHDD、ROTA=0 がSSD・NVMeだ。2. スケジューラの現在設定を確認する
/sys/block/<デバイス名>/queue/scheduler を読むと、現在のスケジューラと対応しているスケジューラの一覧を確認できる。現在有効なスケジューラは [ ](角括弧)で囲まれている。# SATAディスクの例 $ cat /sys/block/sda/queue/scheduler # NVMeディスクの例 $ cat /sys/block/nvme0n1/queue/scheduler
# SATAディスク(mq-deadlineが有効) [mq-deadline] none bfq # NVMeディスク(noneが有効) [none] mq-deadline
3. 全デバイスをまとめて確認する
複数のディスクが存在する場合は、シェルのforループで一括確認すると効率的だ。$ for dev in /sys/block/*/queue/scheduler; do echo "$dev: $(cat $dev)" done
/sys/block/sda/queue/scheduler: [mq-deadline] none bfq /sys/block/nvme0n1/queue/scheduler: [none] mq-deadline
主要I/Oスケジューラの特徴と使い分け
現行カーネルで利用できる主要なスケジューラの特徴を整理する。・none(noop):スケジューリングを行わず、キューに届いた順にそのままデバイスに送る。NVMeのような超高速デバイスでは、スケジューラのオーバーヘッドを排除することで最大性能を引き出せる。NVMeのデフォルトとして多くのディストリビューションで採用されている。
・mq-deadline:I/O要求に有効期限(デッドライン)を設定し、期限切れになりそうな要求を優先して処理する。書き込みの「スタベーション」(長時間待たされる現象)を防ぐ効果があり、データベースサーバー(MySQL・PostgreSQL)との相性がよい。SATA SSDのデフォルト値としてよく使われる。
・bfq(Budget Fair Queueing):プロセスごとに帯域を公平に割り当てるスケジューラ。大量のランダムI/Oが発生するHDD環境や、複数プロセスがI/Oを競合するファイルサーバー環境に向く。
・kyber:低レイテンシを優先するスケジューラ。SSDでリアルタイム性が求められるケースを対象に設計されているが、bfqやmq-deadlineと比べて採用実績は限られる。
| スケジューラ | 適したデバイス | 主な特徴 | 代表的な用途 |
|---|---|---|---|
| none | NVMe SSD | パススルー、最小オーバーヘッド | 高速DB、NVMe搭載サーバー |
| mq-deadline | SATA SSD | デッドライン保証、書き込みスタベーション防止 | データベースサーバー(SATA SSD) |
| bfq | HDD | プロセスごとの帯域公平分配 | ファイルサーバー、汎用HDD構成 |
| kyber | SSD | 低レイテンシ優先 | リアルタイム性が必要なケース |
I/Oスケジューラを一時的に変更する方法
スケジューラの変更は/sys/block/<デバイス>/queue/scheduler に echo で書き込むことで即時反映できる。ただし再起動で消える一時的な変更である点に注意が必要だ。本番サーバーで本格適用する前に、まずこの方法で動作を確認し、問題がなければ後述のudev rulesで永続化するのが安全な手順だ。1. 変更前の確認
$ cat /sys/block/sda/queue/scheduler [mq-deadline] none bfq
2. スケジューラを変更する
# HDD(sda)をbfqに変更する $ echo bfq > /sys/block/sda/queue/scheduler # SATA SSD(sdb)をnoneに変更する $ echo none > /sys/block/sdb/queue/scheduler
3. 変更を確認する
$ cat /sys/block/sda/queue/scheduler none [bfq] mq-deadline
[bfq] と角括弧で囲まれていれば変更は成功だ。Invalid argument が返る場合は、デバイス名のタイプミスか、そのスケジューラがカーネルで利用不可能な状態にある。udev rulesで永続化する方法
再起動後も設定を維持するには、udev rules(/etc/udev/rules.d/ 配下のルールファイル)に設定を書く。udevはカーネルがデバイスを検出するたびにルールを評価するため、起動時に自動的にスケジューラが設定される。1. ルールファイルを作成する
ファイル名は任意だが、数字が小さいほど先に評価される慣習があるため60-scheduler.rules あたりが一般的だ(60番台はハードウェア関連の設定によく使われる)。$ sudo vi /etc/udev/rules.d/60-scheduler.rules
2. ルールを記述する
HDDとSSD(SATA)、NVMeに対してそれぞれ条件を書く。ATTR{queue/rotational}=="1" がHDD、=="0" がSSD/NVMeを示す。# hdd (rotational==1) → bfq ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="bfq" # sata ssd (rotational==0) → mq-deadline ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="mq-deadline" # nvme → none ACTION=="add|change", KERNEL=="nvme[0-9]*n[0-9]*", ATTR{queue/scheduler}="none"
3. udev rulesを反映させる
ファイルを保存したら、実行中のシステムに即座に反映させる。# ルールをリロード $ sudo udevadm control --reload-rules # 既存デバイスにルールを再適用 $ sudo udevadm trigger --type=devices --action=change
4. 反映を確認する
$ cat /sys/block/sda/queue/scheduler
udevadm test が便利だ(実際の変更は行わない)。$ sudo udevadm test /sys/block/sda 2>&1 | grep scheduler
用途別の推奨設定
データベースサーバー(MySQL・PostgreSQL)
データベースはランダムI/Oが多く、書き込み完了の保証(fsync)を多用する。スケジューラの選択がDB性能の安定に直結する。・SATA SSD搭載サーバー:
mq-deadline を推奨。書き込みのスタベーション防止と適度なI/O優先度管理が、DB運用の安定化に寄与する。・NVMe SSD搭載サーバー:
none を推奨。NVMeのパフォーマンスを最大限に引き出すには、スケジューラのオーバーヘッドを排除するのが基本的な方針だ。KVM仮想化ホスト
KVM上の仮想ディスク(virtio-blk)は、ゲストOS側で独自のI/OスタックとスケジューラがI/O要求をさらに最適化する。ホスト側でも重複してスケジューリングすると二重オーバーヘッドになるため、ホスト側の物理ディスクスケジューラは none または mq-deadline に設定するのが一般的だ。汎用ファイルサーバー・HDD構成
複数のユーザー・プロセスがHDDへ同時アクセスする環境では、bfq が帯域を公平に分配し、特定プロセスだけI/Oを占有する事態を防いでくれる。NASやバックアップサーバーにHDDを使う場合はbfqが基本の選択肢になる。I/Oの変化をiotopで確認する方法
スケジューラを変更した前後で効果を確認するには、iotop や iostat が役立つ。iotop はプロセス別のディスクI/Oをリアルタイムで表示するツールだ。# I/Oが発生しているプロセスだけ表示する(-oオプション) $ sudo iotop -o
iostat はデバイス単位のI/O統計を確認する。await(I/O要求の平均待ち時間)と %util(デバイスの使用率)が主要な指標だ。スケジューラ変更後に同一ワークロードを実行し、await の変化を比較することでチューニング効果を客観的に評価できる。# 1秒ごとにデバイス詳細を表示 $ iostat -dx 1
トラブルシュート
指定したスケジューラが設定されない場合
echo mq-deadline > /sys/block/sda/queue/scheduler を実行しても Invalid argument エラーが出る場合は、そのスケジューラが利用できない状態にある。まず
cat /sys/block/sda/queue/scheduler で列挙されているスケジューラ名を確認する。そこに候補がなければ、カーネルにそのスケジューラモジュールが組み込まれていないか、当該デバイスが対応していない。NVMeの場合、ディストリビューションによっては mq-deadline しか選べない構成もある。再起動後に設定が消える場合
udev rules を作成したにもかかわらず設定が消える場合は、以下を確認する。・
/etc/udev/rules.d/60-scheduler.rules のファイルが存在するか・ルール構文にタイポがないか(
ATTR{queue/scheduler}="bfq" の引用符は二重引用符)・同じデバイスに対して競合するルールが他のファイルに存在しないか(
ls /etc/udev/rules.d/ で確認)ルールの評価結果を確認するには
udevadm test を使う。仮想マシンのディスク(/dev/vdaなど)の場合
KVM仮想マシンのゲストOSでは、仮想ディスク(/dev/vda など)に対して選択できるスケジューラが [none] だけに制限されていることが多い。$ cat /sys/block/vda/queue/scheduler
[none]
本記事のまとめ
| やりたいこと | コマンド/設定 |
|---|---|
| 現在のスケジューラを確認する | cat /sys/block/sda/queue/scheduler |
| 全デバイスのスケジューラを一覧確認する | for dev in /sys/block/*/queue/scheduler; do echo "$dev: $(cat $dev)"; done |
| スケジューラを一時的に変更する | echo mq-deadline > /sys/block/sda/queue/scheduler |
| udev rulesで永続化する | /etc/udev/rules.d/60-scheduler.rules にATTR設定を記述 |
| udev rulesを即座に反映させる | sudo udevadm control --reload-rules && sudo udevadm trigger --type=devices --action=change |
| I/Oをプロセス別に確認する | sudo iotop -o |
| デバイス別I/O統計を確認する | iostat -dx 1 |
I/Oスケジューラの最適化はLinuxサーバーのパフォーマンスチューニングの一手段に過ぎない。実際の本番運用では、ストレージ設定・ネットワーク設定・セキュリティ設定・障害対応など、体系的な知識と実践スキルが求められる。現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、Linux Master Pro Seminar では20年以上の実務経験をもとにしたサーバー構築・運用の知識をハンズオン形式で習得できる。
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:ripgrepコマンドでLinuxのテキストを高速検索する方法|grepとの使い分けと実践的なオプション
- この記事の属するカテゴリ:Linuxtipsへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます