MongoDB 倾向于将数据都放在一个 Collection 下吗?
ringa_lee
ringa_lee 2017-04-24 09:09:33
0
3
743

举个例子,有一个用户信息和用户间关系的数据库,如果按照 SQL 的思路,会建立用户信息和用户关系两张表。那么,在 MongoDB 中,是倾向于将用户关系嵌入到用户信号,组成一个单独的文档吗?

ringa_lee
ringa_lee

ringa_lee

모든 응답(3)
Peter_Zhu

그렇지 않습니다.

컬렉션의 단일 문서 크기에는 현재 16MB의 상한이 있으므로 모든 것을 컬렉션에 담는 것은 불가능합니다. 또한 컬렉션 구조가 너무 복잡하면 쿼리 및 업데이트 효율성에 영향을 미칠 뿐만 아니라 유지 관리의 어려움과 운영 위험도 발생합니다. 실수로 악수로 문서를 null로 저장하려고 한 적이 있나요? 어쨌든 한 사람의 정보가 이 모음집에 모두 들어있다면 기분이 상당히 묘할 것 같습니다.

일반 원칙은 다음과 같습니다.

  • 쿼리 방식에 따른 클러스터

    • 자주 읽어야 할 데이터를 함께 모아두세요.
    • 논리적으로 밀접하게 관련된 정보를 함께 배치하세요.
    • 맵 축소/집계 요구 사항이 있는 데이터가 통합되며 이러한 작업은 단일 컬렉션에서만 작동할 수 있습니다.
  • 데이터량에 따라 분할

    • 컬렉션에서 배열을 사용해야 하고 배열의 길이가 계속해서 늘어나는 경우 데이터 콘텐츠를 특수 컬렉션에 넣어야 하며 각 데이터 조각은 기본 키를 참조합니다. 현재 문서(mysql의 1..N과 마찬가지로 외래 키 종속성과 동일)
    • 특정 문서가 너무 깊다면(2레벨 이상) 분할을 고려할 것입니다. 그렇지 않으면 성능과 유지 관리에 문제가 있을 것입니다.
  • 테이블 구조에 따른 디자인

    • MongoDB에는 테이블 구조라는 개념이 없지만, 실제 사용 시 다양한 구조의 문서가 컬렉션에 포함되어 있는 경우는 거의 없습니다. 문서 구조의 차이가 점점 커지는 경우 고려해야 합니다. 비슷한 구조로 추상화하고, 변경된 내용을 다른 컬렉션에 넣고, 외래 키 종속성을 사용하여 서로 참조하는 방법입니다.

예를 들어, 사용자 시스템을 설계할 때 사용자 컬렉션에는 이름, lastLoginAt 등 일반적으로 사용되는 정보가 포함되어야 하며, 이는 사용자의 액세스 권한에 대한 정보도 포함되어야 할 수 있습니다. , 그러나 사용자의 로그인 로그 정보를 포함하지 않으면 계속 증가합니다.

사용자 간의 관계에 있어서는 사용자 컬렉션이 있는지 여부에 대한 논의가 필요합니다. 사용자 간의 관계를 저장하고 친구의 uid만 기록하면 되고 친구 수가 너무 많지 않아 최대 수백 명 정도라면 컬렉션에 넣는 경향이 있습니다. 관계 데이터 자체가 복잡하거나, 친구 수가 수천 명이면 나누는 편이에요.

또한 MongoDB의 공식 데이터 모델 설계 패러다임도 읽어 볼 가치가 있으니 꼭 보시기를 권합니다.

刘奇

원본주소 : http://pwhack.me/post/2014-06-25-1 전재시 출처를 밝혀주세요

이 글은 "The Definitive Guide to MongoDB"의 8장에서 발췌한 것입니다. 이 글은 다음 두 가지 질문에 대해 철저하게 답할 수 있습니다.

  • /q/1010000000364944
  • /q/1010000000364944

데이터를 표현하는 방법은 여러 가지가 있는데, 가장 중요한 문제 중 하나는 데이터를 어느 정도 정규화해야 하는가입니다. 정규화는 데이터를 여러 개의 서로 다른 컬렉션으로 분산시키는 프로세스이며, 서로 다른 컬렉션은 서로 데이터를 참조할 수 있습니다. 많은 문서가 특정 데이터 조각을 참조할 수 있지만 이 데이터 조각은 하나의 컬렉션에만 저장됩니다. 따라서 이 데이터 조각을 수정하려면 이 데이터 조각을 저장하는 문서만 수정하면 됩니다. 그러나 MongoDB는 조인 도구를 제공하지 않으므로 서로 다른 컬렉션 간에 조인 쿼리를 수행하려면 여러 쿼리가 필요합니다.

