java 速度 - DB実装のためのメモリマップされたMappedByteBufferまたはDirect ByteBuffer?




randomaccessfile nio (3)

これは、すべての文脈のために長い質問のように見えます。 下の小説には2つの質問があります。 これを読んで援助をしていただきありがとうございます。

状況

私は、数KBからTB以上のサイズのデータ​​ファイルを32ビットまたは64ビットシステム上で処理することをサポートできるスケーラブルなデータストアの実装に取り​​組んでいます。

データストアは、コピーオンライト(Copy-on-Write)デザインを使用します。 新しいデータまたは変更されたデータを常にデータファイルの末尾に追加し、既存のデータに対してインプレース編集を行うことはありません。

システムは、1つまたは複数のデータベースをホストすることができます。 各ファイルはディスク上のファイルで表されます。

実装の詳細は重要ではありません。 重要な点は、常にファイルに追加してKBからMB、GBからTBに増やす必要があることだけです。同時に、クライアント要求に応答するためにファイルをランダムにスキップします。

最初の考え

一見すると、メモリマップされたファイルを使用したいので、データのインメモリ状態を効率的に管理する負担をホストOSとコードから奪い取ることができたということを私は知っていました。

次に、すべての私のコードは、append-to-file操作の書込みをシリアライズすることを心配する必要があります。また、任意の数の同時読者が要求に答えるためにファイル内をシークすることができます。

設計

個々のデータファイルはMappedByteBufferの2GBの限界を超えて拡大する可能性があるため、私のデザインでは、書き込みオフセットを取り、特定の2GBセグメント内のオフセットに変換する抽象レイヤーを含める必要があります。

ここまでは順調ですね...

問題

これが私がハングアップし始めたところで、これとは別のデザイン(以下に提案されている)がこれを実行するより良い方法かもしれないと考える。

読んでからここに20ほどの "メモリマップされた"関連の質問があるので、mmap呼び出しは割り当てられたときに連続したメモリの実行を望んでいるように感じます。 たとえば、メモリの断片化のために2GBのファイルをmmapしようとすると、32ビットのホストOS上では、マッピングが成功する可能性は低く、代わりに一連の128MBマッピングのようなものを使用して全体をのファイル。

1TBファイルで表現される数多くのデータベースをホストしているDBMSに対して、1024MBのmmapサイズを使用していると言っても、私はメモリ上に何千ものメモリマップ領域を持っています。複数のGBファイルに数百mmのギャップを作成するために、私は例外に遭遇しただけではなく、実際にはJVMをsegfaultに割り当てすぎて割り当てすぎてしまいました。私が今までに見たことのないOSエラーポップアップで切り取って再初期化します。

"大規模なファイルを処理することは決してありません"または "これは人為的な例です"という議論に関係なく、これらのタイプの副作用で何かをコーディングすることができるという事実は、代わりのインプラント(下記)を検討しました。

BESIDES問題は、私のメモリマップされたファイルの私の理解は、ファイルが成長するたびにマッピングを再作成しなければならないということです。そのため、このファイルは設計上付加されています。

私はある程度ファイルを塊(一度に8MBと言う)で成長させ、8MBごとに再作成するだけでこれをある程度は戦うことができますが、これらのマッピングを常に再作成する必要があります。 Javaでサポートされています

質問1/2

この点まで私が得た知見をすべて踏まえ、メモリマップファイルは、主に読み取り重視のソリューションや読み取り専用ソリューションの優れた解決策ではなく、常に重視したソリューションではなく、マッピングを常に再作成する必要があるためです。

しかし私はMongoDBのようなソリューションを使って私の周りを見回しています。ここではメモリマップされたファイルを取り入れています。私はここでいくつかのコアコンポーネントが欠けているように感じます(2GBのエクステントを一度に割り当てることができます私は彼らがこのロジックで再マップコストを回避し、順次実行をディスク上に維持するのを助けていると想像しています)。

この時点で私は、この問題がJavaのアンマップ操作の欠如であるかどうかわかりません。これは、これを非常に危険なものにし、自分の用途には不適切であるか、または私の理解が間違っていて、

オルタナティブデザイン

上記で提案されたメモリマップされたものに対する別の設計は、mmapの理解が正しいかどうかを次のように説明します:

妥当な構成可能なサイズ( 2,4,8,16,32,64,128KB程度)ダイレクトByteBufferを任意のホストプラットフォームと容易に互換性があるように定義ます(DBMS自体がスラッシングシナリオを引き起こすことを心配する必要はありません)。オリジナルのFileChannelは、一度にファイル1のバッファー・キャパシティー・チャンクの特定のオフセット読み取りを実行し、メモリー・マップ・ファイルをまったく実行しません。

欠点は、今では私のコードが "完全なレコードをロードするのにファイルから十分に読み込んだのか?"

もう一つの欠点は、私がOSの仮想メモリロジックを利用することができなくなって、よりホットなデータをメモリに自動的に保持できることです。 代わりに、私はOSで採用されているファイルキャッシュロジックが、ここで私にとって役立つ何かをするのに十分な大きさであることを願っています。

質問#2 of 2

私はこれについて私が理解していることを確認することを望んでいました。

たとえば、ファイルキャッシュが幻想的で、両方の場合(メモリマップまたは直接読み取り)、ホストOSはできるだけ多くのホットデータを利用可能にし、大きなファイルのパフォーマンスの差はごくわずかです。

または、メモリマップされたファイル(連続メモリ)の機密要件を正しく理解していない可能性があります。


Answers

私はあなたが最大2GBのファイルをmmap'pingファイルについて心配するべきではないと思う。

メモリマップされたファイルを使ってDBの例としてMongoDBのソースを見ると、 MemoryMappedFile::mapWithOptions() (これはMemoryMappedFile::map()を呼び出しますMemoryMappedFile::mapWithOptions()に常にフルデータファイルをマップすることがわかります。 DBデータは、それぞれ最大2GBのサイズの複数のファイルにまたがっています。 また、データファイルがあらかじめ割り当てられているので、データが大きくなると再マップする必要がなくなり、ファイルの断片化を防ぐことができます。 一般的に、このDBのソースコードであなた自身を刺激することができます。


https://github.com/peter-lawrey/Java-Chronicleに興味があるかもしれませんhttps://github.com/peter-lawrey/Java-Chronicle

この場合、私は同じファイルに複数のメモリマッピングを作成します(サイズは最大2 GBの1 GBです)。ファイルは任意のサイズ(ハードドライブのサイズまで)

また、インデックスを作成するので、任意のレコードをランダムに見つけることができ、各レコードは任意のサイズにすることができます。

プロセス間で共有され、プロセス間の低遅延イベントに使用されます。

大量のデータを使用する場合は、64ビットOSを使用していることを前提にしています。 この場合、MappedByteBufferのリストはあなたが必要とするすべてのものになります。 職場に適したツールを使用することは理にかなっています。 ;)

私はあなたのメインメモリサイズの約10倍のデータサイズでもパフォーマンスは良くなっています(私は高速SSDドライブを使用していましたので、YMMV)


このようにしてみてください

if (!(a | b)) {
    //blahblah
}

それは

if (a | b) {}
else {
    // blahblah
}




java file-io database-design memory-mapped-files bytebuffer