В чем можно хранить около триллиона значений key=>value?

Собственно, вопрос. Длина ключа - 8 симовлов [a-z-A-Z0-9], длина значения 64 символа [a-f0-9].
В чем можно создать такую базу, чтобы быстро поместить в нее все значения и потом быстро извлекать данные по нужному ключу?
  • Вопрос задан
  • 948 просмотров
Пригласить эксперта
Ответы на вопрос 6
mayton2019
@mayton2019
Bigdata Engineer
Давайте прикинем объем который понадобится. Что такое триллион?
Это 12 нулей. Или 1 000 000 000 000 элементов. Какая у нас data-row?
8 + 64 символов типа ASCII (байт подходит чтоб покрыть все символы).
Итого 72 байта на строку. Там можно еще поужимать биты в байтах но только
сложность повышает а большой пользы для дела не дает. Пускай будет ASCII == 1 байт.

Вобщем такой расчет

72000000000000 байтов на весь сегмент данных когда таблица загружена.
Или 65 терабайт. А сколько магнитных блинов надо прикупить? Возьмем популярный магнитный
Western Digital Purple 10TB 7200rpm 256MB WD102PURZ 3.5" SATA III при цене 290$
Порядка 7 штук надо. Вобщем готовте котлету денег 290$ * 7 = 2030$

По поводу DBMS. Да key-value здесь подходит. Можно начинать с LevelDb или RocksDb но у них
расход дисковой памяти на 1 строчку может быть больше чем я посчитал. Я ведь считал эконом-эконом
вариант в виде бинарного типизированного файла где все записи строго по 72 байта. Сколько именно
захватит РоксДб или ЛевлДб - чорт его знает. Вряд-ли документация об этом что-то говорит.
Но берите 1% датасета. Загружайте
и аппроксимируйте сколько выйдет после полной прогрузкуи. Это - надежный способ оценки.
Ответ написан
@rPman
ключ 49 бит - log(66^8) - пусть для простоты 8байт, значение 32 байта (у тебя там hex строка)
только на значения тебе нужно 30 терабайта на каждый триллион - 32*10^12 и даже в идеальном случае еще 16тб на индекс ключа (чем больше оптимизируешь хранение тем больше операций на чтение и запись каждой)

Недавно была статья на хабре про тесты производительности работы mssql с похожими ключами миллионы записей

Я бы предложил схему с самописным индексом (мне кажется тут колхозить идеальнее всего).
* делишь ключ на 2 части (если бы ключ был не такой равномерный, то нужно было бы брать хеш от него), например по 4 байта
* младшие 4 байта (они наиболее равномерно будут распределены) - это номер блока в общем хранилище (на 1 триллион примерно 9кб, рекомендую. 16кб или 32кб, ssd на таких кластерах идеально работают), с массивом элементов: каждый из которых это вторые 4 байта ключа (старшие байты) + 32 байта искомое значение
для 16кб блока итоговое хранилище будет 70 тб - 2^32*16кб, можно прямо в дисковое устройство писать без файловой системы, по дискам пусть какой-нибудь рейд 0 раскидывает
* последняя запись в 16кб блоке - ссылка на дополнительное хранилище переполнений неравномерного распределения, его можно организовать как хочешь, на отдельном носителе

Итого на каждый запрос ты делаешь ровно одно чтение 16кб блока с диска, в полученном массиве ищешь нужный ключ и получаешь значение (если нет значит переполнения из-за неравномерного заполнения индекса, топать в дополнительное хранилище), кстати можно читать по секторно в процессе поиска, тогда если диск сумеет это оптимизировать будет 2х профит. Запись то точно так же - 1 чтение 16кб и запись 1 сектора диска, дозапись в массив. Кстати, если контролировать порядоковый номер ключевого значения (а что то мне говорит там будет простой перебор всех паролей) то будет последовательная запись блоков на диск, для hdd это идеальная ситуация. Иначе всю оперативную память используешь как самодельный lazy write буфер, при переполнении записываешь его на диск, отсортировав согласно дисковым устройствам и отсортировав порядок номеров секторов (тогда либо понимать как работает рейд либо самому раскидывать по дискам), операционные системы и контроллер диска это умеют но у них кеш маленький.

Такой хеш самый быстрый из всех возможных, так как запилен под задачу, оверхед хранения десяток другой терабайт. Кодить тут минимум, строк сотня другая, и то половина - это код сетевого сервиса, ты ведь захочешь разнести сервис хранения и логику по сети.

Добавь еще один уровень, будет 2 чтения по 8-16кб (когда ты не на 2 части делишь ключевое значение а на 3, первая часть ссылается на список ссылок на вторую часть, которые уже ссылаются на блоки с третьей частью), можно уменьшить этот оверхед но мне кажется скорость в твоей задаче важнее, ведь она упадет в два раза.
Универсальные базы данных делают многоуровневые древовидные индексы (это настраивается) и ради удобства и универсальности ты теряешь в скорости.
Ответ написан
dimonchik2013
@dimonchik2013
non progredi est regredi
@u007
Так, а что, решения от Яндекса никто не предложил? ClickHouse - это как раз про то, когда данные текут рекой. Но там конечно не key-value.
Ответ написан
alfss
@alfss
https://career.habr.com/alfss
Предлагаю взглянуть на tarantool
Еще может apache ignite
Ответ написан
Комментировать
@GLeBaTi
Что пробовали: файловая система (хранение в подпапках вида /a/b/c.../a1.txt, скорость вставки получилась медленная

Тут много времени уходит на создание папок.

Есть такой вариант:
1) Генерируете в памяти по N штук (в завис от размера ram).
dataBundle (словарь/массив/hashSet или т.п.):
   key = pass[0..M] //первые M символов (играем с этим значением)
   value = [[pass, hash], [pass2, hash2], [pass3, hash3]]

2) Скидываем в файл большим массивом (т.е. построчно будет каждый раз открывать)
foreach(var bundle in dataBundle)
{
     // ! не текстовой файл - бинарный
     AppendBytesToFile(d.key, d.value);
    // можем запоминать открытые файлы, чтобы не открывать их потом опять.
}


Результат:
0000.txt
0001.txt
...
asdf.txt:
   asdf0001 hash1
   asdf0002 hash2
   ...
   asdfZZZZ hash
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы