본문 바로가기
프로그래밍/Study정리

2장:데이터 중심 애플리케이션 설계

by 방구석개발자 2022. 12. 12.
반응형

이번 챕터를 읽고 얻고 싶은 목표

어플리케이션 개발자로써 적합한 데이터 모델선택 할때 도움을 얻고 싶다.


다양한 범용 데이터 모델

역사적으로 데이터 모델이 많이 생기고 사라졌지만 현대에 주로 사용하는 데이터 모델은 3가지 입니다.

  • 관계형 데이터베이스
  • 문서 데이터베이스
  • 그래프 데이터베이스

그리고 각 데이터 베이스는 장단점이 있고 각각의 특화된 질의언어(Query Language)를 가지고 있습니다.

관계형 데이터베이스

오늘날의 가장 잘 알려진 데이터 모델 : 1970년 에드가 코드(Edgar Codd)가 제안한 관계형 모델이다.

관계형 데이터베이스의 근원은 비즈니스 데이터 처리에 있다. 트랜잭션 처리일괄 처리 등등을 할수 있다.

문서 데이터베이스

NoSQL은 관계형 모델의 우위를 뒤집으려는 가장 최신 시도다.

NoSQL 데이터베이스를 채택한 데는 다음과 같은 원동력이 있다.

  • 매우 높은 쓰기 처리량 달성을 관계형데이터베이스보다 쉽게 할 수 있는 뛰어난 확장성의 필요
  • 관계형 모델에서는 지원하지 않는 특수 질의 동작
  • 관계형 스키마의 제한에 대한 불만과 더욱 동적이고 표현력이 풍부한 데이터 모델에 대한 바람

json vs xml

JSON

XML - 위키백과, 우리 모두의 백과사전

JSON vs XML

Json 과 XML의 차이

[프로그래밍] XML과 JSON의 차이

객체 관계형 불일치

객체지향 프로그래밍 언어로 개발하면 애플리케이션계층과 데이터베이스 모델 객체 사이에 거추장스러운 계층이 필요하다. 이런 모델사이의 분리를

임피던스 불일치라고 한다.

임피던스 불일치(impedance mismatch): 비슷한 이야기로 jpa에서 객체지향과 관계형데이터베이스의 지향 차이를 패러다임 불일치 라고 한다.

잠깐만! Dao Repository 차이
https://www.baeldung.com/java-dao-vs-repository

 

이런 계층간 임피던스 불일치를 json 모델이 줄인다고 생각하는 일부 개발자들이 있다.

그치만 데이터 부호화 형식으로써 json이 가진 문제도 있다.

NoSql 스키마의 부족을 장점으로 종종 인용하기도 한다.

어째든 json은 rdb보다 더 나은 지역성(locality)을 가진다.

다대일과 다대다 관계

데이터베이스 정규화로 ID를 가지고 식별하여 정보를 조회한다.

중복된 데이터를 정규화하려면 다대일 관계가 필요한데 안타깝게도 다대일 관계는 문서 모델에 적합하지 않다.

문서 데이터베이스에서는 조인에 대한 지원이 보통 약하다.

데이터베이스 자체가 조인을 지원하지 않으면 애플리케이션 코드에서 조인을 흉내내야 한다. (벌써 냄새가 난다 ㄲㄷ?)

더욱이 애플리케이션에 기능을 추가하면서 데이터는 점차 상호 연결되는 경향이 있다.

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

개발자는 비정규화된 데이터를 중복할지 한 레코드와 또 다른 레코드의 참조를 수동으로 해결할지 결정해야 했다.

오늘날의 문서데이터베이스를 사용해서 작업 중인 개발자가 풀어야할 문제와 매우 비슷하다.

관계형데이터베이스 vs 문서 데이터베이스

문서데이터모델은 스키마 유연성, 지역성에 기인한 더 나은 성능 데이터구조가 애플리케이션에서 사용하는것이랑 가깝다.

관계형은 조인 다대일 다대다 관계를 더 잘 지원한다.

