ディスクストレージ 基礎

サーバは curl https://api.example.com/user/42 の復号を終えた。 今、2 つのことが物理デバイスに話さなければならない:OS が access.logに行を追加し、データベースは user row 42 を住むメディアから読まなければならない。 両方ともデバイスの物理学のコストを払う —— IOPS、書き込み増幅、キュー深度、再構築時間。 4 セクションで全体像を組み立てる:物理(HDD vs SSD vs NVMe、IOPS vs スループット vs レイテンシ); 対話的な書き込み増幅ウォークスルー付きの SSD 内部(NAND ページ、消去ブロック、FTL、GC);インタフェース(SATA、SAS、NVMe、キュー深度、io_uring); そしてボックスレベルとネットワークスケールの RAID。 最後にクイックリファレンス

01

物理 —— HDD vs SSD vs NVMe、IOPS vs スループット vs レイテンシ

4 KB ランダム読み取りが定番のマイクロベンチマーク、なぜならほぼあらゆるデータベースクエリ、 ログ追記、小ファイルルックアップがそれになるから。その読み取りのコストは ファイルシステムの下に座っているもの —— 回転する磁気円盤、SATA SSD、 または PCIe 接続の NVMe —— によって完全に決まる。それらの間に 2 桁の差がある。

今、我々の curl リクエストのために 2 つの書き込みがディスクへ行く: web サーバが access.log に ~200 バイトの行を追記し、user/42 を裏付けるデータベースが数個の B-tree ページ (各 ~4 KB)を読んでクエリを満たす。これらの操作が各 5 ms なのか各 50 μs なのかは、 プロビジョニングした物理層の関数。

HDD —— 回転するプラッタ

ハードディスクは 7,200 RPM(コンシューマ)または 15,000 RPM(エンタープライズ)で 回転する磁気プラッタの積み重ね、可動アクチュエータアームが面ごとに 1 つの 読み書きヘッドを持つ。ブロックを読むには、ヘッドが正しいトラックにシークし、 正しいセクタがその下に回転してくるのを待たなければならない。数学は残酷:

シーク時間             ~3-15 ms   (フルストローク vs 隣接)
回転レイテンシ         ~4 ms      (7200 RPM で半回転)
転送時間              ~0.05 ms   (4 KB には無視できる)
                     ──────
ランダム 4 KB 読み     ~8-15 ms

シーケンシャルスループット   ~150-250 MB/s
ランダム IOPS              ~100-200

だから 200 IOPS の HDD はキュー深度 32 でリクエストあたり平均 160 ms 待つ —— データベースの P99 レイテンシは動く金属アームの物理学。 シーケンシャル読みはランダムより 1,000 倍速い、ヘッドが動かず回転がすでに揃っているから。これがデータベースエンジンがシーケンシャル I/O に執着する根本理由 —— 先行書き込みログ、LSM ツリーコンパクション、バッチ ETL。 どれも IOPS-bound の作業を MB/s-bound に変えようとしている。

SATA SSD —— 可動部品なし、ワイヤはまだ遅い

SSD はプラッタを NAND フラッシュチップで置き換える。シークなし、回転なし。 4 KB ランダム読みは NAND プログラムサイクル(~50 μs)+ SATA ラウンドトリップ —— 合計 100 μs と呼ぼう。SATA ケーブル自体が 6 Gbps = 600 MB/s で上限、 NAND がどれだけ速くても関係ない:

ランダム 4 KB 読み         ~100 μs
シーケンシャルスループット ~500-550 MB/s   (SATA 3.0 で上限)
ランダム IOPS              ~50,000-100,000

AHCI コマンドキュー        32 未決操作

HDD から 500 倍の IOPS 改善だが、SATA プロトコル —— 回転錆のために設計された —— は重要な NAND 性能をテーブルに残す。AHCI、ホストコントローラ仕様は、 単一の 32 深さのコマンドキューを持つ。現代の NAND は数百の並行読みを処理できる; SATA はそれらを要求できない。

NVMe —— PCIe 直結、コアごとのキュー

NVMe は PCIe バス上に直接座る。PCIe 4.0 x4 = 8 GB/s の生帯域 (PCIe 5.0 x4 = 16 GB/s)。NVMe プロトコルは AHCI の単一 32 深さキューを 最大 64K の提出キュー、各最大 64K の深さに置き換える —— 典型的に CPU コアごとに 1 つのキューペア、カーネルはグローバルロックで競合せずに I/O 提出する:

ランダム 4 KB 読み         ~10-50 μs
シーケンシャルスループット ~7 GB/s          (PCIe 4.0 x4)
ランダム IOPS              500,000 - 1.5M+

キュー                     最大 64K × 64K 深

