TT Lab
はじめる
学ぶ 学習パス コース

コンピュータ構成

ストレージ — 回る円盤から並列キューまで

TT Labで続きを見る

一言でいうと

HDDは、物理的にヘッドを動かす必要があるため、ランダムアクセスがミリ秒単位です。SSDはその制約がない代わりに、消去の単位が大きいため、書き込みに独自のコスト構造を持ちます。

なぜ必要なのか

データベースとファイルシステムの設計の大部分は、「回転する円盤」という前提の上に築かれました。シーケンシャルアクセスがランダムアクセスより100倍速いという事実が、Bツリーの構造、ログ先行書き込み、ログ構造化マージツリーのようなアイデアを生みました。記憶媒体が変わったとき、その前提のうち何が残り、何が崩れるのかを知っておく必要があります。

どう動くのか

HDD: 1つのブロックを読むには、ヘッドを該当のトラックへ移動し(シークタイム、通常5–10ms)、目的のセクターがヘッドの下に回ってくるのを待ちます(回転待ち時間、7200rpmなら平均約4ms)。そのため、ランダム読み取りは毎秒数百回が限界です。一方、ヘッドを動かさずに続けて読めば、毎秒数百MBが出ます。シーケンシャルとランダムの格差が100倍以上というこの特性が、すべての古典的な設計の出発点です。

SSD: 動く部品がないので、ランダム読み取りがマイクロ秒単位です。しかし、書き込みが対称ではありません。NANDフラッシュは、ページ(数KB)単位で書き込み、ブロック(数MB)単位でしか消去できません。すでに書いた場所に上書きできないので、コントローラーは新しい場所に書き込み、古い場所を無効と記録したあと、のちに有効なページを集めて移し、ブロックを丸ごと消去します(ガベージコレクション)。この過程で、ホストが要求した量よりも実際には多く書き込むことになり、その比率を書き込み増幅(write amplification)といいます。また、各セルは消去できる回数に限界があるので、コントローラーがウェアレベリング(wear leveling)で均等に書き込みます。

NVMe: SATAは、コマンドキューが1つで、深さは32でした。NVMeは、キューを数万個まで、各キューを数万の深さに置けます。ここで重要な転換が起きます。個々のリクエストのレイテンシは大きくは減りませんが、同時に投げるリクエスト数(キューの深さ)を増やさなければ、帯域幅が埋まりません。キューの深さ1で測定したIOPSは、デバイスの性能の一部しか示しません。

現場での姿

PostgreSQLのrandom_page_costのデフォルト値4.0は、回転ディスクが基準です。SSDやNVMeの上でこの値をそのままにすると、オプティマイザーがランダムアクセスを実際より4倍高く評価して、インデックスを過小評価します。「インデックスを作ったのに使われない」という問い合わせの相当数が、この設定1つを1.1付近に下げれば解決します。媒体が変わったなら、媒体を前提としたコストモデルも変える必要があるという事例です。

逆に、依然として有効な前提もあります。シーケンシャル書き込みがランダム書き込みより有利だという点は、SSDでもそのままです。理由が変わっただけです。HDDではヘッドを動かさないからでしたが、SSDではガベージコレクションと書き込み増幅を減らしてくれるからです。

数字で感覚をつかむ

ストレージの違いは、表で見ると明らかです。桁が違います。

HDD SATA SSD NVMe SSD メモリ
ランダム読み取りのレイテンシ 5–10 ms 0.1 ms 0.02 ms 0.0001 ms
IOPS(ランダム4K) 100–200 数万 数十万から100万 —
シーケンシャル帯域幅 150 MB/s 500 MB/s 3–7 GB/s 50 GB/s以上
キューの深さ 1(事実上) 32 65,536×複数のキュー —

HDDのランダムIOPSが200しかない理由は、物理的なものです。ヘッドが動いて、ディスクが回らなければならないので、1回に5–10msかかります。そのため、データベースをHDDに置くと、毎秒200件のランダムな検索が上限です。

NVMeがSATA SSDより速いのは、メディアではなくインターフェースのためです。SATAはキューが1つで深さ32ですが、NVMeはキューを複数持ち、それぞれの深さが65,536です。CPUコアごとに自分のキューを持てるので、並列性がまったく違います。

シーケンシャルとランダムを区別することが設計です

同じデバイスでも、アクセスパターンによって性能が10倍以上分かれます。

3つ目が、実務でよく引っかかります。画像のサムネイルやログの断片を1つずつファイルとして置くと、ディスク容量は余っているのにinodeが尽きて、それ以上書き込めなくなります。df -iで確認します。

書き込みが実際にディスクに届く時点

アプリケーションがwrite()を呼び出したからといって、データがディスクにあるわけではありません。

앱 버퍼 → 페이지 캐시(커널) → 장치 캐시 → 매체
              ↑ write() 는 여기까지만 보장한다
              ↑ fsync() 가 아래로 밀어낸다

電源が落ちると、fsyncしていないものは消えます。データベースがコミットごとにfsyncする理由で、それが書き込み性能の上限を決めます。

ここで、書き込みキャッシュのあるディスクの落とし穴が出てきます。デバイスがキャッシュに入れただけで「完了」を返すと、fsyncも嘘になります。サーバー用のSSDは、電源喪失保護(PLP)のコンデンサーを搭載して、この問題を防ぎます。コンシューマー向けのSSDをDBサーバーに使ってはいけない理由がこれです。

続くクイズで確認すること

媒体が変わると、どの設計の前提が崩れ、どれが残るのかを区別できるかを確認します。