MongoDB University课程M320 Data Modeling 学习笔记

news/2025/3/5 0:52:52/

讲数据模型,课程介绍参见这里。

Chapter 1: Introduction to Data Modeling

需要具备的基础知识

MongoDB Concepts and Vocabulary

Database and Collection in MongoDB
Performing joins with $lookup

Relational Database Concepts and Vocabulary

Table (Wikipedia Definition)
Table (Textbook Definition)
Entity Relationship Model (Wikipedia Definition)
The Entity Relationship Data Model
Crow’s Foot Notation and ERD
Crow’s Foot Notation Definition

General Database Concepts and Definitions

Database (Wikipedia Definition)
Schema (Wikipedia Definition)
Schema Short Definition
Database Transactions (Wikipedia Definition)
Database Transactions Short Description
Throughput vs Latency
NoSQL Databases

MongoDB Compass and Atlas

Download Compass Here
More About Atlas Here

Data Modeling in MongoDB

关于MongoDB的第一个误解是其为Schemaless。MongoDB也是有Schema的,只不过它更灵活,更能适应应用的改变。有灵活的Data Model,可以通过UML或ERD(实体关系图)设计。

好的设计需要考虑:

  • 使用模式
  • 数据如何访问
  • 哪些查询对应用重要
  • 读写比例

第二个误区是将所有信息存于一个document。

第三个误区是不能做Join,$lookup可以实现Join。

总的来说,设计好的schema是不容易的。

Data Modeling in MongoDB

数据库由collection(类似表)组成,collection由document(类似行)组成,document是JSON格式,由一系列field和value(类似列)组成,value可以是单个值或数组或嵌入document。JSON内部储存用BSON。

Document Structure in MongoDB

Supported Datatypes in MongoDB

Constraints in Data Modeling

计算机应用的约束条件包括:

  • 硬件:内存,存储性能和价格
  • 数据:大小,安全,数据主权(sovereignty)
  • 应用:网络延迟
  • 数据库:更新原子性,document最大16M

working set: 应用在典型操作中访问的数据,例如经常访问的数据和索引。

最佳实践:

  • 将常用的数据置于内存
  • 将索引置于内存
  • 尽量选择SSD而非磁盘
  • 不常用数据可置于磁盘

约束条件变了,应用设计也需相应改变。

The Data Modeling Methodology

分为3阶段:

  1. 描述工作负载,如数据大小,重要的读写操作
  2. 描述实体间的关系,分开还是嵌入
  3. 选择模式(Pattern)
    在这里插入图片描述
    此图缺少第3阶段,正是本课程要讲的。

Model for Simplicity or Performance

需要在简单性和性能间取得平衡。
在这里插入图片描述

Identifying the Workload

这一节以一个IoT的场景进行了分析,分析过程和结构参见M320-workload-IOT.xlsx。

有几点小的收获:

  • 每一个读写操作可以选择不同的Write Concern和Read Concern
  • hidden secondary可专门用作报表分析

Chapter 2: Relationships

Introduction to Relationships

NoSQL数据库也是relational的,其也有实体和关系。

在MongoDB中,需要考虑是嵌入还是关联。而在关系型数据库中,这些都是通过Join实现的。

Relationship Types and Cardinality

常见的关系包括1-1,1-N和N-N。例如客户ID和客户名称是1对1关系,客户和invoice是1对多关系,invoice和产品是多对多关系。

1-N关系,N也需考虑基数,例如一个人的子女与一个名人的粉丝基数差异就很大。用[min, likely, max]表示更加准确。

1-Zillion(无数)在大数据场景中常见。

1-to-Many Relationship

一个人拥有的信用卡和写的博客都是1-N关系。

1-N关系的实现,如果采用嵌入(embeded),通常嵌入到最常查询的一边;如果采用参考(reference),一般放在N端。嵌入比参考简单,如果关联信息不多,也可以考虑嵌入,这样一个查询就可以得到全部信息。

如果最常用的查询并不总是需要关联的信息,可以考虑参考。

例如:

  • 嵌入到1端:文档和审阅者,由于审阅者不多,所以嵌入到文档端。
  • 嵌入到N端:订单与配送地址,将配送地址嵌入到订单,因为N端查询更频繁。关系型相比,地址会有冗余,当然,冗余也是有好处的。
  • 在1端参考(reference):邮编与店铺,在邮编文档中包括所有的店铺ID。
  • 在N端参考:同上例,在店铺中包括邮编,似乎更合理。

