1. 개요

MicroStream 은 JVM용으로 구축된 개체 그래프 지속성 엔진 입니다 . Java 개체 그래프를 저장하고 메모리에 복원하는 데 사용할 수 있습니다. 사용자 지정 직렬화 개념을 사용하는 MicroStream을 사용하면 모든 Java 유형을 저장하고 전체 개체 그래프, 부분 하위 그래프 또는 단일 개체를 로드할 수 있습니다.

이 사용방법(예제)에서는 먼저 이러한 개체 그래프 지속성 엔진을 개발하는 이유를 살펴보겠습니다. 그런 다음 이 접근 방식을 기존 관계형 데이터베이스 및 표준 Java 직렬화와 비교합니다. 개체 그래프 저장소를 만들고 이를 사용하여 데이터를 유지, 로드 및 삭제하는 방법을 살펴보겠습니다.

마지막으로 로컬 시스템 메모리와 일반 Java API를 사용하여 데이터를 쿼리합니다.

2. 객체-관계 불일치

MicroStream을 개발하게 된 동기부터 살펴보겠습니다. 대부분의 Java 프로젝트에는 일종의 데이터베이스 저장소가 필요합니다.

그러나 Java와 널리 사용되는 관계형 또는 NoSQL 데이터베이스는 서로 다른 데이터 구조를 사용합니다 . 따라서 Java 개체를 데이터베이스 구조에 매핑하거나 그 반대로 매핑하는 방법이 필요합니다. 이 매핑에는 프로그래밍 노력과 실행 시간이 모두 필요합니다. 예를 들어 관계형 데이터베이스의 필드와 일치하는 속성 및 테이블에 매핑되는 엔터티를 사용할 수 있습니다.

데이터베이스에서 데이터를 로드하려면 종종 복잡한 다중 테이블 SQL 쿼리를 실행해야 합니다. Hibernate 와 같은 개체 관계형 매핑 프레임워크가 개발자가 이러한 격차를 해소하는 데 도움이 되지만 많은 복잡한 시나리오에서 프레임워크 생성 쿼리는 완전히 최적화되지 않습니다.

MicroStream은 메모리 내 작업에 데이터 지속과 동일한 구조를 사용하여 이러한 데이터 구조 불일치를 해결하려고 합니다.

3. JVM을 스토리지로 사용

MicroStream은 JVM을 저장소로 사용하여 순수한 Java로 빠른 메모리 내 데이터 처리를 달성합니다 . JVM과 분리된 스토리지를 사용하는 대신 현대적인 기본 데이터 스토리지 라이브러리를 제공합니다.

3.1. 데이터베이스 관리 시스템

MicroStream은 데이터베이스 관리 시스템(DBMS)이 아닌 지속성 엔진입니다 . 사용자 관리, 연결 관리 및 세션 처리와 같은 일부 표준 DBMS 기능은 설계상 생략되었습니다.

대신 MicroStream은 애플리케이션 데이터를 저장하고 복원하는 쉬운 방법을 제공하는 데 중점을 둡니다.

3.2. 자바 직렬화

MicroStream은 레거시 DBMS에 대한 보다 성능이 뛰어난 대안을 제공하기 위해 의도적으로 구축된 Custom형 직렬화 개념을 사용합니다 .

몇 가지 제한 사항으로 인해 Java의 내장 직렬화를 사용하지 않습니다.

  • 완전한 개체 그래프만 저장하고 복원할 수 있습니다.
  • 스토리지 크기 및 성능 측면에서 비효율
  • 클래스 구조를 변경할 때 필요한 수동 작업

반면에 사용자 지정 MicroStream 데이터 저장소는 다음을 수행할 수 있습니다.

  • 객체 그래프를 부분적으로 그리고 주문형으로 유지, 로드 또는 업데이트
  • 스토리지 크기 및 성능을 효율적으로 처리
  • 내부 휴리스틱 또는 사용자 정의 매핑 전략을 통해 데이터를 매핑하여 클래스 구조 변경 처리

4. 개체 그래프 저장소

MicroStream은 하나의 데이터 모델에 하나의 데이터 구조만 사용하여 소프트웨어 개발을 단순화하려고 합니다 .

