インメモリインデックスに適したスナップショットテーブルデータ構造


12

私は、非常に具体的なユースケース向けにインメモリオブジェクトデータベースを設計しています。単一のライターですが、効率的な同時読み取りをサポートする必要があります。読み取りは分離する必要があります。クエリ言語はありません。データベースは次のもののみをサポートします。

  • 属性/属性のセットによってオブジェクト/ -sを取得します(式のサポートがあるかもしれません、例えばx.count < 5
  • オブジェクトの属性を取得

クエリは、上記の任意の数の操作で構成される必須のスクリプトです。データサイズは<<メモリであるため、ほとんどの属性のすべてのオブジェクトとインデックスは、スワップすることなく快適に収まるはずです。

必要なのは、オブジェクトの属性インデックスのデータ構造です。これは、書き込み時のO(n)であり、書き込みの同時実行性をサポートしませんが、理想的にはO(1)スナップショット(書き込み時のコピー)およびO(logN)アクセスをサポートする必要があります。理想的には、バージョン間で最大限の構造共有を行うことで、読み取りの同時実行性を高めることができます。

私はCTriesコンカレントBSTコンカレントスプレイツリーを見ていましたが、ここで本当に正しい方向を見ているかどうかはわかりません。上記の構造は、私が気にしない挿入の複雑さに多くの注意を払っています。

質問:すぐに使用できるユースケースに適した既知のデータ構造はありますか?

編集:もう少し考えた後、永続的なBST / Splayツリーが機能するようです。ライターは「マスター」コピーを更新し、クエリは実行の開始時にツリーを取得し、完了後に破棄します。しかし、より良い解決策があるかどうかはまだ興味があります。


1
メモリにスナップショットが必要ですか、それともディスク/ネットワークに保存する必要がありますか?純粋に機能的なデータ構造により、メモリ内のスナップショットが自動的に提供されるため、必要な場合は最善の方法です。
ジル 'SO-悪であるのをやめる'

それはすべてメモリ内にあります。一定時間のスナップショットを備えた効率的な可変バージョンがあるのではないかと思っていました(同時書き込みなしのCTrieのような)。
dm3

2
問題は、データ構造の選択ではなく、同時実行制御の種類です。
ラファエル

おそらく、もう少し詳しく説明していただけますか?
dm3

回答:


5

あらゆる種類の永続的/不変(つまり、機能的な)ツリーベースのデータ構造を使用します。@Raphaelがコメントで指摘したように、鍵はロックを正しくすることです。

機能的/永続的なツリーベースのデータ構造の良いところは、無料で「スナップショット」を取得できることです。データ構造にtreap(ランダム化バイナリ検索ツリー)を使用すると仮定します。:ここに行くに書かれた1の例ですhttps://github.com/steveyen/gtreap。著者はこのように説明しています:

不変であるため、treapへの更新/削除は、内部ノードを以前のtreapと共有できる新しいtreapを返します。この実装のすべてのノードは、作成後は読み取り専用です。これにより、変更は新しいデータ構造を作成するだけで、既存のデータ構造を変更することはないため、並行リーダーは並行ライターで安全に動作できます。これは、MVCCまたはマルチバージョンの同時実行制御を実現するための簡単なアプローチです。

Oログn

ロックを使用して、ルートへのポインターを保護します。データ構造は不変であるため、読み取りは同時に実行でき、古いスナップショットへのポインターを保存できます。読み取りは次のとおりです。

lock
tmp = ptr_to_root
unlock
value = search(tmp, <value to search for>)
return value

検索には時間がかかる場合がありますが、ポインターをコピーしている間のみロックを保持するため、検索を同時に実行できます。

書き込みは次のとおりです。

lock
old_ptr_to_root = ptr_to_root
ptr_to_root = insert(old_ptr_to_root, <new key/value pair>)
unlock

このバージョンでは、書き込みはツリーの新しいバージョンを作成するプロセス全体を通してロックを保持する必要があります。書き込みを次のようなものに変更することで、読み取りパフォーマンスを向上させることができます(書き込みトランザクションが失敗することもあります)。

top:
  lock
  old_ptr_to_root = ptr_to_root
  unlock
  new_ptr_to_root = insert(old_ptr_to_root, <new key/value pair>)
  lock
  if (ptr_to_root == old_ptr_to_root)   # make sure no other write happened in the interim
    ptr_to_root = new_ptr_to_root
    unlock
  else                                  # transaction fails, try again
    unlock
    goto top

プログラミング言語にアトミックな比較とスワップ操作を行うアトミック変数が含まれている場合は、少し改善することができます(「ロックを解放する」)。(たとえば、C ++ 11を使用してatomic<T*>。)


精巧な答えをありがとう。私はそれを知っていました、たぶん質問自体にそれを十分に明確に入れなかったかもしれません。しかし、答えはまだ素晴らしいです!
dm3

「改善された」バージョンは、使用中のシステムのメモリモデルによって異なります。一部のシステムでは、verablesをvolatileとして宣言する必要があり、コーディングを正しく行うには優れたスキルが必要になる場合があります。
イアンリングローズ

1

Microsoftは、メモリ内の新しいデータベースに関する詳細を公開しています。書き込み中に読み取りをブロックしないインデックスがあります。

例えば:

Justin Levandoski、David Lomet、Sudipta Sengupta、The Bw-Tree:A B-tree for New Hardware、2013年IEEE 29th International Conference on Data Engineering(ICDE)、International Conference on Data Engineering、2013年4月8日

それらの出版物のリストについては、http://research.microsoft.com/en-us/projects/main-memory_dbs/を参照してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.