Many-to-Many Relationship

例如store和item,就是N-N关系,很容易被误认为是1-N关系。

象电影和演员也是N-N关系,N-N关系需要从两端同时看,实际就是两个1-N关系。

people和phone_number也是N-N关系,因为多个人可能共享一个家庭电话。但这种关系可以转换为1-N关系,通过将家庭电话复制到每一个人的电话列表中,当家庭迁移时,就需要更新所有人的家庭电话。像这种情形,一些冗余可能是更合适的。

N-N关系可以通过embeded或reference实现。

例如,将较少查询的N端嵌入到另一N端(或者说嵌入到查询较多的一端),较少查询的N端仍然保留,虽然有数据冗余,但保证了如果嵌入的数据删除,源数据(较少查询的N端)仍存在。而且嵌入的数据也未必是全部。

嵌入的信息最好是不常变化的。

又例如,在主端加入reference,通常是一个array。当然也可以在另一端加入reference,主要看你查询什么。

reference比embeded好的地方在于可以避免重复。

One-to-One Relationship

1-1关系可通过嵌入实现,如同一级的field,或子文档中的field。好处是简单,易于理解。

通过reference实现,实际是将一个id对应的信息一劈两半,两边通过此id连接。可能是为了优化访问。

One-to-Zillions Relationship

是1-N关系的一种,只不过N特别大。

实现只有一种方式,即在Zillion那一端使用reference。

Crow’s Foot Notation Definition
Crow’s Foot Notation and ERD

Chapter 3: Patterns (Part 1)

pattern不是整体的解决方案,而是解决方案的一部分,类似于武术中的招式。

推荐了Gang of Four的Design Patterns: Elements of Reusable Object-Oriented Software一书。

Guide to Homework Validation

介绍了课程使用的作业检查工具,需要下载一个可执行程序:

$ ./validate_m320 --version
validate_m320 version 02.01# 错误时$ cat answer_schema.json
{"_id": "<objectId>","title": "<string>","artist": "<string>","room": "<string>","spot": "<string>","on_display": "<bool>","in_house": "<int>","events": [{"k": "<string>","v": "<date>"}]
}$ ./validate_m320 example --file answer_schema.jsonThe solution is incorrect, use --verbose if you prefer getting some hints$ ./validate_m320 example --file answer_schema.json --verbose
Answer Filename: /home/vagrant/M320/answer_schema.jsonErrors:
in_house: in_house must be one of the following: <bool># 正确时
$ cat answer_schema.json                              {"_id": "<objectId>","title": "<string>","artist": "<string>","room": "<string>","spot": "<string>","on_display": "<bool>","in_house": "<bool>","events": [{"k": "<string>","v": "<date>"}]
}$ ./validate_m320 example --file answer_schema.json --verbose
Answer Filename: /home/vagrant/M320/answer_schema.json
The document passes validationCongratulations - here is your validation code: 5d124f9bd971a774b97b5fc7

注意,这不是一个JSON检查工具。JSON语法可参见这里。

Handling Duplication, Staleness and Integrity

Handling Duplication

Duplication产生的原因是需要嵌入信息以提供快速访问,主要问题是一致性。

有时,Duplication是更优的方案,例如订单中嵌入客户信息或配送地址,成单时这些信息是固定的,但后续例如配送地址可能会换,因此嵌入在一起是合理的。
还有对于电影和演员这种多对多的关系,一旦电影上映,这些信息不会再变,因此在电影和演员两个collection中使用嵌入而非参照会更合理,有一些重复影响也很小。

以上案例,在document中嵌入的重复信息对于本文档而言不会在改变,后续的变化也不会影响已有的文档。

还有一种重复和预计算有关,例如计算电影票房,需要由应用负责更新,不过更新的频率需参考下一节。

因为有重复,万一这些重复的信息后续需要修改,则需要批量更新。

Handling Staleness

数据陈旧是由于数据变化太快。始终看到最新的数据是无法保证的,关键是看用户能忍受数据的陈旧程度。例如数仓实际针对的都是过去的某一时间点。