객체 인스턴스는 바이트 스트림으로 저장되며 이들 사이의 참조는 고유 식별자로 매핑됩니다. 따라서 개체 그래프를 간단하고 빠르게 저장할 수 있습니다. 또한 전체 또는 부분적으로 로드할 수 있습니다.

4.1. 의존성

MicroStream을 사용하여 개체 그래프를 저장하기 전에 두 가지 의존성을 추가해야 합니다 .

<dependency>
    <groupId>one.microstream</groupId>
    <artifactId>microstream-storage-embedded</artifactId>
    <version>07.00.00-MS-GA</version>
</dependency>
<dependency>
    <groupId>one.microstream</groupId>
    <artifactId>microstream-storage-embedded-configuration</artifactId>
    <version>07.00.00-MS-GA</version>
</dependency>

4.2. 루트 인스턴스

개체 그래프 저장소를 사용할 때 전체 데이터베이스는 루트 인스턴스에서 시작하여 액세스됩니다 . 이 인스턴스는 MicroStream에 의해 유지되는 개체 그래프의 루트 개체라고 합니다.

루트 인스턴스를 포함한 개체 그래프 인스턴스는 모든 Java 유형일 수 있습니다. 따라서 간단한 String 인스턴스를 엔티티 그래프의 루트로 등록할 수 있습니다.

EmbeddedStorageManager storageManager = EmbeddedStorage.start(directory);
storageManager.setRoot("baeldung-demo");
storageManager.storeRoot();

그러나 이 루트 인스턴스에는 자식이 없으므로 String 인스턴스는 전체 데이터베이스를 구성합니다.  따라서 일반적으로 애플리케이션에 특정한 사용자 지정 루트 유형을 정의 해야 합니다 .

public class RootInstance {

    private final String name;
    private final List<Book> books;

    public RootInstance(String name) {
        this.name = name;
        books = new ArrayList<>();
    }

    // standard getters, hashcode and equals
}

비슷한 방식으로 setRoot()storeRoot() 메서드를 호출하여 사용자 정의 유형을 사용하여 루트 인스턴스를 등록할 수 있습니다 .

EmbeddedStorageManager storageManager = EmbeddedStorage.start(directory);
storageManager.setRoot(new RootInstance("baeldung-demo"));
storageManager.storeRoot();

지금은 책 List이 비어 있지만 사용자 지정 루트를 사용하면 나중에 책 인스턴스를 저장할 수 있습니다.

RootInstance rootInstance = (RootInstance) storageManager.root();
assertThat(rootInstance.getName()).isEqualTo("baeldung-demo");
assertThat(rootInstance.getBooks()).isEmpty()
storageManager.shutdown();

애플리케이션이 저장소 작업을 완료하면 안전을 위해 shutdown() 메서드 를 호출하는 것이 좋습니다 .

5. 데이터 조작

MicroStream이 유지하는 개체 그래프를 통해 표준 CRUD 작업을 수행할 수 있는 방법을 확인하겠습니다.

5.1. 보관

새 인스턴스를 저장할 때 올바른 객체에서 store() 메서드를 호출해야 합니다 . 올바른 개체는 새로 생성된 인스턴스의 소유자입니다. 이 예에서는 List입니다 .

RootInstance rootInstance = (RootInstance) storageManager.root();
List<Book> books = rootInstance.getBooks();
books.addAll(booksToStore);
storageManager.store(books);
assertThat(books).hasSize(2);

새 객체를 저장하면 이 객체가 참조하는 모든 인스턴스도 저장됩니다. 또한 store() 메서드를 실행하면  데이터가 일반적으로 파일 시스템인 기본 스토리지 계층에 물리적으로 기록되었음을 보장합니다 .

5.2. 즉시 로딩

MicroStream으로 데이터를 로드하는 것은 Eager와 Lazy의 두 가지 방식으로 수행할 수 있습니다. 즉시 로드는 저장된 개체 그래프에서 개체를 로드하는 기본 방법입니다. 시작하는 동안 이미 존재하는 데이터베이스가 발견되면 저장된 개체 그래프의 모든 개체가 메모리에 로드됩니다 .

EmbeddedStorageManager 인스턴스를 시작한 후 개체 그래프의 루트 인스턴스를 가져와서 데이터를 로드할 수 있습니다.

