ファイルシステムの場合、読み取りと書き込みの効率がシステム全体のパフォーマンスに決定的な影響を与えます。この記事では、JuiceFSの機能をよりよく理解できるように、JuiceFSの読み取りと書き込みの要求処理フローを紹介します。
書き込みプロセス
JuiceFSは、大きなファイルを複数のレベルに分割し(JuiceFSがファイルを保存する方法を参照)、読み取りと書き込みの効率を向上させます。書き込み要求を処理するとき、JuiceFSは最初にクライアントのメモリバッファにデータを書き込み、チャンク/スライスの形式でデータを管理します。チャンクは、ファイル内のオフセットに従って64 MiBで分割された連続論理ユニットであり、異なるチャンクは完全に分離されています。各チャンクは、アプリケーションの書き込み要求の実際の状況に応じてさらにスライスに分割されます。新しい書き込み要求が継続的であるか、既存のスライスと重複している場合は、スライス上で直接更新されます。それ以外の場合は、新しいスライスが作成されます。 。
スライスは、データの永続性を開始する論理ユニットです。フラッシュする場合、最初にデータをデフォルトのサイズ4 MiBに従って1つ以上の連続するブロックに分割し、オブジェクトストレージにアップロードします。各ブロックはオブジェクトに対応します。メタデータを更新したら、新しいスライス情報を書き込みます。もちろん、シーケンシャル書き込みを適用する場合は、継続的に成長するスライスを1つだけ必要とし、最後に1回のフラッシュのみが必要です。このとき、オブジェクトストレージの書き込みパフォーマンスを最大化できます。
簡単なJuiceFSベンチマークテストを例にとると、最初の段階では、1MiBIOを使用して1GiBファイルを順番に書き込みます。各コンポーネントのデータの形式を次の図に示します。
注:画像の圧縮と暗号化はデフォルトでは有効になっていません。関連する機能を有効にするには、ファイルシステムをフォーマットする
--compress value
ときにまたは--encrypt-rsa-key value
オプションを追加する必要があります。
これは、テスト中にstats
コマンド、関連情報をより直感的に確認できます。
上の画像のステージ1:
- オブジェクトストレージ書き込みの平均IOサイズは
object.put / object.put_c = 4 MiB
、ブロックのデフォルトサイズと同じです。 - オブジェクトストレージの書き込み数に対するメタデータトランザクションの数の比率は約
meta.txn : object.put_c ~= 1 : 16
であり、スライスフラッシュに必要な1つのメタデータの変更と16のオブジェクトストレージのアップロードに対応します。また、各フラッシュで書き込まれるデータの量が4 MiB*16であることも示しています。 = 64 MiB、チャンクのデフォルトサイズ - FUSE層の平均リクエストサイズは、
fuse.write / fuse.ops ~= 128 KiB
デフォルトのリクエストサイズ制限に沿って約です。
シーケンシャル書き込みと比較すると、大きなファイルへのランダム書き込みの状況ははるかに複雑です。各チャンクに複数の不連続スライスが存在する可能性があるため、データオブジェクトのサイズが4 MiBに達するのが難しくなり、一方でより多くのメタデータが必要になります。 。更新。同時に、チャンクに書き込まれるスライスが多すぎると、圧縮がトリガーされてこれらのスライスをマージしてクリーンアップしようとします。これにより、システムの負担がさらに増加します。したがって、JuiceFSは、このようなシナリオでの順次書き込みよりも明らかにパフォーマンスが低下します。
小さなファイルの書き込みは通常、ファイルが閉じられたときにオブジェクトストレージにアップロードされ、対応するIOサイズは通常ファイルサイズです。上記のインジケーターグラフのフェーズ3(小さな128 KiBファイルの作成)からも確認できます。
- オブジェクトストレージPUTのサイズは128KiBです
- メタデータトランザクションの数は、PUTカウントの約2倍であり、ファイルごとに1つの作成と1つの書き込みに対応します。
ブロックが1つ未満のこのようなオブジェクトの場合、JuiceFSは、後続の可能な読み取り要求の速度を向上させるために、アップロード中にローカルキャッシュ(--cache-dir
で指定さます。 。また、インジケーターグラフから、小さなファイルを作成する場合、ブロックキャッシュの書き込み帯域幅は同じであり、読み取り(ステージ4)の場合、ヒットのほとんどがキャッシュ内にあるため、小さなファイルの読み取り速度が特別に見えることがわかります。 。速い。
書き込み要求はクライアントのメモリバッファに書き込むことで返されるため、JuiceFSの書き込みレイテンシは通常非常に低く(数十マイクロ秒)、オブジェクトストレージへの実際のアップロードは内部で自動的にトリガーされます(単一のスライスが大きすぎる場合は、スライスが多すぎる、バッファ時間が長すぎるなど)、またはアプリケーションがアクティブにトリガーされます(ファイルを閉じる、呼び出すfsync
など)。バッファ内のデータは、永続化された後にのみ解放できます。したがって、書き込みの同時実行性が比較的大きい場合、またはオブジェクトストレージのパフォーマンスが不十分な場合、バッファがいっぱいになり、書き込みがブロックされる可能性があります。
具体的には、バッファーのサイズはマウントパラメーターで--buffer-size
指定さ。デフォルトは300 MiBです。そのリアルタイム値は、インジケーターグラフのusage.buf列に表示されます。使用量がしきい値を超えると、JuiceFSクライアントは書き込みに約10msの待機時間を積極的に追加して、書き込み速度を遅くします。使用量がしきい値の2倍を超えると、バッファーが解放されるまで新しい書き込みが一時停止されます。したがって、通常、書き込みレイテンシの上昇が観察され、バッファが長時間しきい値を超えた場合は、より大きな値を設定する必要があり--buffer-size
ます。さらに、--max-uploads
パラメーター(オブジェクトストレージへの同時アップロードの最大数、デフォルトは20)を増やすことで、オブジェクトストレージへの書き込みの帯域幅を増やし、バッファーの解放を高速化することもできます。
ライトバックモード
データの一貫性と信頼性の要件が高くない場合は、マウント中に追加して、システムパフォーマンス--writeback
をます。ライトバックモードを有効にすると、スライスフラッシュはローカルのステージングディレクトリ(キャッシュと共有)に書き込むだけで戻り、データはバックグラウンドスレッドによってオブジェクトストレージに非同期でアップロードされます。JuiceFSのライトバックモードは、ローカルキャッシュディレクトリにデータを書き込む必要がある一般的に理解されているライトファーストメモリとは異なることに注意してください(特定の動作は、キャッシュディレクトリが配置されているハードウェアとローカルファイルシステムによって異なります)。別の見方をすれば、ローカルディレクトリは、現時点ではオブジェクトストレージのキャッシュレイヤーです。
ライトバックモードを有効にすると、アップロードされたオブジェクトのサイズチェックはデフォルトでスキップされ、すべてのデータが可能な限りキャッシュディレクトリに積極的に保持されます。これは、多くの中間ファイル(ソフトウェアのコンパイルなど)を生成するシナリオで特に役立ちます。
さらに、JuiceFS v0.17には、オブジェクトストレージへのデータのアップロードを遅らせ、より積極的な方法でローカルにキャッシュするための--upload-delay
パラメーター。待機時間内にアプリケーションによってデータが削除された場合、データをオブジェクトストレージにアップロードする必要がないため、パフォーマンスが向上するだけでなく、コストも節約できます。同時に、ローカルハードディスクと比較して、JuiceFSはバックエンド保証を提供します。キャッシュディレクトリの容量が不足している場合でも、アプリケーション側がこれによるエラーを認識しないように、データを自動的にアップロードします。この機能は、一時ストレージを必要とするSparkシャッフルなどのシナリオを処理する場合に非常に効果的です。
読み取りプロセス
JuiceFSは、読み取り要求を処理するときに、通常、4つのMiBブロックの配置に従ってオブジェクトストレージを読み取り、特定の先読み機能を実現します。同時に、読み取られたデータは後で使用するためにローカルキャッシュディレクトリに書き込まれます(インジケータ図の第2段階など、ブロックキャッシュには高い書き込み帯域幅があります)。もちろん、シーケンシャルリードでは、事前に取得したこれらのデータに後続のリクエストでアクセスし、キャッシュヒット率が非常に高いため、オブジェクトストレージのリード性能も十分に活用できます。この時点で、各コンポーネントのデータの流れを次の図に示します。
注:読み取られたオブジェクトがJuiceFSクライアントに到達すると、書き込まれるときとは異なり、復号化されてから解凍されます。もちろん、関連する機能が有効になっていない場合、対応するプロセスは直接スキップされます。
大きなファイルでランダムな小さなIO読み取りを行う場合、JuiceFSのこの戦略は効率的ではありませんが、読み取りの増幅とローカルキャッシュの頻繁な書き込みと削除により、システムリソースの実際の使用率が低下します。残念ながら、このようなシナリオでは、一般的なキャッシング戦略が十分に有益であることはめったにありません。現時点で検討できる方向の1つは、必要なデータをほぼ完全にキャッシュする効果を実現するために、キャッシュの全体的な容量を可能な限り増やすことです。もう1つの方向は、キャッシュを直接閉じる(set --cache-size 0
)ことです。オブジェクトストレージパフォーマンスの読み取りアクセスを最大化します。
小さなファイルの読み取りは比較的簡単で、通常は1回のリクエストでファイル全体を読み取ります。小さなファイルは書き込み時に直接キャッシュされるため、書き込み直後に読み取られるJuiceFSベンチのようなアクセスパターンは、基本的にローカルのCacheディレクトリにヒットし、パフォーマンスは非常に優れています。
要約する
上記は、この記事で簡単に説明したJuiceFSの読み取りおよび書き込み要求処理プロセスに関連するコンテンツです。大きなファイルと小さなファイルの特性の違いにより、JuiceFSは、サイズの異なるファイルに対して異なる読み取りおよび書き込み戦略を実装し、それによって大幅に改善されます。全体的なパフォーマンスと可用性。これにより、さまざまなシナリオのユーザーのニーズをより適切に満たすことができます。
推奨読書:KubernetesクラスターでFluid+JuiceFSを操作する方法
それが役に立ったら、私たちのプロジェクトJuicedata / JuiceFSに従ってください!(0ᴗ0✿)