完璧なDBでも壊れる――「コミット成功」なのにデータが消えかねない3つの問題:ハードウェアが「うそ」をつく?
業務システムにおいて、データベースへの保存処理の完了は「データが安全に守られた」ことを意味するとは限らない。データの破損を招きかねない、インフラ層が抱える3つの構造的な問題とは。
システムのクラウド化が進み、インフラがブラックボックス化する現代において、ストレージ層の物理的な制約を意識する機会は減少している。業務システムにおいて、データベースへのデータ保存は「できて当たり前」だと認識され、コミット(確定処理)が成功すればデータは永続化されたと安心するのが一般的だ。しかし、データベースエンジン、OS、物理的なストレージデバイス間で仕様の認識に食い違いがあれば、最悪の場合、致命的なデータ消失につながる。
DBaaS(Database as a Service)ベンダーPlanetScaleのインフラエンジニアであるクリス・シンジャクリ氏は、システムの下層に潜むデータ消失のリスクと、データベースがデータを守るための仕組みについて、「データベースをレプリケーション(複製)していれば安全だというのは誤りだ」と警鐘を鳴らす。個々のノード(サーバ)において、システム異常終了時にもデータ破損を防ぐクラッシュセーフティを確保できていなければ、「破損したデータ」を複製してしまう恐れがあるという。
データベースエンジン、OS、ハードウェア間の認識の食い違いが招く、3つの構造的な問題とは。
MySQLを悩ませる「半書き」と二重書き込みのジレンマ
併せて読みたいお薦め記事
データベース運用のヒント
以下では、システム管理者向けのイベント「SREcon25 Europe/Middle East/Africa」において、シンジャクリ氏がセッション「The Computer Wants to Lose Your Data」で語った3つの構造的課題から、データの永続性を確実なものにするための条件を読み解く。
1つ目の課題は、リレーショナルデータベース管理システム(RDBMS)「MySQL」のデータ書き込み方式に起因する。データベースはデータを効率的に管理するため、一定のブロック単位(MySQLのデフォルトは16KB)でストレージに書き込む。
一方で、一般的なHDDやコンシューマー向けSSDが「完全に実行されるか、全く実行されないか」の不可分(アトミック)な状態で一度に書き込める単位は、512Bから4KB程度にとどまる。もし16KBのデータを書き込んでいる間に電源喪失やシステムクラッシュが発生すると、データの一部だけが上書きされ、残りは古いデータのままになる「Torn Write」(半書き)という状態に陥る。この状態ではデータの構造が破壊され、変更履歴を記録した復旧用のファイル(トランザクションログ)さえ適用が不可能になる。
この致命的な破損を防ぐための仕組みとして、MySQLに備わっているのが、データ保護のために一時的に書き込む専用領域「Doublewrite Buffer」(ダブルライトバッファー)だ。データをテーブルに書き込む前に、まず専用領域に記録することで、もしテーブルへの書き込み中にクラッシュが発生しても、バッファーから正常なデータを復旧できる仕組みを構築している。
しかし、同じデータを2回ディスクに書き込むため、パフォーマンスの低下は避けられない。シンジャクリ氏によれば、これを安全に回避する手段として2つのアプローチが存在する。1つ目は高度なデータ保護機能を備えたファイルシステム「ZFS」(Zettabyte File System)を採用し、16KB単位での書き込みをソフトウェアレベルで保証することだ。2つ目は、16KB以上のデータを一括で書き込める仕様を持つ、エンタープライズグレードのNVMe(Non-Volatile Memory Express)接続SSDを利用することだ。こうした特性を持つストレージを用意できれば、インフラ担当者は二重書き込みを無効化し、安全性とパフォーマンスを両立させることが可能になる。
PostgreSQLの「fsyncgate」が示す低レイヤーの残酷さ
2つ目の課題は、2018年にRDBMS「PostgreSQL」のコミュニティーで見つかった、「fsyncgate」と呼ばれるバグだ。
データをディスクに確実に書き込んだことを保証するため、データベースシステムは「Linux」のカーネル(OSの中核機能)である「fsync」を実行する。fsyncは、メモリ上のデータを物理ディスクに確実に保存するよう指示する機能だ。
通常、fsyncが「成功」を返せばデータは永続化されたと見なされる。しかし当時のLinuxカーネルでは、書き込み時のI/O(入出力)エラーが発生し、fsyncがエラーを返した後、もう一度fsyncを呼び出すと「エラー状態がクリアされて成功を返してしまう」という挙動が存在していた。エラー発生時に再試行する設計だったデータベースは、実際には書き込まれていないデータを「保存完了」と誤認し、深刻なデータ破損を引き起こす恐れがあったのだ。
「上位のソフトウェア層は、下位層のバグや仕様を修正できない」とシンジャクリ氏は指摘する。PostgreSQL開発チームは、fsyncがエラーを返した場合は再試行せず、直ちにデータベースプロセスを強制終了させる決断を下した。一見すると乱暴な手法に見えるが、これ以上書き込みを続けて不整合を広げるよりも、即座にシステムを停止させて既知の安全な状態から復旧するか、正常なノードに切り替える方が、データの完全性を守るためには理にかなっているためだ。
ハードウェアがつく「うそ」と揮発性キャッシュの死角
3つ目の課題は、ストレージデバイス自身が持つ「揮発性ライトキャッシュ」(電源が切れると消えてしまう一時的な高速データ保存領域)の問題だ。HDDやSSDには、書き込み速度の向上や突発的な負荷への対応のために、RAMを用いたキャッシュ領域が搭載されている。
OSからフラッシュ(書き込みの確定)の要求があった場合、ストレージデバイスはキャッシュ内のデータを不揮発性の領域に移す必要がある。しかし、コンシューマー向けの安価なストレージデバイスの一部には、読み書き速度を高く見せるために、データがまだ揮発性のキャッシュにとどまっているにもかかわらず「書き込み完了」とOSに“うその応答”を返すものがある。この状態で電源が失われれば、データは確実に消え去る。
パフォーマンスと安全性を両立させるためのインフラ担当者の選択は、物理的な電源バックアップの導入だ。具体的には、停電時に内蔵電池でデータを保護する「バッテリーバックアップ機能」(BBU)を備えた、複数の記憶装置を制御する専用機器「ハードウェアRAIDコントローラー」を使用するか、大容量のキャパシター(コンデンサー)を搭載したエンタープライズ向けSSDを採用することだ。これによって、想定外の電源喪失時にも内蔵電力を使ってキャッシュ内容を安全に退避させることが可能になる。
「システムは時として、データを守ろうとする努力を裏切り、データを消し去ることがある。データベースを安定稼働させるためには、ソフトウェアの堅牢(けんろう)性だけではなく、ハードウェアを含めたインフラ全体における特性を理解し、適切に投資する必要がある」とシンジャクリ氏は総括した。インフラのクラウド化が進む現代においても、物理層の仕様や制約に対する深い洞察は、システムの信頼性を確保するエンジニアにとって不可欠だ。
Copyright © ITmedia, Inc. All Rights Reserved.
本記事は制作段階でChatGPT等の生成系AIサービスを利用していますが、文責は編集部に帰属します。