如何在 MongoDB 中设计文档结构?与关系型数据库的表结构设计有何不同?

news2025/5/21 2:08:13

在 MongoDB 中设计文档结构是一个核心且重要的环节,它直接影响应用的性能、可扩展性和可维护性。

MongoDB 文档结构设计原则与方法

MongoDB 的核心思想是数据如何被应用访问,就如何存储它。

  1. 嵌入 (Embedding / Denormalization) vs. 引用 (Referencing / Normalization):
    这是 MongoDB 设计中最关键的决策点。

    • 嵌入 (Embedding):

      • 概念: 将相关联的数据直接存储在父文档内部,通常作为嵌套文档或数组。
      • 优点:
        • 读取性能高: 一次查询即可获取所有相关数据,无需额外的查询(类似 JOIN)。
        • 原子性操作: 对单个文档的更新是原子的。如果嵌入的数据与父文档一起更新,可以保证操作的原子性。
      • 缺点:
        • 文档大小限制: 单个 BSON 文档最大为 16MB。如果嵌入的数据量巨大,可能超出限制。
        • 数据冗余: 如果嵌入的数据也需要在其他地方独立访问或被多个父文档共享,会导致数据冗余和更新困难(需要更新所有包含该数据的文档)。
        • 大型文档更新开销: 更新大型文档中的一小部分数据,可能比更新小型独立文档更耗时。
      • 适用场景:
        • “包含”关系 (contains relationship)。
        • 一对一 (one-to-one) 或一对少量 (one-to-few) 关系,且子数据总是与父数据一起被访问。
        • 数据不经常独立变化,或者变化时总是与父文档一起。
        • 例如:博客文章及其评论(如果评论数量可控且随文章展示)。
    • 引用 (Referencing):

      • 概念: 类似于关系型数据库中的外键。在一个文档中存储另一个文档的 _id(或其他唯一标识符)。查询时需要通过额外的查询(或使用 $lookup 操作符)来获取关联数据。
      • 优点:
        • 数据规范化: 避免数据冗余,更新一个地方即可。
        • 更小的文档: 父文档体积更小。
        • 灵活性: 关联数据可以独立访问和更新。
      • 缺点:
        • 读取性能较低: 需要额外的查询(或 $lookup)来获取关联数据,增加了数据库的往返次数。
        • 非原子性: 获取和更新关联数据通常需要多个操作,缺乏事务保证(虽然 MongoDB 4.0+ 支持多文档事务,但设计时仍需考虑)。
      • 适用场景:
        • 一对多 (one-to-many) 且“多”的一方数量巨大或无界。
        • 多对多 (many-to-many) 关系。
        • 关联数据经常独立更新或被多个父文档引用。
        • 需要避免大型文档或数据冗余。
        • 例如:订单及其关联的产品(产品是独立实体,被多个订单引用)。
  2. 考虑数据访问模式 (Data Access Patterns):

    • 读写比例: 如果应用是读密集型的,嵌入可以提高性能。如果是写密集型且嵌入数据经常变化,引用可能更好。
    • 查询频率和类型: 哪些数据经常一起被查询?设计文档结构以匹配最常见的查询。
    • 数据增长: 考虑数组或嵌套文档是否会无限增长,导致文档过大。
  3. 权衡数据冗余和查询性能:

    • 在 MongoDB 中,适度的冗余是可以接受甚至推荐的,如果它能显著提高读取性能并简化查询。
    • 例如,在订单文档中嵌入产品名称和价格,即使产品信息存储在单独的集合中。这样在显示订单列表时就不需要再去查询产品集合。但要注意,如果产品价格变动,需要机制去更新所有相关订单中的冗余数据(或接受历史订单价格不变的事实)。
  4. 原子性需求:

    • 如果一组数据的更新必须是原子的,将它们放在同一个文档中。MongoDB 保证单个文档操作的原子性。
  5. 索引策略:

    • 设计文档结构时,要考虑哪些字段会被频繁查询,并为这些字段创建索引。嵌入式文档的字段也可以被索引。
  6. 常见模式:

    • 属性模式 (Attribute Pattern): 对于字段繁多但很多是稀疏的情况,可以将属性作为键值对数组存储。
    • 桶模式 (Bucket Pattern): 用于时间序列数据,将一段时间内的数据分组到同一个文档中,减少索引条目。
    • 多态模式 (Polymorphic Pattern): 同一个集合存储不同结构的文档,通过一个公共字段(如 type)来区分。

