개요
- 데이터 저장과 질의를 위한 다양한 범용 데이터 모델을 알아보자.
- 관계형 모델, 문서 모델, 그래프 기반 모델을 비교해보자.
관계형 모델과 문서 모델
- 관계형 모델: 데이터가 관계(테이블)로 구성되고 각 관계는 순서 없는 튜플(Row)의 모음
- 관계형 모델은 관계형 데이터베이스(RDBMS)로 발전했으며 정규화된 구조를 데이터를 저장하고 질의할 때 사용되었다.
- 관계형 모델의 목표는 정리된 인터페이스 뒤로 구현 세부사항을 숨기는 것이다.
NoSQL의 탄생
- NoSQL의 등장 배경은 다음과 같다.
- 대규모 데이터셋이나 쓰기 연산에 대한 처리량을 관계형 데이터베이스보다 쉽게 도달할 수 있게하는 확장성의 필요도
- 관계형 모델에서 지원하지 않는 특수 질의 동작
- 제한적인 관계형 스키마에 비해 동적이고 표현력이 풍부한 데이터 모델에 대한 바람
- 애플리케이션의 요구사항은 모두 다르기 때문에 관계형 데이터베이스와 NoSQL이 함께 사용될 것이다.
이를 다중 저장소 지속성(polyglot persistence)라고 한다.
객체 관계형 불일치
- 객체 지향 프로그래밍에서 데이터를 관계형 테이블에 저장하기 위해선 애플리케이션 ↔ DB 모델 객체 사이의 전환 계층이 필요하다. (임피던스 불일치,
impedence mismtach
)- 전통적인 관계형 모델은 외래 키를 통한 참조, Join을 통해 표현이 가능하다.
- JSON 모델은 다중 테이블 스키마보다 좋은 지역성을 갖는다.
- JSON 모델에서는 관련된 데이터가 한 곳에 모여 있기 때문에 한번의 질의를 통해 데이터를 가져올 수 있다. (데이터 내에 일대다 관계가 녹여져 있다.)
- 일대다 관계는 의미상 데이터 트리 구조와 동일하며, JSON 표현에서 명시적으로 드러난다.
다대일과 다대다 관계
- 중복된 데이터를 정규화 하기 위해선 다대일 관계가 필요하다.
- e.g) N (사람) → 1 (직장) / 사람 테이블에서 직장을 텍스트로 표현하면 중복이 많아지기 때문에 ID를 통해 표현할 수 있다.
- 다대일 관계 (Many-to-One)는 문서 모델에 적합하지 않다.
- 관계형 데이터베이스는 조인이 쉽기 때문에 ID로 다른 테이블 로우 참조가 간단하다.
- 문서 데이터베이스는 조인에 대한 지원이 약하다. (일대다 관계에선 조인이 필요하지 않기 때문)
- 애플리케이션 레벨에서 조인이 구현될 가능성이 높다. (Join-Free → Join required)
다양한 데이터 모델
- 계층 모델: JSON 구조와 비슷하게 모든 데이터를 레코드 내에 중첩된 레코드 트리로 표현하는 모델
- 다대다 관계 표현이 어렵고, 조인을 지원하지 않음
- 네트워크 모델(코다실 모델)
- 하나의 레코드가 다중 부모를 가질 수 있다. → 다대일 관계 가능
- 레코드에 접근하기 위해선 최상위 레코드부터 연결된 경로를 따라가는 접근 경로 방식이다. (프로그래밍 언어의 포인터와 유사)
- 다대다 관계에서는 하나의 레코드에 접근하는 경로가 여러 개이기 때문에 다양한 경로 추적이 필요하다.
- 데이터 모델을 바꾸기 위해선모든 접근 경로를 파악해야하며 이는 데이터 모델을 바꾸는데 어려움이 생긴다.
- 원하는 데이터에 대한 경로가 없는 경우 새로운 접근 경로를 다루기 위해 재작성 필요
- 관계형 모델
- 다른 테이블과의 외래 키 관계와 상관없이 편리하게 데이터를 저장할 수 있다.
- 임의 조건과 일치하는 테이블, 로우를 선택해서 읽을 수 있다.
- 관계형 데이터베이스에서 쿼리 옵티마이저는 데이터 접근 경로를 자동으로 만든다.
- 쿼리 옵티마이저는 가장 최적의 인덱스를 사용한다.
- 현재의 문서 데이터베이스는 관계형 데이터베이스의 외래 키처럼 문서 참조를 사용해 다대일, 다대다 관계를 해결하고 있다.
문서 데이터베이스, 관계형 데이터베이스 선택
- 문서 데이터 모델: 스키마 유연성, 지역성에 기인한 좋은 성능 및 애플리케이션 데이터 구조와 유사
- 관계형 모델: 조인, 다대일, 다대다 관계의 지원
- 애플리케이션에서 데이터가 문서와 비슷한 구조인 경우 문서모델을 사용
- 애플리케이션에서 다대다 관계를 사용하면 관계형 모델 사용
- 애플리케이션 코드 상에서 비정규화된 데이터 일관성 유지하기 위한 코드 작업이 필요하기 때문이다.
- DB 내에서 진행되는 조인 보다 애플리케이션 레벨 코드가 속도가 더 느리기 때문이다.
문서 모델의 스키마 유연성
- JSON은 문서 데이터에 특정 스키마를 강요하지 않는다.
- 문서 데이터베이스는 암묵적인 스키마가 있지만 데이터베이스는 이를 강요하지 않는다.
- 쓰기 스키마: 스키마가 명시적이고 데이터베이스는 모든 데이터가 스키마를 따르고 있음을 보장 (컴파일 타입과 유사)
- 읽기 스키마: 데이터 구조는 암묵적이고 데이터를 읽을 때만 해석된다. (동적 타입과 유사)
- 애플리케이션이 데이터 타입을 변경하는 경우
- 쓰기 스키마: 마이그레이션이 필요해 중단시간이 발생
- 읽기 스키마: 과거 데이터를 읽는 경우를 예외처리만 해주면 되어 유연하다.
- 읽기 스키마 접근 방식은 컬렉션 안의 항목이 모두 동일한 구조가 아닐 때 유리하다.
- 다른 유형의 오브젝트가 있고 각 유형의 오브젝트별로 자체 테이블에 넣는 방법은 실용적이지 않다.
- 외부 시스템에 의해 데이터 구조가 결정된다.
질의를 위한 데이터 지역성
- 애플리케이션이 자주 전체 문서에 접근해야할 때 저장소 지역성을 활용하면 성능적으로 장점이 있다.
- 대부분의 자업이 문서 단위이기 때문에 문서 크기에 상관 없이 전체 문서를 조회하거나 갱신해야 한다.
- 문서를 작게 유지하고, 문서의 크기가 증가하는 쓰기를 피하자.
- 지역성은 관계형 모델에서도 지원한다.
- e.g) 구글 - 스패너, 오라클 - 다중 테이블 색인 클러스터 테이블
데이터를 위한 질의 언어
- 명령형 언어
- 특정 순서로 특정 연산을 수행하는 형식
- 병렬 실행에 적합하지 않음
- 선언형 언어
- 데이터 패턴 (결과 충족 조건과 데이터 변환 방법)을 지정하는 방식
- 실제 처리의 순서는 옵티마저이저가 수행
- 병렬 실행에 적합
- 맵리듀스
- 많은 컴퓨터에서 대량의 데이터를 처리하기 위한 프로그래밍 모델
- 많은 문서에 대해 읽기 전용 질의 수행시 주로 사용
// 몽고 DB 예시
db.observations.mapReduce(
// map
function map() {
var year = this.observationTimestamp.getFullYear();
var month = this.observationTimestamp.getMonth() + 1;
emit(year + "-" + month, this.numAnimals)
},
// reduce
function reduce(key, values) {
return Array.sum(values);
},
{
query: { family: "Sharks" },
out: "monthlySharkReport"
}
);
몽고DB map, reduce는 순수 함수여야 한다.
그래프형 데이터 모델
- 다대다 관계가 일반적인 경우에는 그래프로 데이터를 모델링하는 것이 자연스럽다.
- 그래프는 정점과 간선으로 구성된다.
- e.g) 소셜 그래프 - 정점: 사람, 간선: 사람간 알고 있는 관계
- e.g) 웹 그래프 - 정점: 웹 페이지, 간선: 다른 페이지에 대한 HTML 링크
- 그래프에서 데이터를 구조화하고 질의하는 대표적인 방법
- 속성 그래프 모델, 트리플 저장소 모델
속성 그래프
- 정점의 구성
- 고유한 식별자, 유출/유입 간선 집합, 속성 컬렉션 (Key/Value)
- 간선의 구성
- 고유한 식별자, 간선이 시작하는 정점(꼬리 정점), 간선이 끝나는 정점(머리 정점), 두 정점 간 관계 유형을 설명하는 레이블, 속성 컬렉션 (Key/Value)
CREATE TABLE vertices (
vertex_id integer PRIMARY KEY,
properties json
);
CREATE TABLE edges (
edge_id integer PRIMARY KEY,
tail_vertex integer REFERENCES vertices (vertex_id),
head_vertex integer REFERENCES vertices (vertex_id),
label text,
properties json
);
CREATE INDEX edges_tails ON edges (tail_vertex);
CREATE INDEX edges_heads ON edges (head_vertex);
- 정점은 다른 정점과 간선으로 연결되며 특정 여부를 제한하는 스키마는 없다.
- 정점이 주어지면 유입/유출 간선을 찾을 수 있고 정점 기준 앞/뒤로 그래프 순회가 가능하다.
- 다른 유형의 관계에 서로 다른 레이블을 사용하면 단일 그래프에 다른 유형의 정보를 저장하면서도 데이터 모델을 깔끔하게 유지할 수 있다.
- 그래프 모델은 발전성이 좋아 애플리케이션 기능 추가 시에 데이터 구조 변경이 되어도 그래프를 쉽게 확장할 수 있다.
# 사이퍼 질의 예시
MATCH
(person) -[:BORN_IN ]-> () -[:WITHIN*0..]-> (us:Location {name:'United States'}),
(person) -[:LIVES_IN ]-> () -[:WITHIN*0..]-> (us:Location {name:'Europe'}),
RETURN person.name
그래프 데이터를 관계형 구조로 넣었을 때 SQL로 질의할 수 있지만 조인할 테이블 수를 고정하기 어렵기 때문에 질의가 어렵다.
WITH RECURSIVE 문을 사용해서 표현 가능
트리플 저장소
- 모든 정보를 주어, 서술어, 목적어와 같이 세 부분의 구문으로 구성해 저장한다.
- 주어: 그래프의 정점
- 목적어
- 문자열이나 숫자 같은 원시 데이터타입의 값 - 서술어의 목적어는 주어 정점에서 속성의 키, 값
- e.g) (루시, 나이, 33) ⇒
{"age" : 33}
- e.g) (루시, 나이, 33) ⇒
- 그래프의 다른 정점 - 서술어 = 그래프 간선, 주어 = 꼬리 정점, 목적어 = 머리 정점
- e.g) (루시, 결혼하다, 알랭) ⇒ 루시, 알랭 = 정점, 결혼하다 = 간선
- 문자열이나 숫자 같은 원시 데이터타입의 값 - 서술어의 목적어는 주어 정점에서 속성의 키, 값
# 스파클 wlfdml dPtl
PREFIX : <urn:example>
SELECT ?personName WHERE {
?person :name ?personName/
?person :bornIn / :within* / :name "United States".
?person :livesIn / :withIn /:name "Europe"
반응형
'기술 서적 > 데이터 중심 애플리케이션 설계' 카테고리의 다른 글
3장. 저장소와 검색 (0) | 2025.03.02 |
---|---|
1장. 신뢰할 수 있고 확장 가능하며 유지보수하기 쉬운 애플리케이션 (0) | 2024.05.19 |