ジャーナリングファイルシステムは、ファイルシステムのメイン部分にまだコミットされていない変更を追跡するファイルシステムであり、そのような変更の目的を「ジャーナル」と呼ばれるデータ構造(通常は循環ログ)に記録します。システムクラッシュや停電が発生した場合、このようなファイルシステムは破損する可能性が低く、より迅速にオンラインに戻すことができます。[ 1 ] [ 2 ]
実際の実装によっては、ジャーナリングファイルシステムは保存されたメタデータのみを追跡する場合があり、その結果、データ破損の可能性が高まる代わりにパフォーマンスが向上します。あるいは、ジャーナリングファイルシステムは保存されたデータと関連するメタデータの両方を追跡する場合があり、一部の実装ではこの点に関して選択可能な動作が可能です。[ 3 ]
1990年、IBMはAIX 3.1でJFSを導入し、ジャーナリングを実装した最初のUNIX商用ファイルシステムの1つとなりました。[ 4 ] 翌年、このアイデアはログ構造ファイルシステムに関する広く引用された論文で普及しました。[ 5 ] その後、1993年にMicrosoftのWindows NTのNTFSファイルシステム、1998年にAppleのHFS Plusファイルシステム、2001年にLinuxのext3ファイルシステムに実装されました。 [ 6 ]
ファイルやディレクトリの変更を反映させるためにファイルシステムを更新するには、通常、多くの個別の書き込み操作が必要です。そのため、書き込みの間に中断(停電やシステムクラッシュなど)が発生すると、データ構造が無効な中間状態になる可能性があります。[ 1 ]
例えば、Unixファイルシステムでファイルを削除するには、次の3つのステップが必要です。[ 7 ]
ステップ1の後、ステップ2の前にクラッシュが発生した場合、孤立したinodeが発生し、ストレージリークが発生します。ステップ2とステップ3の間にクラッシュが発生した場合、ファイルによって以前使用されていたブロックは新しいファイルに使用できなくなるため、ファイルシステムのストレージ容量が実質的に減少します。ステップの順序を変更しても効果はありません。ステップ3がステップ1より前に実行された場合、その間にクラッシュが発生すると、ファイルのブロックが新しいファイルに再利用される可能性があります。つまり、部分的に削除されたファイルには別のファイルの内容の一部が含まれることになり、どちらかのファイルへの変更が両方に反映されます。一方、ステップ2がステップ1より前に実行された場合、その間にクラッシュが発生すると、ファイルは存在しているように見えても、アクセスできなくなります。
このような不整合を検出して復旧するには、通常、fsck (ファイルシステムチェッカー)などのツールを使用して、データ構造を完全に走査する必要があります。 [ 2 ] これは通常、ファイルシステムが次に読み書きアクセス用にマウントされる前に実行する必要があります。ファイルシステムが大きく、I/O 帯域幅が比較的少ない場合、これには時間がかかり、システムの残りの部分がオンラインに戻るのを妨げると、ダウンタイムが長くなる可能性があります。
これを防ぐため、ジャーナル方式のファイルシステムは、事前に変更内容を記録するための特別な領域(ジャーナル)を割り当てます。クラッシュ後、復旧作業はファイルシステムからジャーナルを読み込み、ファイルシステムが再び整合性を保つまで、このジャーナルから変更内容を再生するだけです。したがって、変更内容はアトミック(分割不可能)であると言えます。つまり、変更が成功する(元々成功していたか、復旧中に完全に再生される)か、まったく再生されない(クラッシュ発生前にジャーナルに完全に書き込まれていなかったためスキップされる)かのどちらかです。
ファイルシステムによっては、ジャーナルを通常のファイルと同様に拡張、縮小、再割り当てできるものもあれば、ファイルシステムがマウントされている間は移動やサイズ変更が保証される連続領域または隠しファイルにジャーナルを配置するものもあります。また、一部のファイルシステムでは、ソリッドステートドライブやバッテリーバックアップ付き不揮発性RAMなどの別のデバイス上に外部ジャーナルを配置することも可能です。ジャーナルへの変更自体をジャーナルに記録して冗長性を高めたり、ジャーナルを複数の物理ボリュームに分散させてデバイスの障害から保護したりすることもできます。
ジャーナルの内部フォーマットは、ジャーナルへの書き込み中にクラッシュが発生しないように保護する必要があります。多くのジャーナル実装(ext4のJBD2レイヤーなど)では、ログに記録されるすべての変更をチェックサムで囲みます。これは、クラッシュが発生した場合、部分的に書き込まれた変更にチェックサムの欠落(または不一致)が残るものの、次回の再マウント時にジャーナルを再生する際には、その欠落を無視できるという前提に基づいています。
物理ジャーナルは、メインファイルシステムに書き込まれるすべてのブロックの事前コピーを記録します。メインファイルシステムへの書き込み中にクラッシュが発生した場合、ファイルシステムが次にマウントされたときに、書き込み処理を最初から最後まで再生できます。書き込み処理がジャーナルに記録されている最中にクラッシュが発生した場合、部分的な書き込みにはチェックサムが欠落または不一致となるため、次のマウント時に無視できます。
物理ジャーナルは、変更されたブロックごとにストレージに2回コミットする必要があるため、パフォーマンスに大きなペナルティを課しますが、絶対的な障害保護が必要な場合には許容される可能性があります。[ 8 ]
論理ジャーナルはファイルメタデータの変更のみをジャーナルに保存し、耐障害性を犠牲にして書き込みパフォーマンスを大幅に向上させます。[ 9 ] 論理ジャーナルを備えたファイルシステムはクラッシュ後も迅速に復旧しますが、ジャーナル化されていないファイルデータとジャーナル化されたメタデータが同期しなくなり、データ破損を引き起こす可能性があります。
例えば、ファイルへの追記には、以下の3つの別々の書き込みが含まれる場合があります。
メタデータのみのジャーナルでは、ステップ3はログに記録されません。ステップ3が実行されなかった場合でも、リカバリ中にステップ1とステップ2が再生されると、ファイルには不要なデータが追加されます。
ほとんどのオペレーティングシステムの書き込みキャッシュは、スループットを最大化するために、書き込みを(エレベーターアルゴリズムまたは同様の方式を使用して)ソートします。メタデータのみのジャーナルで順不同書き込みハザードを回避するには、ファイルデータの書き込みは、関連するメタデータよりも先にストレージにコミットされるようにソートする必要があります。これは、オペレーティングシステムカーネル内でファイルシステムドライバと書き込みキャッシュ間の連携が必要となるため、実装が難しい場合があります。デバイスがブロックを基盤となるストレージにすぐに書き込めない場合、つまり、遅延書き込みが有効になっているために書き込みキャッシュをディスクにフラッシュできない場合にも、順不同書き込みハザードが発生する可能性があります。
さらに問題を複雑にしているのは、多くの大容量ストレージデバイスには独自の書き込みキャッシュがあり、パフォーマンス向上のために書き込みを積極的に並べ替える場合があることです。(これは特に磁気ハードドライブでよく見られ、磁気ハードドライブはシークレイテンシーが大きく、エレベーターソートで最小限に抑えることができます。)一部のジャーナリングファイルシステムは、このような書き込みの並べ替えが常に行われることを保守的に想定し、ジャーナル内の特定のポイント(ext3およびext4ではバリアと呼ばれます)でデバイスにキャッシュをフラッシュさせることで、パフォーマンスを犠牲にして正確性を確保しています。[ 10 ]
UFS の実装の中にはジャーナリングを回避し、ソフトアップデートを実装しているものもあります。つまり、ディスク上のファイルシステムが不整合にならないように書き込みの順序を調整したり、クラッシュ時に発生する不整合がストレージリークのみとなるようにしています。これらのリークから復旧するために、次のマウント時にファイルシステムの完全なウォークに対して空き領域マップが調整されます。このガベージコレクションは通常バックグラウンドで実行されます。[ 11 ]
ログ構造ファイルシステムでは、ジャーナル自体がファイルシステムであるため、書き込みの二度目のペナルティは適用されません。ジャーナルはストレージデバイス全体を占有し、通常のファイルシステムと同様に走査できるように構造化されています。
フルコピーオンライト方式のファイルシステム( ZFSやBtrfsなど)は、新しく割り当てられたブロックにデータを書き出し、続いて新しいデータを指し古いデータを否定する更新されたメタデータを書き出し、さらにそのメタデータを指し示すメタデータを書き出す、という手順をスーパーブロック(ファイルシステム階層のルート)まで繰り返すことで、ファイルデータへの直接的な変更を回避します。これにより、ジャーナルと同様の正当性保持特性を持ちながら、2回書き込むオーバーヘッドが発生しません。