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 ГБ. Кроме того, он предварительно выделяет файлы данных, поэтому нет необходимости переназначать их по мере роста данных, что предотвращает фрагментацию файлов. Как правило, вы можете вдохновиться исходным кодом этой БД.