与关系型数据库表结构设计的核心不同

特性关系型数据库 (RDBMS)MongoDB
核心思想数据规范化,减少冗余,保证数据一致性。数据如何被访问,就如何存储,优先考虑查询性能和开发便捷性。
Schema严格模式 (Schema-on-Write):表结构预先定义,强制执行。灵活模式 (Schema-on-Read):集合不强制文档结构,结构可变。
数据关系通过外键和 JOIN 操作来表示和查询数据间的关系。通过嵌入文档或引用(_id$lookup)来表示关系。
范式通常遵循第三范式 (3NF) 或 BCNF 来减少数据冗余。经常使用反范式化(嵌入)来提高读取性能,接受一定程度的数据冗余。
数据完整性通过外键约束、检查约束、触发器等在数据库层面强制执行。主要依赖应用层面来保证数据完整性。MongoDB 本身约束较少。
JOIN 操作SQL JOIN 是核心且高效的操作。$lookup 类似于左外连接,但通常性能不如 RDBMS 的 JOIN,设计上尽量避免。
原子性支持跨多表的 ACID 事务。单文档操作是原子的。多文档 ACID 事务从 4.0 版本开始支持,但设计时仍需权衡。
数据冗余极力避免。在特定情况下为了性能可以接受和鼓励。
设计驱动主要由数据本身的逻辑关系驱动。主要由应用程序的访问模式和性能需求驱动。
数据单元行 (Row) 在表 (Table) 中。文档 (Document) 在集合 (Collection) 中。

设计示例思考过程:博客系统

  • 实体: 用户 (User), 文章 (Post), 评论 (Comment), 标签 (Tag)

  • 关系型设计思路:

    • users 表 (user_id, name, email, …)
    • posts 表 (post_id, user_id (FK), title, content, …)
    • comments 表 (comment_id, post_id (FK), user_id (FK), text, …)
    • tags 表 (tag_id, name)
    • post_tags 关联表 (post_id, tag_id)
  • MongoDB 设计思路:

    1. Users Collection:

      {
        "_id": ObjectId("user1"),
        "name": "Alice",
        "email": "alice@example.com"
      }
      
    2. Posts Collection:

      • 方案 A (嵌入评论和标签,如果量不大且总是随文章展示):

        {
          "_id": ObjectId("post1"),
          "title": "My First MongoDB Post",
          "content": "...",
          "author_id": ObjectId("user1"), // 引用作者
          "author_name": "Alice",         // 可选的冗余,用于快速显示
          "tags": ["mongodb", "nosql", "database"], // 嵌入标签名
          "comments": [
            {
              "user_id": ObjectId("user2"),
              "user_name": "Bob",
              "text": "Great post!",
              "timestamp": ISODate("...")
            },
            {
              "user_id": ObjectId("user3"),
              "user_name": "Charlie",
              "text": "Very informative.",
              "timestamp": ISODate("...")
            }
          ],
          "publication_date": ISODate("...")
        }
        
        • 考虑: 如果评论非常多,嵌入会导致文档过大,这时应考虑将评论作为独立集合并通过 post_id 引用。
      • 方案 B (评论作为独立集合,引用文章ID):
        Posts Collection:

        {
          "_id": ObjectId("post1"),
          "title": "My First MongoDB Post",
          "content": "...",
          "author_id": ObjectId("user1"),
          "author_name": "Alice",
          "tags": ["mongodb", "nosql", "database"],
          "publication_date": ISODate("...")
        }
        

        Comments Collection:

        {
          "_id": ObjectId("comment1"),
          "post_id": ObjectId("post1"), // 引用文章
          "user_id": ObjectId("user2"),
          "user_name": "Bob",
          "text": "Great post!",
          "timestamp": ISODate("...")
        }
        
    3. Tags Collection (如果需要对标签进行管理或聚合分析):

      {
        "_id": ObjectId("tag_mongo"),
        "name": "mongodb",
        "description": "About MongoDB NoSQL database"
      }
      

      文章中可以存储 tag_ids 数组,或者像方案 A 那样直接嵌入标签名。如果嵌入标签名,可以通过对 posts.tags 字段创建多键索引来高效查询。

