Cassandra'ya benzer bir veritabanı sunucusu geliştiriyorum.
C'de gelişme başladı, ancak sınıflar olmadan işler çok karmaşık hale geldi.
Şu anda her şeyi C ++ 11'de taşıdım, ancak hala "modern" C ++ öğreniyorum ve birçok şey hakkında şüphem var.
Veritabanı Anahtar / Değer çiftleriyle çalışacaktır. Her çiftin daha fazla bilgisi vardır - ne zaman sona ereceği de oluşturulur (süresi dolmazsa 0). Her Çifti değişmezdir.
Anahtar C string, Value void *, ama en azından C string olarak değerle çalıştığım an için.
Soyut bir IList
sınıf var. Üç sınıftan miras alınır
VectorList
- C dinamik dizi - std :: vector'a benzer, ancak kullanırrealloc
LinkList
- kontroller ve performans karşılaştırması için yapılmıştırSkipList
- sonunda kullanılacak olan sınıf.
Gelecekte ben de Red Black
ağaç yapabilirim .
Her biri IList
, anahtarlara göre sıralanmış olarak sıfır veya daha fazla çiftçi işaretçisi içerir .
IList
Çok uzun olursa , diske özel bir dosyaya kaydedilebilir. Bu özel dosya bir çeşittir read only list
.
Bir anahtar aramanız gerekiyorsa,
- bellekteki ilk
IList
arama (edilirSkipList
,SkipList
ya daLinkList
). - Daha sonra arama tarihe göre sıralanmış dosyalara gönderilir
(önce en yeni dosya, en eski dosya - son).
Tüm bu dosyalar bellekte eşlenir. - Hiçbir şey bulunamazsa, anahtar bulunamadı.
IList
İşlerin uygulanması konusunda hiçbir kuşkum yok .
Şu anda beni şaşırtan şey şu:
Çiftler farklı büyüklüktedir, onlar tarafından tahsis edilir new()
ve onlara std::shared_ptr
işaret etmişlerdir.
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];
};
"buffer" üye değişkeni farklı boyuta sahip olan değişkendir. Anahtar + değerini depolar.
Örneğin, anahtar 10 karakterse ve değer başka bir 10 bayt ise, tüm nesne olacaktır sizeof(Pair::Blob) + 20
(arabellek iki boş sonlandırma baytı nedeniyle 2 başlangıç boyutuna sahiptir)
Aynı düzen diskte de kullanılır, bu yüzden böyle bir şey yapabilirim:
// 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);
Ancak bu farklı boyut, C ++ kodu olan birçok yerde bir sorundur.
Mesela kullanamıyorum std::make_shared()
. Bu benim için önemlidir, çünkü 1M Çiftlerim varsa 2M tahsislerim olurdu.
Diğer taraftan, dinamik diziye "tampon" yaparsam (örn. Yeni karakter [123]), mmap "hüner" i kaybederim, anahtarı kontrol etmek istersem iki dereferences yapacağım ve tek bir işaretçi ekleyeceğim - Sınıfa 8 bayt.
Ben de , sadece tampon olmak için , tüm üyeleri Pair::Blob
içine "çekmeye" çalıştı , ama ben test zaman, muhtemelen nesne verileri etrafında kopyalama nedeniyle, oldukça yavaştı.Pair
Pair::Blob
Ben de düşünüyorum başka bir değişiklik Pair
sınıf kaldırmak ve yerine std::shared_ptr
ve tüm yöntemleri geri "itmek" Pair::Blob
, ama bu bana değişken boyut Pair::Blob
sınıf ile yardımcı olmaz .
Daha fazla C ++ dostu olmak için nesne tasarımını nasıl geliştirebileceğimi merak ediyorum.
Tam kaynak kodu burada:
https://github.com/nmmmnu/HM3
IList::remove
veya IList tahrip edildiğinde yapılır . Çok zaman alıyor, ama ayrı bir iş parçacığında yapacağım. Kolay olacak çünkü IList std::unique_ptr<IList>
yine de olacak . bu yüzden yeni listeyle "değiştirebiliyorum" ve eski nesneyi d-tor diyebileceğim bir yerde tutabileceğim.
C string
ve veriler her zaman bir arabellektir void *
veya char *
char dizisini geçebilirsiniz. Sen benzer bulabilirsiniz redis
ya memcached
. Bir noktada std::string
anahtar için char dizisi kullanmaya veya sabit diziye karar verebildim , ancak altını çizmek hala C string olacak.
std::map
ya dastd::unordered_map
? Değerler neden (anahtarlarla ilişkili) bazılarıvoid*
? Muhtemelen bir noktada onları yok etmeniz gerekir; nasıl, ne zaman? Neden şablon kullanmıyorsunuz?