Please enable JavaScript to view the comments powered by Disqus.2. 데이터 모델과 질의 언어 (Data Models and Query Languages)
Search
🕹️

2. 데이터 모델과 질의 언어 (Data Models and Query Languages)

작성일자
2021/08/18
공개여부
tags
data
model
query
language
책에서 데이터 모델링에 대해 다루는데 그 데이터 모델링이 무엇인지 먼저 짚고 넘어간다. SQL 을 포함하여 그래프 까지 다루게 되는 광범위한 내용이다.

데이터 모델링이란?

데이터를 생산하여 Database에 저장하기 위해 처리하는 과정
데이터 모델에는 그 깩체가 다른 객체와 규칙을 대표하도록 설계되어야 하고 이 과정에서 데이터는 비즈니스 규칙, 규제 그리고 정부의 정책까지 데이터를 통해 표현할 수 있도록 한다(이러한 로직 들)
데이터 모델은 데이터의 설명과 의미 및 데이터의 일관성 제약 조건을 구성하는 추상 모델로 정의된다.
추상화: 현실 세계를 일정한 형식에 맞추어 표현한다.
단순화: 현실 세계를 약속된 규약이나 제한된 표기법과 언어로 표현한다
명확화: 누구나 이해가기 쉽게 애매모호함을 제거한다.

모델링의 3단계

개념적 모델링 → 논리적 모델링 → 물리적 모델링
개념적 모델링: 현실 세계의 데이터를 추상화하여 데이터로 표현하는 과정
Entity를 구성하고 그 entity 의 속성, 필드 등을 정의한다.
논리적 모델링: 논리적으로 데이터를 저장하기 위해 수립하는 단계
데이터의 구성 요소를 구조화 하고, 그들간의 관계를 정의한다.
물리적 모델링: 디스크에 데이터가 저장될 수 있도록 논리적 모델을 물리적 데이터 구조로 변환시키는 과정
DB에서 구현하기 위한 데이터 모델을 표현한다

데이터 모델과 질의 언어

데이터 모델 S/W 가 어떻게 작성됐는지 뿐 아니라 그 문제를 어떻게 생각해야 하는지에 대해서도 영향을 미친다.
대부분의 app은 이러한 데이터 모델에 다른 데이터 모델의 계층을 두어 만든다.
핵심은 다음 하위 계층의 관점에서 데이터 모델을 표현하는 방법이다.
아래로 갈 수록 컴퓨터 친화적이고
위로 갈 수록 더 복잡한 문제를 추상화 한다.
1.
개발자는 현실을 보고 객체나 데이터 구조, 그 구조를 다루는 API를 모델링한다
2.
데이터 구조를 저장할 때는 Json, XML, RDB, graph 등의 모델로 표현한다
3.
엔지니어는 2번의 모델을 네트워크상 바이트 단위로 표현하는 방법을 결정하고 그것을 질의, 탐색, 조작, 처리할 수 있게 한다.
4.
더 낮은 수준에서 하드웨어 엔지니어는 전류, 빛의 파동, 자기장 등의 관점에서 바이트를 표현하게 한다
데이터 모델은 그 위에서 소프트 웨어가 할 수 잇는 일과, 할 수 없는 일의 선택에 지대한 영향을 주기 때문에 애플리케이션에 적합한 모델을 선택하는 것은 상당히 중요하다
이 챕터에서는 relation model, document model, graph-based data model 을 비교한다

관계형 모델과 문서 모델