为保证数据的新鲜度,一般可采取实时复制或批量更新发,即所谓的涓流式。

Handling Referential Integrity

参照一致性用来连接两个文档,MongoDB不支持cascade deleting(因为不支持主外键)。

可以用多文档事务或change stream解决一致性问题。或者干脆就放在一个文档里。

Attribute Pattern

先来看两个例子。
例一:关于产品的document,有很多共同的属性,如制造商,品牌,价格。还有另外一些属性是各自不同的,但这些属性不多。如果需要查询这些不同的属性,就需要建立太多的索引。因此我们将其装换为子文档,其中k表示属性名,v表示属性值。例如:

从
{
...
"input" : "5V/1300 mA",
"output" : "5V/1A",
"capacity" : "4200 mAh"
}
变为
{
...
"add_specs" : {{ "k" : "input", "b" : "5V/1300 mA"}, { "k" : "output", "b" : "5V/1A"}, { "k" : "capacity", "b" : "4200 mAh"}
}
}

然后针对子文档做索引:

db.products.createIndex({"add_specs.k":1, "add_specs.v":1})

然后就可以有效的处理一下查询:

db.products.find({"add_specs" : {"elementMatch" : {"k" : "capacity", "b" : "4200 mAh"}}})

例2:在文档中的一些属性名字不同,但值的含义类似,例如不同国家的电影上映日期:

{
...
"release_USA":"2020/01/01",
"release_UK":"2020/02/01",
"release_China":"2020/03/01"
}

和例一类似,可以将其转换为array:

"release_date" : 
[
{"k":"release_USA""v":"2020/01/01"},
{"k":"release_UK""v":"2020/02/01"},
{"k":"release_China""v":"2020/03/01"}
]

所以Attribute Pattern解决的问题是:

  • 有不多的相似的属性
  • 需要查询这些属性

解决方法就是将filed:value转换为子文档:

fieldA:field,
fieldB:value

场景包括产品特性,或具有相同value类型的field。

好处是建索引容易,并且可以减少索引。未来添加新的field也能处理,而且容易理解原来的属性关系。可以很好的组织相同属性的field,或不确定的field。

Attribute Pattern是多态的orthogonal(正交),我理解就是90度翻转(Transpose),例如行式变为列式。

Attribute Pattern解决的问题也可用MongoDB 4.2新特性Wildcard Index解决。

注意,在做练习时,一定要看清题意,另外可以通过–verbose获取错误信息,例如:

$ ./validate_m320 pattern_attribute --file pattern_attribute.json --verbose
Answer Filename: /home/vagrant/M320/pattern_attribute.jsonErrors:
(root): Additional property date_acquisition is not allowed
events.0.k: events.0.k must be one of the following: <string>
events.1.k: events.1.k must be one of the following: <string>

这表示以下答案中不会出现具体的值:

  "in_house": "<bool>","events": [{ "k": "moma", "v": "<date>"},{ "k": "louvres", "v": "<date>"},{ "k": "date_acquisition", "v": "<date>"}]

而应该是下面的格式

{"events": [{ "k": "<string>","v": "<date>"},{ "k": "<string>","v": "<date>"}]
}

Extended Reference Pattern

Join在关系型数据库中很容易实现,在MongoDB中,Join可以通过以下方法实现:

  • 通过应用
  • 通过lookup,如 $lookup$graphLookup
  • 通过嵌入文档避免Join

Extended Reference是指在1-N关系中,在N端嵌入1的数据,如客户与订单,可以在订单中嵌入客户数据,嵌入的只是部分数据,即需要经常Join的数据。因此最终还是两个文档。

嵌入的数据在两端都存在,因此数据有重复。为避免一致性问题,挑选的数据应不经常变化,而且应尽量少。

Extended Reference避免了大量Join,通过将频繁查询的数据嵌入到主文档。例如catalog,移动应用和实时分析。可提供更快的读,减少Join,坏处是数据有重复。

Subset Pattern

Working set应全部容纳在内存中。如果内存容纳不下Working set,会影响性能。

若Working set过大,可以通过纵向扩展或横向扩展(Sharding)添加内存,或减小Working set的大小。

