백엔드 공부

데이터 중심 애플리케이션 설계 3장 정리

철매존 2024. 11. 19. 06:36
728x90
반응형
    • 대부분 애플리케이션은 하나의 데이터 모델을 다른 데이터 모델 위에 계층을 둬서 만듬

      • 그래서 데이터 저장과 질의를 위한 다양한 범용 데이터 모델을 살펴본다.

      관계형 모델과 문서 모델

    • Relation

      • 각 관계는 순서 없는 tuple 모임
      • 근원은 비즈니스 데이터 처리(트랜잭션이나 일괄처리)
    • NoSQL

      • Not Only SQL
      • RDB에서 지원하지 않는 특수 질의
      • RDB보다 동적이고 표현력이 풍부한 데이터 모델 요구 등

      객체 관계형 불일치

    • 보통은 개발이 객체지향 프로그래밍으로 이루어짐

    • 임피던스 불일치에의 문제

      • 관계형 <-> 객체 사이의 전환 계층을 필요로 하는것
      • ORM은 이러한 보일러플레이트 양을 줄여주지만 차이를 완벽히 숨길수는 없음

      예시

    • 이력서 같은걸 생각해보면 JSON표현으로 적합

      • JSON을 쓰면 모든 정보가 한곳에 있어서 한번 가져오면 싹다 들고옴
    • 다대일, 다대다 관계

      • 지역이나 회사같은것들은 그냥 평문이 아니라 ID값을 가지고 있으면 여러 장점이 있다.
        • 일관된 문자 저장
        • 모호함 회피(동명의 다른거)
        • 갱신의 편리함
        • 현지화 지원(한 ID에 여러 언어를 저장한다면)
        • 더 나은 검색 가능
      • ID는 변경 필요X
      • documentDB는 조인 지원이 약함(JSON같은거는 위에서 아래를 찾아가기는 쉽지만 하위를 통해 상위를 찾기는 힘드니까)
        • 다대일 관계 적합하지 않다는것
          • 하려면 DB가 아니라 애플리케이션에서 데이터를 가져와서 처리해야한다.
        • 보면 처음에는 Join이 없어도 됐어도 구현하다보면 필요해질수 있다. 어떻게 하지?
          • 네트워크 모델
            • == 코다실 모델
            • 얘는 다중 부모가 있을 수 있다.
              • 아까 이력서같은걸로 생각해보면 지역->그지역사람 전체 이렇게 사람밑에 있으면서 동시에 지역밑에 있는 느낌
                • 요런 식으로 하면 다대일, 다대다 모델링 가능
            • 레코드 접근을 하려면 최상위부터 쫘라락 내려가는 수밖에 없음
            • 딱봐도 변경을 하거나 하면 모든 부모를 찾아가는 복잡한 문제가 있어 유연성 떨어짐
          • 관계형 모델
            • 얘는 우리가 아는 그 RDB같은걸로 단순히 튜플의 컬렉션이 다임
    • 정리하자면 documentDB는 별도 테이블이 아닌 상위 레코드 내에 중첩된 레코드(일대다)를 저장함.

    • 그러나 다대일과 다대다의 경우 RDB와 동일하고, 거기서는 FK라고 부르던걸 여기서는 Document Reference 즉 문서 참조라고 부른다.

      RDB와 요즘의 documentDB

    • 일단 차이만 집중하면

      • documentDB의 장점
        • 스키마 유연성, 지역성에 기인한 더 나은 성능
        • 특정 애플리케이션의 경우 그곳의 데이터 구조와 비슷
      • RDB
        • 조인, 다대일, 다대다를 더 잘 지원함
    • 보통 한번에 데이터를 가져오기만 하는 문서같은 느낌에는 documentDB

    • 다대다같은걸 쓰면 RDB가 좋지

      document의 스키마 유연성

    • JSON은 문서의 데이터에 어떤 스키마를 강요하지 않는다.

    • 문서형데이터베이스는 종종 스키마리스로 불리지만 오해의 소지가 있다.

      • 암묵적인 스키마가 있기는 한데 DB가 이를 강요하지 않음
      • 읽기 스키마 : 데이터 구조는 암묵적으로 데이터를 읽을 때에만 해석된다.
      • 쓰기 스키마 : RDB의 전통적인 방법이고 DB는 데이터가 스키마를 따르고 있음을 보장
      • 그러니까 오브젝트별로 유형이 다르거나, 관계에 의해 변경하는 구조가 아니면 documentDB가 아주 좋음

      질의를 위한 데이터 지역성

    • 애플리케이션이 자주 전체 문서에 접근해야 할 때 저장소 지역성을 활용하면 성능 이점이 있다.

      • 한번에 많은것을 요구한다면 이게 좋은거지 다가져오니까
    • 구글 스패너, 오라클 다중 테이블 색인 클러스터 테이블, 빅테이블데이터모델의 칼럼 패밀리개념 등등

      • 이거 신기하다... 데이터를 저장할 때에 아예 부모 및에 데이터를 저장시키는 식으로 지역성을 확보

      RDB와 documentDB의 통합

    • RDB는 Json지원, document는 관계형 조인 지원

    • 둘은 비슷해지고 있다.

      데이터를 위한 질의 언어

    • SQL : 선언형

    • IMS, 코다실 : 명령형

    • 프로그래밍언어는 보통 명령형이다.

      • 명령형은 보통 순서랑 연산을 수행하도록 컴퓨터에게 지시한다.
    • SQL은 어떻게 가져와라! 하는 방법이 아니라 그 결과에 필요한 조건과 데이터 변환(정렬, 그룹화, 집계)를 지정하기만 하면 된다. -> 선언하는거니까

      • 그러면 최적화는 최적화기가 알아서 해줌^~^
      • 이 장점은 명령형보다 간결하고, 특히 중요한것은 상세 구현이 숨겨져있어 질의 변경 없이 성능 향상이 가능하다는것
      • 병렬 실행에도 적합하다.
        • 얘는 명령의 순서가 있는것도 아니라 그냥 조건대로 가져만 와라 하는거라 병렬에 더 좋음

      웹에서의 선언형 질의

    • CSS같은것도 선언형이다(어디가 어떻게 변경되는걸 원하고 뭐 어찌 되는지는 관심없)

      • 그거를 javascipt의 코어DOM 명령형을 쓰면 삭제되거나 태그 변경 등에 따라 코드를 바꾸어 주어야하거나 코드도 굉장히 길고 난해해진다.

      맵리듀스 질의

    • 이거 자바에서 스트림할때도 봤지

    • 읽기 전용 질의를 수행할 때에 사용된다.

    • 여기서는 몽고DB모델 사용에 대해서

      • 선언형도 아니고 명령형도 아닌 어딘가
      • 처리 프레임워크가 반복적으로 호출하는 조각 코드
      • map, reduce함수 기반
    • 제약사항이 있는데, 순수함수여야한다(입력으로 전달된 데이터만 사용가능, 추가적인 데이터베이스 질의 수행 불가, 사이드이펙트 없음)

      • 요런 제약이 있어서 DB가 임의 순서로 어디서나 이 함수를 실행가능. 또 장애가 있어도 함수 재실행 가능
    • 참고로 이거 좋긴 한데 이것만 쓸수 있는것도 아니고 작성이 어려울수도 있다.

    • JSON기반을 쓰는 집계 파이프라인이라는것도 있다.


    GraphDB

    • 오랜만에 본다...

    • 다대다가 많으면 GraphDB를 쓴다.

    • 그래프디비는 정점(노드 혹은 엔티티)랑 간선(관계 혹은 호)

    • 속성 그래프 모델

      • 네오포제이, 타이탄, 인피니티그래프 등
    • 트리플 저장소 모델

      • Datomic, 알레그로그래프 등

      속성 그래프

    • 정점

      • 고유 식별자
      • 유출 간선 집합
      • 유입 간선 집합
      • 속성 컬렉션(키-값 쌍)
    • 간선

      • 고유 식별자
      • 간선이 시작하는 정점(꼬리정점)
      • 간선이 끝나는 정점(머리 정점)
      • 두 정점 간 관계 유형 설명하는 레이블
      • 속성 컬렉션(키-값 쌍)
    • 정점은 다른 정점과 간선으로 연결된다. 특정 유형과 관련 여부를 제한하는 스키마는 없다.

    • 정점이 주어지면 정점의 유입과 유출 간선을 효율적으로 찾을 수 있고 그래프를 순회할 수 있다. 즉 일련의 정점을 따라 앞뒤 방향으로 순회

    • 다른 유형의 관계에 서로 다른 레이블을 사용하면 단일 그래프에 다른 유형의 정보를 저장하면서도 데이터 모델을 깔끔하게 유지 가능

    • 국가마다 지역 구조가 다른것같은 RDB로 보여주기 힘든것도 얘는 쉽게 보여줄 수 있음

      사이퍼 질의 언어

    • 속성 그래프를 위한 선언형 질의 언어

    • 예를 들어 미국에서 유럽으로 이민 온 모든 사람들의 이름 찾기 를 한다면?

      • person에서 name이 US인 속성을 갖는 location이 될 때 까지 born_in간선을 따라감
      • person에서 name이 eurpe인 속성을 갖는 location이 될 때 까지 live_in간선을 따라감
      • 이거 두개가 MATCH되면 됨
    • 이런 식으로 하면 수행의 상세한 것을 지시하지 않아도 최적화된 방법을 알아서 찾아간다(선언형)

      SQL의 그래프 질의

    • RDB도 가능하다. 근데 어렵다.

    • 재귀 공통 테이블 식? 을 쓴다고 한다..

      트리플 저장소와 스파클

    • 속성 그래프 모델과 거의 동등하다.

    • 이거는 모든 정보를 주어, 서술어, 목적어처럼 간단한 세 부분 구문형식으로 저장한다.

    • 주어

      • 정점과 동등
    • 목적어

      • 문자열이나 숫자 등의 원시 데이터타입의 값
      • 그래프의 다른 정점

      시맨틱 웹

    • 이미 사람이 읽을 수 있는 텍스트와 그림으로 정보를 게시하고 있으니 컴퓨터가 읽게끔 기계가 판독 가능한 데이터로도 정보를 게시하는건 어떨까? 하는 개념

    • 자원기술프레임워크(RDF)는 서로 다른 웹사이트가 일관된 형식으로 데이터를 게시하기 위한 방법 제안

    • 일단 지금은 잘 안쓰기는 한데 좋은 작업이 있음.

      RDF데이터 모델

    • 이거는 결국 인터넷에 있는 정보 교환을 위해 설계하는 것이다.

    • 그래서 주어, 서술어, 목적어는 주로 URL이다.

      스파클 질의 언어

    • 구조적으로 사이퍼랑 비슷

    • 얘는 근데 속성이랑 간선을 따로 구분하지 않고 서술어를 사용하기 떄문에 보기 더 쉽고 동일한 구문 사용 가능

      데이터로그

    • 질의 언어의 기반이 되는 초석을 제공하는 오래된 녀석

    • 얘는 (주어, 서술어, 목적어) 형태의 트리플과는 다르게

    • 서술어(주어, 목적어) 로 작성한다.

      정리

    • 요구사항에 맞춰서 어떤걸 쓸지 찾아보는게 중요

    • NoSQL

      • 문서형 : 서로 연관은 없고 데이터가 문서 자체인 경우 좋음
      • 그래프 : 서로 연관이 있을거라고 잠재적으로 생각하고 ㄱㄱ
