- 1秒あたり200万件のメッセージを処理する NoSQL DB クラスタ(ScyllaDB)を運用中
- DB性能に最も大きな影響を与えるのは物理ディスクハードウェアのレイテンシ
→ クエリ量が低い水準では問題にならないが、ある時点を超えると、1~2msかかる読み取り時間だけでもディスク読み取り待ちキューが発生し、クエリ自体がタイムアウトする
- ディスクレイテンシは通常マイクロ秒単位なのに、なぜディスク操作に1~2msもかかるのか?
- Discord は大半のハードウェアを Google Cloud で運用
- NVMe ベースのローカル SSD をサポートしているが、独自にテストしたところ安定性の問題があり、重要なデータストアに使うには不安があった
- Persistent Disk はサーバーへリアルタイムに接続・切断でき、ダウンタイムなしでリサイズ可能で、いつでもスナップショットを作成でき、標準でレプリケーションされるよう設計されている
→ 問題は、サーバーに直接接続されておらず、ネットワーク経由でつながっていること
- ローカルネットワーク接続のレイテンシがどれだけ低くても、PCI/SATA より低くはならない
→ ネットワークは 1~2ms、直結ディスクは 0.5ms
- ローカル SSD は、HDD のようにハードウェア障害が起きるとそのディスク上のデータを失い、ホスト自体に問題が起きるとスナップショットも取れず、データを完全に失う事態が起こりうる
→ そのため Discord は Local SSD を使わず、Persistent Disk を使用
問題分析
- ローカル SSD と Persistent Disk の長所だけを集めたストレージがあれば理想だが、そのようなものはない。長所の一部だけを取り込むとしたら?
- Discord では書き込みレイテンシは問題ではない。性能に影響するのは「読み取りレイテンシ」
- 「ダウンタイムなしのディスクリサイズ」は必須機能ではない。サイズは事前に予測可能
- 最終要件は
- GCP にそのままとどまりつつ
- データバックアップのために Point-in-Time スナップショットを使い
- 読み取りレイテンシの最小化を最優先にし
- 既存データベースのアップタイム保証を犠牲にしないこと
- 読み取りは GCP の Local SSD を活用し、書き込みは Persistent Disk に行えればよさそう
→ ソフトウェアレベルでこうした Super-disk を作れるだろうか?
Super-Disk を作る
- 要件は基本的に Write-Through キャッシュだった。GCP のローカル SSD をキャッシュとして使い、PD を保存レイヤーとして使う
- DB サーバーに Ubuntu を使っているため、Linux カーネル層でディスクレベルのキャッシュを適用できる(dm-cache、lvm-cache、bcache などのモジュール)
- しかし実験してみると、キャッシュディスクに不良セクタが発生した場合、読み取り処理全体が失敗した
- 不良セクタが発生したらストレージレイヤーから読み出して上書きしなければならないが、評価したディスクキャッシュソリューションにはその機能がなかった
- 不良セクタ発生時に、データベースがデータ整合性の問題でシャットダウンしてしまう
- 追加要件として「ローカル SSD に不良セクタが発生しても生き残れること」が加わった
- そこで Linux カーネルの「md」を調査
- md はソフトウェア RAID の作成をサポートする
- SSD と PD をミラーリングするだけでは問題は解決しない。読み取りの半分以上が PD から行われてしまうため
- md には従来の RAID にはない「write-mostly」がある
- 特定のディスクを write-mostly に指定すると、通常の読み取り対象から外され、ほかに選択肢がないときだけ読み取りが行われる。「低速に接続されたデバイスに有用」
- つまり、SSD と PD を RAID1 にまとめ、PD を write-mostly に設定すれば要件を満たせる
- 最後に残った問題は、GCP の Local SSD の容量がちょうど 375GB であること
- Discord では特定のアプリケーション向けに、DB インスタンスあたり 1TB 以上が必要な場合もある
- そこで複数の SSD を RAID0 にまとめることにした
- 最終構成は
- RAID0 でまとめたローカル SSD 4台を md0
- md0 と Persistent Disk を RAID1 にまとめた md1 を構成
DB 性能
- 結果はまさに予想どおりだった
- ピーク時でもディスク操作がキューに積み上がらず、クエリレイテンシが変化しない
- 性能が向上し、各サーバーあたりの処理クエリ量も増えた
- RAID を使ったことがある人なら「これで本当に動くのか?」と疑問に思うかもしれないが、実際にはさまざまなことがあり、残りは別途詳しく紹介する予定
3件のコメント
以前はGolangのパフォーマンスに満足できず、Rustでサーバーまで書いていたのを見ると、Discordという会社のギークさも相当なものだと思います。
大したことではないのに、大げさな手段で防いでいる感じです。
HNでは「それって単にGCPの問題じゃない?」という話もありますが..
https://news.ycombinator.com/item?id=32474093
こういう試みも可能なんだな、程度に知っておくとよさそうです。