转自:http://www.cnblogs.com/WeiGe/…
Part 1
原文:6 Rules of Thumb for MongoDB Schema Design: Part 1
By William Zola, Lead Technical Support Engineer at MongoDB
“我有丰盛的 sql 应用教训,然而我是个 MongoDB 的初学者。我应该如何在 MongoDB 中针对一对多关系进行建模?”这是我被问及最多的问题之一。
我没法简略的给出答案,因为这有很多计划去实现。接下来我会教诲你如何针对一对多进行建模。
这个话题有很多内容须要探讨,我会用三个局部进行阐明。在第一局部,我会探讨针对一对多关系建模的三种根底计划。在第二局部我将会笼罩更多高级内容,包含反范式化和双向援用。在最初一部分,我将会回顾各种抉择,并给出做决定时须要思考的因素。
很多初学者认为在 MongoDB 中针对一对多建模惟一的计划就是在父文档中内嵌一个数组子文档,然而这是不精确的。因为你能够在 MongoDB 内嵌一个文档不代表你就必须这么做。
当你设计一个 MongoDB 数据库构造,你须要先问本人一个在应用关系型数据库时不会思考的问题:这个关系中汇合的大小是什么样的规模?你须要意识到一对很少,一对许多,一对十分多,这些轻微的区别。不同的状况下你的建模也将不同。
Basics: Modeling One-to-Few
一对很少
针对集体须要保留多个地址进行建模的场景下应用内嵌文档是很适合,能够在 person 文档中嵌入 addresses 数组文档:
这种设计具备内嵌文档设计中所有的优缺点。最次要的长处就是不须要独自执行一条语句去获取内嵌的内容。最次要的毛病是你无奈把这些内嵌文档当做独自的实体去拜访。
例如,如果你是在对一个工作跟踪零碎进行建模,每个用户将会被调配若干个工作。内嵌这些工作到用户文档在遇到“查问昨天所有的工作”这样的问题时将会十分艰难。我会在下一篇文章针对这个用例提供一些适当的设计。
Basics: One-to-Many
一对许多
以产品整机订货零碎为例。每个商品有数百个可替换的整机,然而不会超过数千个。这个用例很适宜应用间接援用 — 将整机的 objectid 作为数组寄存在商品文档中(在这个例子中的 ObjectID 我应用更加易读的 2 字节,事实世界中他们可能是由 12 个字节组成的)。
每个整机都将有他们本人的文档对象
每个产品的文档对象中 parts 数组中将会寄存多个整机的 ObjectID:
在获取特定产品中所有整机,须要一个应用层级别的 join
为了能疾速的执行查问,必须确保 products.catalog_number 有索引。当然因为整机中 parts._id 肯定是有索引的,所以这也会很高效。
这种援用的形式是对内嵌优缺点的补充。每个整机是个独自的文档,能够很容易的独立去搜寻和更新他们。须要一条独自的语句去获取整机的具体内容是应用这种建模形式须要思考的一个问题(请认真思考这个问题,在第二章反反范式化中,咱们还会探讨这个问题)
这种建模形式中的整机局部能够被多个产品应用,所以在多对多时不须要一张独自的连贯表。
Basics: One-to-Squillions
一对十分多
咱们用一个收集各种机器日志的例子来探讨一对十分多的问题。因为每个 mongodb 的文档有 16M 的大小限度,所以即便你是存储 ObjectID 也是不够的。咱们能够应用很经典的解决办法“父级援用”— 用一个文档存储主机,在每个日志文档中保留这个主机的 ObjectID。
以下是个和第二中计划略微不同的利用级别的 join 用来查找一台主机最近 5000 条的日志信息
所以,即便这种简略的探讨也有能察觉出 mongobd 的建模和关系模型建模的不同之处。你必须要留神一下两个因素:
Will the entities on the“N”side of the One-to-N ever need to stand alone?
一对多中的多是否须要一个独自的实体。
What is the cardinality of the relationship: is it one-to-few; one-to-many; or one-to-squillions?
这个关系中汇合的规模是一对很少,很多,还是十分多。
Based on these factors, you can pick one of the three basic One-to-N schema designs:
基于以上因素来决定采取一下三种建模的形式
一对很少且不须要独自拜访内嵌内容的状况下能够应用内嵌多的一方。
一对多且多的一端内容因为各种理由须要独自存在的状况下能够通过数组的形式援用多的一方的。
一对十分多的状况下,请将一的那端援用嵌入进多的一端对象中。
Part 2
原文:6 Rules of Thumb for MongoDB Schema Design: Part 2
By William Zola, Lead Technical Support Engineer at MongoDB
在上一篇文章中我介绍了三种根本的设计方案:内嵌,子援用,父援用,同时阐明了在抉择计划时须要思考的两个关键因素。
一对多中的多是否须要一个独自的实体。
这个关系中汇合的规模是一对很少,很多,还是十分多。
在把握了以上根底技术后,我将会介绍更为高级的主题:双向关联和反范式化。
双向关联
如果你想让你的设计更酷,你能够让援用的“one”端和“many”端同时保留对方的援用。
以上一篇文章探讨过的工作跟踪零碎为例。有 person 和 task 两个汇合,one-to- n 的关系是从 person 端到 task 端。在须要获取 person 所有的 task 这个场景下须要在 person 这个对象中保留有 task 的 id 数组,如上面代码所示。
在某些场景中这个利用须要显示工作的列表(例如显示一个多人合作我的项目中所有的工作),为了可能疾速的获取某个用户负责的我的项目能够在 task 对象中嵌入附加的 person 援用关系。
这个计划具备所有的一对多计划的优缺点,然而通过增加附加的援用关系。在 task 文档对象中增加额定的“owner”援用能够很快的找到某个 task 的所有者,然而如果想将一个 task 调配给其余 person 就须要更新援用中的 person 和 task 这两个对象(相熟关系数据库的童鞋会发现这样就没法保障操作的原子性。当然,这对工作跟踪零碎来说并没有什么问题,然而你必须思考你的用例是否可能容忍)
在一对多关系中利用反范式
在你的设计中退出反范式,能够使你防止应用层级别的 join 读取,当然,代价是这也会让你在更新是须要操作更多数据。上面我会举个例子来进行阐明
反范式 Many -< One
以产品和整机为例,你能够在 parts 数组中冗余存储整机的名字。以下是没有退出反范式设计的构造。
反范式化意味着你不须要执行一个应用层级别的 join 去显示一个产品所有的整机名字,当然如果你同时还须要其余整机信息那这个应用层的 join 是防止不了的。
在使得获取整机名字简略的同时,执行一个应用层级别的 join 会和之前的代码有些区别,具体如下:
反范式化在节俭你读的代价的同时会带来更新的代价:如果你将整机的名字冗余到产品的文档对象中,那么你想更改某个整机的名字你就必须同时更新所有蕴含这个整机的产品对象。
在一个读比写频率高的多的零碎里,反范式是有应用的意义的。如果你很常常的须要高效的读取冗余的数据,然而简直不去变更他 d 话,那么付出更新上的代价还是值得的。更新的频率越高,这种设计方案的带来的益处越少。
例如:假如整机的名字变动的频率很低,然而整机的库存变动很频繁,那么你能够冗余整机的名字到产品对象中,然而别冗余整机的库存。
须要留神的是,一旦你冗余了一个字段,那么对于这个字段的更新将不在是原子的。和下面双向援用的例子一样,如果你在整机对象中更新了整机的名字,那么更新产品对象中保留的名字字段前将会存在短时间的不统一。
反范式 One -< Many
你也能够冗余 one 端的数据到 many 端:
如果你冗余产品的名字到整机表中,那么一旦更新产品的名字就必须更新所有和这个产品无关的整机,这比起只更新一个产品对象来说代价显著更大。这种状况下,更应该谨慎的思考读写频率。
在一对很多的关系中利用反范式
在日志零碎这个一对许多的例子中也能够利用反范式化的技术。你能够将 one 端(主机对象)冗余到日志对象中,或者反之。
上面的例子将主机中的 IP 地址冗余到日志对象中。
如果想获取最近某个 ip 地址的日志信息就变的很简略,只须要一条语句而不是之前的两条就能实现。
事实上,如果 one 端只有大量的信息存储,你甚至能够全副冗余存储到多端上,合并两个对象。
另一方面,也能够冗余数据到 one 端。比如说你想在主机文档中保留最近的 1000 条日志,能够应用 mongodb 2.4 中新退出的 $eache/$slice 性能来保障 list 有序而且只保留 1000 条。
日志对象保留在 logmsg 汇合中,同时冗余到 hosts 对象中。这样即便 hosts 对象中超过 1000 条的数据也不会导致日志对象失落。
通过在查问中应用投影参数(相似{_id:1})的形式在不须要应用 logmsgs 数组的状况下防止获取整个 mongodb 对象,1000 个日志信息带来的网络开销是很大的。
在一对多的状况下,须要谨慎的思考读和更新的频率。冗余日志信息到主机文档对象中只有在日志对象简直不会产生更新的状况下才是个好的决定。
总结
在这篇文章里,我介绍了对三种根底计划:内嵌文档,子援用,父援用的补充抉择。
应用双向援用来优化你的数据库架构,前提是你能承受无奈原子更新的代价。
能够在援用关系中冗余数据到 one 端或者 N 端。
在决定是否采纳反范式化时须要思考上面的因素:
你将无奈对冗余的数据进行原子更新。
只有读写比拟高的状况下才应该采取反范式化的设计。
Part 3
原文:6 Rules of Thumb for MongoDB Schema Design: Part 3
By William Zola, Lead Technical Support Engineer at MongoDB
这篇文章是系列的最初一篇。在第一篇文章里,我介绍了三种针对“一对多”关系建模的根底计划。在第二篇文章中,我介绍了对根底计划的扩大:双向关联和反范式化。
反范式能够让你防止一些应用层级别的 join,然而这也会让更新变的更简单,开销更大。不过冗余那些读取频率远远大于更新频率的字段还是值得的。
如果你还没有读过前两篇文章,欢送一览。
让咱们回顾下这些计划
你能够采取内嵌,或者建设 one 端或者 N 端的援用,也能够三者兼而有之。
你能够在 one 端或者 N 端冗余多个字段
上面这些是你须要谨记的:
1、优先思考内嵌,除非有什么无可奈何的起因。
2、须要独自拜访一个对象,那这个对象就不适宜被内嵌到其余对象中。
3、数组不应该无限度增长。如果 many 端有数百个文档对象就不要去内嵌他们能够采纳援用 ObjectID 的计划;如果有数千个文档对象,那么就不要内嵌 ObjectID 的数组。该采取哪些计划取决于数组的大小。
4、不要胆怯应用层级别的 join:如果索引建的正确并且通过投影条件(第二章提及)限度返回的后果,那么应用层级别的 join 并不会比关系数据库中 join 开销大多少。
5、在进行反范式设计时请先确认读写比。一个简直不更改只是读取的字段才适宜冗余到其余对象中。
6、在 mongodb 中如何对你的数据建模,取决于你的应用程序如何去拜访它们。数据的构造要去适应你的程序的读写场景。
设计指南
当你在 MongoDB 中对“一对多”关系进行建模,你有很多的计划可供选择,所以你必须很审慎的去思考数据的构造。上面这些问题是你必须认真思考的:
关系中汇合的规模有多大:是一对很少,很多,还是十分多?
对于一对多中”多“的那一端,是否须要独自的拜访它们,还是说它们只会在父对象的上下文中被拜访。
被冗余的字段的读写的比例是多少?
数据建模设计指南
在一对很少的状况下,你能够在父文档中内嵌数组。
在一对很多或者须要独自拜访“N”端的数据时,你能够采纳数组援用 ObjectID 的形式。如果能够减速你的拜访也能够在“N”端应用父援用。
在一对十分多的状况下,能够在“N”端应用父援用。
如果你打算在你的设计中引入冗余等反范式设计,那么你必须确保那些冗余的数据读取的频率远远大于更新的频率。而且你也不须要很强的一致性。因为反范式化的设计会让你在更新冗余字段时付出肯定的代价(更慢,非原子化)