본문 바로가기
App 설계

DDIA - 그래프형 데이터 모델

by ocwokocw 2021. 11. 21.

- 이 글은 마틴 클레프만의 데이터 중심 애플리케이션 설계를 기반으로 작성되었습니다.

- 개요

다대다 관계가 데이터 모델을 구별하는 중요한 기능임을 살펴봤다. 앞서 애플리케이션이 1:N 관계이거나 관계가 없다면 문서 모델이 적합하다고 하였다. 반면 관계형 모델은 M:N 을 다루기 수월하지만 데이터간 연결이 복잡해지면 그래프형 데이터 모델을 사용하는편이 자연스럽다.

 

그래프의 구성요소에는 정점(노드, 엔티티)과 간선(엣지, 관계, 호)이 있다. 예를 들어 소셜그래프에서 정점은 사람이며 간선은 사람간의 관계라고 할 수 있다. 웹 그래프에서는 정점은 웹이며 간선은 다른 페이지의 HTML 링크라고 할 수 있다.

 

위의 예제에서는 정점이 모두 같은 유형(type)을 예로 들었지만 그래프는 같은 유형의 데이터만 정점으로 사용해야 하는 제약이 없다. 예를 들어 페이스북에서 사람, 장소, 이벤트, 사용자가 작성한 코멘트 등이 모두 정점이 된다. 반면 간선은 어떤 사람이 친구인지, 누가 어떤 포스트에 코멘트를 달았는지를 나타낼 수 있는것이다.

위의 그림은 아이다호의 루시와 프랑스 본 출신의 알랭이 결혼해서 런던에 살고있음을 나타내는 그래프이다. 그래프에서 데이터를 구조화하고 질의하는 몇 가지 방법이 있는데 이번 절에서는 속성 그래프 모델(Neo4J, Titan, InfiniteGraph)과 트리플 저장소 모델(Datomic, Allegrograph) 구현을 살펴보고 그래프용 선언형 질의 언어(Cypher, SPARQL, Datalog)를 살펴본다.


- 속성 그래프

속성 그래프 모델에서 정점의 구성요소는 아래와 같다.

  • 고유한 식별자(id)
  • 유출(outgoing) 간선 집합
  • 유입(incoming) 간선 집합
  • 속성 컬렉션(key-value)

간선의 구성요소는 아래와 같다.

  • 고유한 식별자(id)
  • 간선이 시작하는 정점(꼬리 정점)
  • 간선이 끝나는 정점(머리 정점)
  • 두 정점 간 관계 유형을 설명하는 레이블
  • 속성 컬렉션(key-value)

정점 테이블과 간선 테이블을 정의하면 RDB 를 이용하여 그래프 저장소를 표현할 수 있다.

 

create table vertices(
	vertex_id integer primary key,
	properties json
);

create table edges(
	edge_id integer primary key,
	tail_vertex integer,
	head_vertex integer,
	label text,
	properties json
);
select 
properties -> 'name',
properties -> 'age',
* 
from vertices

.....
?column?	?column?	vertex_id	properties
"ocw"	        12	        1	        {"name": "ocw", "age": 12}

 

위의 DDL 에서는 생략했지만 edges 테이블에서 tail_vertex 와 head_vertex 의 경우 검색을 많이 할 가능성이 크므로 인덱스를 설정해주는것이 좋다. json 데이터 타입의 경우 key-value 쌍중 일부에 대해서만 조회하는것도 가능하다.

이런 그래프 모델에는 몇 가지 중요한 특징이 있는데 아래와 같다.

  • 정점은 다른 정점과 간선으로 연결된다. 그리고 특정 유형과 관련여부는 제한하는 스키마가 없다.
  • 하나의 정점에 대해 유입과 유출 간선을 효율적으로 찾을 수 있고 순회가 가능하다.
  • 다른 유형, 다른 레이블을 사용하여도 단일 그래프에 정보를 저장할 수 있다. 만약 그래프 모델이 아니라면 아이다호와 루시에서 사람, 도시, 대륙에 따라서 테이블을 다르게 구성해야하고 이때 관계가 복잡해질 수 있다.

- 사이퍼 질의 언어

Cypher 는 속성 그래프를 위한 선언형 질의 언어로 Neo4j 그래프 DB 용으로 만들어졌다. 루시, 알랭의 예제에서 그래프 DB로 삽입하는 사이퍼 질의를 살펴보자.

 

CREATE
	(NAmerica:Location {name:'North America', type:'continent'}),
	(USA:Location {name:'United States', type:'country'}),
	(Idaho:Location {name:'Idaho', type:'state'}),
	(Lucy:Person, {name:'Lucy'}),
	(Idaho) -[:WITHIN]-> (USA) -[:WIDTHIN]-> (NAmerica),
	(Lucy) -[:BORNIN]-> (Idaho)

 