비정규화는 정규화의 반대입니다. 즉, 각 문서에 필요한 데이터를 문서 내에 포함시키는 것입니다. 각 문서에는 동일한 데이터 복사본을 집합적으로 참조하는 모든 문서가 아닌 자체 데이터 복사본이 있습니다. 즉, 정보가 변경되면 관련 문서를 모두 업데이트해야 하지만 쿼리를 실행하면 모든 데이터를 가져오는 데 쿼리 하나만 있으면 됩니다.

정규화할 시기와 비정규화할 시기를 결정하는 것은 어렵습니다. 정규화는 데이터 쓰기 속도를 향상시킬 수 있고, 비정규화는 데이터 읽기 속도를 향상시킬 수 있습니다. 이는 애플리케이션의 수십 가지 요구 사항과 신중하게 비교되어야 합니다.

데이터 표현 예시

학생 및 강좌 정보를 저장하고 싶다고 가정해 보겠습니다. 이를 표현하는 한 가지 방법은 학생 컬렉션(각 학생은 문서임)과 클래스 컬렉션(각 코스는 문서임)을 사용하는 것입니다. 그런 다음 세 번째 컬렉션 StudentsClasses를 사용하여 학생과 코스 간의 관계를 저장합니다.

으아악

관계형 데이터베이스에 익숙하다면 이전에 이러한 유형의 테이블 조인을 생성했을 수 있습니다. 단, 각 벌점 문서에 (강좌 "_id" 목록이 아닌) 한 명의 학생과 한 강좌만 있을 수 있습니다. 코스를 배열에 넣는 것은 약간 MongoDB 스타일이지만 실제로는 실제 정보를 얻으려면 많은 쿼리가 필요하기 때문에 일반적으로 이와 같이 데이터를 저장하지 않습니다.

학생이 선택한 강좌를 찾고 싶다고 가정해 보겠습니다. 먼저 학생 컬렉션을 검색하여 학생 정보를 찾은 다음, StudentClasses를 쿼리하여 "_id" 강좌를 찾고, 마지막으로 클래스 컬렉션을 쿼리하여 원하는 정보를 얻어야 합니다. 강좌정보를 알아보기 위해서는 서버로부터 3가지 쿼리를 요청해야 한다. 학생 정보와 강좌 정보가 자주 변경되지 않고 데이터 읽기 속도에 대한 요구 사항이 없다면 MongoDB에서는 이러한 종류의 데이터 구성을 사용하고 싶지 않을 것입니다.

학생 문서에 강좌 참조를 삽입하면 쿼리를 저장할 수 있습니다.

으아악

"classes" 필드는 John Doe가 수강해야 하는 강좌의 "_id"를 저장하는 배열입니다. 이러한 강좌에 대한 정보를 찾아야 하는 경우 이러한 "_id"를 사용하여 수업 컬렉션을 쿼리할 수 있습니다. 이 프로세스에는 두 개의 쿼리만 필요합니다. 이러한 데이터 구성 방법은 데이터가 언제든지 액세스할 필요가 없고 언제든지 변경되지 않는 경우 유용합니다("자주"보다 "언제든지"가 더 까다롭습니다).

읽기 속도를 더욱 최적화해야 하는 경우 데이터를 완전히 비정규화하고 학생 문서의 '수업' 필드에 강좌 정보를 포함된 문서로 저장하면 학생의 강좌 정보를 얻을 수 있습니다. 단 하나의 쿼리로 :

으아악

위 방법의 장점은 학생의 강좌 정보를 얻기 위해 한 번의 쿼리만 필요하다는 것입니다. 단점은 더 많은 저장 공간을 차지하고 데이터 동기화가 더 어렵다는 것입니다. 예를 들어, 물리학이 3점 대신 4점 학점이 되면 물리학 과정을 수강한 모든 학생은 "물리학" 문서뿐만 아니라 문서도 업데이트해야 합니다.

마지막으로, 내장된 데이터와 참조 데이터를 혼합할 수도 있습니다. 하위 문서 배열을 만들어 공통 정보를 저장하고, 더 자세한 정보를 쿼리해야 할 경우 참조로 실제 문서를 찾을 수 있습니다.

으아악

필요에 따라 삽입된 정보를 수정할 수 있으므로 이 방법도 좋은 선택입니다. 페이지에 더 많은(또는 더 적은) 정보를 포함하려는 경우 더 많은(또는 더 적은) 정보를 추가할 수 있습니다. 인라인 문서에 배치됩니다.

