本系列教程目录:

MongoDB入门实战教程(1)

MongoDB入门实战教程(2)

MongoDB入门实战教程(3)

MongoDB入门实战教程(4)

MongoDB入门实战教程(5)

MongoDB入门实战教程(6)

前面我们学习了聚合查询,本篇我们来看看在模型设计中如何应用引用模式来提高查询效率。

1内嵌模式

在进行MongoDB的模型设计中,基于JSON文档模型,我们很容易就可以设计出一个内嵌模式的文档模型出来。

可以不夸张地说,80%~90%的场景下,我们优先都会使用内嵌对象 或 内嵌数组 的方式来设计文档模型的所谓的1-1、1-N、N-N的关系

例如下面这个Contacts的文档模型,它描述了一个联系人的关系建模:

Contacts

{
name: "Edison Zhou",
company: "CSCEC YZW",
title: ".NET Engineer",
portraits: {
mimetype: xxx,
data: xxxx
},
addresses: [
{ type: home, … },
{ type: work, … }
],
groups: [
{name: "YZW Football Assocation" },
{name: "YZW .NET Assocation" }
]
}

可以看到,所谓的内嵌类 其实 类似于 预先聚合(关联)这样的操作(引用+冗余)其实对读操作更有性能优势

但是,内嵌设计有一个大前提限制:即内嵌后文档大小不能超过16MB。

此外,如果内嵌的数组(通常是数组)的长度太大,比如数万或更多的时候,也是不适合采用内嵌模式的。

那么,此时我们应该怎么设计呢?

2 引用模式

万级长度的内嵌数组

这里我们仍然适用上面提到的Contacts模型,假设其中的groups是一个内嵌数组,这个groups的数据可能有百万级的长度,且每个Contacts文档都需要冗余这么一份数据,而且groups数据还面临着频繁修改的需求。

Contacts

{
name: "Edison Zhou",
company: "CSCEC YZW",
title: ".NET Engineer",
......
// 假设下面groups有百万级,且一个group的信息改动会引发百万级的DB操作
groups: [
{name: "YZW Football Assocation" },
{name: "YZW .NET Assocation" }
]
}

适当使用引用模式解决

解决方案很简单,就是针对groups使用单独的collection来存储,在Contancts模型中添加对group id的集合的引用。

Collection 1 - Contacts:

Contacts

{
name: "Edison Zhou",
company: "CSCEC YZW",
title: ".NET Engineer",
......
// 假设下面groups有百万级,且一个group的信息改动会引发百万级的DB操作
group_ids: [1,2,3,4,5...]
}

Collection 2 - Groups:

Groups

{
groups_id,
name
}

这样的设计其实类似于关系型数据库模型的设计,用Id来关联,我们再熟悉不过了。

但是,在MQL中,我们就需要额外使用$lookup来实现类似SQL中的关联查询了,严格来说,应该算是LEFT OUTER JOIN查询。

嗯,这又是一种聚合操作:

db.Contacts.aggregate([
{
$lookup:
{
from: "groups",
localField: "group_ids",
foreignField: "group_id",
as: "groups"
}
}]);

这个查询会得到如下图所示的结果:

.NET中的Lookup操作:

上面讲解了如何通过MQL进行操作,那么,在.NET中如何实现$lookup的效果呢?

好在MongoDB Driver已经帮我们提供了这样的一个LookUp,且看下面的代码示例:

假设我们的实体定义如下:

public class Contact
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; } public string Name { get; set; } public string Company { get; set; } public string Title { get; set; } public int[] GroupIds { get; set; } public IList<Group> Groups { get; set; }
} public class Group
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; } public int GroupId { get; set; } public string Name { get; set; }
}

那么,可以通过Driver实现以下操作:

public async Task<IList<Contact>> GetAsync()
{
return await _contacts
.Aggregate()
.Lookup<Contact, Group, Contact>(
_groups,
local => local.GroupIds,
from => from.GroupId,
result => result.Groups)
.ToListAsync();
}

完整示例github地址:https://github.com/EdisonChou/EDT.Mongo.Sample

运行结果如下所示:

什么时候使用引用模式

综上所述,当满足以下条件之一时,你可以开始考虑引用模式设计文档模型:

(1)当内嵌后的文档太大,有可能超过16MB限制的时候;

(2)内嵌的文档 或 数组元素 有可能会频繁修改的时候;

(3)内嵌数组元素 有可能会持续增长且没有封顶的时候;

引用模式设计的限制

引用模式也并非银弹,它存在以下一些限制:

(1)MongoDB对于使用引用的集合之间没有所谓的外键检查;

(2)MongoDB使用聚合框架的$lookup来模仿关联查询;

(3)$lookup只支持LEFT OUTER JOIN,且关联目标(from)不能是分片表;

db.Contacts.aggregate([
{
$lookup:
{
from: "groups", // 这里的from不能是分片表
......
}
}]);

总结

本文简单介绍了MongoDB的模型设计中的内嵌模式和引用模式,探讨了引用模式的使用、何时使用 及 使用限制。

下一篇,我们会学习MongoDB的模式设计中的一些设计模式并套用这些设计模式简化设计难度。

参考资料

唐建法,《MongoDB高手课》(极客时间)

郭远威,《MongoDB实战指南》(图书)

△推荐订阅学习

作者:周旭龙

出处:https://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

