0

ESENT に保存された大量のデータ (100 GB 以上) を扱うアプリケーションがあります。テーブルのスキーマは次のとおりです。12 バイトのJET_bitColumnFixedキーとJET_coltypLongBinary値で、通常のサイズは約 2 KiB です。ページサイズは 32 KiB に設定されています。外部の long 値のデフォルトの 1024 バイト サイズのしきい値は変更しないので、これらの値はほとんどが外部に格納されていると思います。

操作がバッチで行われ、キーが事前にわかっているため、コールド キャッシュのシークと取得のパフォーマンスを改善することに関心があります。私の知る限り、JetPrereadKeys () API はそのような場合のパフォーマンスを向上させるように設計されていますが、結局のところ、この呼び出しの有無にかかわらず、実際の動作に変化は見られません。

詳細は次のとおりです。

  • 私の場合、JetPrereadKeys() は常に、API を呼び出したときに送信したキーの数に等しい適切な数の事前読み取りキーを報告します。ドキュメントに記載されているように、送信されたキーは適切にソートされます。

  • 同期アプローチと非同期アプローチの両方を試しました。非同期アプローチは、現在のスレッドでデータのシークと取得を続けながら、事前読み取り呼び出しをスレッド プールに送信します。

  • JET_paramEnableViewCacheパラメーターとJET_paramEnableFileCacheパラメーターのすべての使用可能な組み合わせを試して、MMAP または専用ページ キャッシュを使用する ESENT の使用可能な両方のキャッシュ モードを試しました。

  • 小さな例外を除いて、事前読み取りの有無にかかわらず、ログに記録された I/O 操作に違いは見られません。つまり、この操作によって、必要な B ツリーの内部ノードが (できれば非同期で) フェッチされることを期待しています。しかし、唯一確認できるのは、JetPrereadKeys() 自体のスタックから時折発生する同期的な小さな読み取りです。必要な情報をすべてプリフェッチできるとは思えないという意味で、読み取りのサイズは小さいです。

  • Windows Search サービスをデバッグすると、JetPrereadKeys() へのさまざまな呼び出しを中断できます。したがって、おそらく何らかの理由で、この API が呼び出されている実際の例が少なくとも 1 つあります。

  • データベース ページ キャッシュが空であることを確認するために、すべての実験はマシンの再起動後に実行されました。


質問:

  1. 説明されているケースでの JetPrereadKeys() の予想される動作は何ですか?

  2. この API を使用すると、別の I/O パターンとパフォーマンスの向上が期待できますか? データの同期または非同期の事前読み取りを期待する必要がありますか?

  3. 今後のバッチについて ESENT を何らかの形でほのめかすことで、I/O パフォーマンスを改善しようとする別のアプローチはありますか?

4

1 に答える 1

2

JetPrereadKeys() API は、読み取りをリーフ レベルの親に同期してから、目的のキー/レコードに必要なすべてのリーフ ページの非同期 IO をキューに入れます。答えは #2 だと思います。メイン テーブル レコード (バーストの長い値/LV が別のツリーに格納されていることに注意してください) が浅いか、完全にキャッシュされている場合、この JetPrereadKeys() は役に立たない可能性があります。ただし、テーブルのプライマリ ツリーが大きくて深い場合、この API は劇的に役立ちます...それは、取得するデータの形状と広がりに依存します。スペースをダンプし、ツリーの深さを見て、「データ」ページの意味を理解することで、テーブルに関するいくつかの基本を知ることができます。

esentutl /ms Your.Db /v /fName,Depth,Internal,Data

テーブルの名前、深さ、内部ページ数、およびリーフ レベル データ ページ数をリストします。テーブル名によってメイン レコード ツリーの個別の行がリストされ、その下に "[Long Values]" として LVs / がリストされます。

また、この事前読み取りキーはバースト LV にも拡張されないことに注意してください。したがって、バースト LV 列をすぐに読み取ると、残念ながら IO の後ろに固定されます。

