Chapter 3: 저장소와 검색
Intro
데이터베이스가 저장과 검색을 내부적으로 처리하는 방법을 애플리케이션 개발자가 주의해야 하는 이유
대개 애플리케이션 개발자가 처음부터 자신의 저장소 엔진을 구현하기보다는, 사용 가능한 여러 저장소 엔진 중에
애플리케이션에 적합한 엔진
을 선택하는 작업이 필요하다특정 작업 부하(workload) 유형에서 좋은 성능을 내게끔 저장소 엔진을 조정하려면,
저장소 엔진 내부 에서 수행되는 작업
에 대해 대략적인 개념을 이해할 필요가 있다
데이터베이스를 강력하게 만드는 데이터 구조
색인
색인의 일반적인 개념은 어떤 부가적인
메타데이터
를 유지하는 것이다이 메타데이터는
이정표
역할을 해서 원하는 데이터의 위치를 찾는 데 도움을 준다동일한 데이터를 여러 가지 다양한 방법으로 검색하고자 한다면 데이터의 각 부분에 여러 가지 다양한 색인이 필요하다
색인은 기본 데이터(primary data)에서 파생된 추가적인 구조다
많은 데이터베이스는 색인의 추가와 삭제를 허용한다
이 작업은 데이터베이스의 내용에는 영향을 미치지 않는다
단지
질의 성능
에만 영향을 준다추가적인 구조의 유지보수는 특히
쓰기 과정
에서오버헤드
가 발생한다
쓰기의 경우 단순히 파일에 추가할 때의 성능을 앞서기 어렵다
왜냐하면 단순히 파일에 추가하는 작업이 제일 간단한 쓰기 작업이기 때문이다
어떤 종류의 색인이라도 대개
쓰기 속도를 느리게
만든다데이터를 쓸 때마다 매번
색인도 갱신
해야 하기 때문!이것은 저장소 시스템에서 중요한
트레이드오프
다색인을 잘 선택했다면 읽기 질의 속도가 향상된다
but, 모든 색인은 쓰기 속도를 떨어뜨린다
이런 이유로 데이터베이스는 보통 자동으로 모든 것을 색인하지 않는다
애플리케이션 개발자나 데이터베이스 관리자가 애플리케이션의 전형적인 질의 패턴에 대한 지식을 활용해 수동으로
색인을 선택
해야 한다그래야 필요 이상으로
오버헤드
를 발생시키지 않으면서 애플리케이션에 가장큰 이익을 안겨주는 색인
을선택
할 수 있다
해시 색인
키를 데이터 파일의
바이트 오프셋
에 매핑해인메모리 해시 맵
을 유지하는 전략파일에 새로운 키-값 쌍을 추가할 때마다 방금 기록한 데이터의 오프셋을 반영하기 위해 해시 맵도 갱신해야 한다
값을 조회 하려면 해시 맵을 사용해 데이터 파일에서
오프셋
을 찾아 해당 위치를 구하고 값을 읽는다
비트캐스크 (Bitcask)
비트캐스크는 해시 맵을 전부
메모리에 유지
하기 때문에 사용 가능한 램(RAM)에 모든 키가 저장된다는 조건을 전제로 고성능으로 읽기, 쓰기를 보장한다값은 한 번의
디스크 탐색
으로 디스크에서 적재할 수 있기 때문에 사용 가능한 메모리보다 더 많은 공간을 사용할 수 있다만약 데이터 파일의 일부가 이미
파일 시스템 캐시
에 있다면 읽기에 디스크 입출력이 필요하지 않다
비트캐스크 같은 저장소 엔진은 각
키의 값이 자주 갱신되는 상황
에 매우 적합하다
세그먼트 (segment)
파일에 항상 추가만 한다면 결국 디스크 공간이 부족해지므로, 특정 크기의
세그먼트(segment)로 로그를 나누는 방식
으로 해결할 수 있다특정 크기에 도달하면 세그먼트 파일을 닫고 새로운 세그먼트 파일에 이후 쓰기를 수행한다
세그먼트 파일들에 대해
컴팩션(compaction)
을 수행할 수 있다컴팩션은 로그에서
중복된 키
를 버리고 각 키의최신 갱신 값만 유지
하는 것을 의미한다
컴팩션은 보통 세그먼트를 더
작게 만들기
때문에 (하나의 키는 세그먼트 내에서 대체로 여러번 덮어쓰기 된 것을 가정함) 컴팩션을 수행할 때 동시에 여러세그먼트
들을병합
할 수 있다세그먼트가
쓰여진 후에는 절대 변경할 수 없기 때문에
병합할 세그먼트 는 새로운 파일로 만든다고정된 세그먼트의 병합과 컴팩션은
백그라운드 스레드
에서 수행할 수 있다
컴팩션을 수행하는 동안
이전 세그먼트 파일
을 사용해 읽기와 쓰기 요청의 처리를 정상적으로 계속 수행할수 있다병합 과정이 끝난 이후에는, 읽기 요청은 이전 세그먼트 대신 새로 병합한 세그먼트를 사용하게끔 전환한다
전환 후에는 이전 세그먼트 파일을 삭제하면 된다
각 세그먼트는 키를 파일 오프셋에 매핑한
자체 인메모리 해시 테이블
을 갖는다키의 값을 찾으려면
최신 세그먼트 해시 맵
을 먼저 확인한다만약 키가 없다면 두 번째 최신 세그먼트를 확인한다
병합 과정
을 통해 세그먼트 수를 적게 유지하기 때문에 조회할 때 많은 해시 맵을 확인할 필요가 없다
고려해야 할 사항
파일 형식
csv는 로그에 가장 적합한 형식이 아니다
바이트 단위
의 문자열 길이를부호화
한 다음,원시 문자열
(이스케이핑할 필요 없이)을 부호화하는 바이너리 형식을 사용하는 편이 더 빠르고 간단하다
레코드 삭제
키와 관련된 값을 삭제하려면 데이터 파일에
특수한 삭제 레코드
(tombstone이라 불림) 를 추가해야 한다로그 세그먼트가 병합될 때 톰스톤은 병합 과정에서
삭제된 키의 이전 값
을무시
하게 된다
Crash 복구
데이터베이스가
재시작
되면 인메모리 해시 맵은손실
된다원칙적으로는 전체 세그먼트 파일을 처음부터 끝까지 읽고, 각 키에 대한
최신 값의 오프셋
을 확인해서 각 세그먼트 해시 맵을복원
할 수 있다but, 세그먼트 파일이 크면 해시맵 복원은 오랜 시간이 걸릴 수 있고, 이는 서버 재시작을 어렵게 만든다
비트캐스크는 각 세그먼트 해시 맵을 메모리로 조금 더 빠르게 로딩할 수 있게
snapshot을 디스크에 저장
해복구 속도
를 높인다
부분적으로 레코드 쓰기
데이터베이스는 로그에 레코드를 추가하는 도중에도 죽을 수 있다
비프캐스크 파일은
checksum
을 포함하고 있어 로그의 손상된 부분을 탐지해 무시할 수 있다
동시성 제어
쓰기를 엄격하게 순차적으로 로그에 추가할 때, 일반적인 구현 방법은
하나의 쓰기 thread만 사용
하는 것이다데이터 파일 세그먼트는
추가 전용
이거나불변
이므로,multi-thread
로 동시에 읽기를 할 수 있다
추가 전용 설계의 장점
추가와 세그먼트 병합은
순차적인 쓰기 작업
이기 때문에 보통 무작위 쓰기보다 훨씬빠르다
세그먼트 파일이
추가 전용
이나불변
이면동시성
과고장 복구
는 훨씬 간단하다값을 덮어 쓰는 동안 데이터베이스가 죽는 경우에 대해 걱정할 필요가 없다
이전 값 부분과 새로운 값 부분을 포함한 파일을 나누어 함께 남겨두기 때문!
오래된 세그먼트 병합은 시간이 지남에 따라
조각화
되는 데이터 파일 문제를 피할 수 있다
해시 테이블 색인의 제한 사항
해시 테이블은
메모리
에 저장해야 하므로키가 너무 많으면 문제
가 된다원칙적으로는 디스크에 해시 맵을 유지할 수 있지만, 디스크 상의 해시 맵에 좋은 성능을 기대하기는 어렵다
무작위 접근 I/O가 많이 필요하고, 디스크가 가득 찼을 때 확장하는 비용이 비싸며,
해시 충돌
해소를 위해 복잡한 로직이 필요하다
해시 테이블은
범위 질의(range query)
에 효율적이지 않다모든 키를 쉽게 스캔할 수는 없다
해시 맵에서 모든 개별 키를 조회해야 한다
SS 테이블과 LSM 트리
SS 테이블이란?
키로 정렬된 형식을
정렬된 문자열 테이블 (Sorted String Table)
또는 짧게SS 테이블
이라 부른다각 키는 각 병합된 세그먼트 파일 내에 한 번만 나타나야 한다
컴팩션 과정은 이렇게 한 번만 나타는 특성을 보장한다!
SS 테이블이 해시 색인을 가진 세그먼트보다 나은점
세그먼트 병합은 파일이 사용 가능한 메모리보다 크더라도 간단하고 효율적이다
이 접근법은
병합 정렬 (merge sort)
알고리즘이 사용하는 방식과 유사하다먼저 입력 파일을 함께 읽고, 각 파일의
첫 번째 키
를 본다 (정렬된 순서에 따라)그리고
가장 낮은 키
를 출력 파일로복사
한 뒤 이 과정을 반복한다이 과정에서 새로운 병합 세그먼트 파일이 생성된다
새로 만든 세그먼트 파일도 키로 정렬되어 있다
이렇게 여러 SS테이블 세그먼트를 병합하고 각 키의 최신 값만 유지한다
여러 입력 세그먼트에 동일한 키가 있으면, 가장 최근 세그먼트의 값은 유지하고 오래된 세그먼트의 값은 버린다
why?
각 세그먼트에는 일정 기간 동안 데이터베이스에 쓰여진 모든 값이 포함되고,
이것은
입력 세그먼트 하나의 모든 값
이다른 세그먼트의 모든 값
보다최신 값
이라는 점을 의미한다
파일에서
특정 키
를 찾기 위해 메모리에모든 키의 색인을 유지
할 필요가 없다찾으려는 키의
오프셋
을 알고 있고, 정렬돼 있으면 어느 범위 안에 특정 키가 있는지 알 수 있다즉, 오프셋으로 이동해 찾으려는 키가 나올 때까지 스캔하면 된다
읽기 요청은 요청 범위 내에서 여러 key-value 쌍을 스캔해야 하기 때문에, 해당 레코드들을
블록
으로그룹화
하고, 디스크에 쓰기 전에압축
한다그러면 희소 인메모리 색인의 각 항목은 압축된 블록의 시작을 가리키게 된다
디스크 공간을 절약한다는 점 외에도 압축은
I/O 대역폭
사용도 줄인다
LSM 트리란?
Log-Structured Merge-Tree
SS 테이블의 형식으로 디스크에 key-value 데이터를 저장하는 색인 방식
SS 테이블 생성과 유지
데이터를
키로 정렬
할 때 디스크 상에 정렬된 구조를 유지하는 일은 가능하지만,메모리
에 유지하는 편이 훨씬 쉽다레드 블랙 트리(red-black tree)
나AVL 트리
와 같이 잘 알려졌고 사용 가능한 트리 데이터 구조는 많이 있다이런 데이터 구조를 이용하면
임의 순서
로 키를삽입
하고,정렬된 순서
로 해당 키를 다시읽을
수 있다
저장소 엔진 만들기
쓰기가 들어오면 인메모리
균형 트리(balanced tree)
데이터 구조(ex. 레드 블랙 트리)에 추가한다이 인메모리 트리는
멤테이블(memtable)
이라고도 한다
멤테이블이 보통 수 메가바이트 정도의 임곗값보다 커지면,
SS테이블 파일
로디스크
에 기록한다SS테이블을 디스크에 기록하는 동안 쓰기는 새로운 멤테이블 인스턴스에 기록한다
읽기 요청을 제공하려면 먼저 멤테이블에서 키를 찾아야 한다
그 다음 디스크 상의 가장 최신 세그먼트에서 찾는다
그 다음으로 두 번째 오래된 세그먼트 세 번째 오래된 세그먼트 등에서 찾는다
위의 저장소 엔진 만들기의
문제점
만약 데이터베이스가 고장나면, 아직 디스크로 기록되지 않고 멤테이블(인메모리 트리)에 있는 가장 최신 쓰기는 손실된다
이런 문제를 피하기 위해서는 이매번 쓰기를 즉시 추가할 수 있게
분리된 로그
를디스크 상에 유지
해야 한다이
로그
는 손 상 후 멤테이블을복원할 때만 필요
하기 때문에 순서가 정렬되지 않아도 된다!
SS 테이블에서 LSM 트리 만들기
여기에 기술된 알고리즘은 기본적으로
레벨DB(LevelDB)
와록스DB(RocksDB)
, 그리고 다른 애플리케이션에 내장하기 위해 설계된 키-값 저장소 엔진 라이브러리에서 사용한다정렬된 파일 병합
과컴팩션
원리를 기반으로 하는 저장소 엔진을LSM 저장소 엔진
이라 부른다
루씬(Lucene)
은 엘라스틱서치나 솔라에서 사용하는전문 검색 색인 엔진
이다루씬은
용어 사전 (term dictionary)
을 저장하기 위해 유사한 방법을 사용한다전문 색인
은 키-값 색인보다 훨씬 더 복잡하지만 이와 유사한 개념을 기반으로 한다검색 질의로 단어가 들어오면 단어가 언급된 모든 문서(웹 페이지, 제품 설명 등)를 찾는다
이 접근법은 키를
단어(용어)
로, 값은 단어를 포함한 모든 문서의ID 목록(포스팅 목록(postings list))
으로 하는 키-값 구조로 구현한다
루씬에서 용어와 포스팅 목록의 매핑은 SS테이블 같은 정렬 파일에 유지하고 필요에 따라 백그라운드에서 병합한다
성능 최적화
LSM 트리 알고리즘
은 데이터베이스에존재하지 않는 키
를 찾는 경우 느릴 수 있다멤테이블을 확인한 다음 키가 존재하지 않는다는 사실을 확인하기 전에는, 가장 오래된 세그먼트까지 거슬러 올라가야 한다
LSM 트리의 최적화 -
블룸 필터 (Bloom Filter)
블룸 필터는 집합 내용을
근사
한(approximating),메모리 효율적 데이터 구조
다!블룸 필터는
키가 데이터베이스에 존재하지 않음
을 알려주므로 존재하지 않는 키를 위한 불필요한 디스크 읽기를 많이 절약할 수 있다
SS테이블을 압축하고 병합하는 순서 & 시기를 결정하는 전략
크기 계층 (sized-tiered)
사이즈 계층 컴팩션은 상대적으로 좀 더
새롭고 작은
SS테이블을 상대적으로오래됐고 큰
SS테이블에 연이어 병합한다
레벨 컴팩션 (leveled compaction)
레벨 컴팩션은 키 범위를 더 작은 SS테이블로 나누고 오래된 데이터는 개별 ‘'레벨”로 이동하기 때문에
컴팩션을 점진적으로 진행
해 디스크 공간을 덜 사용한다
LSM 트리 정리
LSM 트리의 기본 개념은
백그라운드
에서연쇄적
으로SS테이블
을 지속적으로병합
하는 것이다이 개념은 데이터셋이 메모리보다 훨씬 더 크더라도 효과적이다
데이터가
정렬된 순서
로 저장돼 있다면 범위 질의를 효율적으로 실행할 수 있다이 접근법의 디스크 쓰기는
순차적
이기 때문에 LSM 트리가 매우 높은 쓰기 처리량을 보장할 수 있다
B 트리
B Tree (Balanced Tree)
가장 널리 사용되는 색인 구조
구조가 로그 구조화 색인과는 상당히 다르다
SS테이블과 같이
키로 정렬된 키-값 쌍
을 유지하기 때문에 키—값 검색과 범위 질의에 효율적이다but, 비슷한 점은 이 정도이고, B 트리는 설계 철학이 매우 다르다
전통적으로
4KB
크기(때로는 더 큰)의고정 크기 블록
이나페이지
로 나누고,한 번에 하나의 페이지
에 읽기 또는 쓰기 를 한다각 페이지는
주소
나위치
를 이용해 식별할 수 있다이 방식으로 하나의 페이지가 다른 페이지를 참조할 수 있다
포인터와 비슷하지만 메모리 대신
디스크에 있음
한 페이지는 B 트리의 루트(root)로 지정된다
색인에서 키를 찾으려면 루트에서 시작한다
페이지는 여러 키와 하위 페이지의 참조를 포함한다
각 하위 페이지는 키가 계속 이어지는 범위를 담당하고, 참조 사이의 키는
해당 범위 경계
가 어디인지 나타낸다최종적으로는
개별 키(리프 페이지(leaf page))
를 포함하는 페이지에 도달한다이 페이지는 각 키 의 값을 포함하거나 값을 찾을 수 있는 페이지의 참조를 포함한다
B 트리의 한 페이지에서 하위 페이지를 참조하는 수를
분기 계수(branching factor)
라고 부른다B 트리에 존재하는 키의 값을
갱신
하려면,키를 포함하고 있는
리프 페이지
를 검색하고,페이지의 값을 바꾼 다음,
페이지를 디스크에 다시 기록한다
페이지에 대한 모든
참조는 계속 유효
하다
새로운 키를
추가
하려면,새로운 키를 포함하는 범위의 페이지를 찾아 해당 페이지에 키와 값을 추가한다
새로운 키를 수용한 페이지에
충분한 여유 공간
이 없다면,페이지 하나를 반쯤 채워진 페이지 둘로 나누고,
상위 페이지가 새로운 키 범위의 하위 부분들을 알 수 있게 갱신한다
이 알고리즘은 트리가 계속
균형을 유지
하는 것을 보장한다n개의 키를 가진 B 트리는 깊이가 항상
O(log n)
이다대부분의 데이터베이스는 B 트리의 깊이가 3이나 4단계 정도면 충분하므로, 검색하려는 페이지를 찾기 위해 많은 페이지 참조를 따라가지 않아도 된다
신뢰할 수 있는 B 트리 만들기
B 트리의 기본적인 쓰기 동작은 새로운 데이터를 디스크 상의 페이지에
덮어쓴다
이 동작은 덮어쓰기가 페이지 위치를 변경하지 않는다고 가정한다
즉, 페이지를 덮어쓰더라도 페이지를 가리키는 모든
참조
는 온전하게남는다
이 부분은 LSM 트리와 같은
로그 구조화 색인
과는 아주 대조적이다!로그 구조화 색인
은 파일에추가
만 할 뿐(결국 더 이상 쓸모 없는 파일은 삭제됨) 같은 위치의 파일은변경하지 않는다
데이터베이스가 고장 상황에서 스스로 복구할 수 있게 만들려면, 일반적으로 디스크 상에
쓰기 전 로그(write-ahead log, WAL)
(재실행 로그(redo log)라고도 함)라고 하는 데이터 구조를 추가해 B트리를 구현한다동시성 제어
는 보통래치(latch)
(가벼운 잠금(lock))로 트리의 데이터 구조를 보호한다유입 질의의
간섭
없이백그라운드
에서 모든 병합을 수행하고, 이따금원자적
으로 새로운 세그먼트를 이전 세그먼트로 바꾸는 방식으로 로그 구조화 접근을 수행한다
B 트리 최적화
페이지 덮어 쓰기와 고장 복구를 위한 WAL(쓰기 전 로그) 유지 대신, (LMDB 같은) 일부 데이터베이스는
쓰기 시 복사 방식(copy-on-write scheme)
사용페이지에 전체 키를 저장하는 게 아니라 키를
축약
해 쓰면공간 절약
가능리프(leaf) 페이지
를 디스크 상에연속된 순서
로 나타나게끔 트리를 배치하기but, 트리가 커지면 순서를 유지하기가 어렵다
반면에 LSM 트리는 병합하는 과정에서 저장소의
큰 세그먼트
를 한 번에 다시 쓰기 때문에 디스크에서연속된 키
를 서로가깝게 유지
하기가 더 쉽다
트리에
포인터
추가하기각 리프 페이지가 양쪽 형제 페이지에 대한 참조를 가지면, 상위 페이지로 다시 이동하지 않아도 순서대로 키를 스캔할 수 있다
프랙탈 트리 (fractal tree)
같은 B트리 변형은 디스크 찾기를 줄이기 위해로그 구조화
개념을 일부 빌렸다
B 트리와 LSM 트리 비교
B 트리가 LSM 트리보다 일반적으로
구현 성숙도
가 더 높지만, LSM 트리도 성능 특성 때문에 관심을 받고 있다LSM트리는 보통 쓰기에서 더 빠른 반면, B 트리는 읽기에서 더 빠르다고 여긴다
읽기가 보통 LSM 트리에서 더 느린 이유는 각
컴팩션
단계에 있는 여러 가지 데이터 구조와SS테이블
을 확인해야 하기 때문이다
LSM 트리의 장점
B 트리 색인은 모든 데이터 조각을 최소한 두 번 기록해야 한다
쓰기전 로그 1번 & 트리 페이지에 1번
B 트리 색인은 해당 페이지 내 몇 바이트만 바뀌어도 한 번에
전체 페이지
를기록
해야 하는오버헤드
도 있다일부 저장소 엔진은 전원 장애가 발생했을 때
일부만 갱신된 페이지
로 끝나지 않게 동일한 페이지를두 번 덮어쓴다
로그 구조화 색인
또한 SS테이블의 반복된 컴팩션과 병합으로 인해 여러 번 데이터를 다시 쓴다데이터베이스에 쓰기 한 번이 데이터베이스 수명 동안 디스크에 여러 번의 쓰기를 일으키는 효과를
쓰기 증폭 (write amplification)
이라 한다SSD는 수명이 다할 때까지
블록 덮어쓰기
횟수가 제한되기 때문에, 쓰기 증폭은 SSD가 유의해야할 부분이다
쓰기
가 많은 애플리케이션에서성능 병목
은 DB가 디스크에 쓰는 속도일 수 있다이 경우
쓰기 증폭
은 곧성능 비용
이다저장소 엔진이 디스크에 기록할수록, 디스크 대역폭 내 처리할 수 있는 초당 쓰기는 점점 줄어든다
LSM 트리는 보통 B 트리보다
쓰기 처리량
을 높게 유지할 수 있다LSM 트리가 상대적으로
쓰기 증폭
이 더 낮고, 트리에서 여러 페이지를 덮어쓰는 것이 아니라순차적
으로 컴팩션된 SS테이블 파일을 쓰기 때문이다이런 차이는 자기 하드드라이브에서 특히 중요한데, 자기 하드드라이브는 순차 쓰기가 임의 쓰기보다 훨씬 빠르기 때문이다
LSM 트리는
압축률
이 더 좋다보통 B 트리보다 디스크에 더 적은 파일을 생성한다
B 트리 저장소 엔진은
파편화
로 인해 사용하지 않는 디스크 공간 일부가 남는다페이지를 나누거나, 로우가 기존 페이지에 맞지 않을 때 페이지의 일부 공간은 사용하지 않게 된다
LSM 트리는 페이지 지향적이지 않고,
주기적
으로파편화
를 없애기 위해 SS테이블을 다시 기록하기 때문에저장소 오버헤드
가 더 낮다leveled compaction
을 사용하면 특히 그렇다leveled compaction은 키 범위를
더 작은 SS테이블로 나누고,
오래된 데이터는 개별레벨
로 이동하는 것!
LSM 트리의 단점
로그 구조화 저장소의 단점은
컴팩션
과정이 때로는진행 중인 읽기와 쓰기의 성능
에 영향을 준다는 것이다저장소 엔진은 컴팩션을
점진적
으로 수행하고, 동시 접근의 영향이 없게 수행하려 한다But, 디스크가 가진 자원은 한계가 있다
디스크에서 비싼 컴팩션 연산이 끝날 때까지 요청이 대기해야 하는 상황이 발생하기 쉽다
반면 B 트리의 성능은 로그 구조화 저장소 엔진보다 예측하기 쉽다
또 다른 컴팩션 문제는
높은 쓰기 처리량
에서 발생한다디스크의
쓰기 대역폭
은유한
하다초기 쓰기 (logging과 멤테이블을 디스크로 flush)와 백그라운드에서 수행되는 컴팩션 thread가 이 대역폭을 공유해야 한다
데이터베이스가 점점 커질수록 컴팩션을 위해 더 많은 대역폭이 필요한 문제가 있다
B 트리의 장점은 각 키가
색인의 한 곳
에만 정확하게존재
한다는 점이다반면 로그 구조화 저장소 엔진은 다른 세그먼트에
같은 키의 다중 복사본
이 존재할 수 있다이런 측면 때문에 강력한 트랜잭션 시멘틱을 제공하는 데이터베이스에는 B 트리가 더 좋다
많은 RDBMS에서
트랜잭션 격리
는 키 범위의 잠금을 사용해 구현한 반면, B트리 색인에서는트리에 직접 잠금
을 포함한다
색인 안에 값 저장하기
색인에서
키
는 질의가 검색하는 대상이다색인에서
값
은질문의 실제 로우(문서, 정점)거나
다른 곳에 저장된 로우를 가리키는
참조
다로우가 저장된 곳을
힙 파일(heap file)
이라 하고, 특정 순서 없이 데이터를 저장한다추가 전용이거나 나중에 새로운 데이터로 덮어 쓰기 위해
삭제된 로우
를 기록할 수도 있다
힙 파일 접근
여러 보조 색인이 존재할 때,
데이터 중복
을 피할 수 있게 한다각 색인은 힙 파일에서 위치만 참조하고, 실제 데이터는 ㅇ리정한 곳에 유지한다
키를 변경하지 않고 값을 갱신할 때 효율적이다
새로운 값이 이전 값보다 많은 공간을 필요로 하지 않으면, 레코드를 제자리에
덮어쓸 수 있다
새로운 값이 많은 공간을 필요로 하면, 힙에서 충분한 공간이 있는 새로운 위치로
이동
해야 한다이 경우 모든 색인이 레코드의 새로운 힙 위치를 가리키게끔 갱신하거나,
이전 힙 위치에
전방향 포인터
를 남겨둬야 한다
색인에서 힙 파일로 다시 이동하면
읽기 성능
이 안 좋아져서, 어떤 상황에서는 색인 안에 색인된 로우를 저장하는게 좋다이것을
클러스터드 색인(clustered index)
라고 한다
클러스터드 색인
색인 안에 모든 로우 데이터를 저장
비클러스터드 색인
색인 안에 데이터의 참조만 저장
커버링 색인 (covering index)
or포괄열이 있는 색인 (index with included column)
색인 안에 테이블의 칼럼 일부를 저장
이렇게 하면 색인만 사용해 일부 질의에 응답 가능
이런 경우를 색인이 질의를
커버
했다고 말함!
다중 칼럼 색인
테이블의 다중 칼럼 or 문서의 다중 필드에 동시에 질의를 해야할 때 사용
결합 색인 (concatenated index)
다중 칼럼 색인의 가장 일반적인 유형
하나의 칼럼에 다른 칼럼을
추가
하는 방식으로 하나의 키에 여러 필드를 단순히결합
한다필드가 연결되는 순서는 색인 정의에 명시!
이 방식은 (성, 이름) 을 키로 전화번호를 값으로 하는 색인을 제공하는 종이 전화번호부와 유사하다
순서가 정렬돼 있기 때문에 특정 성을 가진 모든 사람을 찾거나, 특정 성-이름 조합을 가진 모든 사람을 찾을 때도 이 색인을 사용할 수 있다
but, 특정 이름을 가진 모든 사람을 찾을 때는 쓸모가 없다
다차원 색인
한 번에 여러 칼럼에 질의하는 조금 더 일반적인 방법
지리 공간 데이터에 중요하게 사용됨
ex)
지도에서 레스토랑 찾을 때, 사용자의 현재 위치 기반으로 네모난 지도 내 레스토랑 찾기
이를 위한 이차원 범위 질의
표준 B트리나 LSM 트리 색인은 이런 유형의 질의에 효율적으로 응답할 수 없다
위도 범위 안의 모든 레스토랑 or 경도 범위 안의 모든 레스토랑을 줄 순 있지만, 둘을 동시에 주진 못한다
이차원 위치를
공간 채움 곡선(space-filling curve)
을 이용해 단일 숫자로 변환한 다음, 일반 B 트리 색인을 사용하여 해결 할 수 있다또는, R 트리처럼
전문 공간 색인(specialized spatial index)
를 사용할 수 있다
다차원 색인의 활용은 지리학적인 위치에만 국한되지 않는다
ex)
커머스 웹 사이트에서 특정 색상 범위의 제품을 검색하기 위해 (빨강, 초록, 파랑)의 3차원 색인 사용
모든 것을 메모리에 보관
지금까지 다룬 데이터 구조는 디스크 한계에 대한 해결책이다
디스크는 메인 메모리와 비교해 다루기 어렵다
디스크의 장점
지속성
디스크 내용은 전원이 꺼져도 손실되지 않음
가격
RAM 보다 기가바이트당 가격이 더 저렴
인메모리 데이터베이스
의 등장RAM이 더 저렴해지고 있고, 데이터셋 대부분 그다지 크지 않기 때문에 메모리에 전체를 보관하는 방식도 적절
여러 장비 간 분산해서 보관할 수도 있음
맴캐시드
같은 일부 인메모리 키-값 저장소는 장비가 재시작되면 데이터 손실을 허용하는캐시
용도로만 사용된다but, 다른 인메모리 데이터베이스는
지속성
을 목표로 한다
인메모리 데이터베이스의
지속성
지속성을 위해 (배터리 전원 공급 RAM과 같은)
특수 하드웨어
를 사용하거나,디스크에 변경 사항의
로그
를 기록하거나,디스크에 주기적인
스냅샷
을 기록하거나,다른 장비에 인메모리 상태를
복제
하는 방법이 있다
레디스(Redis)
와카우치베이스(Couchbase)
는비동기
로 디스크에 기록하기 때문에 약한 지속성을 제공한다디스크 기반 색인
으로 구현하기 어려운데이터 모델
을 제공하는 인메모리 데이터베이스레디스
우선순위 큐와 셋(set) 같은 데이터 구조를 데이터베이스 같은 인터페이스로 제공
메모리에 모든 데이터를 유지하기 때문에 구현이 비교적 간단
안티 캐싱(anti-caching)
접근 방식메모리가 충분하지 않을 때 가장 최근에 사용하지 않은 데이터를 메모리에서 디스크로 내보내고,
나중에 다시 접근할 때 메모리에 적재하는 방식으로 동작
운영체제가
가상 메모리
와스왑 파일
에서 수행하는 방식과 유사하지만, 데이터베이스는 전체 메모리 페이지보다 개별 레코드 단위로 작업할 수 있기 때문에 OS보다 더 효율적으로 메모리를 관리할 수 있다but, 전체 색인이 메모리에 있어야만 한다
트랜잭션 처리나 분석?
트랜잭션이 반드시
ACID
속성을 가질 필요는 없다원자성(atomicity)
일관성(consistency)
격리성(isolation)
지속성 (durability)
트랜잭션 처리는 주기적으로 수행되는
일괄 처리
작업과 달리, 클라이언트가 지연 시간(low-latency) 이 낮은 읽기와 쓰기를 가능하게 한다는 의미다온라인 트랜잭션 (online transaction processing, OLTP)
보통 애플리케이션은 색인을 사용해 일부 키에 대한 적은 수의 레코드를 찾는다
레코드는 사용자 입력을 기반으로 삽입되거나, 갱신된다
이런 애플리케이션은 대화식이기 때문이 이 접근 패턴을 OLTP라고 한다
비즈니스 인텔리전스 (business intelligence)
보통 비즈니스 분석가가 작성하며, 회사 경영진이 더 나은 의사 결정을 하게끔 돕는 보고서
온라인 분석 처리 (online analystic processing, OLAP)
BI를 위한 데이터베이스 사용 패턴을 트랜잭션 처리와 구분하기 위해 OLAP라고 부른다
OLTP vs OLAP
특성트랜잭션 처리 시스템 (OLTP)분석 시스템 (OLAP)주요 읽기 패턴
질의당 적은 수의 레코드, 키 순으로 가져옴
많은 레코드에 대한 집계
주요 쓰기 패턴
임의 접근, 사용자 입력을 낮은 지연 시간으로 등록
대규모 불러오기 (bulk import, ETL) or 이벤트 스트림
주요 사용처
웹 애플리케이션을 통한 최종 사용자/소비자
의사결정 지원을 위한 내부 분석가
데이터 표현
데이터의 최신 상태 (현재 시점)
시간이 지나며 일어난 이벤트 이력
데이터셋 크기
기가바이트 ~ 테라바이트
테라바이트 ~ 페타바이트
데이터 웨어하우징
데이터 웨어하우스 (data warehouse)
분석가들이 OLTP 작업에 영향을 주지 않고 마음껏 질의할 수 있는 개별 데이터베이스
회사 내의 모든 다양한 OLTP 시스템에 있는 데이터의
읽기 전용 복사본
데이터는 OLTP 데이터베이스에서
(주기적인
데이터 덤프
나 지속적인갱신 스트림
을 사용해)추출
(extract) 하고,분석 친화적인 스키마로
변환
(transform) 하고,깨끗하게 정리한 다음 데이터웨어하우스에
적재
(load) 한다
데이터 웨어하우스로 데이터를 가져오는 이 과정을
ETL (Extract - Transform - Load)
이라 한다
데이터 웨어하우스의 장점
분석을 위해 OLTP 시스템에 직접 질의하지 않고, 개별 데이터 웨어하우스를 사용하는 큰 장점은
분석 접근 패턴
에 맞게최적화
할 수 있다는 점이다
OLTP 데이터베이스와 데이터 웨어하우스의 차이점
SQL은 일반적으로
분석 질의
에 적합하기 때문에 데이터 웨어하우스의 데이터 모델은관계형 모델
을 사용한다SQL 질의를 생성하고, 결과를 시각화하고, 분석가가
drill-down
,slicing
,dicing
같은 작업을 통해 데이터를 탐색할 수 있게 해주는 여러 그래픽 데이터 분석 도구가 있다drill-down
요약된 정보에서 상세 정보까지 계층을 나눠 점점 구체적으로 분석하는 작업
slicing
&dicing
상세한 분석을 위해 주어진 큰 규모의 데이터를 작은 단위로 나누고,
원하는 세부 분석 결과를 얻을 때까지 반복한다는 뜻
각각 매우 다른
질의 패턴
에 맞게 최적화됐기 때문에시스템의 내부
는 완전히 다르다
분석용 스키마: 별 모양 스키마와 눈꽃송이 모양 스키마
별 모양 스키마 (star schema)
많은 데이터 웨어하우스는 별 모양 스키마로 알려진 상당히 정형화된 방식을 사용한다
사실 테이블 (fact table)
사실 테이블의 각 로우는 특정 시각에 발생한 이벤트에 해당한다
보통 사실은 개별 이벤트를 담는다
이는 나중에 분석의 유연성을 극대화할 수 있기 때문이다
하지만 이것은 사실 테이블이 매우 커질 수 있다는 의미다
사실 테이블의 일부 칼럼은 제품이 판매된 가격과 공급자로부터 구매한 비용(이익 마진을 계산할 수 있음)과 같은 속성이다
사실 테이블의 다른 칼럼은 차원 테이블(dimension table) 이라 부르는 다른 테이블을 가리키는 외래 키 참조다
사실 테이블의 각 로우는 이벤트를 나타내고 차원은 이벤트의 속성인 누가(who), 언제(when), 어디서(where), 무엇을(what), 어떻게(how), 왜(why) 를 나타낸다
눈꽃송이 모양 스키마(snowflake schema)
차원이 하위차원으로 더 세분화된다
눈꽃송이 스키마는 별 모양 스키마보다 더 정규화됐지만 분석가들이 별 모양 스키마를 작업하 기 더 쉽다는 이유로 선호한다
칼럼 지향 저장소
모든 값을 하나의 로우에 함께 저장하지 않는 대신 각 칼 럼별로 모든 값을 함께 저장한다
각 칼럼을 개별 파일에 저장하면 질의에 사용되는 칼럼만 읽고 구분 분석하면 된다
이 방식을 사용하면 작업량이 많이 줄어든다
정리
고수준에서 저장소 엔진은 트랜잭션 처리 최적화(OLTP)와 분석 최적화(OLAP)라는 큰 두 가지 범주로 나뉜다.
OLTP와 OLAP의 사용 사례에서 보면 접근 패턴 간 큰 차이가 있다
OLTP 시스템
사용자 대면이기 때문에 대량의 요청을 받을 수 있다
부하를 처리하기 위해 보통 애플리케이션이 각 질의마다 작은 수의 레코드만 다룬다
애플리케이션은 키의 일부만 사용하는 레코드를 요청하고 저장소 엔진은 요청한 키의 데이터를 찾기 위해 색인을 사용한다
이 경우는 대개 디스크 탐색이 병목이다
데이터 웨어하우스와 유사한 분석 시스템 (OLAP)
최종 사용자가 아닌 비즈니스 분석가가 주로 사용하기 때문에 덜 알려져 있다
OLTP 시스템보다 훨씬 더 적은 수의 질의를 다루지만 각 질의는 대개 매우 다루기 어렵고 짧은 시간에 수백만 개의 레코드를 스캔해야 한다
이 경우는 일반적으로 디스크 대역폭( 디스크 탐색이 아닌)이 병목이다
칼럼 지향 저장소는 이런 종류의 작업부하를 처리할 때 사용 가능한 날로 인기가 높아지고 있는 솔루션이다
OLTP 측면에서 두 가지 주요한 관점
로그 구조화 관점에서 파일에 추가와 오래된 파일의 삭제만 허용하고, 한 번 쓰여진 파일은 절대 갱신하지 않는다
비트캐스크, SS테이블, LSM 트리, 레벨DB, 카산드라, HBase, 루씬 등이 이 그룹에 속한다
제자리 갱신 관점에서 덮어쓰기 할 수 있는 고정 크기 페이지의 셋으로 디스크를 다룬다
이 관점에서 가장 큰 예가
B트리
다B 트리는 모든 주요 관계형 데이터베이스와 많은 비정형 데이터베이스에서도 사용한다
Last updated