데이터 모델링 관점에서의 sql vs nosql feat. ddia
제목의 sql과 nosql은 각각 relational database와 document database를 의미한다.
join
mongoDB 같은 document database에서는 일반적으로 join연산을 지원하지 않는다.(물론 비슷한 연산을 수행하도록 지원은 하지만 relational database에 비해 훨씬 최적화 돼있지 않으므로 무시하겠다.)
때문에 한 document에 정보를 그대로 때려 박는 경우가 많고 document database가 적절한 환경에서 해당 방법이 더 효율적이다.(비정규화)
하지만 만약 애플리케이션에 다음과 같은 기능이 있다면 어떨까
유저 a,b가 있을 때 만약 a가 b를 추천하면 b의 개인프로필 페이지에 추천한 유저인 a의 이름과 프로필 사진이 보인다.
document database라면 다음과 같이 저장될 것이다.
// user document
{
...
recommender: [ {name: {{ name }}, profile_image: {{ profile_image_url }}}, ... ]
...
}
그런데 만약 a가 프로필 사진을 변경한다면?
a가 추천한 모든 유저의 recommender값을 수정해줘야 한다.
하지만 relational database였다면 프로필 사진을 a의 테이블에 있는 이미지값을 참조하도록 설계 돼있었을 테니
a가 프로필 사진을 바꾸더라도 굳이 recommender 값을 수정해주지 않아도 된다.
때문에 위와 같은 기능이 있으면 relational database를 쓰는 게 더 적합할 것이다.
many to many
document db에서는 many to many관계도 불가능하다.
무론 이 또한 우회하여 가능하지만, 애플리케이션 코드가 복잡해질 수 있다.
하지만 어떤 이벤트가 언제 발생했는지 기록하는 분석용 데이터의 경우 조인이 필요한 경우가 없으므로
document db가 적절할 수 있다.
schema flexibility
보통 document db라 하면 schemaless, 스키마가 없다고 생각할 것이다.
하지만 document db도 스키마가 실제론 있다. 애플리케이션 레벨에서 의도된 스키마가 있을 테니 말이다.
다만 db레벨에서 스키마를 강제하지 않을 뿐이다.
때문에 더 정확한 표현은 document db는 schema-on-read
realtional db는 schema-on-write라 부르는 것이 적절하다.
schema를 강제하지 않으므로 db에 임의의 key:value가 쉽게 추가될 수 있다.
때문에 document db를 사용하는 경우 애플리케이션에서 읽은 데이터가 어떤 필드를 가지고 있다고 보장할 수 없다.
때문에 당연히 스키마 변경에는 document db가 더 유연하다.
만약 기존에 유저이름을 full_name이라는 키, 컬럼에 저장하고 있다가 first_name, last_name으로 나눠 저장하고 싶다면
다음과 같은 로직을 추가하면 된다.
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];
}
하지만 relational db라면 아래와 같이 테이블을 수정해줘야 한다.
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); -- MySQL
위와 같이 스키마를 변경하면 수행시간 동안 db가 느려지거나 down 되는 걱정을 대부분 할 것이다.
하지만 명성에 비해 몇 ms 면 끝난 다고 한다.
하지만 MySQL처럼 주목할만한 예외가 있다.
MySQL은 기존테이블을 전부 복사하기에 테이블이 큰 경우 몇 시간까지 db가 down 될 수 있다.
물론 이 정도의 다운타임이 불가능한 상황이라면 first_name 칼럼을 추가하고 NULL로 둔 채 애플리케이션 레벨에서 추가해 주면 된다.(document db에서 했듯이)
data locality
데이터 지역성은 데이터가 얼마나 물리적으로 가까이 있냐를 의미한다.
document db는 데이터를 한 document에 다 때려박으므로 물리적으로도 데이터가 가까이 있다.
때문에 읽을 때 데이터를 그대로 읽어오면 된다.
이에 비해 Relational db는 같은 곳에 쓰이는 정보도 다른 테이블에 저장하는 방식으로 설계하는 경우가 많다.
때문에 데이터를 읽을 때 여러 index를 읽고 데이터를 가져와야 한다.
이에 random access도 많이 일어날 것이다.
분산환경이라면 각 테이블마다 다른 노드에 있는 경우 더 오래 걸릴 것이다.
하지만 데이터의 일부만 가져오는 경우에는 document db는 일부만 필요해도 전부 읽으므로
이 경우 realtional db보다 비효율적이다.
하지만 데이터 지역성은 document db에만 제한된 것이 아닌데, google spanner의 경우 relational db임에도 두 테이블사이에 interleave를 걸어 물리적으로 같은 위치에 두어 최적화하였다.
Hbase나 cassandra 같은 db의 컬럼 지향 개념도 비슷한 목적을 가지고 있다.
Convergence
사실 최근에는 relational db와 document db의 경계가 조금씩 허물어가고 있다.
그리고 이는 결국 애플리케이션 레벨에서 각 db의 장점을 전부 사용할 수 있다는 걸 의미하기에 좋은 방향이라고 본다.