MongoDB数据模型(一)
一、数据模型介绍
MongoDB中的数据有着灵活的架构。与SQL数据库不同,因为SQL数据库必须先定义表结构,然后才能向其中插入数据,而MongoDB的集合不强制任何文档结构。这个灵活性方便了文档与实体或者对象之间的映射。每个文档可以匹配所表示实体的数据域,哪怕这个数据后面会发生变化。当然实际应用中,最好还是让集合中的文档有着类似的结构。
数据模型最富有挑战的意义是在于能平衡应用需要与数据库引擎性能以及数据获取模式。当设计数据模型时,总是会考虑应用程序对数据的使用(如查询、更新和数据处理),以及数据本身的继承结构。
文档结构
为MongoDB应用程序设计数据模型的关键点是解析文档的结构和应用程序如何表示数据之间的关系。应用程序表示数据关系可以有两种方式:引用和嵌入文档。
引用
引用通过包含链接或从一个文档到另一个文档的引用来存储数据关系。应用程序可以通过解析这些引用来访问有关数据。一般地我们称之为规范化数据模型。
嵌入数据
嵌入文档通过把数据存储到一个独立文档结构中来获取数据之间的关系。MongoDB允许将一个文档结构嵌入到另一个文档的字段或者数组中。这些去规范化数据模型允许应用程序在一个独立的数据库操作中获取和操作有关数据。
写操作的原子性
MongoDB的写操作在文档级别是原子性的,没有单个写操作对超过一个文档或者超过一个集合是原子性的。带有嵌入数据的去规范化数据模型将一个可表现实体的所有有关数据合并到单个文档中。这有益于原子写操作,因为单个写操作可以对一个实体实现插入和更新。规范数据意味着把数据切分到不同的集合,这需要多个写操作,而这些写操作虽然自己本身是原子性的,但是合并起来看则不具有原子性。
文档增长
有些更新,如将元素压入数组或者添加新字段,则会增加文档大小。
对MMAPv1存储引擎而言,如果文档大小超过为这个文档分配的空间,MongoDB会在磁盘上迁移此文档。当使用MMAPv1存储引擎时,文档增长的考虑会影响我们规范化数据还是去规范化数据。
数据使用和性能
设计数据模型时,考虑一下应用程序如何使用数据库。例如,如果应用程序仅使用最近插入的文档,那可以考虑使用Capped Collections。如果程序主要是对集合的读操作,那么对集合添加索引可以提高性能。
二、文档验证
MongoDB在更新和插入操作期间可以验证文档。验证规则使用validator选项在每个集合上指定,这个validator选项用一个文档具现化验证规则和表达式。这些表达式的指定可以使用任何查询操作符,除了$geoNear, $near, $nearSphere, $text和$where。
如要对一个集合添加文档验证,使用带validator选项的collMod命令。你可以在创建新集合时就指定文档验证规则,即,使用带有validator选项的db.createCollection()方法,如下所示:
db.createCollection( "contacts",
{ validator: { $or:
[
{ phone: { $type: "string" } },
{ email: { $regex: /@mongodb\.com$/ } },
{ status: { $in: [ "Unknown", "Incomplete" ] } }
]
}
} )
MongoDB提供了validationLevel选项,这个选项决定了MongoDB在更新文档时应用验证规则的严格程度。还有一个是validationAction选项,这决定了MongoDB是否引发错误并拒绝违背验证规则的文档,还是给出警告并把违背验证规则消息写入日志同时接受无效的文档。
行为
验证在更新和插入时验证。当你添加验证规则到一个集合时,已经存在的文档不会被验证是否符合验证规则,除非这些文档被修改。
存在的文档
可以使用validationLevel选项来控制MongoDB如何处理已存在的文档,即是否验证之前已存在的规则。
默认情况下,validationLevel值为strict,MongoDB应用验证规则到所有的插入和更新操作。设置validationLevel为moderate,则应用验证规则到插入操作和对已存在且满足验证标准的文档的更新操作。在moderate级别下,对不满足验证标准的已存在文档,MongoDB则不检测这些文档的有效性。
例子:
考虑以下contacts集合中的文档:
{
"_id": "125876"
"name": "Anne",
"phone": "+1 555 123 456",
"city": "London",
"status": "Complete"
},
{
"_id": "860000",
"name": "Ivan",
"city": "Vancouver"
}
用以下命令对contacts集合添加一个验证器
db.runCommand( {
collMod: "contacts",
validator: { $or: [ { phone: { $exists: true } }, { email: { $exists: true } } ] },
validationLevel: "moderate"
} )
现在contacts集合有一个moderate级别的验证器。如果试图更新_id为125876的文档,MongoDB将应用验证规则,因为这个已存在文档匹配验证条件。相反地,MongoDB不会应用验证规则到_id为860000的文档更新操作上,因为这个文档原先不满足验证规则。
如果想完全禁用验证规则,可以设置validationLevel为off。
接受或拒绝无效文档
validationAction选项决定了MongoDB如何处理违反验证规则的文档。
默认地,validationAction为error,这时MongoDB拒绝任何违反验证条件的插入或更新操作。当validationAction值设置为warn,MongoDB记录任何违反验证的信息到日志但是允许插入或者更新的操作处理。
例如,下例创建一个contacts集合,这个集合带有一个验证器,这个验证器指定插入和更新文档需要满足以下三个条件中的至少一个条件:
- phone字段是string类型
- email字符满足正则表达式
- status字段要么是Unknown,要么是Incomplete
db.createCollection( "contacts",
{
validator: { $or:
[
{ phone: { $type: "string" } },
{ email: { $regex: /@mongodb\.com$/ } },
{ status: { $in: [ "Unknown", "Incomplete" ] } }
],
validationAction: "warn"
}
}
)
有了验证器之后,以下插入操作不满足验证规则,但是因为validationAction值为warn,故写操作记录此信息到日志。
db.contacts.insert( { name: "Amanda", status: "Updated" } )
日志信息包含了集合的全命名空间和不满足验证规则的文档,以及操作时间
2015-10-15T11:20:44.260-0400 W STORAGE [conn3] Document would fail validation collection: example.contacts doc: { _id: ObjectId('561fc44c067a5d85b96274e4'), name: "Amanda", status: "Updated" }
限制
我们不能为admin,local和config数据库指定验证器,也不能对system.*集合添加验证器。
绕过文档验证
用户可以使用bypassDocumentValidation选项绕过文档验证,参考Document Validation可以获得支持bypassDocumentValidation选项的命令的列表。
如果部署已经允许访问控制,为了绕过文档验证,已认证用户必须需要bypassDocumentValidation动作。内建的dbAdmin和restore角色提供了这个动作。
三、数据模型设计
有效的数据模型可以支持程序的需求。关键考虑点便是文档结构采用嵌入式还是引用式。
嵌入数据模型
上面已经介绍过,即去规范化数据模型
引用数据模型
上面已经介绍过,即规范化数据模型
嵌入文档的一对一模型
考虑以下patron与address之间的关系。这个例子表明了如果需要查看处于其他上下文中的数据实体,那么嵌入式比引用式数据模型具有优势。在这个patron和address数据之间的一对一关系中,address属于patron
在规范化数据模型中,address文档包含了一个队patron文档的引用。
{
_id: "joe",
name: "Joe Bookreader"
} {
patron_id: "joe",
street: "123 Fake Street",
city: "Faketon",
state: "MA",
zip: "12345"
}
如果address数据需要频繁获取名字信息,那在引用式数据模型中,程序需要进行多个查询来解析这个引用。更好的数据模型会把address数据嵌入到patron数据中,如下
{
_id: "joe",
name: "Joe Bookreader",
address: {
street: "123 Fake Street",
city: "Faketon",
state: "MA",
zip: "12345"
}
}
这样,程序在一次查询中就能获取全部信息。
嵌入文档的一对多关系模型
以下例子中,patron与address数据之间存在一对多关系,即patron有多个address实体。
在规范化数据模型中,address文档包含了对patron文档的引用,如下
{
_id: "joe",
name: "Joe Bookreader"
} {
patron_id: "joe",
street: "123 Fake Street",
city: "Faketon",
state: "MA",
zip: "12345"
} {
patron_id: "joe",
street: "1 Some Other Street",
city: "Boston",
state: "MA",
zip: "12345"
}
如果程序频繁获取address数据中的名称信息,那么需要多次查询以解析引用。一个较优的方案是嵌入address数据到patron数据中,如下
{
_id: "joe",
name: "Joe Bookreader",
addresses: [
{
street: "123 Fake Street",
city: "Faketon",
state: "MA",
zip: "12345"
},
{
street: "1 Some Other Street",
city: "Boston",
state: "MA",
zip: "12345"
}
]
}
这样通过一次查询,程序就能获取全部的patron信息。
文档引用的一对多关系模型
考虑以下出版社与书之间关系映射。这个例子说明了引用比嵌入具有的优势,避免了出版社信息的重复。
嵌入式数据模型如下
{
title: "MongoDB: The Definitive Guide",
author: [ "Kristina Chodorow", "Mike Dirolf" ],
published_date: ISODate("2010-09-24"),
pages: 216,
language: "English",
publisher: {
name: "O'Reilly Media",
founded: 1980,
location: "CA"
}
} {
title: "50 Tips and Tricks for MongoDB Developer",
author: "Kristina Chodorow",
published_date: ISODate("2011-05-06"),
pages: 68,
language: "English",
publisher: {
name: "O'Reilly Media",
founded: 1980,
location: "CA"
}
}
可见,这种数据模型导致出版社信息的重复,使用引用数据模型使出版社信息在一个独立的集合中则可以解决冗余问题。
当使用引用数据模型时,关系的增长决定将引用存储到何处。如果每个出版社的书数量增长缓慢,那将书的引用存储到出版社文档中可能会是不错的主意。否则如果每个出版社的书数量没有界限,那这个数据模型会导致可变的,增长数组,如下所示,
{
name: "O'Reilly Media",
founded: 1980,
location: "CA",
books: [12346789, 234567890, ...] // 随着书的数量增长
} {
_id: 123456789,
title: "MongoDB: The Definitive Guide",
author: [ "Kristina Chodorow", "Mike Dirolf" ],
published_date: ISODate("2010-09-24"),
pages: 216,
language: "English"
} {
_id: 234567890,
title: "50 Tips and Tricks for MongoDB Developer",
author: "Kristina Chodorow",
published_date: ISODate("2011-05-06"),
pages: 68,
language: "English"
}
为了避免文档中可变的增长数组,存储出版社信息到书文档中,如下
{
_id: "oreilly",
name: "O'Reilly Media",
founded: 1980,
location: "CA"
} {
_id: 123456789,
title: "MongoDB: The Definitive Guide",
author: [ "Kristina Chodorow", "Mike Dirolf" ],
published_date: ISODate("2010-09-24"),
pages: 216,
language: "English",
publisher_id: "oreilly"
} {
_id: 234567890,
title: "50 Tips and Tricks for MongoDB Developer",
author: "Kristina Chodorow",
published_date: ISODate("2011-05-06"),
pages: 68,
language: "English",
publisher_id: "oreilly"
}
MongoDB数据模型(一)的更多相关文章
- 第二课 MongoDB 数据模型
1.课程大纲 本课程主要介绍MongoDB数据模型相关知识.包含文档.集合与数据库的基本概念.用法及命名规则:MongoDB主要的数据类型介绍以及MongoDB Shell的简单介绍与使用. 文档 ( ...
- MongoDB数据模型和索引学习总结
MongoDB数据模型和索引学习总结 1. MongoDB数据模型: MongoDB数据存储结构: MongoDB针对文档(大文件採用GridFS协议)採用BSON(binary json,採用二进制 ...
- MongoDB数据模型(三)
六.数据模型引用 文档 我们已经知道MongoDB以文档的形式存储数据,而文档是JSON风格的数据结构,由一系列的“字段名-值”对组成,如下所示 { "item": "p ...
- MongoDB数据模型(二)
原文地址 接上一篇 四.模型树结构 父引用的模型树结构 这个数据模型描述了一个树形结构,在子节点中存储父节点的引用. 模式 父引用模式存储每个树节点到文档中,除了树节点外,文档还存储了父节点的id. ...
- Mongodb数据模型
描述表关系的方式: 方式一:嵌入式 > db.person.find({name:'zjf'}).pretty() { "_id" : ObjectId("592f ...
- MongoDB (四) MongoDB 数据模型
在 MongoDB 中的数据有灵活的模式.在相同集合中文档并不需要有相同的一组字段或结构的公共字段的集合,文档可容纳不同类型的数据. MongoDB设计模式的一些考虑 可根据用户要求设计架构. 合并对 ...
- MongoDB 存储引擎和数据模型设计
标签: MongoDB NoSQL MongoDB 存储引擎和数据模型设计 1. 存储引擎 1.1 存储引擎是什么 1.2 MongoDB中的默认存储引擎 2. 数据模型设计 2.1 内嵌和引用 2. ...
- MongoDB快速上手
1. MongoDB简介 MongoDB是一个跨平台的基于Key_Value键值对形式保存数据的NoSQL文档类型数据库. NoSQL(not only sql)数据库,泛指非关系型数据库. 1.1 ...
- MongoDB库设计原则及实践
MongoDB数据模型选择• CAP定理(Consistency ,Availability 和Partition Tolerance )– Consistency(一致性):数据一致更新,所有数据变 ...
随机推荐
- C# 无边框窗口实现拖动
原文地址:http://blog.csdn.net/sky___ice/article/details/11533321 Form1.Designer.cs: // //Form1 // this.M ...
- Thread.Sleep(0) vs Sleep(1) vs Thread.Yeild()
注:本文为个人学习摘录,原文地址:http://www.cnblogs.com/stg609/p/3857242.html 本文将要提到的线程及其相关内容,均是指 Windows 操作系统中的线程,不 ...
- TCP TIME_WAIT详解
转自:http://m.blog.chinaunix.net/uid-20384806-id-1954363.html TIME_WAIT状态 TCP要保证在所有可能的情况下使得所有的数据都能够正确被 ...
- Java学习日志(20170111)
今日新知识点: 1.关键字volatile sychronized是同步锁,这个之前接触过,在类/方法或代码块前加该修饰词,即可实现线程同步: volatile也是一个修饰符,被volatile修饰的 ...
- CodeForces 710C Magic Odd Square
构造. 先只考虑用$0$和$1$构造矩阵. $n=1$,$\left[ 1 \right]$. $n=3$,(在$n=1$的基础上,最外一圈依次标上$0$,$1$,$0$,$1$......) $\l ...
- CF Round #353 Div.2
http://codeforces.com/contest/675 A. Infinite Sequence 题意:给出等差数列的首项a以及公差c,问数b是不是该数列中的数,若是输出YES否则输出NO ...
- Egret 学习之 从HelloWorld项目开始 (二)
1,创建新项目HelloWorld ,可以在界面上点击文件->新建,也可以在命令行使用create: 2,src 目录,存放我们的代码.我们编写的代码都放在src目录下面. bin-debug ...
- spark MLLib的基础统计部分学习
参考学习链接:http://www.itnose.net/detail/6269425.html 机器学习相关算法,建议初学者去看看斯坦福的机器学习课程视频:http://open.163.com/s ...
- 三大框架之hibernate
在DB Browser视图里新建一个数据库连接 Connection URL可填 jdbc:oracle:thin:@localhost:1521:orcl 或 jdbc:oracle:thin: ...
- 推荐几个好的 Maven 常用仓库网址
注意,以下内容转载自:推荐几个好的 Maven 常用仓库网址 Maven 确确实实是个好东西,用来管理项目显得很方便,但是如果是通过 Maven 来远程下载 JAR 包的话,我宿舍的带宽是4兆的,4个 ...