База данных Array vs In-Memory - PullRequest
       30

База данных Array vs In-Memory

0 голосов
/ 19 января 2020

Я создаю REST API, в котором мне нужно сохранить 2D-данные (x: Double, y: Double) в памяти приложения (нет необходимости сохранять данные на диске). Количество записей / объектов данных может расти / уменьшаться в будущем. Пользователь может добавлять / удалять объекты данных, но не может их редактировать. У меня есть 2 варианта. Либо сохраните данные в массиве / коллекции, либо используйте базу данных в памяти, такую ​​как H2.

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

  • Массивы резервируют непрерывный блок памяти. Доступ к данным может быть быстрее. Однако, если объем данных увеличивается, найти непрерывный блок памяти может быть сложно.
  • В функциональном программировании, если функция добавляет / удаляет элементы в массиве / коллекции, она изменяет состояние приложения или, другими словами, , это производит побочный эффект. Это нежелательно, поскольку распараллеливание этой функции может быть затруднено.
  • Одновременные обновления: если несколько массивов обновляют массив, мы можем в итоге получить противоречивые данные или некоторые обновления могут быть потеряны из-за условий гонки. Таким образом, необходимо реализовать механизм блокировки обновлений. В базе данных (реляционной) вы можете использовать транзакции, которые должны решить эту проблему. С другой стороны, реляционные базы данных поддерживают транзакции, которые должны решить проблему несогласованности.

Что мне здесь не хватает, дает ли сохранение данных в базе данных какие-либо другие преимущества? Создание базы данных, которая будет содержать только 1 таблицу с 2-3 швами столбцов, чтобы быть излишним.

Заранее спасибо.

Ответы [ 4 ]

3 голосов
/ 20 января 2020
  1. Использование области шардинга кластера akka, чтобы избежать блокировки памяти при одновременном обновлении.

  2. Предложить использовать мем. Пример кофеина. Это может помочь вам контролировать размер, LRU, срок действия. Scala имеет упаковку с кофеином по имени Скаффин.

  val profileDocumentCache: Cache[String, T] =
    Scaffeine()
      .recordStats()
      .expireAfterWrite(1.hour)
      .maximumSize(1024)
      .build[String, T]()

3 голосов
/ 19 января 2020
  1. Массивы не будут работать в любом случае (вы не можете изменить их размер), будут работать только коллекции. Поэтому пункт 1 может быть неактуальным, в зависимости от того, какой тип коллекции вы используете.

  2. Это не отличается между коллекциями и базами данных.

  3. Важным вопросом здесь является то, как выглядят ваши транзакции. Если они просто добавляют / удаляют один элемент, вам не нужны блокировки, просто выберите параллельную коллекцию (см. «Параллельные коллекции» в https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/package-summary.html; к сожалению, стандартная библиотека не охватывает все варианты использования).

  4. Другой вопрос - как вы хотите запрашивать / получать доступ к вашим данным, можете ли вы использовать индексы для некоторых столбцов (которые поддерживает база данных, но коллекции не используются? и т. д. c.

3 голосов
/ 19 января 2020

Хорошие ответы здесь, уже, пока. Алексей # 4, особенно. Я бы go сделал шаг вперед и предложил бы вам искать базу данных в памяти, которая предлагает геопространственную индексацию (например, индекс r-дерева), так как вы говорите, что ваши данные являются двумерными, и возможность выполнять пространственные запросы может иметь некоторую ценность.

3 голосов
/ 19 января 2020

Рассмотрим одновременных карт , которые обеспечивают атомы c операций и

могут быть доступны сразу нескольким потокам

например

import java.util.concurrent.ConcurrentHashMap
import scala.jdk.CollectionConverters._

case class User(id: Int, name: String)
val chm: collection.concurrent.Map[Int, User] = new ConcurrentHashMap[Int, User]().asScala
chm.addOne(1 -> User(1, "Picard"))

Другой вариант заключается в переносе изменяемого состояния с помощью Akka Actor , который гарантирует

обработка одного сообщения перед обработкой следующего сообщения тем же субъектом

возможно, что-то вроде этого

class MyActor() extends Actor {
  private val _mutableSate = mutable.Map[Int, User]()

  def insertUser(u: User): Unit = _mutableSate.addOne(u.id, u)
}

Относительно того, следует ли использовать коллекцию или базу данных в памяти, IMO, это техническое решение, которое имеет компромиссы, но не дает четких ответов. Например, можно применить принцип минимальной мощности рассуждений и сказать, что если коллекции решают проблему адекватно, то нет необходимости go с более мощным решением для базы данных. С другой стороны, следует также учитывать масштаб решения. Например, будут ли коллекции иметь достаточно выразительный механизм запросов, если есть несколько таблиц, которые необходимо объединить?

...