-
데이터 중심 애플리케이션 설계 2장Data Engineering 2022. 2. 21. 17:58
1. 데이터 모델
- 다양한 유형의 데이터 모델이 있고 각 데이터 모델은 사용 방법에 대한 가정이 있다.
즉 어떤 동작은 쉽고 어떤 연산은 빠르고 다른 연산은 느리고, 어떤 데이터 변환은 자연스럽고 어떤 데이터 변환은 부자연스럽다.
- 하나의 데이터 모델을 완전히 익히는 것도 어려운 일이다. 그러나 데이터 모델은 그 위에서 소프트웨어가 할 수 있는 일과 할 수 없는 일에 많은 영향을 주므로 애플리케이션에 적합한 데이터 모델을 선택하는 작업이 상당히 중요하다.
- 관계형 데이터 모델, 문서 모델, 그래프 기반 데이터 모델(속성 그래프, 트리플 저장소 모델)을 2장에서 다룬다.
관계형 모델(SQL)
- 관계(relation=sql에서 table)로 구성되고 각 관계는 순서없는 튜플(tuple, sql = row)의 모음
비관계형 데이터 모델(NoSQL)
- 임피던스 불일치(impedence mismatch) 해결을 위한 NoSQL 출현
* 임피던스 불일치: 애플리케이션 코드와 데이터베이스 모델 객체(테이블,로우,컬럼)사이에 전환 계층이 필요한 경우
- NoSQL 모델을 쓰면 불필요한 계층 변환 작업이 사라짐
1 문서 모델
- 애플리케이션이 주로 일대다 관계(트리 구조 데이터)거나 레코드간 관계가 없는 경우 적합
- 데이터가 문서 자체에 포함돼 있으면서 하나의 문서와 다른 문서 간 관계가 거의 없는 경우
2 그래프형 데이터 모델
- 다대다 관계가 매우 일반적일 경우 적합
어떤 모델을 사용하는 것이 적합할까?
- 예시: 관계형 스키마에서 이력서의 표현