고려해야 할 또 다른 중요한 질문은 정보가 더 자주 업데이트됩니까, 아니면 정보를 더 자주 읽는가입니다. 데이터가 정기적으로 업데이트된다면 정규화가 더 나은 선택입니다. 데이터가 자주 변경되지 않으면 업데이트 효율성을 최적화하기 위해 읽기 및 쓰기 속도를 희생할 가치가 없습니다.

예를 들어 정규화에 대한 교과서 소개의 예로 사용자와 사용자 주소를 별도의 컬렉션에 저장하는 것이 있을 수 있습니다. 그러나 사람들은 자신의 주소를 거의 변경하지 않으므로 누군가가 자신의 주소를 변경하는 극히 드문 경우에 대해 각 쿼리의 효율성이 희생되어서는 안 됩니다. 이 경우 주소는 사용자 문서에 포함되어야 합니다.

인라인 문서를 사용하기로 결정한 경우 각 업데이트에 대해 모든 문서가 성공적으로 업데이트되도록 문서를 업데이트할 때 크론 작업을 설정해야 합니다. 예를 들어 업데이트를 여러 문서에 분산시키려고 했는데 업데이트가 모든 문서를 완료하기 전에 서버가 충돌했습니다. 이 문제를 감지하고 완료되지 않은 업데이트를 다시 실행할 수 있어야 합니다.

일반적으로 데이터가 더 자주 생성될수록 다른 문서에 포함될 가능성은 줄어듭니다. 포함된 필드 또는 포함된 필드의 수가 무한정 증가하는 경우 이러한 콘텐츠는 별도의 컬렉션에 저장되어야 하며 다른 문서에 포함되는 대신 참조를 사용하여 액세스해야 합니다. 다른 문서에 포함되어서는 안 됩니다.

마지막으로 특정 필드가 문서 데이터의 일부인 경우 해당 필드를 문서에 포함해야 합니다. 문서를 쿼리할 때 필드를 자주 제외해야 하는 경우 이 필드는 현재 문서에 포함되지 않고 다른 컬렉션에 배치되어야 합니다.

更适合内嵌 更适合引用
子文档较小 子文档较大
数据不会定期改变 数据经常改变
最终数据一致即可 中间阶段的数据必须一致
文档数据小幅增加 文档数据大幅增加
数据通常需要执行二次查询才能获得 数据通常不包含在结果中
快速读取 快速写入

사용자 컬렉션이 있다고 가정해 보겠습니다. 다음은 필요할 수 있는 일부 필드와 해당 필드가 사용자 문서에 포함되어야 하는지 여부입니다.

사용자 기본 설정(계정 기본 설정)

사용자 기본 설정은 특정 사용자에게만 관련되며 사용자 문서 내의 다른 사용자 정보와 함께 쿼리해야 할 가능성이 높습니다. 따라서 사용자 기본 설정은 사용자 문서에 포함되어야 합니다.

최근 활동

이 필드는 최근 활동이 얼마나 자주 성장하고 변화했는지에 따라 달라집니다. 고정 길이 필드(예: 마지막 10개 이벤트)인 경우 이 필드는 사용자 문서에 포함되어야 합니다.

친구

일반적으로 친구 정보를 사용자 문서에 포함하면 안 됩니다. 적어도 전체는 포함하지 마세요. 다음 섹션에서는 소셜 네트워크 애플리케이션의 관련 콘텐츠를 소개합니다.

모든 사용자 생성 콘텐츠

사용자 문서에 포함되어서는 안 됩니다.

베이스

한 컬렉션에 포함된 다른 컬렉션에 대한 참조 수를 카디널리티라고 합니다. 일반적인 관계에는 일대일, 일대다, 다대다 등이 있습니다. 블로그 애플리케이션이 있다고 가정해 보겠습니다. 각 블로그 게시물에는 일대일 관계인 제목이 있습니다. 각 작성자는 여러 기사를 가질 수 있으며 이는 대다 관계입니다. 각 기사에는 여러 개의 태그가 있을 수 있고 각 태그는 여러 기사에서 사용될 수 있으므로 이는 다대다 관계입니다.

MongoDB에서는 다수를 다수와 소수라는 두 가지 하위 범주로 나눌 수 있습니다. 예를 들어, 저자와 기사 간의 관계는 일대일 관계일 수 있습니다. 각 저자는 몇 개의 기사만 게시합니다. 블로그 게시물과 태그 사이에는 다대소 관계가 있을 수 있습니다. 즉, 게시물 수가 실제로 태그 수보다 클 수 있습니다. 블로그 게시물과 댓글 사이에는 일대다 관계가 있습니다. 각 게시물에는 많은 댓글이 있을 수 있습니다.

