데이터 모델은 소프트웨어 개발에서 제일 중요한 부분이다. 소프트웨어가 어떻게 작성됐는지 뿐만 아니라 해결하려는 문제를 어떻게 생각해야 하는지에 대해서도 많은 영향을 미치기 때문이다.

대부분의 애플리케이션은 하나의 데이터 모델을 다른 데이터 모델 위에 계층을 둬서 만든다. 각 계층의 핵심적인 문제는 다음 하위 계층 관점에서 데이터 모델을 표현하는 방법이다. 예를들면 아래와 같다.

복잡한 애플리케이션에서는 중간 단계를 더 둘 수 있지만 기본 개념은 여전히 동일하다. 각 계층은 명확한 데이터 모델을 제공해 하위 계층의 복잡성을 숨긴다.

이러한 추상화는 다른 그룹의 사람들(예를들면, 데이터베이스 엔지니어와 소프트웨어 개발자)이 효율적으로 함께 일할 수 있게 해주기 때문에 중요하다.

관계형 모델과 문서 모델

오늘날 가장 잘 알려진 데이터 모델은 1970년 에드가 코드가 제안한 관계형 모델을 기반으로 한 SQL이다. 데이터는 (SQL에서는 테이블[(table)이라 불리는) 관계(relation)로 구성되고 각 관계는 순서 없는 튜플(tuple)(SQL에서는 로우(row))의 집합이다.

대부분의 서비스는 여전히 이러한 관계형 모델을 통해 제공되고 있다.

NoSQL의 탄생

NoSQL은 원래 오픈소스, 분산 환경, 비관계형 데이터베이스 meetup용 인기 트위터 해시태그 였으나 지금은 Not Only SQL로 재해석됐다.

NoSQL이 등장한 배경은 다음과 같은 다양한 필요성에 의해서이다.

대부분의 NoSQL은 오픈소스이며 Schema-less 특징을 가지고 있다. 집합-지향(Aggregate-oriented) 모델을 사용하여 Scale-out에 적합하다. (그래프 모델은 해당하지 않는다.)

객체 관계형 불일치

객체지향 코드와 데이터베이스 모델 객체(table, row, column) 사이에 거추장스러운 전환 계층이 필요한데 이를 임피던스 불일치(impedance mismatch)라고 부른다.

객체 관계형 매핑(ORM) 프레임워크는 전환 계층의 보일러 플레이트 코드 양을 줄이지만 두 모델간의 차이를 완벽히 숨길 수 없다.

JSON 표현은 다중 테이블(multi-table) 스키마보다 더 나은 지역성(locality)을 갖는다. 관계형 데이터베이스 모델에서는 다중 조인이 필요한 케이스 일지라도 JSON 표현에서는 모든 관련 정보가 한 곳에 있어 질의 하나로 충분한다.

다대일과 다대다 관계

중복된 데이터를 정규화하려면 다대일(many-to-one) 관계가 필요하다. 이러한 다대일 관계는 join이 쉬운 관계형 데이터베이스에 적합하지만 문서 데이터베이스도 표현할 수 있다.

더 복잡한 관계를 표현하는 다대다(many-to-many)는 관계 표현이 더 어렵기 때문에 문서 데이터베이스에 더 적합하지 않다.

문서 데이터베이스는 역사를 반복하고 있나?

문서 데이터베이스와 NoSQL은 데이터베이스에서 다대다 관계를 표현하는 방법에 대해 오랜 기간 논쟁중이다.

1970년대 IBM의 정보 관리 시스템(Information Management System, IMS)은 계층 모델(hierarchical model)을 사용했는데 계층 모델은 JSON과 흡사하다.

IMS는 문서형 데이터베이스와 유사하게 일대다에서는 잘 작동했지만 다대다 관계 표현이 어려웠다. 이러한 계층 모델의 한계 극복을 위해 제안된 해결책들은 아래와 같다.

결국 관계형 모델로 회귀하게 되는것일까?

네트워크 모델

네트워크 모델은 계층 모델을 일반화한다. 계층 모델이 트리구조에서 모든 레코드는 정확하게 하나의 부모를 가질 수 있었다면 네트워크 모델은 다중 부모를 가질 수 있다.

레코드에 접근하는 유일한 방법은 최상위 레코드(root record)에서부터 연속된 경로를 따르는 방법이다. 이를 접근 경로라 한다.

만약 레코드가 다중 부모를 갖는다면 다양한 관계를 모두 추적해야 한다는 문제가 있으며 원하는 데이터에 대한 경로가 없다면 어려운 상황에 놓인다. 또한, 접근 경로를 변경할 수 있지만 수작업으로 재작성하는 노력이 필요하다.

나중에 이야기할 그래프 데이터베이스와 유사하지만 네트워크 모델 데이터베이스는 더 낮은 수준의 추상화로써 동작하며 일련의 엣지 간의 용이한 횡단이 불가능하다

관계형 모델

알려진 모든 데이터를 배치한다. 관계(테이블)는 단순히 튜플(로우)의 컬렉션이 전부다. 따라서 복잡한 접근 경로가 존재하지 않으며 필요한 테이블을 읽으면 된다.

관계형 데이터베이스에서 질의 최적화기(query optimizer)는 질의의 어느부분을 어떤 순서로 실행할지를 결정하고 사용할 색인을 자동으로 결정하는데 이러한 행위는 접근 경로를 만드는 것이다.

새로운 방식으로 데이터를 질의하고 싶은 경우 새로운 색인을 선언하기만 하면 질의는 자동으로 가장 적합한 경로를 찾는다. 즉, 관게형 모델은 애플리케이션에 새로운 기능을 추가하는 작업이 훨씬 쉽다.

문서 데이터베이스와의 비교

문서 데이터베이스는 별도의 테이블이 아닌 상위 레코드 내에 중첩된 레코드를 저장한다는 점에서 계층 모델과 유사하지만 다대일/다대다 관계 표현시 관계형 데이터베이스와 근본적으로 다르지 않다.

둘다 관련 항목은 고유한 식별자로 참조하기 때문이다. 관계형 모델에서는 외래키라고 부르며 문서 모델에서는 문서 참조(document reference)라고 부른다.

관계형 데이터베이스와 오늘날의 문서 데이터베이스

문서 데이터 모델

관계형 모델

어떤 데이터 모델이 애플리케이션 코드를 더 간단하게 할까?

애플리케이션 데이터가 문서와 비슷한 구조(일대다 관계 트리로 보통 한 번에 전체 트리를 적재 하는 구조)라면 문서 모델을 사용하는 것이 좋다.

애플리케이션에서 다대다관계를 사용한다면 문서 모델보다는 관계 모델이 더 적합하다.

일반적으로 어떤 데이터 모델이 애플리케이션 코드를 더 간단하게 만드는지 말할 수 없다. 데이터 항목 간에 존재하는 관계 유형에 따라 다르다. 상호 연결이 많은 데이터의 경우 문서 모델은 곤란하지만 관계형 모델은 무난하고 그래프 모델은 매우 자연스럽다.

문서 모델에서의 스키마 유연성

문서 모델은 스키마를 강요하지 않는다. 즉, 임의의 키와 값을 문서에 추가할 수 있고 읽을 때 클라이언트는 문서에 포함된 필드의 존재 여부를 보장하지 않는다.

스키마에 대한 관계모델과 문서 모델의 차이는 아래와 같다.

질의를 위한 데이터 지역성

관계 모델에서 데이터가 다중 테이블로 나뉘어져 있다면 전체를 검색하기 위해 다중 색인 검색이 필요하고 많은 리소스가 낭비된다.

반면 문서 모델은 더 나은 지역성을 갖기 때문에 성능 이점이 있다. 다만, 지역성의 이점은 한 번에 해당 문서의 많은 부분을 필요로 하는 경우에만 적용된다.

문서 모델은 문서의 작은 부분만 갱신을 해도 전체 문서를 적재해야 하기에 낭비일 수 있다. 그렇기 때문에 문서를 작게 유지하는것을 권장한다.

문서 데이터베이스와 관계형 데이터베이스의 통합

대부분의 관계형 데이터베이스는 2000년대 중반 이후로 문서 데이터베이스 모델과 비슷한 데이터 모델(XML, JSON)을 사용할 수 있게 지원하고 있다.

문서 데이터베이스도 관계형 모델처럼 관계형 조인을 지원하고 스키마를 정의하는 움직임도 있다.

이처럼 관계형 데이터베이스와 문서 데이터베이스는 시간이 지남에 따라 점점 더 비슷해지고 있다. 각 데이터 모델의 부족한 부분을 보완해 나가고 있기 때문이다.

데이터를 위한 질의 언어

명령형 질의와 선언형 질의

IMS와 코다실(네트워크 모델)은 프로그래밍 언어와 유사한 명령형 질의 언어이다. 반면 SQL은 선언형 질의 언어이다.

명령형 질의 언어는 아래와 같다.

function getSharks(){
    let sharks = [];
    for(let i=0; i<animals.length; i++){
        if(animals[i].family === "Sharks") sharks.push(animals[i]);
    }
    return sharks;
}

선언형 질의 언어는 아래와 같다.

SELECT * FROM animals WHERE family = 'Sharks';

명령형 언어는 특정 순서로 특정 연산을 수행하게끔 지시한다. 반면 선언형 언어는 목표를 달성하기 위한 방법이 아닌 알고자 하는 데이터의 패턴을 지정한다.

선언형 언어의 장점은 아래와 같다.

맵리듀스 질의

맵리듀스(MapReduce)는 많은 컴퓨터에서 대량의 데이터를 처리하기 위한 프로그래밍 모델이다. 맵리듀스는 선언형 언어와, 완전한 명령형 질의 API의 중간 정도에 있다.

맵리듀스는 함수형 프로그래밍 언어에 있는 map(collect) 과 reduce(fold 혹은 inject) 함수를 기반으로 한다.

그래프형 데이터 모델

애플리케이션이 주로 일대다 관계(트리 구조 데이터)이거나 레코드 간 관계가 없다면 문서 모델이 적합하다.

관계형 모델은 단순한 다대다 관계를 다룰 수 있지만 데이터 간 연결이 더 복잡해지면 그래프로 데이터를 모델링하는 편이 더 자연스럽다.

그래프는 두 유형의 객체로 구성된다.

그래프 모델링의 예

그래프에서는 데이터를 구조화하고 질의하는 몇 가지 다른 방법이 있다. 속성 그래프 모델과 트리플 저장소 모델 그리고 그래프용 선언형 질의 언어 사이퍼와 스파클, 데이터로그 등이 있다.

속성 그래프

속성 그래프 모델에서 각 정점은 다음과 같은 요소로 구성된다.

각 간선은 다음과 같은 요소로 구성된다.

관계형 스키마를 사용해서 속성 그래프를 표현해 보면 아래와 같다.

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..]-> (eu:Location {name:'Europe'})
RETURN person.name