가장 잘 알려진 모델, 관계형 모델을 기반으로 한 SQL 이다.
SQL은 테이블에서 관계(Relation)로 구성되고 순서 없는 tuple(row) 모음이다.
관계형 모델은 이론적 제안이었기 때문에 효율성에 많은 문제를 제기했었다.
하지만, 정규화된 구조로 데이터를 저장하고 질의할 필요가 있는 곳에서 대부분이 선택하는 도구가 됐다.
비즈니스 데이터처리가 근원이 되었다.
트랜잭션 처리(은행 거래, 항공 예약, 창고 재고)
일괄 처리(송장 작성, 급여 지불, 보고)
다른 DB는 database 내부 데이터 표현에 대한 고민이 필요했지만, RDB 모델은 구현 세부 사항을 숨긴다
1970~80년대는 네트워크 모델계층형 모델 이 우위를 선점했지만, 그리 오래가지 못했다.
RDB는 비즈니스 데이터 처리라는 본래 영역을 넘어 대부분의 서비스(온라인 게시물, SNS, 전자 상거래, 게임, SaaS(Software-as-a-Service)에 보편화되었다

NoSQL의 탄생

2010년대 NoSQL이 관계형 모델을 뒤집으려는 가장 최신의 시도이다.
NoSQL DB를 채택한 데는 다음과 같은 원동력이 있다.
대규모 data set 에서 매우 높은 쓰기 처리량 달성이 RDB 보다 쉽다.
상용 DB 보다 무료 오픈소스에 대한 선호도 확산
관계형 모델에서 지원하지 않는 특수 질의
관계형에서 스키마 제한에 대한 불만, 더 동적이고 풍부해진 데이터 모델에 대한 바람
한 사용 사례에 맞는 기술이 또 다른 기술에서 최적화 될 순 없다. 그러므로 RDB가 다양함을 가진 비관계형 data store와 함께 사용될 것이다.
이것을 ployglot persistence 다중 저장소 지속성이라 한다.

객체 관계형 불일치

임피던스 불일치(impedance mismatch)
객체 지향형 언어로 개발하면 관계형 DB를 사용할 경우 로우, 칼럼을 객체로 변경하는 전환 계층이 필요하다. 이러한 불일치를 의미한다.
Impedance: 전기 회로에서 입력과 출력에 일정한 임피던스(교류에 대한 저항)을 갖고있다. 한 회로의 출력을 또 다른 회로로 연결할 때 두 회로의 입력 임피던스가 일치할 경우 전력 전달은 최대가 된다. 불일치하면 신호 반사 및 여러 문제를 일으킨다. (전자공학 용어)
아무리 Hibernate 과 같은 ORM을 사용하더라도 두 모델 간의 차이를 완벽히 숨길 수 없다.
자주 인용될 예시이다.
이러한 프로필은
SQL에선 정규화 표현으로 직위, 학위, 학력, 연락처 정보를 개별 테이블에 저장하고 위의 테이블 처럼 보여준다
가장 전통적인 RDB 설계
SQL에 xml, json, object 타입이 지원되면서 하나의 row에 다중 값을 저장할 수 있고 문서 내 질의와 색인이 가능해졌다.
컬럼에 object를 저장하는 방식
직업, 학력, 연락처 정보를 json 이나 xml로 부호화해 DB 텍스트 칼럼에 저장하여 반환한다
이러면 텍스트 칼럼에 검색이 불가능하다 (CC)
이력서는 모든 내용을 갖추고 있는 문서 이기 때문에 JSON이 표현에 적합하다

Json 구조는

임피던스 불일치를 해결할 수 있다고 생각한다.
다중 테이블 스키마보다 더 나은 지역성을 갖는다
지역성: 한번 접근한 데이터는 다시 접근할 가능성이 높다
일대다 관계는 의미상 데이터 트리 구조와 같다
트리 구조는 JSON 표현이 명시적이다.

다대일과 다대다 관계

책을 읽어보다 일대다와 다대일은 개념은 갖지만, 다른 용도로 설명한다.
이력서에서 지역(region_id) 와 (industry_id)는 평문이 아닌 ID로 주어졌고, 이렇게 데이터를 관리할 경우 다음과 같은 이점이 있다.
일관된 스타일과 철자
모호함 회피(서울, 서울시, 서울특별시와 같은 단어를 정제하여 사용)
갱신의 편의성
현지화해 지역과 업계를 사이트를 보는 사람의 언어로 표시
더 나은검색 제공(워싱턴 주에 있는 자선가)
중복의 문제: ID를 사용하면 해당 아이디를 사용하면 되지만, text로 저장하면 모든 레코드에 중복이 발생한다.
로직: ID를 가지면 의미 없는 데이터 이지만, 의미를 갖는 데이터라면 로직이 변경되면서 데이터가 갱신되어야 할 필요도 있다.
하지만, 다대일은 문서 모델에 적합하지 않다. 조인 기능을 제공하지만, 지원이 약하기 때문이다.
join 이 없거나 약하다면 다중 질의를 통해 마치 join을 수행하는 것 처럼 보일 수 있지만 application code 로 모든것을 구현해야 한다.

Application 이 성장함에 따라 데이터는 점점 상호 연결되는 경향이 있다.

entity 로 조직을 연결한다면 조직에 관련된 정보를 링크하거나, 그 조직의 로고, 추가 정보를 관리할 수 있다.
이력서에 추천서를 추가할 경우 추천인의 사진이 변경되면 한번에 모든 추천서의 사진이 변경되도록 구현이 가능하다.
아래 그림은 추천서와 같은 새로운 기능이 Many-to-Many를 어떻게 필요로 하는지 보여준다.

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

NoSQL 에서 Many-to-Many 표현하는 방법에 대한 논쟁이 반복되고 있다.
계층모델: 1970년대 가장 인기있는 DB는 IMS(Information Management System) 인데 이 설계를 계층 모델이라 부른다.
계층 모델은 JSON 과 매우 비슷한 데이터를 사용하는데 중첩된 record tree 로 표현한다.
여기서도 개발자는 비정규화 된 데이터를 중복으로 표현할지, 참조를 수동으로 해결할지 결정했다.
이러한 계층 모델의 한계를 극복하기 위해 관계형 모델(SQL이 되어 세상을 지배했다)과 네트워크 모델이 있다.

네트워크 모델

CODASYL(Conference on Data System Language) 모델
코다실 모델은 계층 모델을 일반화 한다
계층 모델에서 tree 구조는 정확히 하나의 부모가 있다.
네트워크 모델에선 다중 부모가 있을 수 있다.

네트워크 모델의 "접근 경로"

접근 경로: 레코드에 접근하는 방법은 root record 에서 연속된 연결경로를 따르는 방식이다.
문제는 다중 부모가 가능하기 때문에 M:N을 구현하면 마치 N차원의 공간을 항해하는 것 같다고 한다.
1970년대는 disk가 아닌 테이프이기 때문에 이런 환경에서 효율적일 수 있지만 현대 사회의 디스크와 application 요구사항에선 어려운 상황에 놓인다. → 결국 코드를 다시 짜야한다

관계형 모델

관계형 모델이 하는 일: 알려진 모든 데이터를 배치하는 것이다.
1.
얽히고 설킨 구조에서 따라가야할 복잡한 경로가 없다.
2.
임의 조건에 해당하는 모든 row를 볼 수 있다.
3.
다른 테이블과의 관계를 신경쓰지 않고 새로운 row를 삽입할 수 있다.
이미 앞선 책에서 다뤘지만, 관계형 모델에선 query optimizer 가 존재하고 이를 통해 어떤 순서로 접근할지를 개발자가 결정하지 않는다 → 이것이 네트워크 모델의 "접근 경로"가 된다.
심지어 index를 변경하면 옵티마이저가 자동으로 적합한 방법을 선택하니 새로운 기능을 추가하는 것이 훨씬 쉽다

문서 데이터베이스와 비교

이러한 측면에서 계층 모델로 돌아왔다고 볼 수있다.
물론 FK와 같은 식별자가 존재하고 M:N 관계를 표현하는 방법이 RDB와 크게 다르지 않다
"문서 참조(document reference)" 라 표현하고 읽기 시점에 확인한다.

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

document db를 선택하는 주된 이유는 스키마의 유연성, 지역성에 기인한 더 나은 성능 때문이다.
RDB는 join, M:1, M:N 을 지원하면서 documentDB에 대항한다

어떤 데이터 모델이 코드를 더 간단하게 할까?

데이터 구조(관계의 유무)에 따라 다르다
문서모델을 중심으로 설명함
1:N 이며 관계 트리로 한 번에 전체 tree를 적재한다면 문서 모델이 적합하다
바로 참조할 수 없기 때문에 "사용자 251의 position 목록 2번째 item" 과 같이 표현해야 한다
문서 구조를 나누어 찢는 상황(정규화)으로 구현하면 스키마가 복잡해져 다루기 어려워진다
Document DB의 부족한 join 기능은 데이터에 따라 독이 될 수도 있고, 약이 될 수도 있다.
다대다 관계가 필요하다면 RDB가 더 적합하다
비정규화 된 데이터의 일관성을 유지하기 위해 추가 작업이 수반된다.
따라서, 데이터 항목에 존재하는 관계 유형에 따라 코드가 복잡해질 수도 간단해질 수도 있다.
상호연결이 더 많아진다면 RDB 보다도 graph 데이터 모델이 자연스럽게 될 수도 있따.

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

schema-on-write(쓰기 스키마) - 정적
compile type 과 유사하다
관계형 DB의 접근 방식, 명시적이고 모든 data 는 모두 스키마를 따르고 있음을 보장한다
schema-on-read(읽기 스키마) - 동적
runtime type 확인과 유사하다
데이터 구조는 암묵적이고 읽을때만 해석된다.
흔히 Document 모델을 schemaless 라 하지만 암묵적으로 스키마가 있는것과 같지만, 이를 강요하지 않는다.
이 차이는 application의 데이터 타입을 변경할때 뚜렷이 드러난다
// fullname을 first_name 과 name으로 쪼개어 변경하고자 한다 if (user && user.name && !user.first_name) { user.first_name = user.name.split(" ")[0]; }
Java
복사
document model의 경우
insert 하는 새로운 문서부터 이 로직을 적용하여 저장하고
기존의 문서는 읽었을때 수정한다
정적 타입의 데이터베이스
migration이 필요하다
alter table users add column first_name text; update users set first_name = split_part(name, ' ', 1); # postgress update users set first_name = substring_index(name, ' ', 1); # mysql
SQL
복사
이런 migration이 싫다면 default null을 해두고 읽는 시점에 update 를 실행한다
이러한 스키마 변경은 느리고 중단시간을 요구하기 때문에 평판이 안좋다 MySQL은 alter table을 실행하면 테이블 전체를 복사하는 과정이 있기 때문에 다른 DB와 예외적으로 중단시간이 발생한다
읽기 스키마(동적) 방식은 모두 동일한 구조가 아닐 때 유용하다
각각 다른 유형의 object를 자체 테이블에 저장하는 방법은 실용적이지 않다
사용자가 제어할 수 없고 변경 가능한 외부 시스템에 의해 데이터 구조가 결정된다.
스키마리스에 대한 내용은 4장에서 다룬다

질의를 위한 데이터 지역성

문서는 xml이나 json을 bson 같은 형태나 이진변형으로 저장한다
application에 전체 문서(select all 이 아니라 여러 테이블에 접근하여)에 접근해야 할 때 저장소의 "지역성을 활용"하면 성능에 이점이 있다.
데이터가 multi table 이고, 전체 범위를 검색한다면, 색인도 여러개가 필요하고 다중 색인 검색이 동반된다
지역성의 이점은 한 번에 해당 문서의 많은 부분을 필요로 하는 경우에만 적용된다.
지역성을 위해 데이터를 그룹화 하는 개념은 문서 모델에만 국한되지 않는다
오라클에선 multi-table index cluster table 이라는 기능이 있다.

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

관계형 DB에 xml, josn을 다루는 문서형 색인을 지원한다.
postgreSQL (9.3~), mysql(5.7~), IBM DB2 (10.5~)
문서형 DB는 관계형 조인을 지원한다 (다만 네트워크 왕복이 추가로 필요하고, 최적화가 덜 되기 때문에 RDB보다 느리다)
이 두 모델은 시간이 지나면서 점점 더 비슷해지고 있다.
이러한 변화는 긍정적이며, 미래 DB들이 가야할 올바른 길이다.

데이터를 위한 질의 언어

관계형 모델은 등장했을때 질의하는 새로운 방법이 함께 등장했다.
SQL은 선언형 질의 언어이다.
IMS, CODASYL은 명령형 질의 언어이다
function getSharks() { var sharks = []; for (var i = 0; i < animals.length; i ++) { if (animals[i].family === "Sharks") { sharks.push(animals[i]); } } return sharks; }
SQL
복사
관계 대수로는 다음과 같이 작성한다
sharks=σfamily="sharks"(animal)sharks = σ_{family = "sharks"}(animal)σ 는 선택연산자이고 family = "Sharks" 조건에 일치하는 동물만 반환한다
SQL은 관계 대수 구조를 상당히 유사하게 따랏다
select * from animals where family = 'Sharks';
SQL
복사
이러한 선언형 질의 언어는 목표를 달성할 방법이 아닌 알고자 하는 데이터의 패턴, 결과가 충족할 조건과 반환(정렬, 그룹화, 집계) 방법을을 명시한다

목표를 달성할 방법은 optimizer가 알아서 할 일이다.

디스크를 확보할 목적으로 record를 다른 disk로 옮긴다면 동물이 나타나는 순서가 달라질 수 있다.
그러면 현재 정렬과 동일하게 반환하려면 명령문의 코드가 바뀌어야 할 필요가 있다
SQL은 순서를 보장하지 않으므로 record가 옮겨져 순서가 바뀌어도 문제 없다
SQL이 기능적으로 제한적 → database 에게 자동으로 최적화할 수 있는 여지를 준다.
순서를 보장하지 않기 때문에 병렬 실행에 적합하다

웹에서의 선언형 질의

<ul> <li class="selected"> <p>Sharks</p> <ul> <li>Great White Shark</li> <li>Tiger Shark</li> <li>Hammerhead Shark</li> </ul> </li> <li> <p>Whales</p> <ul> <li>Blue Whale</li> <li>Humpback Whale</li> <li>Fin Whale</li> </ul> </li> </ul>
HTML
복사
선택한 항목을 .selected css로 표시한다
<p>Sharks</p> 가 현재 선택한 제목이다
li.selected > p { background-color: blue; }
CSS
복사
CSS 대신 XSL로 동일한 기능을 표현한다
<xsl:template match="li[@class='selected']/p"> <fo:block background-color="blue"> <xsl:apply-templates/> </fo:block> </xsl:template>
XML
복사
명령형 접근 방식을 사용한다면 다음과 같다
var liElements = document.getElementsByTagName("li"); for (var i = 0; i < liElements.length; i++) { if (liElements[i].className === "selected") { var children = liElements[i].childNodes; for (var j = 0; j < children.length; j++) { var child = children[j]; if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "P") { child.setAttribute("style", "background-color: blue"); } } } }
JavaScript
복사
sharks 대신 다른것을 선택하여 .selected 가 삭제된 경우 파란색은 삭제가 되지 않는다
css를 사용한다면 .selected 를 감지하여 sharks 선택을 해제할 때 배경을 제거하겠지만 위 코드에선 그렇지 않다
document.getElementsByTagName("selected"); 이나 document.evaluate() 같은 API의 장점을 취하고 싶다면 코드를 재작성 해야한다
웹 브라우저는 선언형 CSS 스타일을 사용하는 것이 더 효율적이다
마찬가지로 DB에서 SQL 같은 선언형 질의 언어가 명령형 API 보다 훨씬 좋다고 나타났다
IMS와 CODASYL은 모두 명령형 질의 API를 사용했다.

