Реализация B-дерева для ключей переменного размера

Я хочу реализовать B-дерево (на Java) для индекса «одноразового использования», куда вставляются несколько миллионов ключей, а затем запросы выполняются несколько раз для каждого ключа. Ключи представляют собой строки ascii ‹= 40 байтов, а связанные данные всегда занимают 6 байтов. Структура B-tree была выбрана потому, что мой бюджет памяти не позволяет мне хранить в памяти весь временный индекс.

Моя проблема связана с практическими деталями выбора фактора ветвления и хранения узлов на диске. Мне кажется, есть два подхода:

  • Один узел всегда помещается в один блок. Достигается за счет выбора коэффициента ветвления k, так что даже для наихудшей длины ключа требования к хранению ключей, данных и управляющих структур составляют ‹= размер системного блока. k, вероятно, будет низким, и в большинстве случаев в узлах будет много пустого места.
  • Один узел может храниться на нескольких блоках. Коэффициент ветвления выбирается независимо от размера ключа. Для загрузки одного узла может потребоваться загрузка нескольких блоков.

Тогда возникают следующие вопросы:

  • Второй подход обычно используется для ключей переменной длины? или есть какой-то совершенно другой подход, который я пропустил?
  • Учитывая мой вариант использования, вы бы порекомендовали другое общее решение?

В заключение я должен упомянуть, что я знаю о проекте jdbm3 и рассматриваю возможность его использования. В любом случае попытаюсь реализовать свою собственную, как в качестве обучающего упражнения, так и для того, чтобы увидеть, может ли оптимизация для конкретного случая повысить производительность.

Изменить: чтение о SB-Trees на данный момент:


person Thingfish    schedule 15.02.2012    source источник


Ответы (3)


Мне не хватает варианта C:

  • В один блок всегда помещается как минимум два кортежа, размер блока выбирается соответственно. Блоки заполняются максимально возможным количеством пар ключ / значение, что означает, что коэффициент ветвления является переменным. Если размер блока намного больше среднего размера кортежа (ключа, значения), потраченное впустую пространство будет очень низким. Поскольку оптимальный размер ввода-вывода для дисков обычно составляет 4 КБ или больше, а у вас максимальный размер кортежа, равный 46, в вашем случае это автоматически выполняется.

И для всех вариантов у вас есть несколько вариантов: B * или B + Trees (см. Википедию).

person A.H.    schedule 17.02.2012
comment
Это интересно, но определение B-дерева требует от меня выбора некоторой константы q, и тогда все узлы должны содержать от q до 2q записи. Что, если записи q не помещаются в блок в каком-то разделе дерева? - person Jules; 17.09.2014

JDBM BTree уже выполняет самобалансировку. У него также есть дефрагментация, которая выполняется очень быстро и решает все проблемы, описанные выше.

Один узел может храниться на нескольких блоках. Коэффициент ветвления выбирается независимо от размера ключа. Для загрузки одного узла может потребоваться загрузка нескольких блоков.

Не обязательно. JDBM3 использует отображаемую память, поэтому он никогда не читает полный блок с диска в память. Он создает «представление» поверх блока и считывает только частичные данные по мере необходимости. Таким образом, вместо чтения всего блока 4 КБ он может читать всего 2 x 128 байт. Это зависит от размера блока базовой ОС.

Второй подход обычно используется для ключей переменной длины? или есть какой-то совершенно другой подход, который я пропустил?

Я думаю, вы упустили момент, что увеличение размера диска снижает производительность, так как необходимо прочитать больше данных. И одно дерево может иметь оба подхода (сначала вставленные узлы, а затем после дефрагментации).

В любом случае, плоский файл с отображенным буфером памяти, вероятно, лучше всего подходит для вашей проблемы. Поскольку у вас фиксированный размер записи и всего несколько миллионов записей.

Также посмотрите leveldb. У него новый порт java, который почти превосходит JDBM:

https://github.com/dain/leveldb

http://code.google.com/p/leveldb/

person Jan Kotek    schedule 18.02.2012
comment
JDBM3 использует отображаемую память, поэтому он никогда не читает полный блок с диска в память. Он создает «представление» поверх блока и считывает только частичные данные по мере необходимости. Таким образом, вместо чтения всего блока 4 КБ он может читать всего 2 x 128 байт. Это зависит от размера блока базовой ОС. - это просто неправда; Файлы с отображением памяти используют возможность разбиения на страницы оборудования, и поэтому должны быть реализованы с полным размером страницы за раз. В x86 это означает, что все операции с отображаемыми в память файлами выполняются (как минимум) блоками по 4 КБ во всех реализациях. - person Jules; 17.09.2014

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

Вы также пишете: «несколько миллионов ключей» ... «[макс.] 40-байтовые строки ascii» и «6 байтов [связанные данные]». Это не считается правильным. Один гигабайт оперативной памяти позволит вам разместить более «нескольких миллионов» записей.

person A.H.    schedule 15.02.2012
comment
Бюджет памяти для этой задачи составляет однозначное число МБ. Я не против использования существующей базы данных, но любое решение должно запускаться внутри процесса, в потоке, соответствовать бюджету памяти и иметь быстрое время запуска, чтобы его можно было использовать и для небольших входов. JDBM3 может соответствовать всем требованиям, но мне все еще интересно узнать, как это должно быть сделано :) - person Thingfish; 16.02.2012