Back to the Basics

데이터 중심 애플리케이션 설계 - 3장 저장소와 검색 본문

book

데이터 중심 애플리케이션 설계 - 3장 저장소와 검색

9Jaeng 2023. 5. 2. 14:43
728x90
반응형

이번 장에서는 데이터배이스가 검색 및 저장을 내부적으로 어떻게 처리하는지에 대해 설명한다.
개발자가 이를 알아야 하는 이유는 여러 저장소 앤진 중 애플리케이션에 적합한, 좋은 성능을 내는 엔진을 선택하는 작업이 필요하기 때문이라고 책은 설명한다.
가령 여러 라이브러리 중 하나를 선택해야 할 때 우리의 애플리케이션에 적합한 라이브러리를 찾기 위해 내부를 뜯어보게 되는 일이 있다. 그냥 많이 사용한다고 해서 무거운 라이브러리를 사용하는 것은 오버스팩일 뿐이다. 해당 내용도 이런 맥락이라고 이해하였다.
특정 작업부하 유형에서 좋은 성능을 내는 저장소 엔진을 찾기 위해 엔진이 내부적으로 어떻게 수행되는지 대략적으로 알아야 할 필요가 있다.

데이터베이스를 강력하게 만드는 데이터 구조

많은 데이터베이스에서는 내부적으로 append-only 데이터 파일인 로그를 사용한다고 한다. (여기서 사용되는 로그란 추가 전용 레코드이다)
하지만 데이터를 찾기 위해서는 전체 데이터를 스캔해야 한다.O(n)의 검색 비용이 들어가므로 매우 비효율적이다. 이를 개선하기 위해 색인(index)라는 데이터 구조를 사용한다. 색인은 데이터베이스에서 검색 성능을 개선하기 위해 사용된다.
하지만 색인은 검색 성능을 좋게 만들지만 쓰기 성능은 반대이다. 데이터를 쓸 때마다 매번 색인도 갱신이 되어야 하기 때문이다.
이런 필요 이상의 오버헤드가 발생되지 않기 위해 색인은 데이터베이스에서 자동으로 해주지 않고 개발자가 판단하여 수동으로 색인을 선탣해야한다.
색인의 종류는 어떤 것이 있는지 알아보자

해시 색인

해시 색인은 사전 타입과 매우 유사하다. 아래는 바이트 오프셋 해시맵을 사용한 예이다.
해시 맵을 사용해 파일에서 오프셋을 찾아 해당 위치를 구하고 값을 읽는다.

image

이 방식은 Riak(NoSQL중 하나)의 기본 저장소 엔진인 Bitcask가 근본적으로 사용하는방식이다.
Bitcask는 해시맵을 전부 메모리에 유지하기 때문에 사용가능한 ram에 모든 키가 저장되며 고성능으로 읽기 쓰기를 보장한다.
이런 저장소 엔진은 각 키의 값이 자주 갱신되는 상황에 매우 적합하다. 이런 유형의 작업부하에는 쓰기가 많지만 고유 키는 많지 않아 메모리에 모든 키를 보관할 수 있다.

하지만 데이터를 계속 쌓기만 할 수 없으므로 여기에 아래의 그림과 같이 최적화를 할 수 있다.
특정 크기의 segment로 로그를 나누고 각 segment 파일들에 대해 compaction을 수행할 수 있다. 이를 통해 segment를 더욱 작게 만들 수 있고 중복된 키를 버리고 최신 키만 유지한다.

image

segment의 병합과 compaction은 백그라운드 스레드에서 수행될 수 있으며 수행이 되는 동안 이전 segment 파일을 사용해 읽기, 쓰기 요청의 처리를 정상적으로 수행할 수 있다. 병합이 끝나면 새로 변합한 segment에서 작업을 수행하게 되고 이전 segment는 삭제된다.

추가 전용 로그는 낭비일 수 있지만 아래와 같은 장점도 있다

  • 추가와 segment 병합은 순차적인 쓰기 작업이기 때문에 무작위 쓰기보다 빠르다.
  • segment 파일이 추가 전용이거나 불변이면 동시성 및 고장 복구는 빠르다. (이전 부분과 새로운 값 부분을 포함한 파일을 나누어 남겨두기 때문)
  • segment 병합은 시간이 지남에 따라 조각화되는 데이터 파일 문제를 피할 수 있다.

단점으로는 아래와 같다

  • 메모리에 저장하므로 키가 많으면 문제가 된다. 디스크에 해시 맵을 유지할 수 있지만 빈번한 I/O가 발생할 수 있고 디스크가 차게되면 확장 비용이 비싸다. 또한 해시 충돌 해소를 위해 귀찮은 로직이 필요하게된다.
  • 해시 테이블은 범위 질의(range query)에 효율적이지 않다.

728x90
반응형
Comments