HDD より 100 倍レイテンシ改善、SATA SSD より 10 倍 IOPS 改善。 だがその数字はキュー深度 32 以上でしか見えない —— シングルスレッド同期読みループは 100 万 IOPS デバイスでもせいぜい 3 万 IOPS、 デバイスは次のリクエストを待ってアイドルだから。 だから fio --iodepth=32 のようなベンチマークが重要で、 現代の I/O パス(io_uring、カーネルバイパス SPDK)が存在する —— 実際にそれらのキューを満たすために。

レイテンシ崖 —— ストレージが階層のどこに座るか

ストレージのレイテンシは CPU が待つかもしれない他のすべてに対して相対的にのみ意味を持つ:

CPU L1 キャッシュ           ~1 ns
CPU L2 キャッシュ           ~4 ns
CPU L3 キャッシュ           ~15 ns
DRAM(主メモリ)             ~80 ns
NVMe SSD(ランダム読み)     ~10-50 μs    ← L1 より 100,000 倍遅い
SATA SSD(ランダム読み)     ~100 μs
ネットワーク SSD / EBS gp3  ~500 μs
HDD(ランダム読み)          ~5-15 ms     ← L1 より 10,000,000 倍遅い
S3 / コールドストレージ(初B) ~10-100 ms

2 つの要点。第一に、キャッシュヒットとコールドディスク読みの差は大体「キッチンへ歩く」と 「別の大陸へ歩く」の差 —— これがストレージの上のあらゆる層 (Postgres shared_buffers、Redis、カーネルページキャッシュ)がキャッシュにそれほど積極的な理由。 第二に、NVMe と HDD の差(1,000 倍)は DRAM と NVMe の差(~500 倍)より大きい —— ネットワーク接続 HDD 時代は任意のレイテンシ敏感ワークロードに対して真に過ぎ去った。

IOPS vs スループット —— 4 KB では同じ数

小さい I/O(4 KB、典型的なデータベースページ)では、IOPS × ブロックサイズ = スループット。 100,000 IOPS × 4 KB = 400 MB/s。だから SATA SSD の「500 MB/s シーケンシャル」スペックと 「100,000 ランダム IOPS」スペックは大体同じ壁を記述する。 大きい I/O(1 MB ETL 読み)ではスループットが支配する —— HDD が 250 MB/s シーケンシャルでも問題ない。4 KB ランダム読みが定番ベンチマークなのは 最も痛い現実のワークロードに合うから:OLTP、ここでは各ユーザクエリが データファイルに散らばった数個のインデックスページを触る。

要点。「4 KB ランダム読みは HDD で ~5-15 ms(シーク + 回転)、 SATA SSD で ~100 μs、NVMe で ~10-50 μs。IOPS × ブロックサイズ = スループット; OLTP ワークロードでは IOPS が支配する。NVMe の ~1M IOPS はキュー深度 32+ でのみ出現する —— シングルスレッド同期 I/O は 30 倍の性能をテーブルに残す。HDD から NVMe の差(1,000 倍)は 現在 DRAM から NVMe の差(500 倍)より大きい。」

02

SSD 内部 —— NAND、FTL、ガベージコレクション、書き込み増幅

NAND フラッシュチップは「可動部品のないハードドライブ」ではない —— ハードドライブのふりをする非常に異なる獣。そのふりは Flash Translation Layer(FTL)が結びつけており、3 つの癖を隠す: 書き込みはページ粒度だが消去はブロック粒度、すべての cell が摩耗する、 デバイスはフリープールを維持してランダム書き性能を偽造する。

NAND セル —— SLC、MLC、TLC、QLC

SSD の底部は NAND セルのグリッドで、 技術によって各 1 から 4 ビットを保存:

  • SLC(1 ビット / セル)—— 最速、~10 万回 P/E サイクル、 エンタープライズ / キャッシュ専用。スタンドアロン形式ではほぼ絶滅。
  • MLC(2 ビット / セル)—— ~1 万回 P/E サイクル。 ほとんどの古いエンタープライズ SSD。
  • TLC(3 ビット / セル)—— ~3 千回 P/E サイクル。 コンシューマと大半の現代エンタープライズ SSD の支配的技術。
  • QLC(4 ビット / セル)—— ~1 千回 P/E サイクル。 安く密;読み中心の大量ストレージ(Samsung 870 QVO、Intel D5-P5316)に使われる。

セルあたりの密度が高いほど $/TB は安いが、セルが壊れる前に書き直せる回数は少ない。 これが耐久性レーティング(TBW —— 総書き込みバイト数)が ベンダ公開される理由:コンシューマ 1 TB TLC SSD は ~600 TBW 評価、 エンタープライズ NVMe は ~3,000+ TBW。 その寿命内、セル磨耗均等化と過剰プロビジョニングがデバイスを保つ。