쿼리 최적화

[데이터베이스] 쿼리 최적화에 대해서 (Query Optimization)

The SQL Server Query Optimizer - Simple Talk

스키마의 유연성

일반적으로 json를 지원하는 데이터베이스는 스키마를 강요하지 않는다.

문서 데이터베이스는 종종 스키마 리스로 불리지만 이는 오해의 소지가 있다.

스키마리스(schemaless)란?
https://www.itworld.co.kr/news/200094
https://jangseongwoo.github.io/hadoop/schema-on-read-schema-on-write/

즉 문서데이터베이스는 읽기 스키마다

스키마 강제는 논쟁의 여지가 있는 주제이며 옳고 그른 정답은 없다.

접근 방식 간 차이는 데이터 타입을 변경하고자 할때 뚜렷하게 나타단다.

예를 들어 필드 추가를 하면

문서데이터베이스는 예전 데이터 조회 처리하는 코드만 있으면 된다.

반면 관계형은 스키마 변경부터 때리고 시작한다.(대용량 데이터 일때는 조진다. alter table ~ )

읽기 스키마 접근 방식은 컬렉션 안의 항목이 어떤 이유로 모두 동일한 구조가 아닐때 유리하다. 그 이유는

  • 다른 여려 유형의 오브젝트를 각각의 테이블에 넣는게 실용적이지 않다.
  • 사용자가 제어할 수 없고 언제나 변경 가능한 외부 시스템에 의해 데이터 구조가 결정된다.

질의를 위한 데이터 지역성

웹 페이지 상에 문서를 보여주는 동작처럼 애플리케이션이 자주 전체 문서에 접근해야 할 때 저장소 지역성(storage locality)을 활용하면 성능 이점이 있다.

지역성의 이점은 한 번에 해당 문서의 많은 부분을 필요로 하는 경우에만 적용된다.

문서를 갱신할 때도 보통 전체 문서를 재작성해야 한다. 부호화(?)된 문서의 크기를 바꾸지 않는 수정은 쉽게 수행할 수 있다.

지역성을 위해 관련 데이터를 그룹화하는 개념은 문서모델에만 국한되지는 않는다.

이건 3장에 자세히~

부호화 뜻

부호화 - 위키백과, 우리 모두의 백과사전

문서모델 ,관계형 데이터베이스의 통합

둘이 부족한 부분은 보완해 나가고 있다.

관계형과 문서의 혼합 모델은 미래 데이터베이스들이 가야 할 올바른 길이다.

데이터를 위한 질의 언어(Query Language)

관계형 모델이 등장했을 때 데이터를 질의하는 새로운 방법도 함께 나타났다.

SQL(Structured Query Language)는 선언형 질의언어 이다.

select * from animals where family = 'Sharks';

반면 IMS와 코다실은 명령형 코드로 질의한다.

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

선언형 질의 언어는 명령어보다 간결하고 쉽게 작업할수 있다 더 중요한 점은 질의를 변경하지 않고도 데이터베이스 시스템의 성능을 향상시킬 수 있다는 점이다.

마지막으로 선언형 언어는 병렬 실행에 적합하다.

맵리듀스 질의

맵리듀스는 많은 컴퓨터에서 대량의 데이터를 처리하기 위한 프로그래밍 모델로 일부 NoSQL 데이터 저장소는 제한된 형태의 맵리듀스를 지원한다.

맵리듀스 - 위키백과, 우리 모두의 백과사전

맵리듀스는 선언형도 아니고 명령형도 아닌 중간 정도에 있다.

여러 프로그래밍 언어에 있는 map과 reduce 함수를 기반으로 한다.

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;
db.observations.mapReduce(
	function map() {
		var year = this.observationsTimestamp.getFullYear();
		var month = this.observationsTimestamp.getMonth() + 1;
		emit(year + "-" + month, this.numAnimals);
	},
	function reduce(key, values){
		return Array.sum(values);
	},
	{
		query: {family: "Shark"},
		out:"monthlySharkReport"
	}
)