总结:
MongoDB 的文档设计是一个权衡的过程,没有一刀切的“正确”答案。最佳设计取决于具体的应用需求、数据访问模式、数据关系、性能要求和数据一致性要求。通常需要迭代的进行设计和优化。核心是理解嵌入和引用的优缺点,并根据你的应用场景做出明智的选择。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2380380.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MYSQL 故障排查与生产环境优化

目录 一.前置知识点 1. 案例需求 (1)mysql 常见故障解决 (2)mysql 性能优化 2.案例实施思路 (1)单库常见故障分析 (2)主从常见故障分析 (3)从几个不同…

解决使用@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss“, timezone = “GMT+8“)时区转换无效的问题

前言 对于一些时间的字段,我们从数据库查询出来通常需要转换后返回给前端展示,前端需要的格式一般为yyyy-MM-dd HH:mm:ss,可以通过JsonFormat注解来作转换和时区转换。 问题场景 原因 LocalDateTime类本身不带时区信息所以转换无效 解决办…

计算机网络概要

⽹络相关基础知识 协议 两设备之间使⽤光电信号传输信息数据 要想传递不同信息 那么⼆者ᳵ就需要约定好的数据格式 层 封装 继承 多态是计算机的性质 它们⽀持了软硬件分层的实现 同层协议可以ᳵ接通信 同层协议ᳵ不直接通信 是各⾃调⽤下层提供的结构能⼒完成通信 分层…

Word压缩解决方案

Word压缩解决方案:基于图片压缩的 .docx 优化实践 📌 背景 在日常科研写作或项目文档整理中,Word 文档(.docx)往往因为插入大量高清图表、扫描图像、公式图等导致文件体积过大,或者毕业学位论文查重要求上…

TDengine 2025年产品路线图

TDengine OSS 之 2025 年年度路线图如下表所示。 季度功能2025Q1 虚拟表查询能力:REGEXP、GREATEST、LEAST、CAST 函数支持判断表达式、单行选择函数的其他列值、INTERP 支持插值时间范围存储能力:支持将查询结果写入超级表、超级表支持 KEEP 参数、STM…

Ubuntu服务器部署多语言项目(Node.js/Python)方式实践

Ubuntu服务器部署多语言项目(Node.js/Python)方式实践 服务器脚本运行方式命令行直接执行nohup后台执行进程 Screen概述安装基本操作命令启动 Screen退出当前会话(不终止进程)查看所有会话重连会话关闭会话 常用快捷键典型使用场景…

计算机网络 - 2.基础协议

1.TCP协议 1.TCP(Transmission Control Protocol):传输控制协议2.TCP协议是一种面向连接的、可靠的、 基于字节流的传输层通信协议 1.面向连接:两个使用TCP协议的应用(通常一个客户和一个服务器)在彼此交换数据包之前必须先建立一个TCP连接2.可靠的 1.数据传输之前都要建立…

初识css,css语法怎样学好css以及常见问题与避坑

一、CSS 是什么? CSS(Cascading Style Sheets)是一种用于描述网页文档(HTML 或 XML)呈现样式的语言。它负责控制网页元素的视觉表现,如颜色、字体、布局等,使内容与展示分离。 二、CSS 语法结构…

Axure疑难杂症:垂直菜单展开与收回(4大核心问题与专家级解决方案)

亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢!如有帮助请订阅专栏! Axure产品经理精品视频课已登录CSDN可点击学习https://edu.csdn.net/course/detail/40420 课程主题:垂直菜单展开与收回 主要内容:超长菜单实现、展开与收回bug解释、Axure9版本限制等问题解…

vue2.0 组件生命周期

