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(一致性):数据一致更新,所有数据变 ...
随机推荐
- hdu 3842 Machine Works(cdq分治维护凸壳)
题目链接:hdu 3842 Machine Works 详细题解: HDU 3842 Machine Works cdq分治 斜率优化 细节比较多,好好体会一下. 在维护斜率的时候要考虑x1与x2是否 ...
- <video>和<audio>标签,对视频和音频的支持
H5新增了<video>和<audio>标签,提供对视频和音频的支持 <audio>的属性与<video>属性相同 <video> vide ...
- POJ 1118 Lining Up
枚举,排序. 先将所有点按双关键字排序,然后枚举线的顶点$P$,剩余的点以$P$为中心进行极角排序,可以取个$gcd$,这样一样的点就排在一起了,然后统计一下更新答案. #pragma comment ...
- SQL2008将服务器的数据库表数据插入到本地数据库
一,配置参数 exec sp_configure reconfigure exec sp_configure RECONFIGURE 若不配置参数会出现,提示这个错误: SQL Server 阻止了对 ...
- 2016 ACM/ICPC Asia Regional Qingdao Online 1005 Balanced Game
Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission( ...
- JavaScript忍者秘籍——闭包
概要:本篇博客主要介绍了JavaScript的闭包 1.闭包的工作原理 简单地说,闭包就是一个函数在创建时允许该自身函数访问并操作该自身函数之外的变量时所创建的作用域. 例如: var outerVa ...
- ReactJS 生命周期、数据流与事件
React是一个JavaScript库文件,使用它的目的在于能够解决构建大的应用和数据的实时变更.该设计使用JSX允许你在构建标签结构时充分利用JavaScript的强大能力,而不必在笨拙的模板语言上 ...
- validate验证
导入需要的js 自动验证 <script src="${ctx }/static/assets/js/jquery-2.1.4.min.js">< ...
- [Q]AdobePDF虚拟打印机自动保存PDF
使用打图精灵打印时,选择“Adobe PDF”虚拟打印机打印(注意不选择“打印到文件”),每张图纸都会弹出一个保存对话框,如何避免? 从 操作系统->控制面板->硬件和声音->设备和 ...
- Error C1189: #error: Please use the /MD switch for _AFXDLL builds
在VS 2013中编译程序时出现错误: 错误提示1: error C1189: #error : Building MFC application with /MD[d] (CRT dll versi ...