맵리듀스는 순수함수여야하고 추가적인 데이터베이스 질의를 수행할 수 없어야 한다.

맴비듀스의 사용성 문제는 연계된 자바스크립트 함수 두개를 신중하게 작성해야 한다는 점인데 종종 하나의 질의를 작성하는 것보다 어렵다.

더욱이 선언형질의는 질의최적화기가 성능을 높힐 기회를 제공한다.

이런 이유로 몽고디비는 집계 파이프라인이라 부르는 선언형 질의 언어 지원을 추가 했다.

집계 파이프라인 언어

  • 표현 측면에서 SQL의 부분 집합과 유사
  • JSON 기반 구문을 사용(SQL의 영어 문장 스타일 구문과 달리)

그래프형 데이터 모델

다대다 관계가 매우 일반적이라면,관계형 모델이 단순한 다대다 관계를 다룰 수 있다.

데이터 간 연결이 복잡해지면 그래프로 데이터를 모델링하는 편이 더 자연스럽다.

그래프는 두 유형의 객체로 이뤄진다.

  • 정점(vertex) = 노드나 엔티티라고도 한다.
  • 간선(edge) = 관계나 호(arc)라고도 한다.

일반적으로 그래프 상에서 동작하는 알고리즘은 “자동차 내비게이션 시스템”, “페이지랭크”가 있다.

그래프 모델

  • 속성 그래프 모델 : Neo4j, Titan InfiniteGraph
  • 트리플 저장소 모델 : Datomic, Allegrograph

그래프용 질의 언어

  • 선언형 질의 언어 : Cypher, SPARQL, Datalog
  • 명령형 질의 언어 : Gremlin

그래프 처리 프레임워크 Pregel가 있다.

지금부터 속성 그래프 모델, 트리플 저장소 모델, 선언형 질의 언어들에 대해서 살펴본다.

속성 그래프

정점을 구성하는 요소

  • 고유한 식별자
  • 유출(outgoing) 간선 집합
  • 유입(incoming) 간선 집합
  • 속성 컬렉션(키-값 쌍)

간선을 구성하는 요소

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

두 개의 관계형 테이블로 구성된 그래프 저장소를 생각해보면 아래와 같다.

관계형 스키마를 사용해 속성 그래프 표현

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);

이 모델의 중요한 점

  1. 정점은 다른 정점과 간선으로 연결특정 유형과 관련 여부를 제한하는 스키마가 없다.
  2. 정점이 주어지면 정점의 유입과 유출 간선을 효율적으로 찾고 그래프를 순회
  3. 다른 유형의 관계는 서로 다른 label을 사용단일 그래프에 다른 유형의 정보를 저장하면서 데이터 모델을 깔끔하게 유지

위의 기능을 통해 그래프는 데이터 모델링을 위한 많은 유연성을 제공한다.또한 그래프는 발전성이 좋아서 데이터 구조 변경을 수용하게끔 그래프를 쉽게 확장할 수 있다.

사이퍼 질의 언어

속성 그래프를 위한 선언형 질의 언어

그림 1을 데이터의 일부를 사이퍼 질의로 표현

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

위의 질의는 다음과 같은 의미다.

  1. person은 어떤 정점을 향하는 BORN IN유출 간선을 가진다.이 정점에서 name 속성이 “United States”인 Location 유형의 정점에 도달할 때까지 일련의 WITHIN 유출 간선을 따라간다.
  2. 같은 person 정점은 LIVES IN유출 간선도 가진다.이 간선과 WITHIN 유출 간선을 따라가면 결국 name 속성이 “Europe”인 Location 유형의 정점에 도달하게 된다.

질의를 실행하는 데는 여러 가지 방법이 있다.(책 참고)하지만 선언형 질의 언어는 질의 최적화기가 효율적이라고 예측한 전략을 자동으로 선택하므로 보통 수행에 대해 자세히 지정할 필요가 없다.