个人简介 👨‍💻‍个人主页: 魔术师 📖学习方向: 主攻前端方向,正逐渐往全栈发展 🚴个人状态: 研发工程师,现效力于政务服务网事业 🇨🇳人生格言&…

在Linux服务器上部署Jupyter Notebook并实现ssh无密码远程访问

Jupyter notebook版本7.4.2(这个版本AI提示我Jupyter7(底层是 jupyter_server 2.x) 服务器开启服务 安装Jupyter notebook 7.4.2成功后,终端输入 jupyter notebook --generate-config 这将在 ~/.jupyter/ 目录下生成 jupyter_…

GPU 超级节点:AWS Trainium2 UltraServer

目录 文章目录 目录时间线Inferentia1Trainium1Inferentia2Trainium2Trainium2 ServerTrainium2 UltraServerTrainium2 UltraClustersTrainium3AWS GPU 实例矩阵与竞品分析SuperNode RackTrn2 ServerTrn2U Server ScaleUp 网络PCIe Gen5:CPU-Trainium2 ScaleUpNeuro…

git仓库中.git 文件很大,怎么清理掉一部分

查询 .git 文件大小,在 git-bash 里执行(后面有些命令不能执行,也请在 git-bash 里执行) windows11 安装好后右键没有 git bash 命令-CSDN博客 du -sh .git // 592m .git 操作前最好先备份一份,避免推送到远程时出错…

MySQL安装实战指南:Mac、Windows与Docker全平台详解

MySQL作为世界上最流行的开源关系型数据库,是每位开发者必须掌握的基础技能。本指南将手把手带你完成三大平台的MySQL安装,从下载到配置,每个步骤都配有详细说明和截图,特别适合新手学习。 一、Mac系统安装MySQL 1.1 通过Homebre…

Rocky Linux 远程服务器画面GUI传输到本地显示教程——Xming

Rocky Linux 远程服务器画面GUI传输到本地显示教程——Xming 下载Xming安装Xming安装Xming字体Xming的使用设置测试 Xming可以提供GUI环境,在Linux服务器上执行GUI应用时,可通过Xming在Windows上执行GUI操作。 下载Xming 下载链接:https://…

游戏引擎学习第283天:“让‘Standing-on’成为一个更严谨的概念

如果同时使用多个OpenGL上下文,并且它们都有工作负载,GPU或GPU驱动程序如何决定调度这些工作?我注意到Windows似乎优先处理活动窗口的OpenGL上下文(即活动窗口表现更好),挺有意思的…… 当多个OpenGL上下文…

React集成百度【JSAPI Three】教程(001):快速入门

文章目录 1、快速入门1.1 创建react项目1.2 安装与配置1.3 静态资源配置1.4 配置百度地图AK1.5 第一个DEMO1、快速入门 JSAPI Three版本是一套基于Three.js的三维数字孪生版本地图服务引擎,一套引擎即可支持2D、2.5D、3D全能力的地理投影与数据源加载,帮助开发者轻松搞定平面…

python学习day2

今天主要学习了变量的数据类型,以及如何使用格式化符号进行输出。 一、认识数据类型 在python里为了应对不同的业务需求,也把数据分为不同的类型。 代码如下: """ 1、按类型将不同的变量存储在不同的类型数据 2、验证这些…

【C++】模版(1)

目录 1. 泛型编程 2. 函数模版 2.1 函数模版概念 2.2 函数模版格式 2.3 函数模版的原理 2.4 函数模版实例化方式 隐式实例化 显式实例化 2.5 模版参数的匹配原则 3. 模版类 模版类的定义格式 模版类的实例化 1. 泛型编程 如何实现一个通用的交换函数呢&#xff1f…

基于开源AI智能名片链动2+1模式S2B2C商城小程序源码的去中心化商业扩散研究

摘要:本文探讨在去中心化商业趋势下,开源AI智能名片链动21模式S2B2C商城小程序源码如何助力企业挖掘数据价值、打破信息孤岛,实现商业高效扩散。通过分析该技术组合的架构与功能,结合实际案例,揭示其在用户关系拓展、流…