既定のモードでは、ESE が独自のデータベース バッファー/ページ キャッシュを排他的に割り当てて制御します。JET_paramEnableFileCache は主に、終了する (または少なくとも JetTerm/JetDetach の DB) および多くの再起動を行う (通常は小さい) クライアント プロセスを対象としています。そのため、終了するたびに ESE のプライベート バッファー キャッシュが失われますが、JET_paramEnableFileCache は最近終了した場合、データがファイルキャッシュに残っている可能性があるためのパラメーター。ただし、データが ESE バッファー キャッシュと NTFS / ReFS ファイル キャッシュに二重にキャッシュされるため、大規模な DB にはお勧めできません。JET_paramEnableViewCache は以前のパラメーターを拡張し、この二重キャッシングをいくらか改善します...しかし、メモリを節約することしかできません/クリーン/変更されていないページバッファーのダブルバッファーではありません。大規模な DB の場合は、これらのパラメーターを両方とも off / false のままにします。また、これらのパラメーターを使用しない場合は、コールド パフォーマンスをテストする方が簡単です。アプリが終了した後、大きな (100 MB、おそらく 1 または 2 GB) ファイルを HD に数回コピーするだけです (HD をクリアするため)。キャッシュ)、データはコールドになります。;-)

キャッシングについて言及したので...最後に1つ-おそらく実際の問題だと思います(上記の「データの形状」でない場合)... perfmonを開いて「データベース」を見つけますおよび/または「データベース ==> インスタンス」パフォーマンス オブジェクト (これらは ESENT 用です) を確認し、キャッシュ サイズ [「データベース キャッシュ サイズ」または「データベース キャッシュ サイズ (MB)」のいずれか] を確認し、使用可能なプールの大きさを確認します。 is / ["Database Cache % Available"]... もちろん、この % を取得し、データベース キャッシュ サイズに対して計算して、アイデアを得る必要があります ... しかし、これが低い場合、これが問題になる可能性があります。 .. これは、JetPrereadKeys が既に利用可能なバッファーのみを使用するためです。そのため、健全で十分な大きさの利用可能なプールが必要です。JET_paramCacheSizeMin を大きくするか、

paramCacheSizeMin = 500
paramCacheSizeMax = 100000
paramStartFlush.. =   1000
paramStopFlushT.. =   2000

開始と停止のしきい値が、現在のキャッシュ サイズのそれぞれ 1% と 2% であることを意味します。したがって、キャッシュが 500 バッファー (最小) の場合、5 と 10 が開始/停止しきい値になります。つまり、使用可能なプールが含まれる範囲です。後で 10000 バッファーに増加した場合、使用可能なプールは 100 から 200 の範囲になります。バッファ。いずれにしても、JetPrereadKeys が必要とする可能性のあるすべてのリーフ ページに対して十分なバッファを確保できるように、これらの数値を適切な範囲にする必要があります。

このメールですべての用語を説明したわけではありません。上の記事では、B ツリーの内部ノードなどについてかなり上級者向けに説明していましたが、不明な点がある場合は、質問してください。解決します。

ありがとう、

ブレット・シャーリー [MSFT]

拡張可能ストレージ エンジンの開発者

この投稿は、無保証で「現状のまま」提供され、いかなる権利も付与しません。

PS - 最後に、JetGetThreadStats / JET_THREADSTATS で遊んでみてください。これは、API の下で行う内部操作の一部を示しています。基本的に、JET API の前後の値を読み取り、それらを差し引いて、その JET API の操作数を取得します。したがって、そこに cPagePreread が表示されます...これは、JetPrereadKeys がパフォーマンスに役立つはずの非同期 IO からディスパッチされているかどうかを確認する良い方法です。残念ながら古い OS では特定のカウンターが壊れていたことに注意してください。Win10 を使用している場合、問題はありません。それまでに確実に修正されています。;-) また、cPageRead は同期読み取りページです (これは内部ノードのために上昇する可能性があります)... これらは、さまざまな JET API のコストについて非常に有益であることがわかると思います。

于 2020-01-15T22:50:44.423 に答える