neo4j - Official Image | Docker Hub

MATCH (n:Person)
DETACH DELETE n

SQL의 그래프 질의

예제 1에서 관계형 데이터베이스에서 그래프 데이터를 표현했다.그렇다면 그래프 데이터를 관계형 구조로 넣어도 SQL을 사용해서 질의할 수 있을까?

가능은 하지만 어렵다.관계형 데이터베이스에서는 보통 질의에 필요한 조인을 미리 알고 있지만그래프 질의에서는 정점을 찾기 위해 가변적인 여러 간선을 순회해야 한다.즉, 조인 수를 고정할 수 없다.

문제는 위의 사이퍼 질의에서 () - [:WITHIN*0..]-> () 문에 있다.이는 0회 이상 WITHIN 간선을 따라가라는 의미다. <– 가변적인 여러 간선을 순회

SQL:1999 이후로 가변 순회 경로에 대한 질의 개념은 재귀 공통 테이블 식(recursive common table expression)(WITH RECURSIVE 문)을 사용해서 표현할 수 있다.

WITH RECURSIVE 
    
     -- in_usa는 미국 내 모든 지역의 정점 ID 집합이다.
     in_usa(vertex_id) AS (
        SELECT vertexid FROM vertices WHERE properties->>'name' = 'United States'
            UNION 
        SELECT edges.tail_vertex FROM edges
            JOIN inusa ON edges.head_vertex = in_usa.vertex_id
            WHERE edges.label = 'within' 
     ),
    
     -- in_europe은 유럽 내 모든 지역의 정점 ID 집합이다.
     in_europe(vertex_id) AS (
        SELECT vertexid FROM vertices WHERE properties->>'name' = 'Europe'
            UNION 
        SELECT edges.tail_vertex FROM edges
            JOIN ineurope ON edges.head_vertex = in_europe.vertex_id
            WHERE edges.label = 'within' 
     ), 
    
     -- born_in_usa는 미국에서 태어난 모든 사람의 정점 ID 집합이다.
     born_in_usa(vertex_id) AS (
        SELECT edges.tail_vertex FROM edges
            JOIN inusa ON edges.head_vertex = in_usa.vertex_id
            WHERE edges.label = 'born_in' 
     ),
    
     -- lives_in_europe은 유럽에서 태어난 모든 사람의 정점 ID 집합이다.
     lives_in_europe(vertex_id) AS (
        SELECT edges.tail_vertex FROM edges
            JOIN ineurope ON edges.head_vertex = in_europe.vertex_id
            WHERE edges.label = 'lives_in' 
     )
    
     SELECT vertices.properties->>'name'
     FROM vertices
        -- 미국에서 태어나 유럽에서 자란 사람을 찾아 조인
        JOIN bornin_usa ON vertices.vertex_id = born_in_usa.vertex_id
        JOIN livesin_europe ON vertices.vertex_id = lives_in_europe.vertex_id;

사이퍼 질의 대비 SQL은 매우 길다.

이는 다양한 데이터 모델이 서로 다른 사용 사례를 지원하기 위해 설계됐다는 것을 보여준다.

따라서 애플리케이션에 적합한 데이터 모델을 선택하는 작업은 중요하다.

트리플 저장소와 스파클

속성 그래프 모델과 거의 동일하다.같은 개념을 다른 용어를 사용한다.

모든 정보를 (주어(subject), 서술어(predicate), 목적어(object)) 처럼 매우 간단한 세 부분 구문 형식으로 저장한다.

  • 트리플의 주어는 그래프의 정점과 동등하다.
  • 목적어는 다음과 같다.
    1. 문자열이나 숫자 같은 원시 데이터 값트리플의 서술어와 목적어는 주어 정점의 속성의 키, 값과 동등
    2. 그래프의 다른 정점서술어는 그래프의 간선주어는 꼬리 정점목적어는 머리 정점

그림1의 일부를 Turtle 트리플로 표현