ページと消去ブロック —— 非対称性

2 つの粒度が重要、そして異なる:

  • ページ —— プログラム(書き込み)できる最小単位。典型 4 KB から 16 KB。
  • ブロック —— 消去できる最小単位。典型 128 KB から 1 MB (ブロックあたり 64-256 ページ)。

ページをその場で書き直すことはできない。ページが一度でもプログラムされたなら、 その LBA に新しいデータを入れる唯一の方法は(a)別のフリーページに書き、 (b)最終的にブロック全体を消去すること。これがすべての他の SSD の奇妙さの源。

FTL —— これすべてを隠すファームウェア

Flash Translation Layer は SSD コントローラ上で動くソフトウェア。 4 つの仕事をする:

  1. LBA → 物理ページマッピング。大きな RAM 内テーブル (典型的に NAND 1 TB あたり ~1 GB の DRAM)。ホストが LBA 42 に書くと、 FTL は任意のフリー物理ページを選んでマップを更新する。
  2. ガベージコレクション。フリーページが少なくなると、 ほぼ無効なページのブロックを選び、まだ有効なものを再配置し、 ソースブロックを消去する。下のデモで可視化。
  3. 磨耗均等化。P/E サイクルを全ブロックに広げ、 単一ブロックが P/E 限界に達しながら他が使われないままにならないようにする。 コールドデータ(滅多に上書きされない)は定期的に再配置され、 よく磨耗したブロックをホットデータのために解放する。
  4. バッドブロック管理。セルは最終的に摩耗し、プログラムを拒否する。 FTL はそれらを退役させ、予備プールから透過的に置換する。

書き込み増幅 —— 静かな IOPS 乗数

書き込み増幅(WA)= 物理書き込みバイト / 論理書き込みバイト。 理想は 1.0(各ホスト書き込みが 1 デバイス書き込みを要する)。実際には:

シーケンシャル書き、十分なフリー空間            WA ≈ 1.0
混合ランダム + 30% 過剰プロビジョニング         WA ≈ 1.2 - 2.0
ほぼ満杯 SSD で重ランダム書き                   WA ≈ 5 - 10+
敵対的(4 KB ランダム、95% 満、TRIM なし)      WA は 20 を超え得る

高 WA が重要な理由は 2 つ:TBW 耐久性を比例して燃やす (WA 5 は 600 TBW ドライブが 120 TB の実ホスト書き込み後に死ぬことを意味する)、 そして GC IO がアプリケーション IO と競合し、デバイスが最も負荷のあるときに P99 レイテンシを上げる。

TRIM、過剰プロビジョニング、WA との戦い方

2 つのレバーが WA を劇的に減らす:

  • TRIM / discard。OS が SSD に送る「これらの LBA は もはや意味のあるデータを保持していない」というコマンド。これなしには、 FTL は GC 中にどのページを破棄できるか知らない —— それらを有効と見なし、 コピーし続ける。現代のファイルシステム(ext4、XFS、APFS、NTFS)は ファイル削除時に自動的に TRIM を発行する(または週次の fstrimcron 経由)。lsblk --discard で確認。
  • 過剰プロビジョニング。OS に見えない NAND のチャンクを予約する。 960 GB として販売される 1 TB 生ドライブは ~7% OP を持つ; エンタープライズドライブは 20-50% 予約する可能性がある。 OP が多いほど常により多くのフリーページが利用可能、 だから GC は常に回収する低コストブロックを持ち、だから WA は 1 に近いままになる。 コンシューマドライブを手動でアンダーパーティションすれば同じ効果が得られる。

対話 —— 1 つの書き直し、21 の物理書き込み

SSD 書き込み増幅 —— 1 論理書き込み → 21 物理書き込みステップ 0 —— 1 つの消去ブロックは 64 ページの 4 KB を保持。60 有効、4 空き。ワーキングブロック(64 × 4 KB = 256 KB)有効無効空き論理書き込み:0物理書き込み:0書き込み増幅:
SSD の最小書き込み可能単位はページ(典型的に 4 KB)。最小消去可能単位はブロック(ここでは 256 KB、現代の QLC ではしばしば 1 MB+)。一度書かれたページはその場で書き直せない —— 新しいデータを別の場所に書き、古い場所を無効としてマークすることでのみ上書き可能。これがすべての SSD がファームウェアに Flash Translation Layer を出荷する理由。
1 / 8
NAND はページ粒度(4 KB)でプログラム可能だが、消去ブロック粒度 (ここ 256 KB = 64 ページ)でしかできない。ページを書き直すには、 FTL が新しいページに書き、古いものを無効としてマークする。 フリーページが尽きると、GC が最も汚いブロックを選び、 まだ有効なページを別の場所に再配置し、消去する。 再配置 IO はアプリケーションには見えないが、TBW 耐久性には非常に見える。