### 3색 정리

- 이거는 다른사람도 꼭 알아야 한다.(아주 중요)
    - ID는 아무런 의미가 없기 때문에 변경할 필요가 없다(34p)
        - 예전에 공부했던 내용중 자연키vs인조키 관련인데 이거 은근 중요한 내용이라 생각해서 적었다.
    - 선언형 / 명령형 언어에 대해(43p~)
        - java stream에 대해 처음 배울 때에도 선언형에 대해 배웠는데, 실제로 장점을 알 수 있었다.
        - 그래서 인덱스 생성이랑 질의가 달라도 옵티마이저가 알아서 최적화를 잘 해주는걸까? 싶은 느낌
            - 근데 약간의 블랙박스 너낌
- 이거는 알면 좋을 것 같다.(빨간거 다음으로 중요)
    - 문서형 데이터베이스와 관계형 데이터베이스의 통합(42p)
        - 둘이 점점 비슷해지는게 있어보인다.
            - RDB에 json같은거 넣을 수 있는게 기억난다
                - 그리고 RDB에서도 join이 많은것을 지양해서 집계테이블을 만들고 있는데, 두개를 합치면 되게 좋아보임
                    - 예를 들어 책(좋아요한사람-json, 댓글상위몇명-json, 댓글갯수총합-int 등)