위 질의는 2가지 조건을 만족하는 정점을 찾으라는 의미이다.

SQL의 그래프 질의

위에서 관계형 데이터베이스에서 그래프 데이터 베이스를 표현할 수 있음을 보여줬다. 그렇다면 그래프 데이터를 관계형 데이터베이스에 넣어서 SQL을 사용해 질의할 수 있을까?

대답은 ‘예’지만 약간 어렵다. 사이퍼에서 사용된 [:WITHIN*0..]-> ()과 같은 간선 순회를 위한 질의를 대체하는 것이 어렵다.

트리플 저장소와 스파클

트리플 저장소 모델은 속성 그래프 모델과 거의 동등하다. 이 모델은 모든 정보를 주어(subject), 서술어(predicate), 목적어(object)처럼 매우 간단한 세 부분 구문(three-part statments) 형식으로 저장한다.

트리플 저장소는 트리플의 저장과 검색에 최적화되어 있다. 쿼리뿐 아니라 트리플은 자원 기술 프레임워크(RDF)와 기타 포맷을 사용하여 가져오기/내보내기를 할 수 있는 것이 보통이다.

스파클은 RDF 형식으로 저장된 데이터를 검색, 조작할 수 있는 질의어이다.

사이퍼와 그 구조가 매우 유사(나중에 만들어진 사이퍼가 패턴 매칭을 스파클에서 차용했기 때문)해 보인다.

위에서 사이퍼 질의를 스파클 질의와 비교해보면 아래와 같다.

(person) -[:BORN_IN]-> () -[:WITHIN*0..]-> (location)   # Cypher

?person :bornIn / :within* ?location.                   # SPARQL