SMART、磨耗インジケータ、smartctl

すべての SSD が SMART(Self-Monitoring, Analysis, and Reporting Technology) ページを公開する。SSD 健康に重要なフィールド:

# smartctl -a /dev/nvme0n1
...
Percentage Used:                    37%        ← P/E 予算使用済み
Data Units Written:                 312,500,000 [160 TB]
Power On Hours:                     8,760
Available Spare:                    98%        ← 残りの予備ブロック
Media and Data Integrity Errors:    0
Error Information Log Entries:      0

Percentage Used はドライブの評価された TBW にわたって 0 から 100 に上昇する; 100 を超えるとベンダはデータの完全性を保証しなくなる。Available Spare はバッドブロック予備 —— ~10% を下回ったら、 交換をスケジュールする。任意のプロダクションフリートに対して Prometheus node_exporter(nvme_ メトリクス)経由でこれらをプロットする。

要点。「NAND は 4 KB ページをプログラムできるが、 256 KB-1 MB ブロックしか消去できない。FTL は LBA を新鮮なページに 再マップし、バックグラウンドでガベージコレクションを実行することでこれを隠す。 GC はまだ有効なページを汚れたブロックから移動し、消去する —— OS には見えないが書き込み増幅(しばしば 2-10×)と TBW 耐久性には非常に見える。 TRIM は SSD にどの LBA がフリーかを伝える;過剰プロビジョニングは GC に余裕を与える。 両方なしには、満杯 SSD での持続的ランダム書き込みは 1 ヶ月で 1 年の評価寿命を焼き尽くせる。」

03

インタフェース —— SATA、SAS、NVMe、キュー深度、io_uring

ワイヤは NAND と同じくらい重要。100 万 IOPS の NAND アレイは SATA ケーブルの背後では 10 万 IOPS ドライブ。正しい質問は「SSD はどれだけ速いか」ではなく —— 「OS はどれだけ速く作業を提出でき、デバイスはどれだけ速くそのキューを排出できるか」。 それはホストコントローラ仕様、コマンドキュー深度、syscall パスの関数。

SATA —— レガシー回転プロトコル

SATA 3.0 は 6 Gbps(8b/10b エンコーディング後 ≈ 600 MB/s)で頭打ちし、AHCI ホストコントローラインタフェース —— デバイスが HDD という前提で 設計された仕様 —— を使う。AHCI は 1 つのコマンドキュー、32 エントリ深さを公開し、 コア間並列性の概念がない。デバイスがとにかく一度に 1 つの IO しか処理できなかったときは問題なかった。 NAND が数百の並行読みを処理できる今、AHCI はボトルネック。

SATA SSD はまだ互換性のために出荷される —— 安価なコンシューマドライブ、 2.5 インチエンタープライズベイ —— が、SATA 用に設計された新しい性能ティアハードウェアはない。 データベース層に SATA SSD を見たら、レガシーかコスト判断のどちらか。

SAS —— エンタープライズシリアル

SAS(Serial Attached SCSI)は SATA に対するエンタープライズの答え、 12 Gbps と 24 Gbps の世代、HA 用デュアルポート接続(各ドライブが 2 つのコントローラに接続)、 ずっと良いコマンドキューイング(256 エントリ)を持つ。 エンタープライズ回転ディスクアレイ(3PAR、NetApp FAS)とエンタープライズ SSD で重度に使われる。 NVMe が SAS SSD を大きく置き換えたが、大容量ニアラインストレージでは SAS HDD が支配的。

NVMe —— 並列 NAND のために設計

NVMe(Non-Volatile Memory Express)は PCIe 上の新しいプロトコルで、 2011 年に SSD のために明確に設計された。AHCI の前提を捨て、置き換える:

  • 最大 64K の提出キュー、各最大 64K コマンド深さ。典型的な設定:CPU コアごとに 1 つの提出 + 完了キューペア。 カーネルの NVMe ドライバは CPU ごとのキューを維持し、IO 提出はグローバルロックで競合しない。
  • ホスト RAM 内の提出 / 完了リングバッファ。ホストが Submission Queue Entry を書き、ドアベルを鳴らす(PCIe MMIO 書き込み)で 新しい作業がキューされたことをデバイスに伝える。デバイスはデータを DMA し、 Completion Queue Entry を書き、MSI-X 割り込みを上げるか、ホストがポールする。 モデルは現代の NIC をほぼ正確にミラーする。
  • ~13 の必須コマンド(SCSI の散らばった仕様に対して)。 リーンプロトコル、リーンドライバ、リーンファームウェア。

