前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >MongoDB 大俗大雅,高端的知识讲“通俗” -- 2 嵌套和引用

MongoDB 大俗大雅,高端的知识讲“通俗” -- 2 嵌套和引用

作者头像
AustinDatabases
发布2025-01-15 21:15:30
发布2025-01-15 21:15:30
5600
代码可运行
举报
文章被收录于专栏:AustinDatabasesAustinDatabases
运行总次数:0
代码可运行

这是MongoDB 宣传周的第二集,本集将讨论

2 在模式之间迁移如何更简单,让应用停机的时间更少

3 更好的支持板结构化的数据结构

说到这两个问题,我们首先要引入两个知识点,嵌入文档和引用文档两个概念。

1 什么叫嵌入文档

如果从字面的意思理解,是一个文档中嵌套了或包含了另一个文档,这是一种关联数据的方式,利用这样的方式我们就直接避免了JOIN,对传统数据库的表的JOIN。这也是MongoDB 化解性能问题的第一个方案,因为JOIN的操作在传统数据库就是一个消耗性能的操作。

任何一个搞传统数据库的人都明白访问一个表,要比同时访问两个表要快的道理,这个道理正是MongoDB 化解传统数据库性能不友好的第一方案。

代码语言:javascript
代码运行次数:0
复制
{
  "_id": ObjectId("..."),
"name": "Austin",
"address": {
    "street": "XX路XX号",
    "city": "天津",
    "zipcode": "100000"
  },
"contacts": [
    { "type": "phone", "number": "138xxxxxxxx" },
    { "type": "email", "address": "Liuhuayang@example.com" }
  ]
}

正如上面的这个例子,一个是人的个人信息,一个是他的地址信息,但我们设想一个人现在如果他有俩手机怎么办,他有3个邮箱怎么办,如果他还有真假名怎么办。

传统数据库怎么办,在弄一个表,在弄一个列来记录这些信息??

而在MongoDB中很容易解决这些问题。

代码语言:javascript
代码运行次数:0
复制
{
  "_id": ObjectId("..."),
"name": "Austin",
"address": {
    "street": "XX路XX号",
    "city": "天津",
    "zipcode": "100000"
  },
"contacts1": [
    { "type": "phone", "number": "138xxxxxxxx" },
    { "type": "email", "address": "Liuhuayang@example.com" }
  ],
"contacts2": [
    { "type": "phone", "number": "139xxxxxxxx" },
    { "type": "email", "address": "Liuaustin@example.com" }
  ]
}

你看这就变得简单多了,实际上在讲MongoDB的时候,PG这个数据也有类似的设计,比如PG数据库中的Hstore。这些都是要化解一些传统数据库解决不好,笨拙的地方。

那么这里我们稍微总结一下,嵌套的使用场景是什么

在一个一对少的场景下,使用场景解决 1 减少获取数据的次数,由多次,改为一次获取。

2 与核心信息有关的信息,可以不用分类的存储在核心信息周围

3 简化数据的建模,不进行范式化的建设

4 数据高频进行访问的场景

5 数据量本身不宜过大(单条document)

缺点也显而易见

1 数据可能存在冗余

2 更新数据会更加的复杂

3 使用的场景有局限性,如一对多就不合适了,或者多对多的关系场景。

在出现缺点的时候,我们就应该使用第二种方案,引用。嵌套和引用是MongoDB给我们解决大部分问题的方案。引用主要使用在必须要进行关联,但两个部分实在合不成一个"集合”的状态下。

比如:一个汽车生产商,它的配件信息和车辆信息的对接。一辆车上万个零件,而一个零件可以用在几种车上,这样的情况下,显然不适合用嵌套了。

那么我们怎么办, 1建立一汽车零件表 2每种汽车有一个零件的数组,或者将各个部分的零件门类分类,然后将零件表里面的零件编号以数组的方式存储在另一个表里面。

下面就是一个例子第一个是零件表,第二个是车辆表

代码语言:javascript
代码运行次数:0
复制
{
  "_id": ObjectId("..."), // 零件ID
"part_number": "P123456", // 零件编号(唯一)
"name": "发动机", // 零件名称
"description": "高性能发动机", // 零件描述
"manufacturer": "ABC公司", // 制造商
"price": 10000, // 价格
"compatible_vehicles": [ // 兼容的车辆ID数组(使用引用)
    ObjectId("..."),
    ObjectId("...")
  ],
  // 其他零件相关属性
}
代码语言:javascript
代码运行次数:0
复制
{
  "_id": ObjectId("..."), // 车辆ID
"vin": "VIN1234567890", // 车辆识别码(唯一)
"model": "Model X", // 车型
"year": 2023, // 生产年份
"parts": [ // 车辆包含的零件ID数组(使用引用)
    ObjectId("..."),
    ObjectId("..."),
    ObjectId("...")
    // ... 大量零件ID
  ],
    "parts_categories": { //按零件类别存储零件ID,更方便查询
        "engine": [ObjectId("..."), ObjectId("...")],
        "transmission": [ObjectId("..."), ObjectId("...")],
        "body": [ObjectId("..."), ObjectId("...")],
        // ...其他类别
    },
  // 其他车辆相关属性
}

这里查询起来很简单,如果要查询一辆车的零件,那么值需要分两步走, 第一步:获取第一个表的信息,以及各个零件的ObjectID

第二步:获取第二个表的所有OBJECT_ID对应行的需要提取的信息。

实际上这和阿里的一些MySQL设计的军规如出一辙,强制数据查询解耦。

这样的好处非常明显,如果这两个表都在UPDATE的情况下,产生锁和block的可能性很低,虽然在MongoDB中会产生锁,但在这样的设计下,查询对更新的锁干扰将微乎其微。

同时我们还可以注意到,如果一辆车的零件增加和减少,都只和第一张表有关,如果零件作废了,需要标记,只和第二张表有关。

同时我们还可以注意到,引用可以双向引用,比如我通过零件也可以查到,这个零件用到那些车上,零件也可以自己来一个车辆的数组。

通过有效的索引设计,查询信息将非常快。

写到这里一定有人问,这么好,有什么缺点吗?

聚合,无条件的聚合,无条件聚合且集合的数据量非常大......

返回到这期的主题

2 在模式之间迁移如何更简单,让应用停机的时间更少

3 更好的支持板结构化的数据结构

举例上面的例子中,如果是二位表格,一定会涉及到加减字段,对字段进行格式化的处理,应用需要注意相关的DDL操作,尤其大表,而这一切再MongoDB中根本不存在,因为无结构化的数据,你可以随心,在结构化的数据中,添加一些非结构化的特性,这才是MongoDB的精髓和超出传统数据库设计难题中的过墙梯。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-01-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 AustinDatabases 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档