- 이거는 내가 개인적으로 흥미로웠다.(마음에 들었다)
    - 데이터 지역성(41p ~)
        - 아예 저장할때 부모 밑에 넣어버리는 방식인게 신기했다.
            - 근데 이거는 좀 궁금한게 몇개 있는데
                1. 여러 부모에게 소속되는 경우(예를들어 악어가죽 → 가방, 지갑 등에서 사용 등..)
                    1. 이런거는 가장 많이 사용되는 부모 아래에 일단 두는게 맞을것같기도 한데 모르겠다.
                2. 만약 변경된다면??
                    1. 내가 이직을 하면 현대차→류찬 에서 카카오→류찬 이렇게 바뀔텐데 RDB의 업데이트는 주소를 그대로 두고 변경하지 않나 싶어서 지역성이 깨지지는 않을까? 싶었다. 
  • 3장

    DB는 이제 데이터를 저장하고, 보여주는 역할을 수행하는게 기본 골자이다.

    데이터 구조

    • 많은 데이터베이스가 내부적으로 추가 전용 데이터 파일인 로그를 사용한다. (append-only)

    • 근데 이 로그를 찾는 방법은 데이터를 찾기 위해 전체를 다 둘러봐야 한다. (O(n))

      • 이거를 해결하기 위해 도입된 것이 바로 색인이다.
        • 질의 성능을 높이기 위해 데이터를 찾아가는 메타데이터
        • 참고로 이거를 만드는게 뭐 DB동작방식을 바꾸는건 아니다. 근데 데이터 저장시 성능을 늦추기는 한다(메타데이터를 만들어야 하므로)
          • 즉, 저장 성능을 낮추지만 검색 성능을 놓임.
            • 이 색인도 종류가 많으니까 내 상황에 맞춰 선택을 잘 해야 할 것이다.

      해시 색인

    • 키-값

    • 디스크 상 데이터 색인을 위해 인메모리 데이터 구조를 사용

      • 해시 자체는 이미 인메모리에 있으므로
    • 간단하게 말하면 키를 데이터파일 바이트오프셋에 매핑하는것.

      • 그래서 새로운 데이터가 추가되면 이것도 갱신해야한다.
      • 검색은 여기 키를 통해 간단히 검색 가능
    • 비트캐스트(리악의 기본 저장소엔진)에서 기본적으로 이걸 쓰는데 해시맵을 전부 메모리에 유짛서 RAM에 모든 키가 저장된다는 조건을 전재로 고성능 읽기, 쓰기를 보장함.

      • 이런거는 키값이 자주 갱신되는 상황에 유리하다.
        • 즉, 새로운 데이터가 추가된다기 보다는 기존에 있는 데이터가 갱신되는 상황에 유리한것.
    • 계속해서 새로운게 추가된다면? 인메모리에 저장하는 경우 크기가 부족해질 것이다.

      • 이를 해결하기 위해 세그먼트를 도입한다.
        • 세그먼트별로 저장하는거 -> 꽉차면 새로운 세그먼트를 파서 거기다가 저장함
          • 컴팩션을 통해 세그먼트 병합하고 그게 끝나면 이전꺼를 버리면서 새로 만들어진 세그먼트를 쓰도록 한다.
            • 그러면 이거는 모든 데이터에 대해 이루어져야겠네? 동시성 문제에서는 안전할까?
    • 근데 해시를 쓰면 고려해야 할 것이 많음

      • CSV아니고 원시문자열을 부호화하는 바이너리 형식이 빠름
      • 레코드가 삭제된다면? 해시에서 삭제 표시를 적용해야한다.
        • 이렇게 해야 이전 값을 무시하고 진행가능.
      • 고장복구
        • 해시는 인메모리값에 있는데 재시작되면?
        • 다시 전체 세그먼트 확인하면서 해시맵 만드는건 말안되니까 다른 방법을 씀
          • 각 세그먼트 해시맵 스냅숏을 디스크에 만들어두고 이를 통해 해시맵 복구
            • 이렇게 하면 그냥 그 해시맵으로 만든는거라 그나마 간단
      • 부분적으로 레코드 쓰기
        • 로그에 데이터 쓰던 도중에 DB가 죽어버리면?
          • 체크섬을 포함하여 로그의 손상된 부분 탐지 무시 가능
            • 송신자
              • 모든 데이터 단위를 1의보수 연산(모든 비트 뒤집기)으로 합을 구함
              • 다시 그 합의 1의보수 연산으로 checksum 생성. 요걸 같이 전송
            • 수신자
              • 수신된 메세지그램을 데이터로 나눔(여기에는 체크섬도 포함)
              • 그리고 고거를 1의 보수 연산으로 더해서 합을 구함
              • 합이 0이면 성공적으로 수신된거임
            • 여기서 합이 0이 아니면 뭔가 문제가 있는거
      • 동시성 제어
        • 데이터 파일 세그먼트는 추가 전용이거나 불변이라 다중 스레드로 동시 읽기 가능
        • 쓰기는 하나의 쓰기 스레드만 사용
    • 이렇게 해시맵을 추가해가면서 추가전용을 쓰는 이유는 장점이 있는데

      • 당연히 속도가 빠름 -> 1515619874861651 이렇게 숫자 아무거나 쓰면 뇌빼고 쓰면 되는데 써가면서 혹시 앞에 있나? 있으면 그거 바꿔서 써야지 하면 느려지잖어
      • 세그먼트 파일이 쓰기전용이거나 불변이면 동시성, 고장 복구 측면에서 훨씬 간단
        • 값을 덮어쓰는 과정에서 DB가 죽는다 하면 그 뒤부터 다시 하면 되니까
      • 데이터 조각화 회피 가능(이거는 계속 추가하고 이전꺼를 지우면 되니까)
    • 당연히 단점도 있다.

      • 결국 메모리에 저장하니까 키가 너무 많으면 문제가된다.
        • 디스크에 저장하면 속도가 느려지니까 어지간하면 메모리에 저장하는거
      • 범위해시에 비효율적
        • 어차피 범위로 가져오면 값이 무엇이든 걔들은 append-only라 다른 위치에 저장된다.
        • 각자 가져와서 각자 찾아야함

      SS테이블과 LSM트리

      개념 및 읽기 장점

    • 위의 저 데이터 세그먼트를 한번 키로 정렬해보자.

    • 이런 식으로 정렬된 애들을 SS테이블 - Sorted String Table이라고 한다.

    • 각 키는 병합된 세그먼트 파일 내에 한번만 나타나야 한다.

      • 위에서 배운 컴팩션이 이를 이미 보장
    • 이게 그냥 해시 색인에 비해 가지는 장점들은

      • 세그먼트 병합은 메모리가 커도 간단하고 효율적이다.
        • 일단은 이거는 세그먼트 자체가 이미 키값을 통해 정렬되어 있다는 것을 전재로 생각하자.
        • 병합정렬이랑 유사하게
          • 입력 파일을 읽고
          • 각 파일의 첫번째 키를 확인(정렬 순서에 따라서 확인)
          • 그리고 순서대로 확인하면서 값을 저장
            • 여기서 만약 같은 데이터를 가진 경우 있는거 걍 씀(최신거를 유지 예전꺼 버림)
      • 특정 키를 찾기 위해 모든 키의 색인을 유지할 필요가 없음.
        • 이미 정렬이 되어있으니까 모두 가질 필요는 없고 드문드문 색인이 있으면 그 사이를 찾아가면 됨(이것도 충분히 빠름)
      • 읽기 요청은 요청 범위 내에서 여러 키-값 쌍을 스캔해야한다.
      • 레코드들을 블록으로 그룹화하고 디스크 쓰기 전에 압축 -> 희소 인메모리 색인의 각 항목은 압축된 블록의 시작을 가리킨다.
        • 이거는 잘 이해가 않는데... 해시 색인이 있는 애들을 기준으로 그 내부를 압축한다는 것일까?

      데이터 정렬은?

    • 정렬 방식

      • 레드블랙트리같은거 이용하면 쉽게 가능
      • 값이 들어오면 이런 트리형태(멤테이블)로 하나씩 데이터를 추가하다가
      • 임계값보다 커지면 SS테이블 파일로 디스크에 저장 -> 요게 최신 세그먼트가 된다.
        • 이거 디스크에 기록하는 동안 새로운 맴테이블 인스턴스에 쓰기 기록하는거임.
      • 읽기 요청이 들어오면
        • 지금 기록중인 맴테이블
        • 최신 세그먼트
        • 점점 뒤로 가면서..
        • 찾는다.
      • 가끔 백그라운드에서 컴팩션
    • 잘 동작하기는 한데... 한가지 문제가 있다.

      • DB가 고장난다면 디스크로 저장되지 않은 맴테이블 최신 쓰기가 손실된다는것!
        • 그래서 이를 해결하기 위해 매번 쓰기를 즉시 추가할 수 있게 분리된 로그를 디스크 상에 유지해야 한 다.
          • 참고로 이 로그는 복원할 때에만 필요해서 딱히 순서 정렬은 필요없다.
            • 그래서 맴테이블이 디스크로 저장되면 로그 버리기 가능

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

    • LSM(log-Structured Merge-Tree)

    • 정렬된 파일 병합과 컴팩션 원리를 기반으로 하는 저장소 엔진

    • 전문 검색 색인 엔진 -> 참고로 전문성 그게 아니라 ㄹㅇ전문이다. 여기있는거랑 같음

    • 검색 질의로 단어가 들어오면 언급된 모든 문서(웹페이지, 제품 설명 등)에서 찾는다.

      • 키를 단어(용어)로, 값은 단어를 포함한 모든 문서의 ID목록으로 하는 키-값 구조로 표현
        • 이게 혹시 그 인덱스에 있던 불용어처리(가치없는거 필터링)이랑 어근 분석을 통해 찾는걸까?
        • 아니면 한글에서는 n-gram인가 싶은데 아예 문자를 몇글자씩 잘라서 인덱싱하기인가
    • 이렇게 만들어진 색인을 SS테이블에 저장하고 보는 느낌인듯

      성능 최적화

    • LSM의 경우 실제로 없는 키를 찾는다면 굉장히 느릴 수 있다.

      • 맴테이블 -> 디스크에 있는 가장 오래된 세그먼트까지 가야하므로.
    • 그걸 해결하기 위해 도입된 것이 블룸필터(Bloom filter)

      • 집합 내용을 근사하여 키가 DB에 존재하지 않음을 알려주어 불필요한 디스크 읽기 절약 가능
    • 위에서 있던 SS테이블의 압축/병행의 순서와 시기를 결정하는 전략으로는

      • 크기 계층 컴팩션
        • 상대적으로 좀 더 새롭고 작은 SS테이블을 상대적으로 오래됐고 큰 SS테이블에 연이어 병합
      • 레벨 컴팩션
        • 키 범위를 더 작은 SS테이블로 나누고 오래된 데이터는 개별 레벨로 이동하기 때문에 컴팩션을 점진적으로 진행해 디스크 공간 덜 사용
          • 이게 대체 무슨 뜻일까.. 찾아봤는데 자바의 가비지 컬렉션이랑 비슷한 느낌으로 이해했다. -> 개념적으로 같다보다는, 사용 삔도 등에 따라 나누는 점이
          • 쓰임에 따라 레벨로 나누어 두고, 그 레벨이 특정 크기에 도달하면 아래 레벨과 컴팩션하는것
            • 이렇게 하면 점진적 컴팩션이 가능하다.
    • 일단 LSM의 기본 개념은 백그라운드에서 연쇄적으로 SS테이블을 지속적으로 병합하는것.

      • 데이터셋이 가능한 메모리보다 훨씬 더 크더라도 효과적이다.
      • 데이터가 정렬된 순서로 저장돼있다면 범위 질의 효율적 실행 가능
    • 매우 높은 쓰기 처리량 보장!!

      • 디스크 쓰기가 순차적이기 때문 -> 뒤에서 색인 생성 및 병합하니까?

      B트리

    • 가장 널리 사용되는 색인 구조

    • 요거 설명은 여기 잘 나와있음

    • 참고로 저장할 때에 페이지가 부족하면 그걸 반쯤 채워진 페이지로 나누고 상위 페이지가 하위 페이지 내용을 알 수 있게 해둠

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

    • 이거는 덮어쓰기(변경)할 때에 페이지 위치가 변경되지 않는다고 가정한다.

      • 즉 페이지 덮어쓰기를 해도 참조가 온전히 남음
        • 요게 LSM같은 친구와의 아주 큰 차이이다.
      • 근데 만약에 페이지를 변경할 때에(막 여러 데이터를 변경하는 도중에) DB가 고장난다면?
        • 색인이 훼손될 수 있다.
          • 이거를 스스로 복구할 수 있게 하려면 디스크 상에 쓰기 전 로그(WAL - 재실행 로그)라는 데이터 구조를 추가해 구현한다.
            • 재실행 로그란 트리 페이지에 변경된 내용을 적용하기 전에 모든 B로그 트리의 변경사항을 기록하는 추가 전용 파일
            • 이걸 통해 고장 이후 복구에서 일관성 있는 상태로의 복원 가능
    • 그럼 만약에 하나의 데이터에 여러 스레드에서 동시에 접근한다면?

      • 동시성 제어를 위해 latch를 사용하여 데이터 구조 보호

      B트리 최적화

    • 페이지 덮어쓰기와 고장 복구를 위해 쓰기 전 로그가 아니라 쓰기 시 복사를 채택한다.

      • 변경된 페이지는 다른 위치에 기록하고 상위 페이지의 새로운 버전을 만들어 새로운 위치를 가리키게 함.
        • 동시성 제어에 유용한 이유는? -> 기존에 데이터를 변경하지 않고 그걸 읽은 후에 새로운걸로 만드니까 어떻게 접근해도 가장 최신꺼를 가져오면 기존이 최신으로 변경된 것일거다.
    • 페이지에 전체 키 저장이 아니라 키를 축약해서 쓰면 공간 절약이 가능 -> B+트리가 이렇다고 한다.

      • 키 범위 사이의 경계를 하게끔만 저장하면 된다.
      • 참고로 페이지 하나에 키를 더 많이 채우면 더 높은 분기 계수를 갖고, 트리 깊이 수준을 낮출 수 있음.
    • 페이지는 디스크 상 어디에나 위치 가능.

      • 굳이 키 범위가 가까운 애들끼리 있을 필요는 없다는것.
      • 다만 키 범위의 상당 부분을 스캔해야하면 모든 페이지에 대해 디스크 찾기가 필요
        • 너무 막 디스크 단위로 하는건 비효율적이다.
          • 그래서 일단은 연속된 순서로 나타나게 트리를 배치하려고 한다.
            • 트리가 커지면 순서 유지가 어렵다. 이게 LSM이랑은 다른점인거다. LSM은 병합 과정에서 저장소의 큰 세그먼트를 한번에 다시 쓰므로 디스크 상에서 연속된 키를 유지하기 쉬움
    • 트리에 포인터 추가.

      • 각 리프 페이지가 양쪽 형제 페이지에 대한 참조를 가지면 상위페이지를 가지 않고 이동 가능.
    • 프랙탈 트리같은 B트리 병합은 디스크 찾기를 줄위기 위해 로그 구조화 개념을 일부 빌림 -> 뭐여 이게

      B트리와 LSM트리 비교

    • B트리가 더 오래돼서 성숙됨. 그래도 LSM도 관심을 받고있다.

    • LSM은 보통 쓰기에서 더 빠르구 B트리는 읽기에서 더 빠르다고 여겨진다.

      • LSM이 읽기에서 느린 이유는 각 컴팩션 단계에 있는 여러 데이터 구조와 SS케이블 확인이 필요해서

      LSM의 장점

    • B트리 색인은 단점

      • 데이터 조각을 최소 2번 기록
        • 쓰기전로그 한번
        • 트리 페이지에 한번
        • 장애 발생을 대비해 동일한 페이지를 두번 덮어쓰는 경우도 존재
      • 해당 페이지 내 몇 바이트만 바껴도 한 번에 전체 페이지를 기록해야 하는 오버헤드(이건 왜????)
    • LSM도 SS의 반복된 컴팩션과 병합으로 여러번 데이터를 다시씀.

      • 이런 한번의 쓰기가 DB수명동안 디스크에 여러번의 쓰기를 야기하는걸 쓰기증폭이라 한다.
      • 쓰기가 많은 애플리케이션에서 성능 병목은 데이터베이스가 디스크에 쓰는 속도일 수 있다.
        • 쓰기 증폭이 바로 성능 비용인데, 저장소 엔진이 디스크에 기록할수록 디스크 대역폭 내 처리 가능한 초당 쓰기가 점점 줄어듬
      • LSM은 B트리에 비해 쓰기 증폭이 낮고 순차적으로 컴팩션된 SS테이블 파일을 써서 B트리보다 빠르다(순차적으로 컴팩션되면 임의쓰기보다 빠를테니)
    • 또 LSM은 B트리보다 압축률이 더 좋다.

      • 그래서 B트리보다 디스크에 더 적은 파일을 생성한다.
      • B트리는 파편화로 인해 사용하지 않는 디스크 공간이 남는다.
      • LSM은 지속적인 SS테이블 재기록으로 저장소 오버헤드가 더 낮다.
        • 특히 레벨 컴팩션을 사용하면 (level순서로 새로 쓰인 친구들부터 컴팩션을 진행하고, 이러면 일정 크기까지는 최신애들이 순차를 유지할테니)
    • SSD에서 이런 낮은 쓰기 증폭과 파편화 감소는 훨씬 유리하다.

      • 데이터를 더 밀집해 표현하면 가능한 I/O대역폭 내에서 더 많은 읽기와 쓰기 요청 가능

      LSM의 단점

    • LSM의 단점

      • 컴팩션 과정이 진행중인 읽기와 쓰기 성능에 영향을 줄 수 있다.
        • B트리는 이거를 예측하기 쉬움
      • 또 다른 문제는 높은 쓰기 처리량에서 발생한다.
        • 디스크의 쓰기 대역폭은 유한한데 초기 쓰기[ 로깅과 맴테이블을 디스크로 방출하는 일련의 흐름 ]와 백그라운드에서 수행되는 컴팩션 스레드가 이 대역폭을 공유해야 한다.
          • 즉 DB에 데이터가 많아질수록 -> 데이터베이스가 커질수록 컴팩션을 위한 디스크 대역폭 필요
            • SS기반 테이블은 이런 경우에도 유입 쓰기 속도를 조절하지 않아서 모니터링이 필요하다.
    • 여기서 B트리의 장점은

      • 각 키가 색인의 한 곳에만 정확하게 존재한다는 것.
      • 트랜잭션 시맨틱 제공 DB는 B트리가 훨씬 매력적이다.
        • B트리 색인에서는 트리에 직접 잠금 포함(지금은 이해X)

      기타 색인 구조

    • 세컨더리 인덱스

      • 얘는 기본키 색인과 달리 키가 고유하지 않음. 즉 같은 키를 가진 많은 로우가 있을 수 있다는 것이다.
      • 해결 방법은
        • 각 값에 일치하는 로우 식별자 목록을 만들기
        • 로우 식별자를 추가해 각 키를 고유하게 만드는 방법
      • 해결 방법은 무엇이 되었든 B트리, LSM모두에서 사용 가능

      색인 안에 값 저장

    • 색인에서

      • 키 : 질의가 검색하는 대상
      • 값 : 질문의 실제 row일수도, 다른 곳에 저장된 row일수도 있다.
        • 여기서 다른 곳에 저장된 row에의 값인 경우 로우가 저장된 곳을 heap file이라 하고 특정 순서 없이 데이터 저장
          • heap 영역은 어디서든 접근이 가능하고 저걸로 위치를 참고하게 한다면 중복도 피할 수 있기 때문
            • 이거는 키를 변경하지 않고 값을 변경할 떄에 효율적이다.
            • 참고로 많은 공간을 필요로 하는 경우 힙에서(여기는 파편화가 있으니까) 다른 곳으로 찾아가야해서 곤란해진다.
            • 그래서 그냥 인덱스 자체에서 색인된 로우를 저장하는것이 좋을때가 있다.
              • MySQL에서 클러스터링 인덱스가 기본으로 존재하는것도 이와 비슷한 것.
              • 그래서 MySQL에서는 secondary index가 기본키를 참조한다.
                • 참고로 클러스터드인덱스(색인 안에 모든 로우 데이터 저장)이랑 비클러스터드인덱스(데이터 참조만 저장) 사이의 인덱스를 커버링 색인이나 포괄열이 있는 색인이라고 한다.
                  • 색인 안에 테이블 컬럼 일부 저장.
                    • 이렇게 하면 색인만 사용해 일부 질의 응답 가능(그래서 커버한거라 커버링)
                • 클러스터드 색인이랑 커버링 색인은 읽기 성능을 높일 수 있지만 추가적인 저장소가 필요하고 쓰기 과정 오버헤드가 발생한다.

      다중 칼럼 색인

    • 결합 색인

      • 하나의 키에 여러 필드를 단순히 결합.
    • 다차원 색인

      • 한 번에 여러 칼럼에 질의하는 조금 더 일반적인 방법
      • B트리, LSM트리 모두 여기에 효율적 응답 불가.
      • R트리같은거 쓰면 됨

      전문 검색과 퍼지 색인

    • 지금까지의 색인은 정확한 데이터 대상으로 키의 정확한 값이나, 정렬된 키의 값의 범위를 질의할 수 있다고 가정한다.

    • 근데 철자가 틀린 등의 유사한 것은?

      • 보통 전문검색은 동의어로 질의를 확장한다.
        • 요런거는 보통 작은 인메모리 색인이 필요함.

      모든 것을 메모리에 보관

    • 여기서 설명한 것들은 디스크 한계에 대한 해결책이었다.

    • 디스크는 메인메모리와 비교해 다루기 어렵다.

    • 다만 디스크의 장점은

      • 데이터의 지속성(전원이 꺼져도 데이터 유지)
      • 가격이 쌈
    • 근데 램이 점점 저렴해져서 인메모리 데이터베이스도 개발됐다.

    • memcached같은거는 장비 재시작시 데이터 손실을 허용하는 캐시로 사용

    • 근데 그렇지 않은 인메모리 DB들이 많음.

      • 그 방법은 특수 하드웨어 사용, 디스크에 변경사항 로그 기록, 디스크에 주기적 스냅숏 기록, 다른 장비에 인메모리 상태 복제 등등이 있음.
    • 인메모리DB의 장점은 디스크에서 읽지 않아도 되기 때문에 빠른것만은 아님(뭐 디스크도 그냥 메모리에 두고 쓰면 된다고는 하는데, 이건 좀..)

      • 레디스의 우선순위 큐와 셋같은 데이터 구조를 제공
    • 안티 캐싱

      • 메모리가 충분하지 않을 때 가장 최근에 사용하지 않은 데이터를 메모리에서 디스크로 내보내고 나중에 다시 접근할 때에 가져오는 방식
      • 이것도 근데 어쨌든 전체 색인이 메모리에 있어야 한다.

      트랜잭션 처리나 분석

    • 데이터베이스가 많은 종류의 데이터를 사용하기 시작했지만 기본적인 접근 패턴은 비즈니스 트랜잭션 처리와 유사하다.

      • 애플리케이션은 색인을 사용해 일부 키에 대한 적은 수의 레코드를 찾는다.
      • 레코드는 사용자 입력을 기반으로 삽입/갱신된다.
      • 이런거는 대화식으로 온라인 트랜잭션(OLTP)라고 한다.
    • 데이터 분석에도 DB를 많이 쓰는데, 이거는 많은 레코드를 스캔해 일부 칼럼만 읽어 집계통계(합, 카운트, 평균 등)를 계산해야 한다.

      • 이런거를 보통 온라인 분석 처리(OLAP)이라고 한다.
    • OLTP랑 OLAP는 처음에는 동일한 방법으로 쓰이다가 나중에는 OLTP를 분석 목적으로는 쓰지 않았다.

      • 분석용으로 쓰는 개별 데이터베이스는 데이터 웨어하우스라고 불림

      데이터 웨어하우스

    • 간단하게 말하면 OLTP는 높은 가용성과 낮은 지연시간을 갖기를 원하므로, 여기 방해되지 않게 읽기 전용 공간에서 마음껏 질의할 수 있게 해둔게 데이터 웨어하우스임.

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

    • 데이터 웨어하우스의 데이터 모델은 가장 일반적인 관계형 모델을 사용한다.

    • 표면적으로는 둘다 SQL질의를 지원해서 비슷해 보인다.

      • 근데 이거는 질의 패턴이 분석에 맞춰있어서 내부가 완전 다름

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

    • 별 모양 스키마

      • 차원 모델링이라고도 함
      • 많은 데이터 웨어하우스에서 사용
      • 각 로우는 이벤트를 의미하고
        • 누가, 언제, 어디서, 무엇을, 어떻게, 왜 라는 이벤트 속성을 각 로우에서 나타냄.
      • 그래서 가운데에 사실테이블이 있고 주변에 별처럼 차원테이블로 둘러쌓여 있어서 별모양임
    • 눈꽃송이 모양 스키마

      • 차원이 하위차원으로 더 세분화된다.

      칼럼 지향 저장소

    • 사실 테이블은 보통 칼럼이 100개 이상이지만 일반적인 데이터 웨어하우스 질의는 한번에 4개 또는 5개 칼럼만 접근함.

      • 이유는 아마 분석을 위해서는 필요한 정보만 계산해서 가져오면 되니까 다른 정보는 불필요해서
    • 대부분의 OLTP데이터베이스는 로우 지향 방식으로 데이터를 배치한다.

      • 즉 한 로우의 모든 값은 서로 인접하게 저장되는것(documentDB와 유사)
    • 칼럼 지향 저장소의 기본 개념은 간단한다.

      • 모든 값을 하나의 로우에 저장하지 않는 대신 각 칼럼별로 모든 값을 함께 저장하는것.
      • 각 칼럼을 개별 파일에 저장하면 질의에 사용되는 칼럼만 읽고 구분 분석하면 된다.

      칼럼 압축

    • 압축하면 디스크 처리량 줄일 수 있다.

    • 비트맵 부호화

      • 보통 고유값의 갯수는 로우 수보다 적다(판매 갯수는 수십억이어도 제품수는 그만큼 안되는)
      • 그러면 n개의 고유값을 가진 칼럼을 n개의 비트맵으로 변환 가능
        • 1번이3번, 2번이10번, 3번이1번 판매 ....
    • 근데 이거 조건이 여러개로 검색하면 어찌되는겨

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

    • 이거 보면 수백만 로우를 스캔해야 하는데, 이러면 디스크에서 메모리로 가져오는 대역폭이 큰 병목이다.

    • 솔직히 이해 못하겠음...

      칼럼 저장소의 순서 정렬

    • 로우가 저장되는 순서가 반드시 중요하지는 않다.

      • 삽입된 순서로 저장하는게 가장 쉽다.
    • 다만 이전의 SS테이블처럼 순서를 도입해 이를 색인 메커니즘으로 사용할 수 있다.

      • 칼럼별로 저장해도 데이터는 한번에 전체 로우를 정렬
        • 예를 들어 질의가 지난달 같은걸 한다면 1차 정렬키를 date로 하는게 맞는것처럼
          • 질의 확인 속도에도 도움됨
    • 이거는 압축에도 도움이 된다.

      • 기본 정렬 컬럼에 고유값을 많이 포함하지 않는다면 보통은 같은 값이 연속해서 길게 반복된다.
        • 그러면 압축이 쉬움(비트맵 보면)
    • 첫 번쨰 정렬 키에서 가장 강력하다(두번째, 세번째는 아무래도 반복된 값이 적으므로)

      다양한 순서 정렬

    • 다양한 질의는 서로 다른 정렬 순서의 도움을 받는다.

      • 그럼 같은 데이터를 다양한 방식으로 정렬한다면??
        • 칼럼 지향 저장에서 여러 정렬 순서를 갖는건 로우 지향 저장에서 여러 2차 색인을 갖는거랑 비슷함.
          • 차이는 칼럼저장에서는 데이터를 가리키는 포인터가 없고 값을 포함한 칼럼만 존재한다는것.

      칼럼 지향 저장소에 쓰기

    • 데이터 웨어하우스의 대부분 작업은 분석가가 수행한다.

    • 칼럼 지향 저장소, 압축, 정렬은 읽기를 더 빠르게하지만 쓰기를 어렵게 하는 단점이 있다.

    • 압축된 상태에서 B트리의 제자리 갱신은 불가능

      • 정렬된 테이블의 중간에 삽입하면 모든 칼럼 파일 재작성
    • LSM트리라는 해결책이 존재

      • 먼저 인메모리 저장소로 이동해 정렬된 구조에 추가.
        • 이후 디스크에 쓴다.
          • 이 떄 인메모리 저장소가 로우지향인지 칼럼지향인지는 중요하지 않음.
            • 충분히 쓰면 디스크의 칼럼 파일에 병합하고 대량으로 새로운 파일에 기록

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

    • 칼럼 저장소는 즉석 분석 질의에 대해 상당히 빨라서 급속하게 인기를 얻고 있다.

    • 구체화 집계

      • 보통 데이터 웨어하우스 질의는 집계함수를 포함한다.
        • 동일한 집계를 다양한 질의에서 사용하면 캐싱하는것이 좋다.
    • 구체화 뷰

      • RDB모델에서 이런 캐시를 대개 표준 (가상) 뷰로 정의한다.
        • 테이블같은 객체로 일부 질의 결과 저장.
          • 그래서 원본 데이터 변경시 구체화 뷰의 변경이 필요하다.
          • 이거 딱 회사에서 RDB에서 쓰는 집계테이블이랑 비슷하네. 다만 그게 즉시갱신되는.
    • 데이터 큐브

      • OLAP큐브라고도 함
      • 큐브라는거에서 알다시피, 보통은 사실이 2차원 이상인데(서로 연관된것들이 많은) 요거를 잘 계산해서 저장하고 쓰는거임!
      • 단점은 유연성이 없는것
        • 예를 들어 10달러 기준, 11달러 기준, 12달러 기준... 요렇게 한다면 여기서 찾기는 불가능하지.

      정리

    • DB저장 시 발생하는 것. 그리고 데이터 질의에서 DB가 수행하는 작업

    • 저장소 엔진

      • 트랜잭션 처리 최적화(OLTP)
        • 사용자 대면이라 대량 요청 받기 가능.
        • 보통은 작은 수의 레코드를 다루는게 좋다(부하문제)
        • LSM : 파일 추가와 오래된 파일의 삭제만 허용. 한번 쓰면 갱신없음.
        • B트리 : 제자리 갱신 관점에서 겊어쓰기 가능한 고정 크기 페이지 셋으로 디스크를 나눔.
      • 분석 최적화(OLAP)
        • 비즈니스 분석가가 주로 사용
        • OLTP에 비해 적은 수의 질의를 다루지만 다루기 까다롭고 레코드를 많이 타고있는 것을 가져오는것.
    • 어떤 상황에서 어떤걸 쓰고, 무엇이 이득인지를 잘 파악하자.

      3색 정리

    • 이거는 다른사람도 꼭 알아야 한다.(아주 중요)

      • 색인에서 힙 파일로 다시 이동하는 일은 읽기 성능에 불이익이 너무 많기 때문에 어떤 상황에서는 색인 안에 바로 색인된 로우를 저장하는 편이 바람직하다. 이를 클러스터드 색인이라고 한다.
        • 결국 이것도 읽기의 성능을 위해 도입된 느낌
          • 그리고 여러 인덱스가 존재할 때에 이를 바라보게 하는 것이 클러스터드 색인인지 힙인지도 차이가 큰 느낌이 든다.
            • 개인적으로는 힙을 쓰는 장점을 잘 모르겠기는 한데…? 클러스터드를 쓰면 갱신 문제나 확인 문제가 적지 않을까 싶다.
    • 이거는 알면 좋을 것 같다.(빨간거 다음으로 중요)

      • 레벨 컴팩션은 개별 레밸로 이동하기 때문에 디스크 공간을 덜 사용한다(p81)
        • 이렇게 하면 최근에 정렬된 애들이 순차성을 보장할 수 있다는 장점도 있는데, 그 덕분에 이후에 성능에도 영향을 끼친다는 점에서 중요한 것 같았다.
      • 다중 스레드의 접근에서의 레치 p84~85
        • java에서도 레치가 있는데, 여기서 어떤 식으로 이걸 통해 동시성을 제어할까 생각해 보았다.
          • 결국은 한번에 하나의 데이터에만 접근할 수 있도록 처리해둔것
            • 그렇다면 삭제-후-재기입 이전까지 다른 스레드에서는 데이터의 검색 자체도 불가능하지 않을까?
              • 아 그래서 LSM의 쓰기가 B트리보다 좋은 거구나!! 하는 느낌이 들었다.
                • 걔는 그냥 계속 써가면서 작성 시간에 따라 데이터를 보면 되므로.
          • 근데 DB에서 그냥 여러 쓰레드로 동시접근하면 여러 변환이 되던데 그건 뭐지?
    • 이거는 내가 개인적으로 흥미로웠다.(마음에 들었다)

      • 블룸 필터 을 쓰면서 해시의 문제를 해결한것 p81 (결국 존재하는지를 확인하는것)
        • 뭔가 로그 → 해싱 → 해시정렬 → 디스크저장 → 블룸필터 이렇게 흐름적으로 문제를 해결해 나가는게 굉장히 흥미롭고 재밌었다.
      • 인메모리 관련 p91
        • 진짜 persistence가 가능해지면 엄청나게 좋을것같은 느낌이 든다.
          • 사실 근데 문제가 없다는 가정하에 그냥 캐싱만 해도 상관없기도 하고 암튼 그럼
    • 궁금한 점…

      • p86에서 왜 B트리는 몇 바이트만 바뀌어도 한번에 전체 페이지를 기록해야 하는거지??
        • 그냥 페이지 분할을 말하는걸까?
      • 데이터 웨어하우징 p94는 결국 기존DB가 아닌 새로운 곳에서 데이터를 통해 분석하는 것이라는 생각.
        • 근데 master - slave를 봐도 둘간 데이터 불일치는 무조건 발생할 것 같은데 이를 어떻게 해결했을까?
          • master - slave는 결국 중요한 데이터를 볼때 master를 보도록 한다고 듣긴했는데
          • 5장에 있던 그 파티셔닝일까? → 그런데 이건 결국 실제 서비스용이 아니라 분석용인듯
반응형