ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 3장. 저장소와 검색
    STUDY/데이터 중심 애플리케이션 설계 2025. 6. 18. 22:15

    데이터베이스의 기본적인 기능

    • 어떤 데이터를 받으면 데이터를 저장
    • 데이터를 요청하면 데이터를 제공

     

    애플리케이션 개발자는 사용 가능한 여러 저장소 엔진 중에 애플리케이션에 적합한 엔진을 선택하는 작업이 필요하다. 특정 작업부하(workload) 유형에서 좋은 성능을 내게끔 저장소 엔진을 조정하려면 저장소 엔진의 내부 수행 작업에 대해 이해할 필요가 있다.

     

    • 관계형 데이터베이스와 NoSQL 에서 사용되는 저장소 엔진
    • 로그 구조(log-structured) 계열 저장소 엔진
    • 페이지 지향(page-oriented) 계열 저장소 엔진

     

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

    가장 간단한 데이터베이스

    • 키-값 저장소를 함수로 구현
    • 매 라인마다 쉼표로 구분된 키-값 쌍을 포함한 텍스트 파일 형식
    • db_set을 호출할 때마다 파일의 끝에 추가하므로 키를 여러 번 갱신해도 값의 예전 버전을 덮어 쓰지 않는다.
    #!/bin/bash
    
    db_set() {
    	echo "$1,$2" >> database
    }
    
    db_get() {
    	grep "^$1," database | sed -e "s/^$1,//" | tail -n 1
    }

     

    파일 추가 작업은 매우 효율적이기 때문에 db_set 함수는 매우 간단한 작업의 경우에는 좋은 성능을 보여준다.

    db_set과 마찬가지로 많은 데이터베이스는 내부적으로 추가 전용(append-only) 데이터 파일인 로그(log)를 사용한다. 여기서 로그는 연속된 추가 전용 레코드를 의미하며, 사람이 읽을 수 있는 형식일 필요가 없다.

     

    반면 db_get 함수는 데이터베이스에 많은 레코드가 있으면 성능이 매우 좋지 않다.(검색 비용 O(n))

    데이터베이스에서는 특정 키의 값을 효율적으로 찾기 위해서는 다른 데이터 구조가 필요한데, 이를 색인(index)이라 한다. 색인의 일반적인 개념은 어떤 부가적인 메타데이터를 유지하는 것이다. 이 메타데이터는 이정표 역할을 해서 원하는 데이터의 위치를 찾는데 도움을 준다.

     

    색인은 기본 데이터(primary data)에서 파생된 추가적인 구조이다. 많은 데이터베이스는 색인의 추가와 삭제를 허용하는데, 이는 질의 성능에만 영향을 준다. 추가적인 구조의 유지보수는 특히 쓰기 과정에서 오버헤드가 발생한다. 데이터를 쓸 때마다 매번 색인을 갱신해야 하기 때문에, 어떤 종류의 색인이라도 쓰기 속도를 느리게 만든다.

     

     

    해시 색인

    키-값 저장소는 대부분의 프로그래밍 언어에서 볼 수 있는 사전 타입(dictionary type)과 매우 유사하며, 보통 해시 맵(hash map, 해시 테이블(hash table))으로 구현한다.

     

    단순히 파일에 추가하는 방식으로 데이터 저장소를 구현하는 경우, 가장 간단하게 가능한 색인 전략은 키를 데이터 파일의 바이트 오프셋에 매핑해 인메모리 해시 맵을 유지하는 것이다.

    In-memory hash map

     

    바이트 오프셋은 값을 바로 찾을 수 있는 위치이며, 파일에 새로운 키-값 쌍을 추가할 때마다 데이터의 오프셋을 반영하기 위해 해시 맵도 갱신해야 한다. 값을 조회하려면 해시 맵을 사용해 데이터 파일에서 오프셋을 찾아 해당 위치를 구하고 값을 읽으면 된다.

     

    실제로 많이 사용하는 접근 방식으로, 비트캐스크(Bitcask)라는 저장소 엔진이 근본적으로 사용하는 방식이다. 비트캐스크는 해시 맵을 전부 메모리에 유지하기 때문에 사용 가능한 RAM에 모든 키가 저장된다는 조건을 전제로 고성능으로 읽기, 쓰기를 보장한다. 값은 한 번의 디스크 탐색으로 디스크에서 적재할 수 있기 때문에 사용 가능한 메모리보다 더 많은 공간을 사용할 수 있다. 이러한 저장소 엔진은 각 키의 값이 자주 갱신되는 상황에 매우 적합하다.(키=동영상URL, 값=재생횟수)

     

    파일에 계속 추가만 한다면 결국 디스크 공간이 부족해진다. 이 상황은 특정 크기의 세그먼트(segment)로 로그를 나누는 방식으로 해결할 수 있다. 특정 크기에 도달하면 세그먼트 파일을 닫고 새로운 세그먼트 파일에 이후 쓰기를 수행한다. 이러면 세그먼트 파일들에서 중복된 키를 버리고 각 키의 최신 갱신 값만 유지하는 컴팩션(compaction)을 수행할 수 있다.

    컴팩션(compaction)

     

    컴팩션은 보통 세그먼트를 더 작게 만들기 때문에, 컴팩션을 수행할 때 동시에 여러 세그먼트들을 병합할 수 있다. 고정된 세그먼트의 병합과 컴팩션은 백그라운드 스레드에서 수행되고, 그동안 이전 세그먼트 파일을 사용해 읽기와 쓰기 요청을 처리할 수 있다.

     

    컴팩션과 세그먼트 병합을 동시에 수행

     

    이러면 각 세그먼트는 키를 파일 오프셋에 매핑한 자체 인메모리 해시 테이블을 갖게 된다. 키의 값을 찾으려면 최신 세그먼트 해시 맵을 먼저 확인하면 되고, 키가 없는 경우 두 번째 세그먼트 등을 확인하면 된다. 병합 과정을 통해 세그먼트 수를 적게 유지하기 때문에 조회할 때 많은 해시 맵을 확인할 필요가 없다.

     

    구현할 때 고려해야 할 사항

    • 파일 형식 : 바이트 단위 문자열 길잉를 부호화한 다음 바이너리 형식을 사용하는 편이 더 빠르고 간단하다.
    • 레코드 삭제 : 키와 관련된 값을 삭제하려면 데이터 파일에 특수한 삭제 레코드를 추가해야 한다.
    • 고장(Crash) 복구 : 데이터베이스가 재시작되면 전체 세그먼트 파일을 읽어 인메모리 해시 맵을 복원하는데, 파일이 크면 시간이 오래걸릴 수 있다.
    • 부분적으로 레코드 쓰기 : 데이터베이스는 로그에 레코드를 추가하는 도중에 죽을 수 있다.
    • 동시성 제어 : 로그 쓰기는 하나의 쓰기 스레드만 사용해 동시성 제어한다. 세그먼트는 추가 전용이거나 불변이므로 다중 스레드로 동시에 읽을 수 있다.

     

    추가 전용 설계의 좋은 점

    • 추가와 세그먼트 병합은 순차적인 쓰기 작업이기 때문에 보통 무작위 쓰기보다 훨씬 빠르다.
    • 세그먼트 파일이 추가 전용이나 불변이면 동시성과 고장 복구는 훨씬 간단하다.
    • 오래된 세그먼트 병합은 시간이 지남에 따라 조각화되는 데이터 파일 문제를 피할 수 있다.

     

    해시 테이블 색인의 제한 사항

    • 해시 테이블은 메모리에 저장해야 하므로 키가 너무 많으면 문제가 된다. 디스크에 해시 맵을 유지할 수는 있지만, 무작위 접근 I/O가 많이 필요하고 디스크가 가득 찼을 때 확장하는 비용이 비싸며 해시 충돌 해소를 위한 로직이 필요한다.
    • 해시 테이블은 해시 맵에서 모든 개별 키를 조회해야 하기 때문에 범위 질의(range query)에 효율적이지 않다.

     

     

    SS테이블과 LSM 트리

    세그먼트 파일 형식에서 일련의 키-값 쌍을 키로 정렬하면, 정렬된 문자열 테이블(Sorted String Table) 또는 SS테이블이라 부르는 형식이 된다. 각 키는 병합된 세그먼트 파일 내에 한 번만 나타나야 한다.(컴팩션이 이를 보장함.)

     

    해시 색인을 가진 로그 세그먼트와 비교했을 때 장점

    • 세그먼트 병합은 파일이 사용 가능한 메모리보다 크더라도 간단하고 효율적이다. 이 접근법은 병합정렬(mergesort) 알고리즘에서 사용하는 방식과 유사하다.
    • 파일에서 특정 키를 찾기 위해 더는 메모리에 모든 키의 색인을 유지할 필요가 없다. 
    • 읽기 요청은 요청 범위 내에서 여러 키-값 쌍을 스캔해야 하기 때문에 해당 레코드들을 블록으로 그룹화하고 디스크에 쓰기 전에 압축한다. 그러면 희소 인메모리 색인의 각 항목은 압축된 블록의 시작을 가리키게 된다. 디스크 공간을 절약하고, I/O 대역폭 사용도 줄일 수 있다.

     

    SS테이블 생성과 유지

    디스크 상에 정렬된 구조를 유지하는 일은 가능하지만 메모리에 유지하는 편이 훨씬 쉽다. red-black tree나 AVL 트리와 같은 데이터 구조를 이용하면 임의 순서로 키를 삽입하고 정렬된 순서로 해당 키를 다시 읽을 수 있다.

     

    • 쓰기가 들어오면 인메모리 균형 트리(balanced tree) 데이터 구조에 추가한다. 이 인메모리 트리를 멤테이블(memtable)이라고도 한다.
    • 멤테이블이 보통 수 메가바이트 정도의 임계값보다 커지면 SS테이블 파일로 디스크에 기록한다. 새로운 SS테이블 파일은 데이터베이스의 가장 최신 세그먼트가 된다. SS테이블을 디스크에 기록하는 동안 쓰기는 새로운 멤테이블 인스턴스에 기록한다.
    • 읽기 요청을 제공하려면 멤테이블에서 키를 찾고, 그 다음 디스크 상의 최신 세그먼트 순서로 찾는다.
    • 세그먼트 파일의 병합과 컴팩션 과정을 수행한다.

     

    만약 데이터베이스가 고장나면 아직 디스크로 기록되지 않은 최신 쓰기가 손실되므로, 이런 문제를 피하기 위해서는 매번 쓰기를 즉시 추가할 수 있게 분리된 로그를 디스크 상에 유지해야 한다.

     

     

    SS테이블에서 LSM 트리 만들기

    위에 기술된 알고리즘은 LevelDB, RocksDB, Bigtable, Cassandra, HBase의 키-값 저장소 엔진 라이브러리에서 사용한다.

    위의 색인 구조는 Log-Structured Merge-Tree, LSM(로그 구조화 병합 트리)란 이름으로 발표되었다. 정렬된 파일 병합과 컴팩션 원리를 기반으로 하는 저장소 엔진을 LSM 저장소 엔진이라 부른다.

     

     

    성능 최적화

    많은 세부 사항이 저장소 엔진을 잘 동작하게 만든다.

     

    LSM 트리 알고리즘의 경우 데이터베이스에 존재하지 않는 키를 찾는 경우 느릴 수 있다. 없는 키를 찾기 위해 멤테이블부터 가장 오래된 세그먼트 끝까지 찾기 때문이다. 이런 종류의 접근을 최적화하기 위해 저장소 엔진은 보통 Bloom Filter(블룸 필터)를 제공한다. 블룸 필터는 키가 데이터베이스에 존재하지 않음을 알려주므로, 불필요한 디스크 읽기를 절약할 수 있다.

     

    SS테이블을 압축하고 병합하는 순서와 시기를 결정하는 다양한 전력이 있다. 일반적으로 크기 계층 컴팩션(size-tiered compaction)과 레벨 컴팩션(leveled compaction)이다.

    크기 계층 컴팩션은 상대적으로 좀 더 새롭고 작은 SS테이블을 상대적으로 오래됐고 큰 SS테이블에 병합하고, 레벨 컴팩션은 키 범위를 더 작은 SS테이블로 나누고 오래된 데이터는 개별 "레벨"로 이동시킨다.

     

    LSM 트리의 기본 개념은 백그라운드에서 연쇄적으로 SS테이블을 지속적으로 병합하는 것이다. 이 개념은 데이터셋이 메모리보다 훨씬 크더라도 효과적이고, 데이터가 정렬된 순서로 저장돼 있다면 범위 질의를 효율적으로 실행할 수 있다. 그리고 디스크 쓰기가 순차적이기 때문에 매우 높은 쓰기 처리량을 보장할 수 있다.

     

     

    B트리(B-tree)

    1970년대에 등장한 B-tree는 전통적으로 4KB 크기(때로는 더 큰)의 고정 크기 블록이나 페이지로 나누고 한 번에 하나의 페이지에 읽기 또는 쓰기를 한다. 각 페이지는 주소나 위피를 이용해 식별할 수 있고, 하나의 페이지가 다른 페이지를 참조할 수 있다.

     

    색인에서 키를 찾으려면 루트(root)에서 시작한다. 페이지는 여러 키와 하위 페이지의 참조를 포함하며, 각 하위 페이지는 키가 계속 이어지는 범위를 담당하고 참조 사이의 키는 해당 범위 경계가 어디인지 나타낸다.

    최종적으로는 개별 키(리트 페이지, leaf page)를 포함하는 페이지에 도달하게 되고, 이 페이지는 각 키의 값을 포함하거나 값을 찾을 수 있는 페이지의 참조를 포함한다.

     

    B 트리의 한 페이지에서 하위 페이지를 참조하는 수를 분기 계수(branching factor)라고 부른다. 

     

    B 트리에 존재하는 키의 값을 갱신할 때는 키를 포함하고 있는 리프 페이지를 검색하고 페이지의 값을 바꾼 다음 페이지를 디스크에 다시 기록한다. 새로운 키를 추가하는 경우, 키를 포함하는 범위의 페이지를 찾아 해당 페이지에 키와 값을 추가하고, 만약 여유 공간이 없다면 페이지를 둘로 나누고 상위 페이지가 새로운 키 범위의 하위 부분들을 알 수 있게 갱신한다. 이러한 알고리즘은 트리가 계속 균현을 유지하는 것을 보장한다. n개의 키를 가진 B트리는 깊이가 항상 O(log n)이다. 

     

     

    신뢰할 수 있는 B트리 만들기

    B 트리의 기존적인 쓰기 동작은 새로운 데이터를 디스크 상의 페이지에 덮어쓰는 것이다. (페이지를 덮어쓰더라도 페이지를 가리키는 모든 참조는 온전하게 남는다.)

     

    삽입 때문에 페이지가 너무 많아져 페이지를 나눠야 하는 경우 분할된 두 페이지를 기록하고 두 하위 페이지의 참조를 갱신하게끔 상위 페이지를 덮어써야 한다. 이 때, 일부 페이지만 기록하고 데이터베이스가 고장 난다면 색인이 훼손되기 때문에 고아 페이지(orphan page, 어떤 페이지와도 부모 관계가 없는 페이지)가 발생할 수 있다.

     

    고장 상황에서 스스로 복구할 수 있게 하기 위해 디스크 상에 쓰기 전 로그(write-ahead log, WAL, 재실행 로그(redo log))라고 하는 데이터 구조를 추가해 B 트리를 구현한다. 이는 트리 페이지에 변경된 내용을 적용하기 전에 모든 B 트리의 변경 사항을 기록하는 추가 전용 파일로, 일관성 있는 상태로 B 트리를 복원하는데 사용된다.

     

    다중 스레드가 동시에 B 트리에 접근하는 경우 동시성 제어를 해야하는데, 보통 래치(latch, 가벼운 잠금)로 트리의 데이터 구조를 보호한다.

     

     

    B 트리 최적화

    • 일부 데이터베이스는 쓰기 시 복사 방식(copy-on-write scheme)을 사용한다.(동시성 제어에도 유용)
    • 페이지 전체 키를 저장하는 게 아니라 키를 축약해 쓰면 공간을 절약할 수 있다.
    • B 트리 구현에서 리프 페이지를 디스크 상에 연속된 순서로 나타나게끔 트리르 배치하려 시도한다.
    • 트리에 포인터를 추가한다.
    • 프랙탈 트리(fractal tree, B 트리 변형)은 디스크 찾기를 줄이기 위해 로그 구조화 개념을 일부 빌렸다.

     

     

    B트리와 LSM 트리 비교

      B-Tree LSM-Tree
    쓰기 처리 성능 중간 수준 (디스크 페이지 읽기 및 수정 후 다시 쓰기 필요) 매우 뛰어남 (메모리에 기록 후 배치 단위로 디스크에 순차적 쓰기)
    읽기 성능 (포인트 조회) 빠름 (트리 탐색으로 한 번에 위치 조회 가능) 보통 (여러 SSTable에서 검색 필요하므로 Bloom 필터 등 최적화 필요)
    읽기 성능 (범위 조회) 뛰어남 (정렬된 트리 구조를 따라 범위 순회) 뛰어남 (SSTable이 정렬되어 있으므로 범위 병합 가능)
    디스크 접근 패턴 **임의 접근(Random Access)**이 많음 순차 접근(Sequential Write) 위주, 디스크 효율성 높음
    디스크 공간 사용 다소 비효율적 (중복된 값 존재, 페이지 분할 등으로 단편화 가능) 효율적 (컴팩션 시 중복 제거 및 정렬 저장으로 공간 활용 우수)
    컴팩션/정리 작업 필요 없음 필요 (주기적인 SSTable 병합 및 정리 작업으로 I/O 소모 발생)
    동시성 및 트랜잭션 처리 정교한 메커니즘 필요 (WAL, 페이지 잠금 등) 비교적 단순한 구조이지만, WAL 병행 필요 시 설계 복잡해짐
    구현 복잡도 오래된 구조로 널리 구현되어 있음, 구현은 복잡하나 안정적 구조 자체는 단순하나 컴팩션 전략, Bloom 필터, 멀티 레벨 병합 등은 복잡성 존재
    적합한 경우 읽기와 쓰기 균형이 필요하고, 트랜잭션 강력 지원이 중요한 OLTP 시스템 쓰기 처리량이 중요하고, 대용량 로그성 데이터에 적합 (예: 타임시리즈, 로그 저장 등)

     

     

    기타 색인 구조

    • 보조 색인(secondary index) : 효율적으로 조인을 수행하는 데 결정적인 역할을 한다. 키-값 색인에서 쉽게 생성할 수 있고, 키가 고유하지 않아 같은 키를 가진 많은 로우가 있을 수 있다.
    • 색인 안에 값 저장하기
      • 비클러스터 색인: 색인 안에 데이터의 참조만 저장
      • 클러스터 색인(clustered index): 색인 안에 모든 로우 데이터를 저장
      • 커버링 색인(covering index) / 포괄열이 있는 색인(index with included column) : 색인 안에 테이블의 칼럼 일부를 저장
    • 다중 칼럼 색인
      • 결합 색인(concatenated index) : 하나의 칼럼에 다른 칼럼을 추가하는 방식으로 하나의 키에 여러 필드를 단순히 결합한다.
      • 다차원 색인 : 한 번에 여러 칼럼에 질의하는 조금 더 일반적인 방법
    • 전문 검색과 퍼지 색인
      • 전문 검색 엔진 : 특정 단어를 검색할 때 해당 단어의 동의어로 질의를 확장한다.
    • 모든 것을 메모리에 보관
      • 인메모리 데이터베이스 : 특수 하드웨어를 사용하거나 디스크에 변경 사항의 로그를 기록하거나 디스크에 주기적인 스냅숏을 기록하거나 다른 장비에 인메모리 상태를 복제하는 방법
      • 안티 캐싱(anti-caching) : 메모리가 충분하지 않을 때 가장 최근에 사용하지 않은 데이터를 메모리에서 디스크로 내보내고 나중에 다시 접근할 때 메모리에 적재하는 방식
      • 비휘발성 메모리(non-volatile memory. NVM) 기술

     

     

    트랜잭션 처리나 분석?

     

    온라인 트랜잭션 처리(online transaction processing, OLTP)

    • 색인을 사용해 일부 키에 대한 적은 수의 레코드를 찾고, 레코드는 사용자 입력을 기반으로 삽입되거나 갱신된다.
    • 대화식으로 진행된다.

    온라인 분석 처리(online analytic processing, OLAP)

    • 데이터베이스를 데이터 분석에도 점점 더 많이 사용하면서 등장
    • 많은 수의 레코드를 스캔해 레코드당 일부 칼람만 읽어 집계 통산을 계산하는 방식
    • 더 나은 의사결정을 하게끔 돕는 보고서를 제공(비즈니스 인텔리전스, business intelligence)
      OLTP OLAP
    주요 읽기 패턴 질의당 적은 수의 레코드, 키 기준으로 가져옴 많은 레코드에 대한 집계
    주요 쓰기 패턴 임의 접근, 사용자 입력을 낮은 지연 시간으로 기록 대규모 불러오기 또는 이벤트 스트림
    주요 사용처 웹 애플리케이션을 통한 최종 사용자/소비자 의사결정 지원을 위한 내부 분석가
    데이터 표현 데이터의 최신 상태(현재 시점) 시간이 지나며 일어난 이벤트 이력
    데이터셋 크기 기가바이트에서 테라바이트 테라바이트에서 페타바이트

     

     

    데이터 웨어하우스(data warehouse)

    분석가들이 OLTP 작업에 영향을 주지 않고 마음껏 질의할 수 있는 개별 데이터베이스로, 회사 내의 모든 다양한 OLTP 시스템에 있는 데이터의 읽기 전용 복사본이다. 데이터는 OLTP 데이터베이스에서 추출하고 분석 친화적인 스키마로 변환하고 깨끗하게 정리한 다음 데이터 웨어하우스에 적재한다. 데이터 웨어하우스로 데이터를 가져오는 과정을 ETL(Extract-Transform-Load)이라 한다. 데이터 웨어하우스를 사용하는 큰 장점은 분석 접근 패턴에 맞게 최적화할 수 있다는 것이다.

     

    Extract Transform Load

     

     

    OLTP 데이터베이스와 데이터 웨어하우스의 차이점

    표면적으로 데이터 웨어하우스와 관계형 OLTP 데이터베이스는 둘 다 SQL 질의 인터페이스를 지원하기 때문에 비슷해 보이지만, 각각 매우 다른 질의 패턴에 맞게 최적화됐기 때문에 시스템의 내부는 완전히 다르다.

     

     

    분석용 스키마: 별 모양 스키마와 눈꽃송이 모양 스키마

     

    별 모양 스키마(star schema)

    • 차원 모델링(dimensional modeling) 이라고도 한다.
    • 사실 테이블(fact table)
      • 각 로우는 특정 시각에 발생한 이벤트에 해당한다.
      • 사실 테이블의 일부 칼럼은 속성이고, 다른 칼럼은 차원 테이블을 가리키는 외래 키 참조다
    • 차원 테이블(dimension table)
      • 각 로우는 이벤트의 속성인 누가(who), 언제(when), 어디서(where), 무엇을(what), 어떻게(how), 왜(why) 를 나타낸다.
    • 테이블 관계가 시각화될 때 사실 테이블이 가운데 있고 차원 테이블로 둘러싸고 있다는 사실에서 이름이 비롯됐다.

     

    눈꽃송이 모양 스키마(snowflake schema)

    • 별 모양 스키마 템플릿의 변형으로, 치원이 하위차원으로 더 세분화된다.
    • 별 모양 스키마보다 더 정규화됐다.

     

    칼럼 지향 저장소

    • 대부분의 OLTP 데이터베이스에서 저장소는 로우 지향 방식으로 데이터를 배치한다.(테이블에서 한 로우의 모든 값은 서로 인접하게 저장된다.)
    • 칼럼 지향 저장소의 기본 개념은 모든 값을 하나의 로우에 함께 저장하지 않는 대신 각 칼럼별로 모든 값을 함께 저장하는 것이다.

     

     

    • 칼럼 지향 저장소의 배치는 각 칼럼 파일에 포함된 로우가 모두 같은 순서인 점에 의존한다.

     

     

    칼럼 압축

    데이터를 압축하면 디스크 처리량을 더 줄일 수 있고, 칼럼 지향 저장소는 대개 압축에 적합하다. 칼럼의 데이터에 따라 다양한 압축 기법을 사용할 수 있는데, 그 중 한 가지 기법은 데이터 웨어하우스에서 특히 효과적인 비트맵 부호화(bitmap encoding)이다.

     

     

    메모리 대역폭과 벡터화 처리

    벡터화 처리(vectorized processing) : 비트 AND와 OR 같은 연사자를 이용하여 압축된 컬럼 덩어리를 바로 연산할 수 있도록 처리하는 방법

     

     

    칼럼 저장소의 순서 정렬

    • 칼럼 저장소에서 로우가 저장되는 순서가 반드시 중요하지는 않다.
    • 각 컬럼을 독립적으로 정렬할 수는 없다.
    • 칼럼별로 저장됐을지라도 데이터는 한 번에 전체 로우를 정렬해야 한다.
    • 장점
      • 그룹화하거나 필터링하는 질의에 도움이 된다.
      • 칼럼 압축에 도움이 된다.

     

     

    칼럼 지향 저장소에 쓰기

    칼람 지향 저장소, 압축, 정렬은 모두 읽기 질의를 더 빠르게 하지만 쓰기는 어렵게 하는 단점이 존재한다. 이러한 단점은 LSM 트리를 활용하여 해결할 수 있다. 모든 쓰기는 먼저 인메모리 저장소로 이동해 정렬된 구조에 추가하고 디스크에 쓸 준비를 한다. 인메모리 저장소가 로우 지향인지 칼럼 지향인지는 중요하지 않다. 충분한 쓰기를 모으면 디스크의 칼럼 파일에 병합하고 대량의 새로운 파일에 기록한다.

     

     

    집계: 데이터 큐브와 구체와 뷰

    • 구체화 집계(materialized aggragate)
      • 질의가 자주 사용하는 일부 카운트나 합을 캐시하는 방법
    • 구체화 뷰(materialized view)
      • 캐시를 만드는 방법으로, 관계형 데이터 모델에서는 이런 캐시를 대개 표준 (가상) 뷰로 정의한다.
      • 원본 데이터의 비정규화된 복사본이기 때문에 원본 데이터를 변경하면 구체화 뷰를 갱신해야 한다.
      • 갱신으로 인한 쓰기 비용이 비싸서 OLTP 데이터베이스에서는 구체화 뷰를 자주 사용하지 않지만, 데이터 웨어하우스는 읽기 비중이 크기 때문에 구체화 뷰를 사용하는 전략이 합리적이다.
      • 데이터 큐브(data cube) 또는 OLAP 큐브라고 알려진 구체화 뷰는 특정 질의를 효과적으로 미리 계산했기 때문에 해당 질의를 수행할 때 매우 빠지만, 원시 데이터에 질의하는 것과 동일한 유연성은 없다.

     

    'STUDY > 데이터 중심 애플리케이션 설계' 카테고리의 다른 글

    8장. 분산 시스템의 골칫거리  (0) 2025.08.31
    7장. 트랜잭션  (0) 2025.08.19
    6장. 파티셔닝  (4) 2025.07.30
    4장. 부호화와 발전  (0) 2025.06.25
    2장. 데이터 모델과 질의 언어  (1) 2025.06.11
Designed by Tistory.