MongoDB 스키마 설계를 위한 6가지 법칙(정리중)

cuudev·2022년 12월 1일
1

막연히 NoSQL이 좋다고 생각해서 MongoDB를 쓰다가 설계에 막혀 답답했던 적이 있다. 그래서 찾아보다가 발견한 mongodb 블로그에 있는 글을 토대로 MongoDB 설계 시 무엇을 고려해야 하는지 정리해보았다.

MongoDB 설계의 6가지 규칙

RDBMS에서는 1:N으로 쓰면 되는 관계를 MongoDB에서는 N의 수가 얼마나 되느냐에 따라 설계를 다르게 해야 한다고 한다.

  • One-to-Few
  • One-to-Many
  • One-to-Squillions
  • Two-Way Referencing
  • Database denormalization with one-to-many relationships
  • Database denormalization with one-to-squillions relationships

TL;DR

  • 얼마나 많은 수의 N이 존재하는가?
  • N을 직접 접근하여 CRUD를 해야 하는가?
  • 자식을 통해 부모를 접근해야 하는가?

Basics: One-to-Few

아주 적은 수의 Many에 대한 경우다. 이 경우 MongoDB에선 Embeded Document를 활용할 수 있다.

> db.person.findOne()
{
  name: 'Kate Monster',
  ssn: '123-456-7890',
  addresses : [
     { street: '123 Sesame St', city: 'Anytown', cc: 'USA' },
     { street: '123 Avenue Q', city: 'New York', cc: 'USA' }
  ]
}

장점.

  • N의 정보를 가져오기 위해 Join이나 추가적인 쿼리를 할 필요가 없다.

단점.

  • N의 정보를 직접 엑세스 할 수 없다.

Basics: One-to-Many

많은 수의 N에 대한 경우다. 제품과 부품의 관계를 예로 들어 설명이 되어있다.
한 제품에는 수백개의 부품이 존재할 수 있다. 이 경우 부모가 되는 Document에 ObjectID를 저장하여 referencing을 할 수 있다.

부품.

> db.parts.findOne()
{
    _id : ObjectID('AAAA'),
    partno : '123-aff-456',
    name : '#4 grommet',
    qty: 94,
    cost: 0.94,
    price: 3.99

제품.

> db.products.findOne()
{
    name : 'left-handed smoke shifter',
    manufacturer : 'Acme Corp',
    catalog_number: 1234,
    parts : [     // array of references to Part documents
        ObjectID('AAAA'),    // reference to the #4 grommet above
        ObjectID('F17C'),    // reference to a different Part
        ObjectID('D2AA'),
        // etc
    ]

장점.

  • N은 부모에 종속적이지 않기 때문에 N을 독립적으로 추가, 조회, 수정, 삭제 할 수 있다.
  • RDBMS와 달리 추가적인 Collection이 없이 Many-to-Many(N:N) 스키마를 설계할 수 있다.

단점.

  • 제품정보를 가져올 때 추가적으로 부품정보를 가져오기 위한 추가적인 쿼리를 실행해야 한다.

Basics: One-to-Squillions

아주 많은 수의 Many에 대한 경우이다. MongoDB의 Document의 size는 16MB로 제한되어 있다. 12B를 사용하는 ObjectID가 아주 많아지게 되면 문제가 생긴다.
다음 예제는 로그메시지에 대한 것이다. 부모 Document에 자식의 ObjectID를 Array로 저장하는 것이 아닌 자식 Document에 부모의 ObjectID를 저장하는 방식으로 referencing을 할 수 있다.

호스트.

> db.hosts.findOne()
{
    _id : ObjectID('AAAB'),
    name : 'goofy.example.com',
    ipaddr : '127.66.66.66'
}

로그.

>db.logmsg.findOne()
{
    time : ISODate("2014-03-28T09:42:41.382Z"),
    message : 'cpu is on fire!',
    host: ObjectID('AAAB')       // Reference to the Host document
}

장점.

  • N은 부모에 종속적이지 않기 때문에 N을 독립적으로 추가, 조회, 수정, 삭제 할 수 있다.

단점.

  • 로그정보를 가져올 때 추가적으로 부품정보를 가져오기 위한 쿼리를 실행해야 한다.

Basics를 종합하면, 위의 3가지 방식 중 어떤 방식을 사용할지 결정하기 전 다음 사항을 고려해봐야 한다.

  • 얼마나 많은 수의 N이 존재하는가?
  • N을 직접 접근하여 CRUD를 해야 하는가?

Intermediate: Two-Way Referencing

이슈트래킹이나 프로젝트 관리 시스템 같은 경우에 사용할 수 있는 방법이다. 작업을 사람에게 할당할 수 있다. 사람을 조회할 때 작업리스트를 볼 수 있어야 하고, 작업을 조회할 때 어떤 사람에게 할당되었는지 조회 및 수정을 할 수 있어야 한다.
이런 경우 자식 Document에 부모의 ObjectID를 저장하여 양방향 Referencing을 할 수 있다.

사람.

db.person.findOne()
{
    _id: ObjectID("AAF1"),
    name: "Kate Monster",
    tasks [     // array of references to Task documents
        ObjectID("ADF9"), 
        ObjectID("AE02"),
        ObjectID("AE73") 
        // etc
    ]
}

작업.

db.tasks.findOne()
{
    _id: ObjectID("ADF9"), 
    description: "Write lesson plan",
    due_date:  ISODate("2014-04-01"),
    owner: ObjectID("AAF1")     // Reference to Person document
}

장점.

  • 자식 Document에서 빠르고 쉽게 무보를 찾을 수 있다.

단점.

  • 관계의 변경시 두개의 Document를 모두 수정해야 한다. 위 예제에서 task의 owner를 변경할 경우 person의 Documenut도 같이 수정해줘야 한다.

Intermediate: Database denormalization with one-to-many relationships

Intermediate: Database denormalization with one-to-squillions relationships

참조한 블로그의 글은 2014년도에 작성된 글이다. 조인의 경우 어플리케이션 레벨에서 추가적인 쿼리를 실행해야 한다고 나와 있는데, populate 기능이 없던 시절에 작성된 글이 아닌가 싶다.

참조.
https://www.mongodb.com/blog/post/6-rules-of-thumb-for-mongodb-schema-design

profile
자꾸 까먹어서...

0개의 댓글