MongoDB入门实战教程(7)的更多相关文章

  1. Kafka入门实战教程(7):Kafka Streams

    1 关于流处理 流处理平台(Streaming Systems)是处理无限数据集(Unbounded Dataset)的数据处理引擎,而流处理是与批处理(Batch Processing)相对应的.所 ...

  2. ZooKeeper入门实战教程(一)-介绍与核心概念

    1.ZooKeeper介绍与核心概念1.1 简介ZooKeeper最为主要的使用场景,是作为分布式系统的分布式协同服务.在学习zookeeper之前,先要对分布式系统的概念有所了解,否则你将完全不知道 ...

  3. Node+Express+MongoDB+Socket.io搭建实时聊天应用实战教程(一)--MongoDB入门

    前言 本文并不是网上流传的多少天学会MongoDB那种全面的教程,而意在总结这几天使用MongoDB的心得,给出一个完整的Node+Express+MongoDB+Socket.io搭建实时聊天应用实 ...

  4. Node+Express+MongoDB + Socket.io搭建实时聊天应用实战教程(二)--node解析与环境搭建

    前言 本来开始写博客的时候只是想写一下关于MongoDB的使用总结的,后来觉得还不如干脆写一个node项目实战教程实战.写教程一方面在自己写的过程中需要考虑更多的东西,另一方面希望能对node入门者有 ...

  5. MongoDB入门必读(概念与实战并重)

    MongoDB入门必读(概念与实战并重) 一.概述 MongoDB是一个基于分布式文件存储的数据库开源项目.由C++语言编写.旨在为WEB应用提供可护展的高性能数据存储解决方案. MongoDB是一个 ...

  6. mybatis实战教程(mybatis in action),mybatis入门到精通

    转自:http://www.yihaomen.com/article/java/302.htm (读者注:其实这个应该叫做很基础的入门一下下,如果你看过hibernate了那这个就非常的简单) (再加 ...

  7. mybatis实战教程(mybatis in action),mybatis入门到精通(转)

    转自:http://www.yihaomen.com/article/java/302.htm (读者注:其实这个应该叫做很基础的入门一下下,如果你看过Hibernate了那这个就非常的简单) (再加 ...

  8. mongodb入门教程二

    title: mongodb入门教程二 date: 2016-04-07 10:33:02 tags: --- 上一篇文章说了mongodb最基本的东西,这边博文就在深入一点,说一下mongo的一些高 ...

  9. mongodb入门教程

    title: mongodb入门教程 date: 2016-04-06 14:47:18 tags: --- 为什么要认识呢,因为这玩意就一傻逼 借用一下百科的介绍 MongoDB 是一个介于关系数据 ...

  10. MongoDb 入门教程

    MongoDb 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的. 它是可扩展的高性能数据存储解决方案,经常被用于非关系型数据的存储,能存储海量的数据. 常 ...

随机推荐

  1. 【SpringCloud】SpringCloud Stream消息驱动

    SpringCloud Stream消息驱动 消息驱动概述 是什么 什么是SpringCloudStream 官方定义Spring Cloud Stream是一个构建消息驱动微服务的框架. 应用程序通 ...

  2. 开源姿势识别 Demo

    最近项目中要用到姿势识别,调研了 BlazePose. MoveNet 等模型,以下是一些详细的对比. 包括 mediapipe 和 tfjs 等运行环境,webgl,webgpu,wasm 都做了尝 ...

  3. CoreOS 发行版本介绍

    大多数的软件通常都有什么内测版.公测版什么的. CoreOS 发行版本 而在 CoreOS 中, 有以下3个版本: alpha - α版,音译:阿尔法,俗称尝鲜版,是最新的版本,但是容易出现bug,最 ...

  4. 扫盲ASM

    在进行程序跟踪时,会出现汇编.由于ASM盲,所以添加不少烦恼.有烦恼得想办法解决.对,扫盲ASM. 这里是教材,感觉大白话很好理解(感谢 http://www.ruanyifeng.com/blog/ ...

  5. FREERTOS_LWIP TcpServer 加快接收速度

    刚开始调试时,关注点都在接收缓存等参数上,接受的间隔上限时钟在250ms左右. 后来发现是其中一个参数的设定决定了接收的速度,调整参数后,可以达到每80ms接收1024个字节. 发文留存,备忘.

  6. 基于UPD的快速局域网聊天室

    UPD与TCP对比: UDP是无连接的协议,也不保证可靠交付,只在IP数据报服务之上增加了很少的功能,主要是复用和分用以及差错检测的功能.这适用于要求源主机以恒定速率发送数据,允许网络拥塞时丢失数据, ...

  7. 函数组件的声明方式及差异+React.memo和userCallback区别

    1.函数组件的声明方式及差异 普通函数声明 箭头函数声明 使用React.FC类型(TypeScript专用) interface Props { content: string } // 写法一 c ...

  8. Spring IOC源码解析

    Spring 框架的启动过程是其核心机制之一,主要涉及 IoC 容器的初始化.Bean 的加载和生命周期管理. 以下是 Spring 启动过程的详细步骤 环境准备 项目结构: <?xml ver ...

  9. 移动web开发——flex布局

    目录 1.0传统布局和flex布局对比 1.1传统布局 1.2 flex布局 1.3 建议 2.0 flex布局原理 3.0 父项常见属性 3.1 flex-direction设置主轴的方向 3.2 ...

  10. 【记录】LangChain|Ollama结合LangChain使用的速通版(包含代码以及切换各种模型的方式)

    官方教程非常长,我看了很认可,但是看完了之后呢就需要一些整理得当的笔记让我自己能更快地找到需求.所以有了这篇文章.[写给自己看的,里面半句废话的解释都没有,如果看不懂的话直接看官方教程再看我的] 我是 ...