ワイヤ速度は PCIe でスケール:PCIe 3.0 x4 = 4 GB/s、PCIe 4.0 x4 = 8 GB/s、 PCIe 5.0 x4 = 16 GB/s。現代のエンタープライズ NVMe ドライブ (Samsung PM9A3、Intel D7-P5520)は 100 万+ ランダム読み IOPS と シーケンシャル 7 GB/s を提供する。インタフェースはもはやボトルネックではない —— NAND 自体が通常そう。

キュー深度 —— なぜあなたのベンチマークは嘘をつくか

キュー深度(QD)はホストがデバイスに渡したが、まだ完了を収集していない 飛行中の IO 操作の数。NVMe SSD は QD ≥ 32、しばしば QD ≥ 128 でのみ評価された IOPS に当たる:

# fio --rw=randread --bs=4k --iodepth=1 ...
read: IOPS=28.4k, BW=111MiB/s, lat avg 35.1 μs

# fio --rw=randread --bs=4k --iodepth=32 ...
read: IOPS=812k, BW=3.17GiB/s, lat avg 39.4 μs

# fio --rw=randread --bs=4k --iodepth=128 ...
read: IOPS=1.04M, BW=4.07GiB/s, lat avg 122 μs

シングルスレッド同期読みループは QD 1:提出、待ち、繰り返し。 100 万 IOPS のデバイスで ~3 万 IOPS を得る —— デバイスの 97% の容量を床に残す。 全数を得るにはスレッド(各 QD 1)、libaio、または io_uring —— 各々でブロックすることなく多くのリクエストを発行できる何かが必要。QD 注釈なしのベンチマーク数は無意味。

io_uring —— 現代 Linux 非同期パス

io_uring(2019 年 Linux 5.1 で追加)は、ユーザ空間に一対の共有リングバッファ —— 提出キューと完了キュー —— を与え、NVMe の提出 / 完了モデルをミラーする。 アプリケーションは SQE を共有メモリに直接書き、オプションで 1 つの syscall でドアベルを鳴らし、 個々の IO のためにカーネルに遷移することなく CQE を読み出す。IORING_SETUP_SQPOLL(カーネルが syscall を必要とせずに SQ をポール)とIORING_OP_READ / WRITE_FIXED(事前登録バッファ)と組み合わせると、 結果はゼロ syscall、ゼロコピー IO パス。トレーディングシステムと現代のデータベース (ScyllaDB、現代 Cassandra)がネイティブに使う。Postgres 16+ は I/O ワーカープールにそれを採用した。

SPDK とカーネルバイパスの極端

SPDK(Storage Performance Development Kit、Intel から)はもう一歩進む: VFIO 経由で NVMe ドライバを完全にユーザ空間に引っ張り、専用コアで完了をポールし、 カーネルブロック層をスキップする。勝利はカーネルでは不可能だった ~1 μs のレイテンシと デバイスごと ~1000 万 IOPS。コストは運用:汎用ファイルシステム、汎用パーミッション、 汎用 iostat を諦める。ストレージベンダ(Pure Storage)、 高頻度取引、いくつかの最先端データベースで使われる。 99% のサービスにとって、カーネルを通る io_uring が正しいトレードオフ。

NVMe-oF —— ネットワーク越しの NVMe

NVMe over Fabrics は NVMe の提出 / 完了セマンティクスをネットワーク越しに エクスポートする:互換性のための TCP、~5 μs の追加レイテンシのための RDMA (RoCE / InfiniBand)。これが現代の共有ストレージアレイ(Pure Storage FlashBlade、 NetApp ASA、DigitalOcean Block Storage)がワイヤ越しに NVMe ライクな性能を配信する方法。 ホストの視点はローカル NVMe と本質的に同じ —— 同じドライバ、同じキュー、同じ fio 数字 —— ただしレイテンシは 50 μs ではなく 100-500 μs。

要点。「インタフェースは性能の物語の一部。SATA は 600 MB/s と 32 未決操作で上限;NVMe は 8-16 GB/s の PCIe 上で 64K キュー × 64K 深までスケールする。 NVMe ドライブは QD 32+ でのみ評価された IOPS に当たる; シングルスレッド同期 IO はデバイスを 95% アイドルにする。 io_uring はユーザ空間からそれらのキューを供給する操作ごとに syscall なしの現代の方法; SPDK はトレーディングシステムの極端のためにカーネルを完全にスキップする。 QD コンテキストのない『1M IOPS』スペックシートは何も伝えない。」

04

RAID、ZFS RAIDZ、分散ストレージ