Subset Pattern即将不常用的field移至另一个文档,以减小Working set的大小。例如,对于1-1的field,可直接移到另一文档,对于1-N关系的field,如电影和演员,如果有1000个演员,可以仅保留20个主要演员在常用文档中,而在另一文档中保留所有演员,当然,这样会有数据重复。

Subset Pattern解决的问题是文档过大,导致内存容纳不下,而且文档中大部分的信息不常查询。解决方法是拆成常用和不常用两个文档。

场景包括产品和文章的评价,电影的演员,好处是Working set变小,访问更快,坏处是重复导致的空间占用,少量操作需要应用多轮查询。

Chapter 4: Patterns (Part 2)

Computed Pattern

计算非常消耗资源,因此减少计算对性能有益,例如:

  • 数学计算。如统计,例如计算总和,每次插入都需重新计算;通过缓存以前的结果,就可以将多次累加变为一次累加。将中间结果写入另一个document,后台一个进程负责更新,这样实现了读写分离。
  • fan out操作。就是一个逻辑操作展开为多个操作,例如fan out 读指需要从多个地方读取数据,fan out写指需要写到多个地方。这样做实际是推送到多个数据需要的地方,用空间换取了性能。
  • roll up操作 是drill down操作的反面,例如每小时,每天的统计,可以不断向上卷积。

在计算资源紧张后希望减少读延迟时使用Computed Pattern。

其解决的问题是昂贵的计算操作,频繁在相同数据上计算,并产生相同的结果。解决方法为将计算结果存放于另一个文档。应用场景包括IoT,事件溯源,时间序列数据,聚合框架。好处是读操作更快,节省了计算和磁盘资源。不好在于需求难确定。

Bucket Pattern

Bucket Pattern是每一信息一个文档和一个文档存放所有信息的折衷。实际上也是1-N关系,但N不是所有,而是Total/M(1-Total容纳不下),M的确定需要很好地理解工作负载特征,例如N可能表示每天,或每小时。其实有点像数据库分区,不过分区的大小需要确定。

在文档中存放的某field的信息是以array的信息存放的。类似于列式存储。

使用场景包括IoT,数仓,一个对象包括太多信息。

好处是返回数据和存放空间的平衡。数据更易于管理。删除数据也容易。

坏处是对BI应用不友好,如果设计不当,查询性能不好。

Schema Versioning Pattern

Alter Table NightMare,这几天正好经历过,确实不太方便。但设计上确实应该尽可能精准,以避免后续schema的改动。

关系型数据库修改schema可能需要更新数据,可能需要停机,一旦失败很难回退。

Schema Versioning Pattern的本质是问文档均添加一个版本field,然后应用可以识别版本并做相应处理。所以需要升级应用和迁移文档。

可以避免的问题包括宕机时间,更新文档的耗时,不想更新所有文档。场景包括重度使用的应用,有很多老旧数据的应用。

好处包括无需宕机,迁移可控,无技术债务。

Tree Patterns

树状结构就是层级结构,例如组织架构,目录结构等。在层级结构中经常需要寻找承继关系等操作。

Tree Patterns包括以下4个子模式,这些子模式可以组合:
A. Parent Reference: 存parent,只有一个。更新方便。
B. Child Reference:存children,一到多个,以数组存放
C. Array of Ancestors 数组存放从此节点到根节点(含)间的所有节点,一直向上可以溯源到根。
D. Materialized Paths 以".root.x.y.z…"方式存放节点所有祖先,因此可以使用正则表达式

以下4类问题可以用这些模式解决:

  1. X的祖先 - C,D
  2. 谁汇报给Y - A,C
  3. Z下的所有节点 - B, C?
  4. 将N下的所有children移到P下 - A

场景包括组织架构,产品分类。

Polymorphic Pattern

Polymorphic Pattern指大部分属性相同,可以归为一类,例如产品。

此Pattern也适合于子文档,例如存各个国家的地址,大部分都相同,但有的国家叫省,有的叫州。

schema versioning pattern也使用了此Pattern,通过版本号,所以此模式是一基础模式。

此模式的实现是通过一个field来跟踪collection的shape,然后应用有不同的处理。

此模式的好处是容易实现,可以提供统一视图。可以将对象放入同一个collection中,因此也可以只在一个collection中查询。

场景包括统一视图,产品目录,内容管理。