적음과 많음의 관계가 결정되는 한 삽입된 데이터와 참조 데이터 간의 균형을 맞추는 것이 더 쉽습니다. 일반적으로 말해서, "적은" 관계에는 인라인 방법을 사용하는 것이 더 좋고, "다" 관계에는 참조 방법을 사용하는 것이 더 좋습니다.

친구, 팬 등 귀찮은 일

친구는 가까이 두고 적과는 멀리하세요

많은 소셜 애플리케이션은 사람, 콘텐츠, 팬, 친구 등을 연결해야 합니다. 관련성이 높은 데이터에 대해 인라인 양식과 참조 양식을 사용하는 것 사이의 균형을 맞추는 것은 쉽지 않습니다. 이 섹션에서는 소셜 그래프 데이터와 관련된 고려 사항을 소개합니다. 종종 팔로잉, 친구 또는 즐겨찾기는 게시-구독 시스템으로 단순화될 수 있습니다. 한 사용자는 다른 사용자와 관련된 알림을 구독할 수 있습니다. 이런 식으로 효율적이어야 하는 두 가지 기본 작업이 있습니다. 구독자를 저장하는 방법과 모든 구독자에게 이벤트를 알리는 방법입니다.

일반적인 구독 구현 방법에는 세 가지가 있습니다. 첫 번째 방법은 구독자 문서에 콘텐츠 제작자를 포함시키는 것입니다.

으아악

이제 특정 사용자 문서에 대해 db.activities.find({"user": {"$in": user["following"]}}) 양식을 사용하여 사용자가 관심 있는 모든 활동 정보를 쿼리할 수 있습니다. 그러나 최근 공개된 활동 정보에 대해 해당 정보에 관심이 있는 모든 사용자를 알고 싶다면 전체 사용자의 '팔로잉' 필드를 쿼리해야 합니다.

또 다른 방법은 구독자를 생산자 문서에 포함시키는 것입니다.

으아악

이 제작자가 새 메시지를 게시하면 어떤 사용자에게 알림이 필요한지 즉시 알 수 있습니다. 이 방법의 단점은 사용자가 팔로우하는 사용자 목록을 찾으려면 전체 사용자 컬렉션을 쿼리해야 한다는 것입니다. 이 방법의 장점과 단점은 첫 번째 방법과 정반대입니다.

동시에 두 방법 모두 또 다른 문제가 있습니다. 사용자 문서가 점점 더 커지고 변경이 점점 더 자주 발생한다는 것입니다. 종종 "following" 및 "followers" 필드는 반환할 필요조차 없습니다. 팔로어 목록은 얼마나 자주 쿼리됩니까? 사용자가 특정 사람을 더 자주 팔로우하거나 일부 사람을 팔로우 해제하는 경우에도 많은 조각화가 발생합니다. 따라서 최종 솔루션은 이러한 단점을 피하기 위해 데이터를 더욱 정규화하고 구독 정보를 별도의 컬렉션에 저장합니다. 이러한 종류의 정규화는 약간 과할 수 있지만 자주 변경되고 문서의 나머지 부분과 함께 반환될 필요가 없는 필드에 유용합니다. "팔로어" 필드를 정규화하는 것이 합리적입니다.

컬렉션을 사용하여 게시자와 구독자 간의 관계를 저장하세요. 문서 구조는 다음과 같습니다.

으아악

이렇게 하면 사용자 문서가 더욱 간소화될 수 있지만 팬 목록을 가져오려면 추가 쿼리가 필요합니다. "followers" 배열의 크기는 자주 변경되므로 이 컬렉션에서 "usePowerOf2Sizes"를 활성화하여 사용자 컬렉션을 최대한 작게 만들 수 있습니다. 팔로어 컬렉션이 다른 데이터베이스에 저장되어 있는 경우 사용자 컬렉션에 큰 영향을 주지 않고 압축할 수 있습니다.

윌 휘튼 효과에 대처하기

어떤 전략을 사용하든 인라인 필드는 하위 문서나 참조 수가 특별히 많지 않은 경우에만 효과적으로 작동할 수 있습니다. 유명 사용자의 경우 팬 목록을 저장하는 데 사용되는 문서가 오버플로될 수 있습니다. 이 상황에 대한 한 가지 해결책은 필요할 때 "연속" 문서를 사용하는 것입니다. 예:

으아악

이 상황에서는 애플리케이션의 "tbc"(계속) 배열에서 데이터를 가져오는 관련 로직을 추가해야 합니다.

말해 보세요

만병통치약은 없습니다.

伊谢尔伦

비즈니스에서 항상 사용자 간의 관계를 쿼리해야 하는 경우 관계를 컬렉션으로 분리하는 것이 좋습니다

최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