Disk-persisted-lazy-cacheable-List ™ в Scala
Мне нужно иметь очень длинный список пар (X, Y) в Scala. Настолько большой он не поместится в памяти (но хорошо вписывается на диск).
- Все операции обновления - минусы (head appends).
- Все обращения к чтению начинаются в голове и упорядоченно перемещаются по списку до тех пор, пока он не найдет предопределенную пару.
- Кэш-память будет отличной, так как большинство обращений к чтению будут хранить одни и те же данные снова и снова.
Итак, это в основном "диск с постоянным доступом к ленивым кэшам"
Любые идеи о том, как их получить, прежде чем я начну раскачивать свои собственные?
Добавление: да.. mongodb, или любой другой не встраиваемый ресурс, является излишним. Если вас интересует конкретный прецедент для этого, см. Здесь Timeline
. В принципе, я должен иметь очень, очень большую временную шкалу (миллионы пар в течение нескольких месяцев), хотя мои матчи нужно только коснуться последних часов.
Ответы
Ответ 1
Самый простой способ сделать что-то вроде этого - расширить Traversable
. Вам нужно определить foreach
, и у вас есть полный контроль над обходом, поэтому вы можете делать такие вещи, как открывать и закрывать файл.
Вы также можете расширить Iterable
, что требует определения iterator
и, конечно же, возврата своего рода iterator
. В этом случае вы, вероятно, создадите iterator
для данных диска, но будет намного сложнее управлять такими вещами, как открытые файлы.
Вот один пример Traversable
, который я описал, написанный Джошем Суэретом:
class FileLinesTraversable(file: java.io.File) extends Traversable[String] {
override def foreach[U](f: String => U): Unit = {
val in = new java.io.BufferedReader(new java.io.FileReader(file))
try {
def loop(): Unit = in.readLine match {
case null => ()
case line => f(line); loop()
}
loop()
} finally {
in.close()
}
}
}
Ответ 2
Вы пишете:
mongodb, или любой другой не встраиваемый ресурс, является избыточным
Знаете ли вы, что есть встраиваемые системы баз данных, в том числе некоторые очень маленькие? Если вы знаете, я не уверен в вашем конкретном требовании и почему бы вам не использовать их.
Вы уверены, что Hibernate + встраиваемый DB (скажем, SQLite) будет недостаточно?
Альтернативно, BerkeleyDB Java Edition, HSQLDB или другие встроенные базы данных может быть вариантом.
Если вы не выполняете запросы по самому объекту (и это действительно так, как вы этого не сделали), возможно, сериализация будет проще, чем объектно-реляционное сопоставление для сложных объектов, но я никогда не пробовал, и я не делаю знаете, что будет быстрее. Но сериализация, вероятно, является единственным способом быть полностью общим в типе, предполагая, что ваша структура выбора предлагает подходящий интерфейс для записи [T <: Serializable]
. Если нет, вы можете написать [T: MySerializable]
после создания собственного "типа-класса" MySerializable[T]
(например, Ordering[T]
в стандартной библиотеке Scala).
Однако вы не хотите использовать стандартную сериализацию Java для этой задачи. "Anything serializable" звучит плохое требование, потому что это предполагает использование сериализации для этого, но я думаю, вы можете расслабиться, чтобы "что-нибудь сериализуемое с моей каркасом выбора". Сериализация крайне неэффективна по времени и пространству и не предназначена для сериализации одного объекта, вместо этого она возвращает вам файл со специальными заголовками. Я бы предложил использовать некоторые различные рамки сериализации - посмотрите здесь для сравнения.
Дополнительные причины не идти по пути пользовательской реализации
Кроме того, похоже, что вы будете читать файл по существу в обратном направлении и что довольно плохой шаблон доступа, с точки зрения производительности, на дисках, отличных от SSD: после прочтения сектора требуется почти полное вращение диска для доступа предыдущий.
Кроме того, как отметил Крис Шайн в комментарии выше, вам нужно будет использовать решение на основе страниц, и вам нужно будет справиться с объектами с переменным размером.
Ответ 3
Эта библиотека Java может содержать то, что вам нужно. Он предназначен для хранения записей в памяти более эффективно, чем стандартные коллекции Java.
http://code.google.com/p/vanilla-java/wiki/HugeCollections
Ответ 4
Если вы не хотите подходить к одной из встраиваемых БД, как насчет стека в файлы с отображением памяти?
- Кажется, что стек соответствует вашим желаемым характеристикам доступа. (Нажимайте кучу данных и часто повторяйте последние данные)
- Вы можете использовать Java MappedByteBuffer непосредственно из Scala. Вы можете обращаться к файлу как к его памяти, не пытаясь фактически загрузить файл в память.
- Таким образом вы получите некоторое кэширование бесплатно от ОС, поскольку сопоставленный файл будет функционировать как виртуальная память. Недавно написанные/доступные страницы останутся в кэше файлов ОС до тех пор, пока ОС не подойдет для их очистки (или вы почистили их вручную) обратно на диск.
- Вы можете создать свой стек с любого конца файла, если вас беспокоит последовательная производительность чтения, но если вы обычно читаете данные, которые вы только что написали, я бы не ожидал, что это будет проблемой, поскольку он все равно будет в памяти. (Хотя, если вы читаете данные, которые вы писали в течение нескольких часов/дней на разных страницах, это может быть проблемой)
- Файл, адресованный таким образом, ограничен размером до 2 ГБ даже на 64-битной JVM, но вы можете использовать несколько файлов для преодоления этого ограничения.