@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".
  • _:주어 서술어 목적어 형식
  • 정점은 _:someName 형식
  • 목적어가 원시 데이터인 경우_:lucy :name “Lucy”:name은 속성 키, “Lucy”는 속성 값
  • 목적어가 다른 정점인 경우_:idaho :within :usa:within은 간선

스파클 질의 언어

RDF 데이터 모델을 사용한 트리플 저장소 질의 언어

예제2와 동일한 질의를 스파클로 표현

PREFIX : <urn:example:>

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

스파클에서 변수는 물음표로 시작사이퍼와 비교하면 아래와 같이 두 표현식이 동등

  • (person) -[:BORN]-> () -[:WITHIN0..]-> (location) #사이퍼?person :bornIn / :within ?location. #스파클
  • IN
  • (usa {name:’United States’}) #사이퍼?usa :name “United States”. #스파클

RDF와 SPARQL을 사용하여 Amazon Neptune 넵튠의 그래프에 액세스

  • 그래프 데이터베이스와 네트워크 모델의 비교
    • 코다실은 다른 레코드 타입과 중첩 가능한 레코드 타입을 지정하는 스키마가 존재그래프 데이터베이스는 제약 없음
    • 코다실에서 특정 레코드에 도달하는 유일한 방법은 접근 경로를 탐색하는 방식그래프 데이터베이스는 고유 ID를 가지고 임의 정점을 직접 참조 OR 색인을 사용해 빠르게 검색
    • 코다실에서 레코드의 하위 항목은 정렬된 집합그래프 데이터베이스에서 정점과 간선은 정렬하지 않음, 질의 결과를 정렬할 수 있음
    • 코다실은 모든 질의는 명령형그래프 데이터베이스는 명령형과 선언형 질의 모두 제공
  • 코다실의 네트워크 모델과 그래프 데이터베이스는 몇 가지 중요한 차이점이 있다.

초석: 데이터로그

오래된 언어이지만 이후 질의 언어의 기반이 되는 초석을 제공

데이터로그의 데이터 모델은 트리플 저장소 모델과 유사하지만 조금 더 일반화서술어(주어, 목적어) 형식

그림1의 일부데이터를 데이터로그 fact로 표현

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

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

name(idaho, 'Idaho').
type(idaho, state).
within(idaho, usa).

name(lucy, 'Lucy').
born_in(lucy, idaho)

예제2와 동일한 질의를 데이터로그로 표현

within_recursive(Location, Name) :- name(Location, Name). /* Rule 1 */

within_recursive(Location, Name) :- within(Location, Via), /* Rule 2 */
                                 within_recursive(Via, Name).

migrated(Name, BornIn, LivingIn) :- name(Person, Name), /* Rule 3 */
                                 born_in(Person, BornLoc),
                                 within_recursive(BornLoc, BornIn),
                                 lives_in(Person, LivingLoc),
                                 within_recursive(LivingLoc, LivingIn).

?- migrated(Who, 'United States', 'Europe').
/* Who = 'Lucy'. */

사이퍼와 스파클은 SELECT로 바로 질의하는 반면데이터로그는 단계를 나눠 데이터베이스에 전달하는 규칙을 정의한다.

  • :- 연산자의 오른편에 있는 모든 서술어를 찾으면 규칙이 적용
  • :- 연산자의 왼편은 데이터베이스에 추가됨(변수는 대응된 값으로 대체)

이제 rule 3을 적용해서 최종적으로 남은 사람이 결과가 된다

데이터로그 접근 방식은 이전에 설명한 질의 언어와는 다른 사고가 필요하지만 규칙을 결합하거나 재사용할 수 있기 때문에 매우 강력한 접근 방식이다.데이터가 복잡하면 더 효과적으로 대처할 수 있다.

반응형

'프로그래밍 > Study정리' 카테고리의 다른 글

4장: 부호화의 발전  (1) 2022.12.26
1장:데이터 중심 애플리케이션 설계  (2) 2022.12.06

댓글