맵리듀스(Map Reduce) 질의

map reduce는 대량의 데이터를 처리하기 위한 programming 모델이다.
NoSQL은 제한된 형태의 map reduce를 사용한다
선언형 질의 언어도 완전한 명령형 질의 API도 아닌 그 중간 어딘가에 있다.
1.
다음 코드는 상어과에 속하는 종만 보이도록 필터링한다
2.
관측치가 발생한 달력의 월(month) 별로 그룹화한다
3.
해당 달의 모든 관측치에 보여진 동물 수를 합친다
자세한 내용은 주석을 참조해 설명한다
db.observations.mapReduce( function map() { // 모든 문서에 한번씩 호출된다 this는 문서 객체로 설정된다. var year = this.observationTimestamp.getFullYear(); var month = this.observationTimestamp.getMonth() + 1; emit(year + "-" + month, this.numAnimals); // emit은 key-value를 생산한다 // ("2013-12"), ("2014-1") 연도와 월을 key로 만든다 // value는 문서의 numAnimal 필드이다. }, function reduce(key, values) { return Array.sum(values); }, { query: { family: "Sharks" }, out: "monthlySharkReport" } );
JavaScript
복사
map reduce 대상 데이터
1.
map: 이 함수의 결과로 emit("1995-12", 3)과 emit("1995-12", 4) 가 실행된다.
2.
reduce: reduce("1995-12", [3, 4]) → 7을 반환한다
MongoDB의 경우 map과 reduce 함수는 수행할 때 제약사항이 있다. 두 함수는 pure 함수여야 한다.
pure 함수라는 제약 사항이 있어도 강력하다
질의 중간에 javascript를 사용하는 것은 고급 질의가 가능한 훌륭한 기능이다
map, reduce에만 해당하는 것은 아니다
map reduce 사용성 문제는 연계된 자바스크립트 함수 두개를 신중하게 작성해야 한다는 점이다. 하나의 query를 작성하는 것 보다 어려운 일이다.
선언형 질의 언어는 optimizer 가 질의 성능을 높힐 기회를 제공하기 때문에 MongoDB는 집계 파이프라인(aggregation pipline) 이라는 선언형 질의 언어를 추가했다
db.observations.aggregate([ { $match: { family: "Sharks" } }, { $group: { _id: { year: { $year: "$observationTimestamp" }, month: { $month: "$observationTimestamp" } }, totalAnimals: { $sum: "$numAnimals" } }} ]); select sum(numAnimals) from report where family = 'Sharks' group by concat(Year(observationTimestamp), Month(observationTimestamp));
JavaScript
복사
이 부분에서 배울점은 NoSQL이 SQL을 재발견 하고 있다는 점이다.

그래프형 데이터 모델

일대다 관계이거나, record 간 관계가 없다면 문서 모델이 적합하다
다대다 관계를 다루게 되면 연결이 더 복잡해지고, 이때는 관계형 보다도 그래프 모델이 더 자연스럽다
그래프는 정점(Vertex - node 혹은 entity 라고도 한다)과 간선(edge 관계나 호 arc라고 부르기도 한다) 두 객체로 이루어져 있다.
일반적인 예로 소셜 그래프, 웹 그래프, 도로나 철도 네트워크에 사용된다.
이러한 그래프 상에서 동작하는 잘 알려진 여러 알고리즘이 있다.
네비게이션 시스템 - 도로간 최단거리
pagerank: 웹 그래프를 사용해 웹 페이지의 인기 검색 결과 순위 결정
그래프는 동종 데이터에 국한되지 않는다. (친구, 게시물, 코멘트 등등)
이번 절에서 사용할 예시이다. 아이다호에서 태어난 루시와, 프랑스 본에서 태어난 알랭은 결혼해 런던에서 살고 있다.
속성 모델: Neo4J, Titan, InfiniteGraph
트리플 저장소: Datomic, Allegrograph
그래프용 선언형 질의 언어: Cypher, Sparql, datalog
를 살펴본다

속성 그래프

정점은 다음과 같은 요소로 구성된다.
고유한 식별자
유출(outgoing) 간선 집합
유입(incoming) 간선 집합
속성 컬렉션 (key-value)
간선은 다음과 같은 요소로 구성된다.
고유한 식별자
간선이 시작하는 정점(꼬리)
간선이 끝나는 정점(머리)
두 정점간 유형을 설명하는 레이블
속성 컬렉션(key-value)
다음의 예시는 간선과 정점을 RDB의 테이블을 이용해 설명한다.
주석을 보며 설명해야한다
# example 2-2 # 정점 테이블 CREATE TABLE vertices ( vertex_id integer PRIMARY KEY, properties json # 간선의 데이터를 저장하기 위한 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 # 정점의 데이터를 저장하기 위한 json ); # 그래프를 앞, 뒤로 순회하기 위한 index 생성 CREATE INDEX edges_tails ON edges (tail_vertex); CREATE INDEX edges_heads ON edges (head_vertex);
SQL
복사
이 모델의 특징은 다음과 같다.
1.
정점은 다른 정점과 간선으로 연결된다.
2.
정점이 주어지면 유입과 유출 간선을 찾을 수 있고 그래프를 순회할 수 있다.(앞, 뒤 방향으로 순회한다)
3.
다른 유형의 관계에 서로 다른 label을 사용하면 단일 그래프에 다른 유형의 정보를 저장하면서도 데이터 모델을 깔끔하게 유지할 수 있다.
그래프는 데이터 모델링에 많은 유연성을 제공하여 전통적인 RDB 스키마에서 표현하기 어려운 사례를 보여준다.
국가마다 지역 구조가 다르다
프랑스는 주(departement), 도(region) 미국은 군(country), 주(state) 이다.
여러 사실을 포함해 그래프를 확장할 수 있다.
그래프는 발전성이 좋아서 application 기능을 추가하는 경우 데이터 구조 변경을 수용하게끔 그래프를 확장할 수 있다.

사이퍼(Cypher) 질의 언어

속성 그래프를 위한 선언형 질의 언어로 Neo4J 를 위해 만들어졌다.
다음 예제는 삽입하는 사이퍼 질의를 보여준다.
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) -[:WITHIN]-> (NAmerica), (Lucy) -[:BORN_IN]-> (Idaho)
SQL
복사
(Idaho) -[:WITHIN]-> (USA) 의 경우 꼬리 노드는 Idaho, 머리 노드는 USA인 within 레이블의 간선이 된다.
이러한 정점과 간선을 보면 이러한 질문도 가능하다 "미국에서 유럽으로 이민 온 모든 사람들의 이름 찾기"
BORN_IN 간선을 가진 정점
LIVES_IN 중에 유럽을 가진 정점
을 모두 찾아서 name 속성을 반환하면 이 질문의 답이 가능하다
# example 2-4 # 미국에서 유럽으로 이민온 사람 찾기 MATCH (person) -[:BORN_IN]-> () -[:WITHIN*0..]-> (us:Location {name:'United States'}), (person) -[:LIVES_IN]-> () -[:WITHIN*0..]-> (eu:Location {name:'Europe'}) RETURN person.name
SQL
복사
이 쿼리는 다음과 같이 읽힌다
1.
person은 어떠한 정점을 향하는 BORN_IN 유출 간선을 가진다. name이 'United States' 인 Location 타입의 도달할 때까지 일련의 WITHIN outgoing 간선을 찾아라
2.
같은 person의 정점은 LIVES_IN outgoing 유출 간선도 갖는다. 이 간선과 WITHIN outgoing 간선을 따라가면 name이 'Europe' 인 Location 타입 정점에 도달한다
이 두 조건을 만족하는 정점(vertex)을 찾아라
하지만, 반대로 질의도 가능하다 name 속성이 미국과 유럽을 나타내는 정점을 찾고 BORN_IN, LIVES_IN incoming 간선을 통해 발견된 person을 반환하는 것도 가능하다
그런데 이것도 선언형 언어이기 때문에 자세히 지정할 필요 없이 쿼리를 작성하고 실행하면 optimizer가 가장 효율적인 전략으로 실행한다

SQL의 그래프 질의

예제 2-2 는 RDB에서도 그래프 데이터를 표현할 수 있음을 제안했다. 하지만, 당연히 어렵고 복잡하다
그래프 질의에서는 찾고자 하는 vertex를 찾기 전에 여러 간선을 순회해야 한다. → join 수를 고정할 수 없다.
() -[:WITHING*0..]-> () 쿼리가 문제인데 사이퍼에서 :WITHING*0.. 의미는 "0회 이상 WITHIN 간선을 따라가라" 라는 의미이다. (정규표현식의 * 연산자와 같다)
다음 예제는 recursive common table expression 재귀 공통 테이블식 을 사용해서 표현할 수 있다.
(postgreSQL, IBM DB2, Oracle, SQL 서버에서 지원한다)
WITH RECURSIVE -- in_usa 는 미국 내 모든 지역의 정점 ID 집합이다. in_usa(vertex_id) AS ( SELECT vertex_id FROM vertices WHERE properties->>'name' = 'United States' # 1. name이 미국인 정점을 찾아 in_use 정점 집합의 첫 element로 만든다. UNION SELECT edges.tail_vertex FROM edges # 2. in_use 집합의 정점들은 모둔 within 유입 간선을 따라가 같은 집합에 추가한다. # 모든 within 간선을 방문할 때까지 수행한다. JOIN in_usa ON edges.head_vertex = in_usa.vertex_id WHERE edges.label = 'within' ), -- in_europe 은 유럽 내 모든 지역의 정점 ID 집합이다. in_europe(vertex_id) AS ( SELECT vertex_id FROM vertices WHERE properties->>'name' = 'Europe' # 3. name 이 유럽인 정점을 찾아 1번과 동일하게 in_europe 집합을 만든다. UNION SELECT edges.tail_vertex FROM edges JOIN in_europe ON edges.head_vertex = in_europe.vertex_id WHERE edges.label = 'within' ), -- born_in_usa 는 미국에서 태어난 모든 사람의 정점 ID 집합이다. born_in_usa(vertex_id) AS ( # 4. 미국에서 태어난 사람을 찾기 위해 in_usa 집합의 각 정점에 대해 born_in incoming 간선을 따라간다 SELECT edges.tail_vertex FROM edges JOIN in_usa ON edges.head_vertex = in_usa.vertex_id WHERE edges.label = 'born_in' ), -- lives_in_europe 은 유럽에서 태어난 모든 사람의 정점 ID 집합이다. lives_in_europe(vertex_id) AS ( # 5. 동일하게 유럽에 사는 사람을 찾기 위해 in_europe 집합의 각 정점에서 lives_in incoming 간선을 따라간다 SELECT edges.tail_vertex FROM edges JOIN in_europe ON edges.head_vertex = in_europe.vertex_id WHERE edges.label = 'lives_in' ) SELECT vertices.properties->>'name' FROM vertices -- 미국에서 태어나 유럽에서 자란 사람을 찾아 join 한다. JOIN born_in_usa ON vertices.vertex_id = born_in_usa.vertex_id # 마지막으로 join을 이용해 미국에서 태어난 사람과 유럽에서 태어난 사람의 교집함을 구한다. JOIN lives_in_europe ON vertices.vertex_id = lives_in_europe.vertex_id;
SQL
복사
cypher 4줄이 SQL에서 29줄로 늘어난다. → 따라서 모델 선택은 중요하다

트리플 저장소와 스파클

트리플 저장소 모델은 그래프 모델과 거의 동등하다. 같은 생각을 용어만 다르게 설명한다.
트리플 저장소는 모든 정보를 (주어(subject), 서술어(predicate), 목적어(object)) 처럼 3 구문의 형식으로 저장한다.
i.e. 짐, 좋아하다, 바나나 → Jim, likes, bananas
Jim: 주어
likes: 서술어(동사)
bananas: 바나나 목적어
subject 주어는 그래프의 정점과 동일하다. 목적어는 다음 두 가지 중 하나다
1.
원시 데이터 값(String, integer) - 주어 정점
key, value가 된다.
(lucy, age, 33) → {"age": 33} 속성의 lucy와 같다
2.
그래프의 다른 정점: 서술어가 그래프의 간선이 되고, 주어는 꼬리 정점, 목적어는 머리 정점이 된다.
(lucy, marriedTo, alain)
주어와 목적어인 lucy, alain 은 모두 정점이고, 서술어 결혼하다는 두 정점을 잇는 간선이 된다.
@prefix : <urn:example:>. _:lucy a :Person. _:lucy :name "Lucy". _:lucy :bornIn _:idaho. _:idaho a :Location. _:idaho :name "Idaho". _:idaho :type "state". _:idaho :within _:usa. _:usa a :Location. _:usa :name "United States". _:usa :type "country". _:usa :within _:namerica. _:namerica a :Location. _:namerica :name "North America". _:namerica :type "continent".
SQL
복사
_:someName 을 그래프의 정점으로 표현한다.
_:someName 은 트리플이 같은 정점을 참조하는지 알지 못하기 때문에 존재한다.
_:usa :name "United States". 와 같이 서술어가 속성(attribute)이면 목적어는 문자열이 된다.
동일한 주어를 반복하는 작업은 단순 반복 작업이다.
# example 2-7 # 위의 쿼리를 더 간단하게 작성하면 이러하다 -> 터틀언어의 장점이 드러나는 예시 @prefix : <urn:example:>. _:lucy a :Person; :name "Lucy"; :bornIn _:idaho. _:idaho a :Location; :name "Idaho"; :type "state"; :within _:usa. _:usa a :Location; :name "United States"; :type "country"; :within _:namerica. _:namerica a :Location; :name "North America"; :type "continent".
SQL
복사
동일한 주어를 반복하는 것은 단순 반복 작업이다.
세미콜론을 통해 주어에 대해 여러 표현을 할 수 있다.

시맨틱 웹

트리플 저장소 데이터 모델은 시맨틱 웹과 완전히 독립적이다.
하지만 이 둘이 밀접한 관계가 있다고 생각하기 때문에 간단히 논의한다
웹 사이트는 사람이 읽을 수 잇는 정보를 게시하고 있으니, 컴퓨터가 읽게끔 기계가 판독 가능한 데이터로도 정보를 게시하는건 어떨까? 라는 개념이다.
RDF는 서로 다른 웹 사이트가 일관된 형식으로 데이터를 게시하기 위한 방법을 제안했다.
RDF는 서로 다른 웹사이트 데이터가 데이터 웹에 자동으로 결합할 수 있게 한다(?)
데이터 웹을 만물 데이터베이스 라 표현하며 이 이상 자세한 설명은 없다.
결론부터 이야기하면 망했다
현실에서 실현된 흔적이 없어 부정적인 견해를 보였다
지나치게 복잡한 표준 제안, 약어 모음

RDF(Resources Description Framework) 데이터 모델

예제 2-7의 터틀언어는 RDF 데이터를 사람이 읽을 수있는 형식으로 표현한다.
RDF를 XML 형식으로 쓰기도 한다
XML은 보기와 같이 장황하기만 하다
RDF 는 인터넷 전체의 데이터 교환을 위해 설계했기 때문에 이상한 점이 있다.
트리플의 주어, 서술어, 목적어는 주로 URI 이다.
서술어로 LIVES_IN, WITHIN 이 아닌 <http://my-company.com/namespace#within> 이나 <http://my-company.com/namespace#lives_in> 을 사용한다.
URL은 반드시 접속 가능한 주소일 필요는 없다.

스파클(SPARQL) 질의 언어

SPARQL: Protocol and RDF Query Language 의 줄임말이다.
RDF 데이터 모델을 사용한 트리플 저장소 질의 언어다.
cypher 보다 먼저 만들어졌으며, cypher의 패턴 매칭을 스파클에서 차용했기 때문에 매우 유사하다
미국에서 유럽으로 이민온 사람 찾기 를 다음과 같이 표현할 수 있다.
PREFIX : <urn:example:> SELECT ?personName WHERE { ?person :name ?personName. ?person :bornIn / :within* / :name "United States". ?person :livesIn / :within* / :name "Europe". }
SQL
복사
(person) -[:BORN_IN]-> () -[:WITHIN*0..]-> (location) # Cypher
?person :bornIn / :within ?location. # SPARQL
이 두 표현식은 동등하다 (스파클은 ?로 시작한다)
그래프 DB와 네트워크 모델의 비교 언뜻 보면 CODASYL의 네트워크 모델과과 graph 모델이 비슷해보인다. 하지만 아래와 같은 명백한 차이점이 있다. 1. 코다실은 다른 레코드 타입과 중첩 가능한 record 타입을 지정하는 스키마가 있으나, 그래프에는 이러한 스키마가 없다. 2. CODASYL은 어떠한 record를 찾기 위해 접근 경로를 통해 하나하나 탐색하지만, 그래프는 id와 색인을 이용해 해당 vertex를 직접 참조한다 3. CODASYL은 정렬된 집합으로 저장한다. 따라서 저장할 때도 그 위치를 고려해 저장해야 하지만 그래프는 그런 개념이 없다 query를 입력할때 정렬 기준을 명시할 수 있다. 4. CODASYL은 명령형 언어를 사용하고, 스키마가 변경되면 명령형 언어를 모두 바꿔야 한다. 그래프도 물론 명령형 언어를 사용할 수 있지만, cypher, sparql 같은 고수준 선언형 언어를 사용할 수 있다.

초석: 데이터 로그

DataLog는 sparql, cypher 보다 오래되었고 1980년대에 광범위하게 사용되었다. 지금은 잘 사용되지 않고 알려져 있지도 않지만, 이후 query language의 초석이 된다.
(주어, 서술어, 목적) 형식의 트리플을 서술어(주어, 목적어) 로 작성한다.

정리

다양한 모델을 간략하게 살펴봤다. 이 내용을 보면서 주어진 요구사항에 알맞은 데이터 모델을 설계하고 싶다는 "욕구"를 자극했으면 한다.
1.
M:N을 표현하기에 tree는 적절하지 않다. 이 문제를 해결하기 위해 RDB 모델이 고안되었다.
2.
최근엔 RDB 모델도 적합하지 않다 여겨 "NoSQL" 비관계형 데이터 저장소가 등장했다. 여기엔 두가지 갈래가 있다.
문서 데이터베이스는 데이터가 문서 자체에 포함되어 있고, 그 문서는 다른 문서와 관계가 없다.
그래프 데이터베이스는 Document DB와 반대로 모든 것이 잠재적으로 연관이 있다고 여긴다.
3.
문서와 그래프의 공통점
스키마를 강제하지 않기 때문에 변화하는 요구사항에 맞춰 application을 변경할 수 있다.
하지만 application은 특정 구조를 갖는다고 가정할 가능성이 높다
스키마가 명시적인지(쓰기), 암시적인지(읽기) 의 문제이다.
4.
각 데이터 모델은 고유의 질의 언어나 프레임워크를 제공한다.
SQL
Map Reduece
aggregation pipeline
cypher
SPARQL
datalog
5.
4번과 연관해 CSS, XSL/XPath 도 다뤘다.
여기서 다루지 않은 데이터모델은 다음과 같다
게놈: 염기 서열 유사도 검색을 수행하는데 사용한다.
입자 물리학자들이 사용하는 대용량 데이터모델(사용자 정의 솔루션)
전문 검색(full-text): 광범위한 주제이지만 3-3에서 간략히 다룬다.