이 글은 927일 전에 작성되었습니다.
최신 정보가 아닐 수 있습니다.
mongoDB 스키마 디자인 설계
현재 진행 중인 프로젝트의 DB를 설계할 때, SQL 처럼 테이블을 잘게 잘게 쪼개야하나?라는 고민을 하게 되었습니다. 특히, 채팅 서비스의 DB 구조를 어떻게 해야 할지 잘 몰라서 찾던 도중 mongoDB 의 좋은 글을 읽고 이 게시글을 작성합니다!
mongoDB는 다른 관계형 DB처럼 스키마 설계를 하지 말라고 합니다.
우선은 관계형 DB와 mongoDB를 비교하도록 하겠습니다.
관계형 데이터베이스
관계형 DB의 스키마를 디자인할 땐, 우선 필요한 데이터를 정하고, 정규화 과정을 통해 데이터들이 중복되지 않도록 여러 테이블로 나눕니다.
예를 들어 하나의 유저
테이블이 있고, 프로젝트
테이블도 있을 때 프로젝트
테이블의 외래키로 유저
의 기본키를 등록하여 쿼리가 가능하도록 설계한다고 합니다. (안 써봐서 잘 몰라요😂)
더불어 관계형 데이터베이스의 경우 기존에 작성된 스키마를 수정하기 어렵다는 의견이 많이 있습니다. (변경이 있을 때 기존에 생성된 데이터들도 수정을 요하기 때문?,,)
mongoDB
mongoDB의 경우 매우 유연하게 스키마를 수정이 가능합니다.
우선 스키마 디자인을 할 때 고려할 수 있는 디자인 방식은 Embedding
or Referencing
방식입니다.
Embedding
장점
- 하나의 쿼리문으로 필요한 모든 데이터를 가져올 수 있습니다.
$lookup
과 같은Join
동작을 수행하지 않고 데이터를 가져올 수 있습니다.
한계점
-
Document
가 커지면 오버헤드 또한 함께 커진다 (문서의 크기를 제한하여 쿼리 성능도 챙길 수 있습니다.) -
Document
는 16mb 의 크기 제한을 갖고 있어서Embedding
방식을 계속 사용한다면 언젠간 한계에 도달할 수도 있습니다.
Referencing
참조형 같은 경우 각각의 Document
들이 가지고 있는 특별한 id 인 object id
와 $lookup
을 이용해 데이터를 참조할 수 있습니다.
이는 관계형 데이터베이스에서의 JOIN
과 흡사하게 동작하며, 이러한 구조는 데이터를 효율적이고 관리하기 쉽게 나누면서도 데이터 간의 관계를 유지할 수 있습니다.
장점
Document
를 쪼개어 더욱 작은 단위의Document
를 가질 수 있으며, 이를 통해 16mb 크기 제한을 피할 수 있습니다.- 필요하지 않은 정보에 대한 접근을 줄일 수 있습니다.
Document
를 참조하여 데이터를 가져오기에Embedding
방식보다 데이터의 중복을 줄일 수 있습니다.
한계점
- 하나의
Document
안에 다수의 데이터가 참조형이라면 다수의 쿼리,$lookup
,populate
가 필요합니다. (현재 프로젝트하며 걱정인 부분)
설계
One-to-One
User
{
"_id": "ObjectId('mdkalsfmk2')",
"nickname": "junseo",
"campus": "42seoul",
}
위와 같은 User document
가 있을 때, nickname
이나 campus
와 같이 하나만 존재하는 값이 1:1 관계며, key-value
로 모델링 할 수 있습니다.
One-to-Few
User
{
"_id": "ObjectId('mdkalsfmk2')",
"nickname": "junseo",
"campus": "42seoul",
"projects": [
{ "name": "42WE", "isDone": false},
{ "name": "Decrypto", "isDone": true}
]
}
위와 같은 User document
에서 projects
의 타입은 배열이며 2개의 값을 가지고 있습니다. 이렇듯 User document
에서 projects
는 One-to-Few
관계입니다.
One-to-Few
처럼 몇 개의 데이터만 가지고 있을 경우엔 값을 Embedding
구조로 설계합니다.
One-to-Many 자식참조
User
{
"_id": "ObjectId('mdkalsfmk2')",
"nickname": "junseo",
"campus": "42seoul",
"projects": [
{ "name": "42WE", "isDone": false},
{ "name": "Decrypto", "isDone": true}
],
"creditCard": ["ObjectID('1234')", "ObjectID('4321')", "ObjectID('9399')"]
}
Card
{
"_id": "ObjectId('4321')",
"bank": "kakao",
"isActive": "true",
"accountNumber": "1234-56-7891234"
}
위의 User document
에서 creditCard
는 Card
스키마로 생성된 한 Document
를 참조하는 중입니다.
이처럼 한 유저가 여러 카드를 갖고 있으며 따로 관리가 필요할 때, 관계가 필요할 때 One-to-Many
구조로 설계합니다.
가능한 경우
$lookup
이나populate
같은Join
을 피하지만 이를 통해 더 나은 스키마 디자인 설계가 가능하다면 걱정하지 말고 사용하기!
One-to-Squillions 부모참조
ChatRoom
{
"_id": "ObjectId('4321')",
"createdAt": ISODate("2021-04-28T09:42:41.382Z"),
"users":"???",
}
Chat
{
"_id": "ObjectId('328492489')",
"chatRoom_id": "ObjectId('4321')", // 부모의 obj id 를 참조
"createdAt": ISODate("2021-04-29T12:42:41.382Z"),
"content":"피곤쓰",
}
이러한 채팅방, 채팅 스키마를 디자인할 때, One-to-Many
방식에서 Embedding
구조를 사용한다면, 채팅 데이터가 300개 넘어갔을 때 Document
의 16mb 제한을 초과해 버릴 것입니다. 더불어 Referencing
구조를 사용한다 하더라도 ObjectID
가 몇 천 개 쌓인다면 똑같이 용량 초과될 것입니다.
One-to-Squillions
구조를 사용한다면 채팅방의 ObjectID
로 해당 채팅방에서 주고받은 데이터를 쿼리 할 수 있게 되고, 용량 제한을 피하면서도 채팅방과 채팅의 관계를 유지시킬 수 있습니다.
결론
-
mongoDB schema design 의 가장 좋은 예는 만들고자 하는 서비스에 알맞게 만드는 것.
-
mongoDB
디자인은 관계형 디비 디자인처럼 하지 말자. -
이전에 걱정하던 다수의 쿼리 접근의 경우, 이를 통해 더 나은 스키마 디자인 설계가 가능하다면 걱정하지 말자.
-
Document
를 나눠야 할 이유가 명확하지 않다면Embedding
하여 사용하자. -
Document
값 중 배열의 경우 제한 없이 데이터가 추가되다 보면 16mb 를 넘길 수 있기에 조심하자. -
원래는
Chat
을One-to-Many
구조로 하려고 하였으나 글을 읽고나니One-to-Squillions
구조로 설계해야하는 이유를 알게됐다.
One-to-One
: key-value 선호
One-to-Few
: Embedding 선호
One-to-Many
: Embedding 선호
One-to-Squillions
: Referencing 선호
Many-to-Many
: Referencing 선호