계층형 모델링

2018-10-21

계층형 설계의 기본

Denormalization

같은 데이터를 중복해서 저장하는 방식. 테이블간에 JOIN을 없앨 수 있다. NoSQL의 경우 JOIN을 지원하지 않기 때문에, 두 테이블을 JOIN해서 데이터를 가지고 오는 로직을 구현하는 경우 2번의 IO가 발생하게 된다.

  • 일반적인 1대 N

tb_todo

idx(pk) 할일 생성일 수정일 완료일
1 집안일 2018.10.01    
2 빨래 2018.10.01    
3 청소 2018.10.01    
4 방청소 2018.10.01    

tb_refs

idx(pk) todo_id ref_id
1 2 1
2 3 1
3 4 1
4 4 3
  • Denormalization

tb_todo_refs

idx(pk) 할일 생성일 수정일 완료일 ref_id
1 집안일 2018.10.01      
2 빨래 2018.10.01     1
3 청소 2018.10.01     1
4 방청소 2018.10.01     1
5 방청소 2018.10.01     3

JOIN을 위해서 몇번씩 쿼리를 하지 않아도 되기 때문에, 쿼리 성능이 올라간다. 그리고 JOIN에 대한 로직이 필요 없이, 한번에 데이타를 가지고 오기 때문에, 쿼리 로직이 단순해 진다.

하지만 INSERT 하거나 UPDATE 하였을때, todo, ref 테이블뿐만 아니라 todo_refs 테이블도 업데이트 해줘야 한다. 만약에 같은 todo 테이블을 업데이트 하다가 에러가 났을 경우, todo_refs 테이블은 업데이트가 안된 상태이면, 양쪽 테이블에 데이타 불 일치성이 발생할 수 있다. 또한 데이타를 중복해서 저장하는 만큼 스토리지 용량이 증가 된다.

Adjacency List

Adjacent List 구조는, 전통적인 자료 구조에서 사용하는 Linked List와 같은 자료 구조형을 사용하여, 각 Tree의 노드에 parent node에 대한 포인터와 child node들에 대한 포인터를 저장하는 방식이다.

Tree의 내용을 검색하려면, root 노드에서 부터 child node를 child node 포인트를 이용하여, 값을 가져오는 방식이다. (Tree traversing)

특정 노드만 알면, 해당 노드의 상위, 하위 노드를 자유롭게 traversing할 수 있어서 traversing에는 장점을 가지고 있지만, 반대로, 하나의 노드를 이동할 때마다, 포인터를 이용해서 매번 쿼리를 해와야 하기 때문에, Tree의 크기가 크다면 IO가 엄청나게 많이 발생한다. (트리의 노드 수가 N이면, N번 쿼리를 해야 한다)

key(name) parent child
집안일   빨래, 청소, 방청소
빨래 집안일  
청소 집안일 방청소

Rows가 많지 않은 상황에서 적합. 하지만 데이터 조회가 빈번한 상황에서는 적합하지 않다. SELECT문에서 계층을 위, 아래로 조회하는데 있어서 성능에 한계가 있기 때문

Materialized Path

Materialized Path는 Tree 구조를 테이블에 저장할때, root에서 부터 현재 노드까지의 전체 경로를 key로 저장하는 방법이다.

이 방법은 구현에 드는 노력에 비해서 매우 효율적인 저장 방식이다. 특히 Key에 대한 Search를 할때, Regular Expression을 사용할 수 있으면, 특정 노드의 하위 트리등을 쿼리해 오는 기능등 다양한 쿼리가 가능하다. MongoDB와 같은 Document DB는 Regular Expression을 지원하기 때문에, 효과적으로 사용할 수 있다.

key(path) 생성일
집안일/ 2018.10.01
집안일/빨래 2018.10.01
집안일/청소 2018.10.01
집안일/청소/방청소 2018.10.01