ドライブは死ぬ。1 つの 10 TB ドライブの 5 年間にわたる平均データ消失時間は、 フリートを運営する誰もが最終的に 1 つを失うほど十分に高い。 RAID、RAIDZ、分散レプリケーションはすべて同じ質問に答える —— 「バイトを失わずに N デバイスの故障を生き残る」—— 異なるコスト / 複雑度ティアで。 古典的なレベルはまだあらゆるプロダクションランブックに現れる。

RAID 0 —— ストライピング、冗長性なし

各書き込みを N ドライブに分割する。スループットと IOPS は N に線形にスケールする; 容量は N × ドライブサイズ。故障率も線形にスケール —— 任意の 1 ドライブを失うとすべてを失う。 スクラッチボリューム、ビデオ編集、ベンチマーク数に使う。 どこか他から再現できないものには絶対使わない。

RAID 1 —— ミラー

2 ドライブが同一データを保持する。読みはどちらのドライブからも提供できる(~2× 読みスループット); 書きは両方に行く(書き高速化なし)。容量 = N / 2。1 ドライブの損失を生き残る。 最も単純な信頼性増分、OS / ブートボリュームでまだ一般的。

RAID 5 —— ストライプ + 1 パリティ

N-1 ドライブにわたってデータをストライプし、XOR パリティを N 番目に保存する (ストライプごとに回転させ、単一ドライブがすべてのパリティを保持しないようにする)。 1 ドライブの損失を生き残る;故障時、再構築は残りの N-1 ドライブを読み、 失われたデータを再計算する。容量 = (N-1) × ドライブサイズ。

隠れたコストが RAID 5 書き込みペナルティ:単一のランダム小書き込みは データブロックとパリティブロックの両方の read-modify-write を意味する —— 論理書き込みあたり 4 物理 IO。大きなシーケンシャル書き込みではペナルティは消える (フルストライプ書きが RAM でパリティを計算し、一度にすべてを書く)、 が、OLTP ワークロードは絶えずそれを払う。

RAID 6 —— ストライプ + 2 パリティ

ストライプごとに 2 つの独立したパリティブロック(XOR + Reed-Solomon 数学を使う 2 番目の関数)。 2 つの同時ドライブ損失を生き残る。容量 = (N-2) × ドライブサイズ。 書き込みペナルティは小さなランダム書き込みあたり 6 IO に上昇する。

なぜこれが今重要か:現代の 10 TB または 18 TB ドライブは故障後12 から 36 時間で再構築する、再構築はグループ内のすべての生き残りドライブの すべてのブロックをドライブの持続スループットで読まなければならないから。 その再構築ウィンドウ中、生き残りドライブは 100% 負荷 —— まさに限界ドライブが死ぬ最も可能性のあるとき。RAID 5 再構築中の 2 回目の故障 = 全損失。 10+ TB ドライブで、RAID 6 が最低限の責任ある選択; 大ドライブの JBOD での RAID 5 は次の障害ポストモーテムになることを乞うているようなもの。

RAID 10 —— ミラー、それからストライプ

ミラーされたドライブのペア、ストライプされる。容量 = N / 2(RAID 1 と同じ)、 書き込みペナルティはただの 2(両方のミラーに書く、パリティ計算なし)、 再構築は生き残りパートナーから 1 ドライブ分のデータをコピーするだけで済む。 OLTP データベースのデフォルト —— 高 IOPS、安い再構築、予測可能な故障動作。 容量オーバーヘッド(50%)が代償。

ZFS と RAIDZ —— コピーオンライトと冗長性の出会い

ZFS はボリュームマネージャ、ファイルシステム、RAID を 1 つのスタックに統合する。 RAIDZ1 / Z2 / Z3 は RAID 5 / 6 / 7 のアナログで、2 つの重要な改善がある:

  • 書き込みホールなし。古典 RAID 5/6 はデータブロックを書くこととその パリティを更新することの間にウィンドウがあり、クラッシュがストライプを不整合状態に残す。 ZFS のコピーオンライトはすべての書き込みが原子的に新鮮な場所に行くことを意味する; 途中までで終わるべきインプレース更新がない。
  • エンドツーエンドチェックサム。各ブロックは親ブロックに (自分の隣ではなく)チェックサムを保存する。読みで、ZFS は検証する; 不一致では、冗長性から再構築し、悪いセクタを書き直す。 定期的な zpool scrub はすべてのブロックをバックグラウンドで読み、 静かに修正する。

btrfs は同様の目標を持つ;両方とも「ディスクが私に嘘をついたら私のファイルシステムに 気づいてほしい」の現代の答え。

Bitrot —— なぜチェックサムが重要か