EmbeddedStorageManager storageManager = EmbeddedStorage.start(directory);
if (storageManager.root() == null) {
    RootInstance rootInstance = new RootInstance("baeldung-demo");
    storageManager.setRoot(rootInstance);
    storageManager.storeRoot();
} else {
    RootInstance rootInstance = (RootInstance) storageManager.root();
    // Use existing root loaded from storage
}

루트 인스턴스의 null 값은 기본 저장소에 존재하지 않는 데이터베이스를 나타 냅니다 .

5.3. 지연 로딩

많은 양의 데이터를 처리할 때 처음부터 모든 데이터를 메모리에 직접 로드하는 것은 실행 가능한 옵션이 아닐 수 있습니다. 따라서 MicroStream은 인스턴스를 지연 필드 로 래핑하여 지연 로딩도 지원합니다 .

Lazy 는 JDK의 WeakReference 와 유사한 간단한 래퍼 클래스입니다 . 해당 인스턴스는 내부적으로 식별자와 실제 인스턴스에 대한 참조를 보유합니다.

private final Lazy<List<Book>> books;

Lazy 로 래핑된 ArrayList는 Reference() 메서드를 사용하여 인스턴스화할 수 있습니다 .

books = Lazy.Reference(new ArrayList<>());

WeakReference 와 마찬가지로 실제 인스턴스를 가져오려면 간단한 get() 메서드를 호출해야 합니다 .

public List<Book> getBooks() {
    return Lazy.get(books);
}

get () 메서드 호출은 개발자가 하위 수준 데이터베이스 식별자를 처리할 필요 없이 필요할 때 데이터를 다시 로드합니다.

5.4. 삭제

MicroStream으로 데이터를 삭제하면 명시적인 삭제 작업을 수행할 필요가 없습니다. 대신 개체 그래프에서 개체에 대한 모든 참조를 지우고  해당 변경 사항을 저장하기 만 하면 됩니다 .

List<Book> books = rootInstance.getBooks();
books.remove(1);
storageManager.store(books);

삭제된 데이터는 스토리지에서 즉시 삭제되지 않습니다. 오히려 백그라운드 하우스키핑 프로세스가 예약된 정리를 실행합니다 .

6. 쿼리 시스템

표준 DBMS와 달리 MicroStream 쿼리는 스토리지에서 직접 작동하지 않고 로컬 시스템 메모리의 데이터에서 실행됩니다 . 따라서 모든 작업이 일반 Java로 수행되므로 특별한 쿼리 언어를 배울 필요가 없습니다.

일반적인 접근 방식은 표준 Java 컬렉션 과 함께 Streams를 사용하는 것입니다 .

List<Book> booksFrom1998 = rootInstance.getBooks().stream()
    .filter(book -> book.getYear() == 1998)
    .collect(Collectors.toList());

쿼리가 메모리에서 실행되는 경우 메모리 사용량이 높을 수 있지만 쿼리는 빠르게 실행할 수 있습니다.

데이터 저장 및 로드 프로세스는 여러 스레드를 사용하여 병렬화할 수 있습니다. 현재 수평 확장은 불가능하지만 MicroStream은 현재 개체 그래프 복제 접근 방식을 개발 중이라고 발표했습니다. 이렇게 하면 향후 여러 노드에서 클러스터링 및 데이터 복제가 가능해집니다.

7. 결론

이 기사에서는 JVM용 개체 그래프 지속성 엔진인 MicroStream을 살펴보았습니다 . 우리는 MicroStream이 메모리 내 작업 및 데이터 지속성에 동일한 구조를 적용하여 객체 관계형 데이터 구조 불일치를 해결하는 방법을 배웠습니다.

사용자 지정 루트 인스턴스를 사용하여 개체 그래프를 만드는 방법을 살펴보았습니다. 또한 즉시 및 지연 로딩 접근 방식을 사용하여 데이터를 저장, 삭제 및 로드하는 방법을 살펴보았습니다. 마지막으로 일반 Java를 사용한 메모리 내 작업을 기반으로 하는 MicroStream의 쿼리 시스템을 살펴보았습니다.

항상 그렇듯이 전체 소스 코드는 GitHub에서 사용할 수 있습니다 .

Persistence footer banner