Я разрабатываю сервер базы данных, похожий на Cassandra.
Разработка была начата в C, но все стало очень сложно без классов.
В настоящее время я перенес все на C ++ 11, но я все еще изучаю "современный" C ++ и у меня есть сомнения по поводу многих вещей.
База данных будет работать с парами ключ / значение. У каждой пары есть еще какая-то информация - когда создается и когда истекает (0, если не истекает). Каждая пара неизменна.
Ключ - строка C, значение - void *, но, по крайней мере, на данный момент я работаю со значением как строка 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];
};
Переменная-член «буфер» - это переменная другого размера. Он хранит ключ + значение.
Например, если ключ - 10 символов, а значение - еще 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.
С другой стороны, если я сделаю «буфер» для динамического массива (например, новый символ [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 *
, таким образом, вы можете передать массив символов. Вы можете найти подобное в redis
или memcached
. В какой-то момент я мог бы решить использовать std::string
или исправить массив символов для ключа, но подчеркнуть, что это все еще будет строка C.
std::map
илиstd::unordered_map
? Почему значения (связанные с ключами) некоторыеvoid*
? Возможно, вам придется уничтожить их в какой-то момент; как и когда? Почему вы не используете шаблоны?