ドライブスペックはコンシューマ HDD で 1014 ビットあたり約 1 の回復不能な読みエラー率を引用する、エンタープライズでは 1015 あたり 1。 1014 ビット = 12.5 TB。だから統計的に、12 TB のドライブを最初から最後まで スキャンすると 1 つの静かに破損した読みが生じることが期待される。週次でスクラブされる 60 TB の RAID 6 アレイで、週に ~5 つの静かな破損に遭遇する —— チェックサムなしには見えず、メタデータブロックやクリティカルなデータベース行に着地すると壊滅的。 ZFS はそれらを捕まえる;MD RAID 上の ext4 は捕まえない。

分散ストレージ —— ネットワークスケールの RAID

ラックとデータセンタースケールでの同じアイデア:

  • レプリケーション —— 各ブロックの N コピーが N マシンに(一般的に N=3)。 マシン粒度の RAID 1 のよう。N-1 マシン故障に耐える。HDFS(デフォルト 3 レプリカ)、 Ceph(レプリケートされたプール)、Cassandra(レプリケーションファクタ)で使われる。
  • 消去符号化 —— Reed-Solomon (k, m) は各オブジェクトを k データシャード + m パリティシャードに分割し、(k+m)/k のオーバーヘッドだけで m 同時シャード損失を生き残る。S3 はこれを使い(11 ナインの耐久性のために ~1.8× のオーバーヘッドの Reed-Solomon)、 Ceph 消去符号化プール、MinIO、Backblaze Vaults も同様。トリプルレプリケーションより安い; 再構築時の CPU コスト。
  • クォーラム —— 読みまたは書きに、レプリカの過半数に連絡する(R + W > N)。 操作ごとに一貫性 / 可用性のトレードオフを調整できる。Dynamo 論文の大きな洞察; Cassandra、DynamoDB、現代 Redis レプリケーションを駆動する。

RAID と同じ原則 —— N ドライブの代わりに N レプリカ、SATA ケーブルの代わりにネットワークリンク、 ヘッドクラッシュの代わりにマシン故障。数学(パリティ、消去符号化、クォーラム)は同じ; 障害ドメインはラックまたはリージョン。

要点。「RAID 0 は速度のためにストライプ、RAID 1 は安全のためにミラー、 RAID 5/6 は容量効率的な冗長性のためにパリティを追加、RAID 10 は OLTP のためにミラー → ストライプ。 10+ TB ドライブで、RAID 5 は危険、再構築に 1 日かかり生き残りにストレスをかけるから; RAID 6(または RAIDZ2)が最低限の責任ある選択。ZFS / btrfs は静かな bitrot を打ち負かすために チェックサムを追加する —— 1014 URE 率で複数 TB アレイはそれらなしに破損が保証される。 ネットワークスケールで同じアイデアがレプリケーション、消去符号化、クォーラム読み / 書きとして現れる。」

05

クイックリファレンス

冷たく説明できる価値のある 6 つの核心問題と、コードレビューで一目で見抜くべき 5 つの赤旗。

HDD でなぜランダム IO はシーケンシャル IO よりずっと遅いか、SSD はそれをどう変えるか?

HDD では、各ランダム読みはシーク時間(ヘッドが正しいトラックに移動する間 3-15 ms)と 回転レイテンシ(正しいセクタを待つ平均 ~4 ms)を払う。 シーケンシャル読みはどちらも払わない —— ヘッドは既に位置にあり、セクタはプラッタ速度で流れる。 これが ~100-200 IOPS ランダムだが ~200 MB/s シーケンシャルが同じドライブである理由。 SSD は可動部品も回転もないので、ランダム読みは ~100 μs(SATA)または ~10-50 μs(NVMe)。 ランダムとシーケンシャルの差は 1000× から約 1× に崩壊する —— NAND プログラム時間だけが残る。 これが SSD でデータベースワークロードがこんなに違って見える理由のすべて。

書き込み増幅とは何で、何がそれを悪くするか?

書き込み増幅 = NAND に書かれた物理バイト / ホストが書いた論理バイト。理想は 1.0。 GC が消去前に汚れたブロックからまだ有効なページを再配置しなければならないときに増える —— フリーページの作業セットが小さいほど、書き込みあたりの再配置が多くなる。 TRIM なしのほぼ満杯 SSD でのランダム書き込みは WA を 10 を超えて押せる。 重要な理由:(a)TBW 耐久性を比例して燃やす —— WA 5 は評価されたドライブ寿命を 5× に切る —— そして(b)GC 読み / 書きがアプリケーション IO と競合し、負荷が最も高いときに P99 を急上昇させる。 TRIM、過剰プロビジョニング、シーケンシャル書き込みパターンで戦う。

プログラミングモデルの観点から NVMe と SATA の違いは何か?