위의 데이터 삽입 예제는 정점과 간선 부분으로 나눌 수 있다.

 

2~5행은 정점 데이터 삽입을 나타낸다. NAmerica 와 USA, Idaho, Lucy 같은 상징적인 이름을 지정하고 key-value 쌍을 json 형태로 선언하였다.

 

마지막 2 행은 간선데이터를 생성한것이다. (Idaho) -[:WITHIN]-> (USA) 에서 (Idaho) 와 (USA) 는 정점을 나타내는데 꼬리노드는 Idaho이며 머리노드는 USA 이다. [:WITHIN] 은 간선을 나타내며 WITHIN 은 간선의 레이블이다.

 

이런 데이터가 있다면 알랭, 루시 예제에서 "미국에서 유럽으로 이민 온 모든 사람들의 이름 찾기"와 같은 질의를 처리할 수 있다. 질의 처리를 위해 다르게 표현하면 "미국내 BORN_IN 레이블 간선을 가진 정점과 유럽내 LIVES_IN 간선을 가진 모든 정점을 찾아서 name 속성을 반환한다." 로 바꿀 수 있다.

 

MATCH
	(person) -[:BORN_IN]-> () -[:WITHIN*0..] -> (us:location {name: 'United States'}),
	(person) -[:LIVES_IN] -> () -[:WITHIN*0..] -> (eu:location {name: 'Europe'})
RETURN person.name
 

질의가 복잡한것 같지만 천천히 살펴보면 의미를 알 수 있다. (person)은 BORN_IN 레이블 유출 간선을 갖는다. 이 정점에서 name 속성이 'United States' 인 Location 유형에 도달할 때 까지 WITHIN 레이블 유출 간선을 따라간다.


- SQL 그래프 질의

앞서 RDB(postgre)로도 그래프 데이터를 표현할 수 있다고 언급했지만 난해한 면이 있다. 데이터 자체를 표현하는것은 그리 복잡하지않지만 정점을 찾기까지 가변적인 간선의 탐색과정, 즉 사이퍼 질의에서 () -[:WITHIN*0..] -> (us: Location {name}) 해당하는 부분을 표현하기가 난해하다. 

 

SQL:1999 이후로 가변 순회 경로에 대한 질의는 재귀 공통 테이블식(WITH RECURSIVE문)을 사용해서 표현할 수 있지만 사이퍼 질의로는 4 줄로 표현할 수 있었던 "미국에서 유럽으로 이민 온 모든 사람들의 이름 찾기" 예제를 30줄 정도에 걸쳐서 작성해야 한다. 표현을 할 수 있다는 점만 알아 두고 굳이 꼭 써야 한다면 필요할 때 예제를 인터넷에서 찾아보도록 하라.


- 트리플 저장소와 스파클

트리플 저장소 모델은 속성 그래프 모델과 거의 동등하며 같은 사상을 다른 용어를 사용해서 설명한다. 그럼에도 App. 구축에 용이한 도구가 트리플 저장소에 종속될 수 있기 때문에 논의할 가치가 있다.

트리플 저장소에서는 모든 정보를 (주어(subject), 서술어(predicate), 목적어(object)) 3 부분으로 표현한다. 트리플의 주어는 정점과 동등하며 목적어는 둘 중 하나이다.

  • 목적어가 원시 데이터 타입: 트리플의 서술어와 목적어는 주어 정점에서 key-value 값과 동등하다. 예를 들어 (루시, 나이, 33) 은 {"age": 33} 속성을 가진 정점 Lucy 와 같다.
  • 목적어가 그래프의 다른 정점: 서술어는 그래프의 간선이 되며 주어는 꼬리 정점, 목적어는 머리 정점이다. 예를 들어 (루시, 결혼하다, 알랭) 인 경우 루시, 알랭은 정점이고 결혼하다는 간선의 레이블이다.
  •  
@prefix : <urn:example:>.
_:lucy	a	:Person.
_:lucy	:name	"Lucy".
_:lucy	:bornIn	_:idaho.
_:idaho	a	:Location.
_:idaho	:name	"Idaho".
_:idaho	:type	"state".
_:idaho :within	_:usa.
 

위의 예제는 사이퍼 질의 언어에서 데이터 생성 부분을 터틀(Turtle)형식의 트리플로 작성한것이다. _:lucy :name "Lucy" 와 같이 서술어가 속성을 나타내면 목적어는 원시 데이터 타입의 문자열 리터럴이 된다. 반면 _:idaho :within _:usa 처럼 서술어가 간선을 나타내면 목적어는 정점이 된다.

 

@prefix : <urn:example:>.
_:lucy	a	:Person;	_:lucy	:name	"Lucy";		_:lucy	:bornIn	_:idaho.
_:idaho	a	:Location;	_:idaho	:name	"Idaho";	_:idaho	:type	"state";	_:idaho :within	_:usa.

 

