Cassandraに似たデータベースサーバーを開発しています。
開発はCで始まりましたが、クラスがなければ非常に複雑になりました。
現在、私はすべてをC ++ 11に移植しましたが、まだ「モダンな」C ++を学習しており、多くのことについて疑問があります。
データベースはキー/値のペアで動作します。すべてのペアにはさらに多くの情報があります。いつが作成され、いつ期限切れになるか(期限切れでない場合は0)。各ペアは不変です。
キーはC文字列、値はvoid *ですが、少なくとも今のところ、値をC文字列として操作しています。
抽象IList
クラスがあります。3つのクラスから継承
VectorList
-C動的配列-std :: vectorに似ていますが、realloc
LinkList
-チェックとパフォーマンス比較のために作成SkipList
-最終的に使用されるクラス。
将来はRed Black
木も作ろうと思います。
それぞれIList
に、キーでソートされた、0個以上のペアへのポインターが含まれています。
IList
長くなりすぎた場合は、特殊ファイルとしてディスクに保存できます。この特殊ファイルは一種ですread only list
。
キーを検索する必要がある場合は、
- 最初にメモリ内
IList
が検索されます(SkipList
、SkipList
またはLinkList
)。 - 次に、日付でソートされたファイルに検索が送信されます
(最新のファイルが最初、最も古いファイル-最後)。
これらのファイルはすべてメモリにmmapされます。 - 何も見つからない場合、キーは見つかりません。
私はIList
物事の実装に疑いはありません。
現在私を困惑させているのは以下の通りです:
ペアは異なるサイズであり、それらはによって割り当てられnew()
、それらをstd::shared_ptr
指し示しています。
class Pair{
public:
// several methods...
private:
struct Blob;
std::shared_ptr<const Blob> _blob;
};
struct Pair::Blob{
uint64_t created;
uint32_t expires;
uint32_t vallen;
uint16_t keylen;
uint8_t checksum;
char buffer[2];
};
「バッファ」メンバー変数は、サイズが異なる変数です。キーと値を格納します。
たとえば、keyが10文字で、valueがさらに10バイトの場合、オブジェクト全体は次のようになりますsizeof(Pair::Blob) + 20
(2つのnull終了バイトのため、バッファの初期サイズは2です)
この同じレイアウトがディスクでも使用されているので、次のようなことができます。
// get the blob
Pair::Blob *blob = (Pair::Blob *) & mmaped_array[pos];
// create the pair, true makes std::shared_ptr not to delete the memory,
// since it does not own it.
Pair p = Pair(blob, true);
// however if I want the Pair to own the memory,
// I can copy it, but this is slower operation.
Pair p2 = Pair(blob);
ただし、この異なるサイズは、C ++コードの多くの場所で問題になります。
たとえば、私は使用できませんstd::make_shared()
。これは私にとって重要です。1Mペアの場合、2M割り当てになるからです。
反対側から、動的配列(たとえば、新しいchar [123])に「バッファリング」すると、mmapの「トリック」が失われ、キーをチェックしたい場合は2つの逆参照を行い、単一のポインタを追加します。 -クラスに8バイト。
私はまた、「プル」からのすべてのメンバーにしようとしたPair::Blob
にPair
そう、Pair::Blob
ちょうどバッファであることを、私はそれをテストしたとき、それはおそらく周りのオブジェクトデータをコピーするので、かなり遅かったです。
私が考えているもう1つの変更は、Pair
クラスを削除して置き換え、std::shared_ptr
すべてのメソッドをに「プッシュ」することPair::Blob
ですが、これは可変サイズのPair::Blob
クラスでは役に立ちません。
C ++との親和性を高めるために、オブジェクトデザインをどのように改善できるのかと考えています。
完全なソースコードはこちら:https :
//github.com/nmmmnu/HM3
IList::remove
またはIListが破棄されたときに行われます。時間はかかりますが、別のスレッドで行います。std::unique_ptr<IList>
とにかく、IListは簡単になるでしょう。新しいリストで「切り替え」て、古いオブジェクトをd-torを呼び出せる場所に保持できるようにします。
C string
、データは常に何らかのバッファvoid *
またはchar *
なので、char配列を渡すことができます。redis
またはで類似のものを見つけることができますmemcached
。ある時点でstd::string
、キーにchar配列を使用するか固定するかを決定できますが、下線はまだC文字列のままです。
std::map
かstd::unordered_map
?値(キーに関連付けられている)がいくつvoid*
かあるのはなぜですか?おそらく、いつかそれらを破壊する必要があるでしょう。どのように&いつ?テンプレートを使用しないのはなぜですか?