SATA / AHCI は 1 つのコマンドキュー、32 エントリ深さ、すべての CPU で共有 —— IO 提出はグローバルロックで競合し、NAND がどれだけ速くても ~10 万 IOPS で頭打ち。 NVMe は最大 64K の提出キュー、各最大 64K 深さ、典型的に CPU コアごとに 1 つのキューペアを公開する —— 提出はロックフリー、ハードウェア多重化、100 万+ IOPS にスケール。 だがその IOPS はキュー深度 ≥ 32 でのみ見える。シングルスレッドで同期読みを行うコードは 100 万 IOPS デバイスで ~3 万 IOPS を得る —— 実際に並列性を使うには libaio、io_uring、 またはワーカースレッドが必要。

なぜ現代の大きなドライブで RAID 5 は危険か?

再構築時間。10-18 TB ドライブは故障後 12-36 時間で再構築する —— 再構築は ドライブの持続スループットですべての生き残りドライブのすべてのブロックを読む。 そのウィンドウ中、生き残りドライブは 100% 負荷、まさに限界ドライブが故障する 最も可能性のあるとき。~1014 URE 率では、再構築中に回復不能な読みに当たる確率も 無視できない。RAID 5 は 1 故障を生き残る;再構築中の 2 故障 = 全損失。 10+ TB ドライブで、RAID 6(2 パリティドライブ)が最低限の責任ある選択。 大ドライブ JBOD での RAID 5 は次の障害ポストモーテムになることを乞うているようなもの。

Bitrot とは何で、何がそれを防御するか?

Bitrot = 静かなデータ破損:ドライブが読みで間違ったバイトをエラーコードなしに返す。 ドライブスペックはコンシューマディスクで 1014 ビットあたり約 1 (~12.5 TB)の回復不能な読みエラー率を引用する。複数 TB アレイで統計的に スクラブサイクルごとにいくつかの静かな破損に当たる。防御:エンドツーエンドチェックサム (ZFS、btrfs、すべての現代オブジェクトストア)をデータから離れて保存し、 両方が同期して腐る可能性が本質的にゼロになるようにし、加えてバックグラウンドスクラブが すべてのブロックを読み、不一致で冗長性から再構築する。MD RAID 上の ext4 は bitrot を 防御しない —— 読みが「成功した」のだから喜んで破損したデータを提供する。

生 NVMe + io_uring vs. ファイルシステムをいつ使うか?

生 NVMe(またはブロックデバイス上の O_DIRECT)はキャッシング、割り当て、 IO スケジューリングを所有したいときに意味を持つ:データベース (Postgres O_DIRECT モード、Oracle ASM、ScyllaDB)、サブ 100 μs レイテンシ目標を持つ トレーディングシステム、ストレージ製品(Pure Storage、MinIO)。 勝利:二重キャッシングなし、予測可能なメモリ圧力、QD と IO パターンの完全制御。 コスト:キャッシング、アライメント、回復を再実装する —— そして lsdu、スナップショット、バックアップを諦める。99% のサービスにとって、 ファイルシステム(NVMe 上の XFS または ext4)とページキャッシュは、 1 四半期で再エンジニアできるものより速い。

コードレビューの 5 つの赤旗

  1. dd if=/dev/zero でのベンチマーク。多くの SSD は全ゼロデータを検出し、書く前に圧縮または重複排除する —— あなたの「6 GB/s」の数字は圧縮器を測り、NAND ではない。fio--rw=randwrite --refill_buffers または/dev/urandom 由来のデータで使う。
  2. fio --iodepth=1 数字からの容量計画。100 万 IOPS デバイスでの 30K-IOPS QD-1 測定は syscall オーバーヘッドについて伝え、 ドライブについてではない。プロダクショントラフィックは多くの並行クライアントから 自然なキュー深度を持つ —— あなたのベンチマークも同じが必要。
  3. ≥10 TB ドライブの JBOD で RAID 5。再構築時間、URE 数学、生き残りへの負荷がすべて結託して、 「再構築中の 2 故障」を可能な結果にする。その容量ティアでは RAID 6 または RAIDZ2 が最低。
  4. 「SSD を使っているから」とコミット時に fsync をスキップ。ストレージメディアは耐久性を決定したことがない —— それは揮発性 DRAM キャッシュ (ページキャッシュ、SSD コントローラキャッシュ)と永続 NAND の間の電源喪失境界について。 fsync なしには、ディスクが回転錆だろうと PCIe 5.0 NVMe だろうと、 クラッシュは最後 5-30 秒の書き込みを失う。
  5. QD やワークロードミックスなしでスペックシート IOPS から SSD を比較。両方とも「100 万 IOPS」を主張する 2 つのドライブが、読み / 書き比率、ブロックサイズ、 キュー深度、満杯レベル、TRIM が配線されているかどうかに依存して、 実際のワークロードで 10× 異なる可能性がある。 常にプロダクションに似たワークロードでベンチマークする。