Summary of Patterns

Approximation Patterns

近似,就是减少应用到MongoDB的写操作,例如每秒1次更新改为每10秒一次更新。

可以减少计算,但前提是业务可以允许不精确。

实现上是更少的写,但每次写的payload可能更大,起码节省了round-trip。

场景包括Web页面计数器,可以允许不精确(但统计上正确)的计数器,指标统计。

缺点是不精确,需要应用实现。

Outlier Pattern

少数文档会影响整个查询的性能。

实现上兼顾文档中最常用的fields,对于异常文档通过应用单独处理。也就是在主文档中会有标识,异常部分需要到外部去参照。

场景包括社交网络等。

缺点是不适合于即席查询的汇聚操作。

Chapter 5: Conclusion

这一章是考试,有一些小难度。
在这里插入图片描述
在这里插入图片描述


http://www.ppmy.cn/news/561906.html

相关文章

学计算机的雷蛇与苹果,雷蛇做了什么 让学霸苹果也抄袭他了

【IT168 资讯】苹果总是以那种&#xff1a;永远被抄袭却从未被超越的姿态展示在世人面前&#xff0c;似乎苹果就是那种不作弊的学生&#xff0c;永远只有他被抄作业却从来没有抄袭别人。不过这种观点&#xff0c;笔者只能说图样图森破!你肯定没见过比苹果还优秀的学生。 最近网…

CVE-2019-13142:雷蛇影音软件(Razer Surround)的权限提升漏洞

软件版本&#xff1a;Razer Surround 1.1.63.0 操作系统版本&#xff1a;Windows 10 1803&#xff08;x64&#xff09; 漏洞说明&#xff1a;Razer Surround软件中文件夹设置缺陷导致的权限提升 目的 我希望这篇文章能鼓励更多的人加入漏洞研究这个看似很困难的领域。虽然这…

绕过雷蛇官网的动态验证码

大家好&#xff0c;我是dhakal_ananda&#xff0c;来自尼泊尔&#xff0c;这是我在Hackerone上参加的雷蛇漏洞悬赏项目的一部分。一开始这个漏洞悬赏项目是一个非公开项目&#xff0c;我接到邀请后并没有参加&#xff1b;后来它变成了公开项目&#xff0c;我反而对它起了兴趣。…

铸博皇御:炒黄金入门这几点别忽略

炒黄金和投资股票一样&#xff0c;也是一种高风险回报大的投资方式。但无论如何&#xff0c;新手投资黄金须谨慎&#xff0c;一定要做好风险抵抗。那么&#xff0c;炒黄金入门需要注意哪几点呢&#xff1f; 任何投资都需要遵循一定的规律&#xff0c;开始以为很简单&#xff0c…

自媒体应该怎么入门,这几点教会你

2022年了&#xff0c;才开始做自媒体还来得及吗&#xff1f;答案是一定来得及&#xff0c;现在自媒体发展相对来说是比较成熟的阶段&#xff0c;那么自媒体新手应该怎么入门&#xff0c;下面这几点教会你&#xff01; 1、了解各平台机制 每个平台的机制是不同的&#xff0c;选…

7-6 然后是几点(15 分)

7-6 然后是几点&#xff08;15 分&#xff09; 有时候人们用四位数字表示一个时间&#xff0c;比如1106表示11点零6分。现在&#xff0c;你的程序要根据起始时间和流逝的时间计算出终止时间。 读入两个数字&#xff0c;第一个数字以这样的四位数字表示当前时间&#xff0c;第二…

java定时执行(设置每天几点执行一次)

今日收获 理解并学习了如何使用定时器完成设置每天早上八点程序执行一次 可自行扩展&#xff1a; public void TimerTaskTest{ // 创建一个定时器任务TimerTask timerTask new TimerTask() {Overridepublic void run() {/*写业务逻辑主体system.out.prinln("早上…

区块链的几点理解

以 比特币 为例进行说明 1. 交易的双方以id为基础进行交易&#xff0c; 不是以姓名等的进行&#xff0c;使双方的重要信息不会被外界所知。目前很多应用&#xff0c;比如病人的病历是可已被研究的&#xff0c;但是病人的信息是隐私不能被暴露的。 2. 信用去中心化&#xff0c…