Я разрабатываю сервер базы данных, похожий на Cassandra.
Разработка была начата в C, но все стало очень сложно без классов.
В настоящее время я перенес все на C ++ 11, но я все еще изучаю "современный" C ++ и у меня есть сомнения по поводу многих вещей.
База данных будет работать с парами ключ / значение. У каждой пары есть еще какая-то информация - когда создается и когда истекает (0, если не истекает). Каждая пара неизменна.
Ключ - строка C, значение - void *, но, по крайней мере, на данный момент я работаю со значением как строка C.
Есть абстрактный IListкласс. Наследуется от трех классов
VectorList- C динамический массив - аналогичен std :: vector, но используетreallocLinkList- сделано для проверки и сравнения производительности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*? Возможно, вам придется уничтожить их в какой-то момент; как и когда? Почему вы не используете шаблоны?