2장 학습 목표 : 다양한 범용 데이터 모델을 살펴 본다. 특히 관계형 모델, 문서 모델, 그래프 기반 모델을 비교하고 다양한 질의 언어를 살펴 보고 사용 사례도 비교한다.
데이터 모델은 소프트웨어가 어떻게 작성되었는지, 해결하려는 문제를 어떻게 생각해야 하는지를 반영해야 한다. 대부분의 어플리케이션은 하나의 데이터 모델을 다른 데이터 모델 위에 계층을 둬서 만든다. 각 계층의 핵심적인 문제는 하위 계층 관점에서 데이터 모델을 표현하는 방법이다. 이 때 명확한 데이터 모델을 제공해서 추상화를 통해 하위 계층의 복잡성을 숨긴다.
관계형 모델과 문서 모델
관계형 모델
관계형 모델은 컴퓨터에 의한 비즈니스 데이터 처리(트랜잭션 처리, 일괄 처리)를 하면서 등장했다. 정리된 인터페이스 뒤로 구현 세부 사항을 숨길 수 있었다.
NoSQL의 탄생
- RDB에서는 지원하지 않는 특수 질의 동작을 수행할 수 있다.
- 관계형 스키마의 제한에 대한 불만과 더욱 동적이고 표현력이 풍부한 데이터 모델에 대한 바람에 의해 등장했다.
=> 어플리케이션은 서로 요구사항이 상이한데 미래엔 관계형 데이터 베이스와 비관계형 데이터스토어를 함께 사용하게 될 것이다. (= 다중 저장소 지속성 (polygot persistency))
객체 관계형 불일치
어플리케이션에서 자주 쓰이는 객체 지향 프로그래밍 언어 + RDB 테이블의 조합은 전환 계층을 필요로 한다. 데이터베이스 시스템(테이블, 행, 열)과 프로그래밍 언어(객체, 배열, 리스트) 간의 데이터 모델이 불일치하기 때문이다. 이걸 임피던스 불일치(impedence mismatch)라고 부른다. 최근 ORM이 이를 최소화 해주는 역할을 해주기는 하지만 두 모델 간의 차이를 완벽히 숨길 수는 없다.
어플리케이션이나 기능마다 더 적합한 데이터 모델이 존재한다. 예를 들어 링크드인 프로필은 문서라서 관계형 데이터 모델보다 JSON 표현이 더 적합하다. 문서 지향 데이터베이스에서는 JSON 모델을 지원한다. 이를 JSON으로 표현 시 다중 테이블 스키마보다 더 나은 지역성을 가진다. 데이터를 더 적극적으로 묶을 수 있기 때문이다. 관계형에서 프로필을 가져오려면 다중 질의를 수행하거나 다중 조인을 수행해야 한다. 반면 JSON은 모든 정보가 한 곳에 있기 때문에 질의 하나로 가능하다.
다대일과 다대다 관계
- 텍스트 문자열이 아닌 개별 고유 ID를 부여해서 사용하는 이유 : 데이터의 중복 저장을 막기 위해서 (각 테이블 별 중복 발생 가능)
ID 자체로는 의미가 없기 때문에 변경을 할 필요가 없다. 식별 정보가 변경되더라도 ID는 동일하게 유지할 수 있다. 만약 의미를 가진다면 ID역시도 변경이 필연적인데 이렇게 되면 쓰기 오버헤드와 일부는 갱신되지만 다른 중복은 갱신되지 않는 불일치 문제가 발생할 수 있다. 따라서 중복 제거가 데이터베이스 정규화 이면에 놓인 핵심 개념이다. - 중복 데이터 정규화는 다대일 관계에서 필수적이다. 하지만 문서 모델에서는 적합하지 않기 때문에 지원하지 않는다. 심지어는 조인을 지원하지 않는 문서 모델도 존재한다. 만약 조인을 지원하지 않는다면 데이터베이스에 대한 다중 질의를 만들어서 어플리케이션 코드 내에서 흉내내야 한다. 이는 초기 버전에서 join-free 문서 모델이 적합할 수도 있지만 기능이 추가되면서 데이터는 점차 상호 연결되기에 불리해질 수 있다.
문서 DB는 역사를 반복하고 있나?
다대다 관계를 표현하는 제일 좋은 방법
1970s : IBM의 정보 관리 시스템(IMS, 계층 모델)이 우세
- 일대다 관계에서 잘 동작하지만 다대다 표현은 어렵고 조인을 지원하지 않는다.
- 개발자는 데이터를 중복시킬지 한 레코드와 또 다른 레코드의 참조를 수동으로 해결해야 할지 고민해야 했다.
- 해결책으로 관계형 모델과 네트워크 모델이 등장했다.
네트워크 모델 ( = 코다실 모델)
계층 모델과의 차이점은
- 계층 모델 : 모든 레코드는 정확히 하나의 부모가 존재한다.
- 네트워크 모델 : 다중 부모가 존재할 수 있다. ex) 시애틀 지역은 모든 사용자에게 연결될 수 있다. -> 따라서 다대일, 다대다 모델링이 가능하다.
레코드 간 연결은 외래키가 아니라 프로그래밍 언어의 포인터와 더 유사한 개념으로 사용했다. 최상위 레코드(root record)에서부터 연속된 연결 경로를 따르는 게 레코드에 접근하는 유일한 방법이다. (이 방법을 접근 경로라고 한다. ) 목록의 맨 앞부터 원하는 레코드까지 한 번에 하나의 레코드를 보는 방식인데, 다대다의 경우에는 같은 레코드로 접근하는 다양한 경로가 존재하기 때문에 경로의 맨앞에서 이런 다양한 접근 경로를 계속해서 추적해줘야 했다.
따라서 수동 접근 경로 선택이 필요했는데, 이는 제한된 하드웨어의 성능을 효율적으로 사용한다는 장점이 있지만 데이터베이스 질의나 갱신을 위한 코드가 복잡하고 유연성이 없다는 단점이 있다.
계층 모델과 네트워크 모델은 모두 접근 경로를 변경할 수 있지만, 다량의 수작업 데이터베이스 질의 코드를 살펴봐야 하고 새 접근 경로가 정해지면 재작성해야 한다는 단점이 있다.
관계형 모델
이와 달리 관계형 모델은 알려진 모든 데이터를 배치하는 일이다. 쿼리 최적화기가 있어서 쿼리의 어느 부분을 어떤 순서로 실행할지를 결정하고 사용할 인덱스를 자동 결정 한다. 즉 접근 경로를 자동 선택하는 것이다. 따라서 새로운 인덱스를 선언하면 쿼리가 자동으로 적합한 인덱스를 선택하기 때문에 질의를 변경할 필요가 없고 어플리케이션에 새 기능을 추가하기가 수월해진다. 쿼리 최적화기는 한 번 만들면 데이터베이스를 사용하는 모든 어플리케이션에서 범용적으로 혜택을 받을 수 있다.
문서 데이터베이스와의 비교
문서 데이터베이스와 계층 모델의 유사점은 별도 테이블 없이 상위 레코드 내에 중첩된 레코드(일대다 관계)를 저장한다는 것이다.
문서 DB와 RDB의 유사점은 다대일/다대다 표현 시에 관련 항목은 고유한 식별자로 참조한다는 것이다 . (단 RDB는 외래키, 문서 모델은 문서 참조를 이용한다) 이 식별자는 조인이나 후속 질의를 사용해서 읽기 시점에 확인한다.
관계형 DB와 오늘날의 문서 DB
이 둘의 차이점에는 내결함성(5장), 동시성 처리(7장)도 있지만 여기서는 데이터 모델 자체에 중점을 둔다.
문서 DB를 선호하는 이유는 스키마 유연성, 지역성에 기인한 더 나은 성능, 혹은 어플리케이션에서 사용하는 데이터 구조와의 유사성이 있다. 반면 관계형 DB의 선호 이유에는 조인, 다대일, 다대다 관계에 더 많은 지원을 하고 있기 때문이다.
어떤 데이터 모델이 어플리케이션 코드를 더 간단하게 만들까?
이건 어플리케이션마다 다르다. 정확히는 어플리케이션 데이터 항목 사이에 존재하는 관계 유형에 따라서 다르다. 다대다 관계를 사용하는 어플리케이션에는 관계형 데이터 모델이 더 적합할 수 있고, 어플리케이션이 문서와 비슷한 구조의 데이터를 가질 때눈 문서 데이터 모델이 더 적합할 수 있다. (이런 경우는 관계형 데이터 모델의 shredding 기법은 다루기 힘든 스키마와 불필요하게 복잡한 어플리케이션 코드를 발생시킬 수 있다.)
문서 모델의 단점은
- 문서 내 중첩 항목을 바로 참조할 수 없고, '사용자 251의 직위 목록의 두 번째 항목'과 같은 방식으로 접근해야 한다.
- 다대다 관계 사용 시 비정규화로 조인의 필요성을 줄이는 방법을 쓸 수 있지만 비정규화된 데이터 일관성 유지를 위한 추가 작업이 필요하다.
- 아니면 다중 요청으로 조인을 흉내내거나 (이 경우 복잡도가 어플리케이션으로 이동하는 것이다) 조인보다 더 처리 속도가 느려질 수 있다.
문서 모델에서의 스키마 유연성
문서 DB나 RDB에서의 JSON은 어떤 스키마를 강요하지 않는다. 반면 RDB의 XML은 선택적으로 스키마 유효성 검사를 할 수 있다. 스키마가 없다는 것은 임의의 키-값을 문서에 추가할 수 있다는 뜻이자 문서에 포함된 필드의 존재 여부를 보장하지 않는다는 것이다.
문서 DB는 스키마리스로 알려져 있지만 읽는 코드에선 구조의 유형을 어느 정도 가정한다. 즉, 암묵적 스키마가 존재하지만 이를 강요하진 않는 것이다.
- 쓰기 스키마 (schema-on-write) : RDB의 전통적 접근 방식으로, 스키마가 명시적이고 데이터베이스는 모든 데이터가 스키마를 따르고 있음을 보장한다. (정적(컴파일 타임) 타입 확인과 유사)
- 읽기 스키마 (schema-onread) : 데이터 구조는 암묵적인 것으로 데이터를 읽을 때만 해석된다. (동적(런타임) 타입 확인과 유사)
이 두 스키마는 어플리케이션이 데이터 타입을 변경하고자 할 때의 차이가 크다.
- RDB (= 쓰기 스키마) : 스키마 수정 후 마이그레이션이 필요하다. 속도가 느리고 중단 시간이 요구된다. (특히 mySQL은 alter 명령어 시 전체 테이블 복사가 되므로 데이터 양이 많은 테이블이라면 몇 분씩 걸릴 수도 있다)
- 문서 DB (= 읽기 스키마) : 예전 문서를 읽은 경우를 처리하는 코드만 있으면 된다.
읽기 스키마 방식은 데이터가 모두 동일한 구조가 아닐 경우에 더 유리하다. 이러한 경우는 사용자가 제어하지 못하는, 언제나 변경 가능한 외부 시스템에 의해 데이터 구조가 결정하는 경우일 수 있다.
질의를 위한 데이터 지역성
저장소 지역성(storage locality) : 전체 문서 접근 시 여러 테이블에 접근하기 위한 다중 인덱스 검색보다 성능 이점이 있지만 한번에 해당 문서의 많은 부분을 필요로 하는 경우에만 이득이 된다. 따라서 문서를 작게 유지하고, 크기가 증가하는 쓰기는 피하는 것이 좋다. RDB에서도 이러한 지역성을 위해 관련 데이터를 함께 그룹화하는 개념이 존재하는데, 예를 들면 구글 스패너나 오라클의 multi-table index cluster table 등이 있다.
문서 DB와 RDB의 통합
RDB에서도 XML, JSON을 지원하는 경우가 많아지고 문서 DB도 쿼리에서 관계형 조인을 지원이 늘어나고 데이터베이스 참조 확인 등 점차 서로를 보완해나가고 있다.
데이터를 위한 질의 언어
- 선언형 질의 언어 : 알고자 하는 데이터의 패턴(=결과가 충족해야 하는 조건과 데이터를 어떻게 변환할지)을 지정하면 된다.
- 어떤 인덱스, 어떤 조인, 쿼리의 다양한 부분을 어떤 순서로 진행할지는 쿼리 최적화기가 수행한다.
- 상세 구현이 숨겨져 있어서 쿼리를 변경하지 않고도 DB 성능 향상이 가능하다.
- 특정 순서를 보장하지 않기 때문에 순서가 바뀌어도 상관 없다. 따라서 DB에 자동 최적화 여지가 더 크고, 병렬 실행에 적합하다.
- 명령형 질의 언어 : 프로그래밍 언어에서 사용.
- 특정 순서로 특정 연산을 수행하게끔 지시한다.
- 데이터베이스는 코드가 순서에 의존하는지 여부를 확신할 수 없다.
- 다중 코어/다중 장비를 통한 병렬 처리가 어렵다. (특정 순서로 수행하도록 지정하기 때문이다)
맵리듀스 질의
- 대규모 데이터 분산 처리를 위한 프로그래밍 모델이다.
- 일부 NoSQL 데이터 저장소는 제한된 형태의 맵리듀스를 지원한다.
- 읽기 전용 (read-only) 질의 수행 시에 사용한다.
- 몽고 DB(NoSQL)에서의 사용은 다음과 같다.
- 선언형 질의 언어와 명령형 질의 API의 중간에 해당한다. map과 reduce 함수를 기반으로 한다.
- 제약 사항) map과 reduce는 모두 순수 함수로, 입력된 데이터만 사용하고 추가 쿼리를 수행하거나 부수 효과를 노리지 않아야 한다.
- 저수준 프로그래밍 모델이고 연계된 js 함수(map, reduce)를 신중하게 작성해야 한다는 사용성 문제가 있다. 따라서 aggregation pipeline(집계 파이프라인)을 같이 사용한다.
- 집계 파이프라인은 데이터 처리를 일련의 단계로 나누어 처리하며, 여러 종류의 연산자를 사용하여 데이터를 집계, 필터링, 그룹화, 정렬, 변환 등을 수행한다. Aggregation Pipeline은 맵리듀스보다 더 간단하고 직관적으로 작성할 수 있으며, 작은 규모의 데이터 처리에서도 유용하다.
그래프형 데이터 모델
데이터 간의 연결이 더 복잡해지면 그래프로 모델링하는 것이 더 자연스럽다.
그래프 모델에는 두 유형의 객체가 있다. 정점(vertex, 노드, 엔티티)과 간선(edge, 관계, 호(arc))이다. 그래프는 같은 유형, 동종의 데이터 뿐만 아니라 완전히 다른 유형의 객체를 일관성있게 저장할 수 있다.
그래프 데이터 모델은 속성 그래프 모델과 트리플 저장소 모델이 있고, 그래프용 선언형 질의 언어에는 사이퍼, 스파클, 데이터로그 등이 있다.
속성 그래프
각 정점의 구성 요소는 다음과 같다.
- 고유 식별자
- 유출 간선 집합
- 유입 간선 집합
- 속성 컬렉션 (키-값 쌍)
각 간선의 구성요소는 다음과 같다.
- 고유 식별자
- 간선이 시작하는 정점(꼬리 정점)
- 간선이 끝나는 정점(머리 정점)
- 두 정점 간 관계 유형을 설명하는 레이블
- 속성 컬렉션 (키-값 쌍)
관계형 스키마를 사용해서 속성 그래프로 표현할 수도 있다.
중요 지점은 다음과 같다.
- 정점은 다른 정점과 간선으로 연결된다. 특정 유형과 관련 여부를 제한하는 스키마는 없다.
- 정점이 주어지면 정점의 유입, 유출 간선을 효율적으로 찾을 수 있다. 그래프 순회가 가능하다. 즉 일련의 정점을 따라 앞뒤 방향으로 순회한다.
- 다른 유형의 관계에 서로 다른 레이블 사용 시 단일 그래프에 다른 유형의 정보를 저장할 수 있고 데이터 모델을 깔끔하게 유지할 수 있다.
속성 그래프는 발전성이 좋아서 어플리케이션에 기능을 추가할 때 데이터 구조 변경을 수용하게끔 쉽게 확장할 수 있다.
사이퍼 질의 언어
속성 그래프를 위한 선언형 질의 언어이다. 선언형 질의 언어이기 때문에 수행에 대해 자세히 지정할 필요 없이 쿼리 최적화기가 처리해준다.
SQL의 그래프 질의
RDB에서 그래프 데이터를 표현할 수 있고 그래프 데이터를 관계형 구조에 넣어 SQL을 사용해서 질의하는 것도 가능하지만 어렵다. 관계형 데이터베이스는 질의에 필요한 조인을 미리 알고 있지만 그래프 질의는 가변적인 여러 간선을 순회해야 한다. 따라서 미리 조인 수를 고정할 수 없다. 이를 위해서 재귀 공통 테이블 식(recursive common table expression)을 사용한다.
트리플 저장소와 스파클
트리플 저장소는 모든 정보를 주어, 서술어, 목적어처럼 매우 간단한 세 부분 구문 형식으로 저장한다.
주어는 그래프의 정점을, 목적어는 원시 데이터 타입의 값 혹은 그래프의 다른 정점을 의미한다.
시멘틱 웹은 웹사이트를 사람이 아닌 컴퓨터도 읽을 수 있게 기계가 판독 가능한 데이터로도 정보를 게시하자는 개념이다. 여기서 등장한 자원 기술 프레임워크(Resource Description Framework, RDF)는 서로 다른 웹사이트가 일관된 형식으로 데이터를 게시하기 위해 고안한 방법이다.
RDF 데이터 모델
인터넷 전체의 데이터 교환을 위해 설계되었다. 그래프 형태로 표현하고, 세 가지 기본 요소를 가진다.
- 리소스(자원) : URI로 식별되는 개체
- 프로퍼티(속성) : 리소스와 리소스, 혹은 리소스와 값 사이의 관계 표현에 사용된다. 이름-값 쌍으로 구성되어 있고, 값에는 문자열, 숫자와 같은 리터럴 형식 혹은 또 다른 리소스 등이 들어간다.
- 문장 : 리소스와 프로퍼티의 결합이다. RDF 트리플이라고도 한다. 주어-동사-목적어 형태이다.
스파클 질의 언어 (SPARQLE)
RDF 데이터 모델을 사용한 트리플 저장소의 질의 언어
그래프 DB와 네트워크 모델의 비교
- 코다실 : 다른 레코드 타입과 중첩 가능한 레코드 타입을 지정하는 스키마가 존재한다.
그래프 : 제한이 없다. 모든 정점은 다른 정점으로 가는 간선을 가질 수 있다. 따라서 유연성이 높다. - 코다실 : 특정 레코드에 도달하는 유일한 방법은 레코드의 접근 경로 중에 하나를 탐색하는 것이다.
그래프 : 고유 ID가 존재하기 때문에 임의 정점에 직접 참조가 가능하고 인덱스를 이용해서 특정 값의 정점에 빠르게 도달할 수 있다. - 레코드의 하위 항목 - 코다실 : 정렬된 집합의 형태로 저장되기 때문에 새로운 데이터 삽입 시 새 레코드 위치를 염두해 두어야 한다.
그래프 : 정점, 간선을 정렬하지 않는다. 질의를 만들 때만 결과를 정렬한다. - 질의 - 코다실 : 명령형 질의를 사용하기에 작성이 어렵고, 스키마 변경 시 질의가 손상될 가능성이 높다.
그래프 : 명령형으로 순회해서 작성할 수도 있지만 사이퍼나 스파클 같은 고수준의 선언형 질의 언어가 제공된다.
데이터로그
질의 언어의 기반이 되는 초석을 제공한다. 트리플보다 조금 더 일반화 된 서술어(주어, 목적어) 형태로 작성한다.
단계를 나눠서 한 번에 조금씩 질의로 나아간다.
'TIL > 데이터 엔지니어링' 카테고리의 다른 글
데이터 중심 어플리케이션 설계 - 5장. 복제 (1) | 2023.05.30 |
---|---|
데이터 중심 어플리케이션 설계 - 4장. 부호화와 발전 (0) | 2023.05.22 |
데이터 중심 어플리케이션 설계 - 3장. 저장소와 검색 (0) | 2023.05.15 |
데이터 파이프라인 저장소 (0) | 2023.05.04 |
데이터 중심 어플리케이션 설계 - 1장. 신뢰성, 확장성, 유지보수성 (0) | 2023.04.23 |
댓글