방법 1: 전통적인 SQL 모델을 사용,
정규화된 테이블을 만들기(상단 그림)
방법 2: XML 데이터 지원이 추가된 SQL 데이터타입 사용,
단일 로우에 다중 값을 저장하고 문서 내 질의와 색인이 가능해짐
방법 3: 직업,학력,연락처 정보를 json이나 xml 문서로 부호화하여 데이터베이스 텍스트 칼럼에 저장하고, 애플리케이션이 구조와 내용을 해석하게 함
일반적으로 부호화된 칼럼의 값을 질의하는 데 데이터베이스를 사용할 수 없음
방법 4: 문서 데이터베이스 사용(지역성 UP)
{ "user_id": 251, "first_name": "Bill", "last_name": "Gates", "summary": "Co-chair of the Bill & Melinda Gates... Active blogger.", "region_id": "us:91", "industry_id": 131, "photo_url": "/p/7/000/253/05b/308dd6e.jpg", "positions": [ {"job_title": "Co-chair", "organization": "Bill & Melinda Gates Foundation"}, {"job_title": "Co-founder, Chairman", "organization": "Microsoft"} ], "education": [ {"school_name": "Harvard University", "start": 1973, "end": 1975}, {"school_name": "Lakeside School, Seattle", "start": null, "end": null} ], "contact_info": { "blog": "http://thegatesnotes.com", "twitter": "http://twitter.com/BillGates" } }관계형 데이터베이스와 달리 조인 필요 X 질의 하나로 충분
문서 데이터베이스
- 스키마리스(Schemaless)?
정확히는 schema-on-read (반대말 schema-on-write)
스키마리스는 언제 강점을 가지는가?
- 예시: 하나의 필드에 사용자의 전체 이름 저장 --> 성과 이름을 분리하여 저장하도록 변경
- schema on read의 경우: 간단함
if (user && user.name && !user.first_name) { // Documents written before Dec 8, 2013 don't have first_name user.first_name = user.name.split(" ")[0]; }- schema on write(rdbms)의 경우: migration 필요, 느리고 중단시간을 요구하여 좋지 않음
ALTER TABLE users ADD COLUMN first_name text; UPDATE users SET first_name = split_part(name, ' ', 1); -- PostgreSQL UPDATE users SET first_name = substring_index(name, ' ', 1); -- MySQL2. 질의 언어
명령형 언어
- 보통의 프로그래밍 언어
- 특정 순서로 특정 연산을 수행하도록 컴퓨터에 지시함. (방법)
선언형 언어
- SQL, (CSS, XSL : 문서의 스타일을 지정하기 위 한 선언형 언어 )
- 알고자 하는 데이터의 패턴, 즉 결과가 충족해야 하는 조건과 데이터를 어떻게 변환(예를 들어 정렬, 그룹화, 집계) 할지를 지정 (목적)
- 어떤 색인과 어떤 조인 함수를 사용할지, 질의의 다양한 부분을 어떤 순서로 실행할지(방법)를 결정하는 것은 데이터베이스 시스템의 질의 최적화기가 할 일이다.
- 장점
- 일반적으로 명령형 API보다 더 간결하고 쉽게 작업할 수 있음
- 데이터베이스 엔진의 상세 구현이 숨겨져 있어 질의를 변경하지 않고도 데이터베이스 시스템의 성능을 향상시킬 수 있음
명령형 언어 vs 선언형 언어
- 예시: 동물 목록에서 상어만 반환하기
- 명령형 언어 (대부분의 프로그래밍 언어가 명령형 언어임)
function getSharks() { var sharks = []; for (var i = 0; i < animals.length; i++) { if (animals[i].family === "Sharks") { sharks.push(animals[i]); } } return sharks; }- 선언형 언어
SELECT * FROM animals WHERE family = 'Sharks';선언형, 명령형 질의
- 선언형: DB, Web Browser에서 Good
- 명령형: 문서형 DB에서 Good (절대적인것은 아님)
- ex. CSS, XSL
- ex. 다음 웹 문서에서
<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>- 선언형 질의 사용시(CSS) : 간단
li.selected > p { background-color: blue; }- 명령형 질의 사용시 : 복잡
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"); } } } }맵리듀스 질의
* postgre sql : 선언형
SELECT date_trunc('month', observation_timestamp) AS observation_month, sum(num_animals) AS total_animals FROM observations WHERE family = 'Sharks' GROUP BY observation_month;* MongoDB 맵리듀스 기능
db.observations.mapReduce( function map() { var year = this.observationTimestamp.getFullYear(); var month = this.observationTimestamp.getMonth() + 1; emit(year + "-" + month, this.numAnimals); }, function reduce(key, values) { return Array.sum(values); }, { query: { family: "Sharks" }, out: "monthlySharkReport" } );* 데이터 예시(문서 2개)
{ observationTimestamp: Date.parse("Mon, 25 Dec 1995 12:34:56 GMT"), family: "Sharks", species: "Carcharodon carcharias", numAnimals: 3 } { observationTimestamp: Date.parse("Tue, 12 Dec 1995 16:17:18 GMT"), family: "Sharks", species: "Carcharias taurus", numAnimals: 4 }* 맵리듀스 질의 결과
emit("1995-12", 3)
emit("1995-12", 4)
=> reduce("1995-12", [3,4])
=> return 7 (monthlySharkReport)
* 몽고 DB에서 지원하는 집계파이프라인 (선언형 질의)
db.observations.aggregate([ { $match: { family: "Sharks" } }, { $group: { _id: { year: { $year: "$observationTimestamp" }, month: { $month: "$observationTimestamp" } }, totalAnimals: { $sum: "$numAnimals" } } } ]);-> 분산환경이라고 꼭 맵리듀스를 사용하는 건 아님
-> map, reduce 함수 선정이 어려워서 몽고DB에서 집계 파이프라인 제공하고 있음. (질의 최적화기가 질의 성능을 높임)
3. 그래프형 데이터 모델
속성 그래프

사이퍼 질의 언어 : 속성 그래프에 적합
데이터 생성
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)질의: 미국에서 유럽으로 이민 온 사람의 이름을 질의
MATCH (person) -[:BORN_IN]-> () -[:WITHIN*0..]-> (us:Location {name:'United States'}), (person) -[:LIVES_IN]-> () -[:WITHIN*0..]-> (eu:Location {name:'Europe'}) RETURN person.name관계형 스키마를 사용해 속성 그래프 표현하기
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);SQL로 속성 그래프 질의 : with recursive - 복잡도 UP
WITH RECURSIVE -- in_usa is the set of vertex IDs of all locations within the United States in_usa(vertex_id) AS ( SELECT vertex_id FROM vertices WHERE properties->>'name' = 'United States' UNION SELECT edges.tail_vertex FROM edges JOIN in_usa ON edges.head_vertex = in_usa.vertex_id WHERE edges.label = 'within' ), -- in_europe is the set of vertex IDs of all locations within Europe in_europe(vertex_id) AS ( SELECT vertex_id FROM vertices WHERE properties->>'name' = '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 is the set of vertex IDs of all people born in the US born_in_usa(vertex_id) AS ( 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 is the set of vertex IDs of all people living in Europe lives_in_europe(vertex_id) AS ( 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 to find those people who were both born in the US *and* live in Europe JOIN born_in_usa ON vertices.vertex_id = born_in_usa.vertex_id JOIN lives_in_europe ON vertices.vertex_id = lives_in_europe.vertex_id;트리플 저장소 모델
(주어, 서술어, 목적어) 형식으로 저장함
- 터틀 형식으로 데이터 표현시
@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".스파클 질의 언어
- RDF(Resource Description Framework. 자원 기술 프레임워크) 데이터 모델을 사용한 트리플 저장소 질의 언어
- 사이퍼와 구조가 매우 유사함

'Data Engineering' 카테고리의 다른 글
Oracle Statistics (0) 2023.11.14 Git Bash로 AWS 접속하기 (0) 2023.04.07 git (0) 2022.02.16 Druid (0) 2022.02.08 elastic search (0) 2022.02.08