我正在开发类似于Cassandra的数据库服务器。
开发从C语言开始,但是没有类,事情变得非常复杂。
目前,我在C ++ 11中移植了所有内容,但我仍在学习“现代” C ++,并对很多事情有疑问。
数据库将使用键/值对。每对都有更多信息-什么时候创建以及何时过期(如果不过期则为0)。每对都是不可变的。
关键是C字符串,值是空*,但至少目前我也使用C字符串作为值。
有抽象IList
类。它从三个类继承
VectorList
-C动态数组-类似于std :: vector,但使用realloc
LinkList
-用于检查和性能比较SkipList
-最终将使用的类。
将来我可能Red Black
也会做树。
每个都IList
包含零个或多个指向对的指针,并按键排序。
如果IList
太长,可以将其保存在磁盘上的特殊文件中。这个特殊文件是的read only list
。
如果您需要搜索密钥,
- 首先在存储器
IList
中搜索(SkipList
,SkipList
或LinkList
)。 - 然后将搜索发送到按日期排序的文件
(最新的文件在前,最早的文件在后)。
所有这些文件都在内存中映射。 - 如果未找到任何内容,则找不到密钥。
我对事情的执行毫无疑问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,因为两个空终止字节)
磁盘上也使用了相同的布局,因此我可以执行以下操作:
// 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个分配。
另一方面,如果我对动态数组执行“缓冲”操作(例如new char [123]),我将丢失mmap“技巧”,如果要检查键,将进行两次取消引用,并添加单个指针-类的8个字节。
我还尝试将所有成员从中“拉” Pair::Blob
到Pair
,因此Pair::Blob
只是缓冲区,但是当我对其进行测试时,它的速度很慢,可能是因为复制了对象数据。
我也在想的另一个更改是删除Pair
类并替换为,std::shared_ptr
然后将所有方法“推”回Pair::Blob
,但这对可变大小的Pair::Blob
类没有帮助。
我想知道如何才能改进对象设计,以便对C ++更加友好。
完整的源代码在这里:https :
//github.com/nmmmnu/HM3
IList::remove
或销毁IList时完成的。这需要很多时间,但是我将在单独的线程中进行。因为IList std::unique_ptr<IList>
无论如何都会很容易。这样我就可以用新列表“切换”它,并将旧对象保留在我可以调用d-tor的地方。
C string
,数据始终为某个缓冲区void *
或char *
,因此您可以传递char数组。您可以在redis
或中找到类似内容memcached
。在某些时候,我可以决定std::string
对键使用或固定的char数组,但要强调的是它仍然是C字符串。
std::map
或std::unordered_map
?为什么值(与键相关)有些void*
?您可能需要在某个时候销毁它们。怎样,几时?您为什么不使用模板?