MappedByteBuffer с отображением в память или Direct Buffer для реализации БД?

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

ситуация

Я работаю над реализацией масштабируемого хранилища данных, которая может поддерживать работу с файлами данных размером от нескольких КБ до ТБ или более в 32-разрядной или 64-разрядной системе.

Хранилище данных использует дизайн Copy-on-Write; всегда добавлять новые или измененные данные в конец файла данных и никогда не вносить правки на месте в существующие данные.

Система может содержать 1 или более баз данных; каждый представлен файлом на диске.

Детали реализации не важны; единственная важная деталь заключается в том, что мне нужно постоянно добавлять файл и увеличивать его с КБ до МБ, с ГБ до ТБ, в то же время произвольно пропуская файл для операций чтения для ответа на запросы клиента.

Первоклассники Мысли

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

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

дизайн

Поскольку отдельные файлы данных могут выходить за пределы 2 Гбайт MappedByteBuffer, я ожидаю, что мой дизайн должен будет включать слой абстракции, который принимает смещение записи и преобразует его в смещение внутри определенного сегмента 2 ГБ.

Все идет нормально...

Проблемы

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

Из 20 или около того вопросов, связанных с "отображением памяти", здесь, в SO, кажется, что вызовы mmap чувствительны к тому, чтобы при выделении выделять непрерывный объем памяти. Так, например, в 32-разрядной операционной системе хоста, если я попытался отобразить файл размером 2 ГБ, из-за фрагментации памяти у меня малые шансы на то, что сопоставление будет успешным, и вместо этого я должен использовать что-то вроде последовательности сопоставлений 128 МБ для извлечения целого файл в.

Когда я думаю об этом дизайне, скажем, даже с использованием 1024 МБ размеров mmap, для СУБД, на которой размещены несколько огромных баз данных, представленных, скажем, файлами по 1 ТБ, у меня сейчас есть тысячи областей с отображенной памятью в памяти, и в ходе моего собственного тестирования на Windows 7 я пытался чтобы создать несколько сотен карт по файлу размером в несколько ГБ, я не просто столкнулся с исключениями, я фактически заставлял JVM работать с сегфоутом каждый раз, когда пытался выделить слишком много, и в одном случае получал видео на моем компьютере с Windows 7 для вырезать и заново инициализировать с помощью всплывающего сообщения об ошибках ОС, которого я никогда не видел.

Независимо от аргумента "вы, вероятно, никогда не будете обрабатывать файлы такого большого размера" или "это надуманный пример", тот факт, что я мог бы кодировать что-то подобное с такими побочными эффектами, поставил мою внутреннюю тревогу в состояние повышенной готовности и сделал рассмотреть альтернативный импл (ниже).

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

Я могу бороться с этим в некоторой степени, растя файл по частям (скажем, 8 МБ за раз) и воссоздавая отображение каждые 8 ​​МБ, но необходимость постоянного повторного создания этих отображений заставляет меня нервничать, особенно без явной функции unmap поддерживается в Java.

Вопрос 1 из 2

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

Но затем я оглядываюсь на окружающий меня ландшафт с такими решениями, как MongoDB, охватывающими файлы с отображением в памяти повсюду, и я чувствую, что мне не хватает какого-то основного компонента здесь (я действительно знаю, что он выделяет что-то вроде 2-гигабайтных экстентов за раз, поэтому Я полагаю, что они работают над изменением стоимости карты с помощью этой логики и помогают поддерживать последовательные прогоны на диске).

На данный момент я не знаю, заключается ли проблема в том, что в Java отсутствует операция unmap, которая делает ее намного более опасной и непригодной для моего использования, или мое понимание неверно, и кто-то может указать мне на север.

Альтернативный дизайн

Предложенный выше вариант, альтернативный отображаемому в памяти, который я выберу, если я правильно понимаю mmap, выглядит следующим образом:

Определите прямой ByteBuffer разумного конфигурируемого размера (примерно 2, 4, 8, 16, 32, 64, 128 КБ), что делает его легко совместимым с любой хост-платформой (не нужно беспокоиться о самой СУБД, вызывающей сценарии сбоев) и используя исходный FileChannel, выполняющий чтение с конкретным смещением для фрагмента буферной емкости файла 1 за раз, полностью отказываясь от отображенных в память файлов вообще.

Недостатком является то, что теперь мой код должен беспокоиться о таких вещах, как "я прочитал достаточно из файла, чтобы загрузить полную запись?"

Другим недостатком является то, что я не могу использовать логику виртуальной памяти ОС, позволяя ей автоматически сохранять в памяти больше "горячих" данных; вместо этого я просто надеюсь, что используемая ОС логика файлового кэша достаточно велика, чтобы сделать что-то полезное для меня здесь.

Вопрос № 2 из 2

Я надеялся получить подтверждение моего понимания всего этого.

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

Или, может быть, мое понимание чувствительных требований к отображенным в память файлам (непрерывной памяти) неверно, и я могу все это игнорировать.

2 ответа

Решение

Вы можете быть заинтересованы в https://github.com/peter-lawrey/Java-Chronicle

В этом я создаю несколько отображений памяти в одном файле (размер от 2 до 1 ГБ). Файл может быть любого размера (вплоть до размера вашего жесткого диска)

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

Он может быть разделен между процессами и использоваться для событий с низкой задержкой между процессами.

Я предполагаю, что вы используете 64-битную ОС, если хотите использовать большие объемы данных. В этом случае список MappedByteBuffer будет все, что вам когда-либо нужно. Имеет смысл использовать правильные инструменты для работы.;)

Я считаю, что это хорошо, даже если объем данных примерно в 10 раз превышает объем вашей основной памяти (я использовал быстрый SSD-накопитель, поэтому YMMV)

Я думаю, вам не стоит беспокоиться о файлах mmap'ов размером до 2 ГБ.

Рассматривая источники MongoDB в качестве примера БД, использующей файлы с отображением в памяти, вы обнаружите, что он всегда отображает полный файл данных в MemoryMappedFile:: mapWithOptions () (который вызывает MemoryMappedFile:: map ()). Данные БД охватывают несколько файлов, каждый размером до 2 ГБ. Кроме того, он предварительно выделяет файлы данных, поэтому нет необходимости переназначать их по мере роста данных, что предотвращает фрагментацию файлов. Как правило, вы можете вдохновиться исходным кодом этой БД.

Другие вопросы по тегам