세미콜론을 이용하면 동일 주어에 대해 여러 경우를 표현할 수 있다. 동일 주어를 같은 행에 나열하므로 훨씬 가독성이 좋아보인다.


- 시맨틱 웹

원래 트리플 저장소 데이터 모델과 시맨틱 웹은 완전히 독립적이지만 트리플 저장소에 관한 내용을 읽다보면 매우 밀접한 관계가 있다고 생각하기 때문에 간략하게 알아보도록 하자.

 

시맨틱 웹의 기본 개념은 웹 사이트가 사람이 읽을 수 있도록 텍스트와 이미지 정보등을 게시하고 있으니 이를 컴퓨터가 읽을 수 있게 기계가 판독 가능한 데이터로도 정보를 게시하는것이 어떨까라는 개념이다. 자원 기술 프레임워크(RDF, Resource Description Framework) 는 서로 다른 웹 사이트가 일관된 형식으로 데이터를 게시하기 위한 방법을 제안한다.


- RDF 데이터 모델

트리플 저장소에서 사용한 터틀 언어는 RDF 데이터를 사람이 읽을 수 있는 형식으로 표현한다. 때로는 RDF를 XML 형식으로 쓰기도 한다. RDF 는 인터넷 전체의 데이터 교환을 위해 설계했기 때문에 이상한 점이 있다.

 

RDF 에서 트리플의 주어, 서술어, 목적어는 주로 URI 다. 예를 들어 WITHIN, LIVES_IN 이 아니라 <http://my-company.com/namespace#within> 과 같은 URI 이다. URI 는 RDF 관점에서 네임스페이스일뿐이라며 반드시 접속가능한 URL 일 필요는 없다.


- 스파클 질의 언어

스파클은 RDF 데이터모델을 사용한 트리플 저장소 질의 언어이다. 스파클은 사이퍼보다 먼저 만들어졌고 사이퍼의 패턴 매칭은 스파클에서 차용했기 때문에 매우 유사한면이 있다.

 

PREFIX : <urn:example:>

SELECT ?personName WHERE {
	?person :name ?personName.
	?person :bornIn / :withIn* / :name "United States".
	?person :liveIn / :withIn* / :name "Europe".
}
 

위의 예제는 사이퍼 질의 언어의 예제를 스파클로 표현한것이다. 매우 유사한것을 알 수 있다. 한 가지 차이점이라면 RDF 는 속성과 간선을 구별하지 않고 서술어만 사용하므로 속성 매칭시 동일한 구문을 사용한다. 예를 들어 사이퍼 질의 언어에서 (usa {name: 'United States'}) 로 표현한것을 스파클에서는 ?usa :name 'United States'. 로 표현할 수 있다.


- 그래프 DB vs 네트워크 모델(코다실)

어떻게 보면 문서 DB 의 역사에서 네트워크 모델은 그래프 DB 와 매우 유사해보이지만 몇 가지 차이점들이 존재한다.

  • 코다실은 다른 레코드타입과 중첩가능 레코드 타입을 지정하는 스키마가 존재하지만, 그래프 DB 는 제한이 없으며 모든 정점은 다른 정점으로 가는 간선을 가질 수 있다.
  • 코다실은 특정 레코드 접근시 접근 경로를 통해서 하나를 탐색해야하지만, 그래프 DB 는 고유 ID 로 임의 정점을 참조할 수 있다.
  • 코다실은 레코드 하위 항목은 정렬된 집합이라서 삽입, 삭제와 같은 연산시 이를 유지하기 위한 비용이 들지만, 그래프 DB는 정점과 간선의 정렬을 고려하지 않는다.
  • 코다실은 질의가 명령형이라서 스키마가 변경되면 질의가 손상되지만, 그래프 DB는 사이퍼나 스파클같은 고수준의 선언형 질의 언어를 제공한다.

- 데이터로그

데이터로그는 스파클이나 사이퍼보다 오래된 언어이며 질의 언어의 기반이 되는 초석을 제공하였다. 데이터로그는 트리플 저장소 모델과 유사하지만 (주어, 서술어, 목적어)로 작성하는 트리플과 달리 서술어(주어, 목적어)로 작성한다.

 

name(namerica, 'North America').
type(namerica, continent).

name(usa, 'United States').
type(usa, country).
within(usa, namerica).

...

 

위와 같이 데이터를 정의할 수 있다. 이전 질의를 데이터로그로 표현하는 부분에서 프롤로그의 부분집합으로 설명하고 있는데, 실제로 사용하지 않는다면 이 부분은 너무 깊은 내용이기 때문에 생략하도록 하겠다. 데이터로그가 어떤것인지만 대략적으로 알고 필요할 때 깊게 공부해보길 바란다.

댓글