目录
3数据模型(Data Models)
数据在Mongodb中是固定框架,但是Collection不强制任何文档结构
3.1数据模型简介
数据库模型的关键是平衡应用程序的需要,和数据库引擎的性能和数据取回方式。
3.1.1数据结构
数据结构有引用和嵌入2种文档。
引用
引用方式是文档中存了一份,另外一个文档的_id然后通过,_id连接相关的文档。
嵌入数据
就是文档以子文档的方式存在另外一个文档下面。
3.1.2 写操作的原子性
写操作的原子性是在文档级别的,只对单个文档内的支持。一些便利的原子写会限制应用程序使用数据,或者限制应用程序的修改。
3.1.3文档增长
当文档大小超过了分配的空间时,Mongodb会重新分配一个空间,增长的考虑会影响正常数据,和非正常数据。
3.1.4数据使用和性能
设计数据模型是,考虑应用程序如何使用数据,若应用程序只是用当前的一些数据,可以考虑使用Capped Collections。或者是以读为主的数据,增加索引来加强性能。
3.2数据模型概述
在设计数据模型时,有很多选项选择,每个都有好处和坏处。
3.2.1数据模型设计
数据设计时主要考虑的问题是:引用还是嵌入。
嵌入数据模型
嵌入数据模型是非正常(denormalized)的数据模型。嵌入数据模型,可以存相关的信息到同一个记录,用很少的操作就能完成查询,更新。以下场景使用嵌入数据模型:
1.对象之间是包含关系,1对1关系
2.对象之间是1对多关系
通常嵌入方式可以通过比较好的读性能,也可以完成原子性,但是文档的增长会成为一个性能问题。而且需要小于BSON文档的最大值。
嵌入文档的访问可以通过.(点)符号来实现。
引用数据模型(normalized)
引用数据模型有一下好处:
1.当嵌入模型有重复数据出现,用引用模型可以减少重复,但是不能提供读性能
2.可以表示多对多关系
3.表示巨大的多次数据集
引用类型需要多趟执行才能获得结果这个是最大的缺点。
3.2.2可选的特性和数据模型
模式化应用程序数据不单单是数据本身的问题,还有一些和Mongodb的特性有关,开发一个数据模型,用一下块来分析读写操作。
文档增长
文档增长造成空间重新分配,导致性能问题,可以使用padding,预分频,或者重构数据模型来处理。
原子性
Mongodb原子性只提供到文档级别,所有之上的级别都不支持原子性,可以把要保证原子性的数据放到同一个文档中,不需要的就放到其他文档。用文档的原子性来处理很方便,不然要把数据按读写分开处理数据(类似二阶段提交的方法)。
Sharding
Sharding提供了水平扩展能力,集群可以支持大的数据集和高吞吐量,sharding是把数据分片到不同的实例上。shard key是分片的主要依据,所以选择一个合理的key是很关键的。
索引
使用索引可以提升一些读取性能,当然Mongodb会自动在_id上创建唯一索引。在创建索引时考虑以下行为:
1.每个索引至少8kb空间
2.添加索引给写操作会带来负面性能
3.Collection读多写少可以使用索,Collection读少写多要尽量减少索引
4.每个索引都要消耗内存和磁盘空间,这方面的使用应该被跟踪并考虑到能力计划内
大量的Collection
大量的Collection并不会给性能带来很大的影响,但是当使用大量collection的时候还是需要考虑一下几点:
1.collection最小也好几个KB的大小
2.每个索引至少要8KB的空间
3.每个数据库都有一个名称空间文件,保存了所有的元数据,大量collection的数据库要考虑名称空间文件的大小问题。
4.Mongodb名称空间有限制,需要考虑名称空间个数问题。查看已用的名称空间可以用db.system.namespace.count(),默认名称空间的大小是16MB,可以使用--nssize的启动参数来配置。
数据生命周期管理
若数据有生命周期,可以考虑使用Time to Live or TTL特性来限制数据的持久性,如果数据只是用当前插入的,可以使用Capped Collections提供先进先出的管理。
3.2.3 GridFS
GridFS主要是来保存超过BSON文档的限制16MB的数据。GridFS会把一个文件分成多分,默认GridFS一块的大小为256KB,GridFS有2个collection,1个用户保存数据,1个用户保存元数据。
当查询这个GridFS的时候,驱动或者客户端或重新组合这些块。
GridFS Collection
GridFS有2个Collection,Chunks用户保存文件的二进制,files用于保存文件的元数据。默认这2个collection是以fs为前缀。只要file中有一个文档,就说明在chunks中有一个文件。
GridFS 索引
GridFS在chunks上使用唯一,复合索引在files_id和n 二个字段上。files_id包含了_id的chunks的付文档,n表示chunks的顺序。
3.3数据模型的例子和方案
3.3.1文档间关系模板
主要介绍:使用嵌入的1对1模型,使用嵌入的1对多模型,使用引用的1对多模型
嵌入1对1模型
就是用子文档的访问嵌入在父文档中,如果使用引用类型,那么会需要多趟查询才能得到结果。
嵌入1对多模型
同嵌入1对1
引用1对多模型
引用方式可以减少重复数据,如果有大量的重复数据那么可以考虑使用引用方式来减少空间浪费。
3.3.2树结构模型
主要介绍一下几种:
Model Tree Structures with Parent References引用父节点,
Model Tree Structures with Child References 引用子节点,
Model Tree Structures with an Array of Ancestors用数据来记录先辈节点,
Model Tree Structures with Materialized Paths用字符串来记录路径,
Model Tree Structures with Nested Sets用前序遍历方式。
Model Tree Structures with Parent References
父节点引用,在每个节点上存上父节点的_id。简单的实现了树的功能,但是比较难查询某一个子树。
Model Tree Structures with Child References
在节点上增加一个数组,把子节点加入到数组中。实现方式很合适,但是子树上面不要有修改的操作,不然比较麻烦操作。
Model Tree Structures with an Array of Ancestors
把祖先节点用在一个数组里面,使用这个方法,很容易找出子树的节点,并且可以马上查出祖先节点。
Model Tree Structures with Materialized Paths
就是把祖先节点用,拼成一个字符串组成路径
Model Tree Structures with Nested Sets
这种方式是存入父节点之外,用前序遍历的方式,把进入时,和退出时的步数记录下来,如:
db.categories.insert( { _id: "Books", parent: 0, left: 1, right: 12 } )
db.categories.insert( { _id: "Programming", parent: "Books", left: 2, right: 11 } )
db.categories.insert( { _id: "Languages", parent: "Programming", left: 3, right: 4 } )
db.categories.insert( { _id: "Databases", parent: "Programming", left: 5, right: 10 } )
db.categories.insert( { _id: "MongoDB", parent: "Databases", left: 6, right: 7 } )
db.categories.insert( { _id: "Postgres", parent: "Databases", left: 8, right: 9 } )
这种方式可以快速的查询出子树,但是如果修改树结构就不是很方便。
3.3.3特定应用程序上下文模型
原子操作数据模型
当你保存在一个文档之后,你可以使用db.collection.findAndModify()方法来保证原子性,如:
db.books.findAndModify ( {
query: {
_id: 123456789,
available: { $gt: 0 }
},
update: {
$inc: { available: -1 },
$push: { checkout: { by: "abc", date: new Date() } }
}
} )
支持关键字查询的数据模型
要支持这个模型,先要在文档中建一个数组,并把关键字逐个添加到数组中,然后在这个数据上面创建索引,当数组很大的时候insert会带来很大的负面性能。
关键字查询的限制:
1.词干:关键字查询不能解析某个词的词根或者相关的词
2.近义词:无法提供近义词查询,要在应用层实现
3.权重:没有权重计算
4.异步索引:Mongodb上索引都是同步的,但是对于全文索引而言异步索引提高性能
3.4数据模型 Reference
3.4.1文档
在Mongodb中用户可以访问的基本上都是文档:
1.所有数据库的记录
2.查询选择器,定义了要过滤那些数据,显示那些数据
3.更新定义,定义了那些数据要被更新
4.索引,指定了索引的key
5.由Mongodb输出的配置信息和报告信息
文档格式
Mongodb使用BSON格式报讯,BSON是用二进制表示JSON文档。
文档结构
文档结构是json的key-value方式,value可以是任意的数据类型。
字段名
字段名是字符串类型,不支持重复的字符串,尽管mongodb的内部文档中有重复的字符串,但是不会让用户文档添加。
文档的一些限制
1.BSON文档最大为16MB,若超过这个到校可以用GridFS保存
2.字段名_id是保留的,不能以$作为字段的开头,字段不能包含字符“.”
3.Mongodb不能保证BSON文档中字段的顺序
_id字段
_id字段通常被用于索引,_id可以是任何类型但不能是数组,_id的值可以有以下几个选择:
1.使用obejctid
2.使用唯一标识符
3.使用自增长数
4.使用UUID,GUID是UUID的一种实现
5.使用驱动的BSON UUID特性生成UUID
点(.)字符
在Mongodb中点字符用来访问数组(array.index)和子文档(subdocument.field)
3.4.2数据库引用
在数据库中引用有2种方式:手动引用,DBRef(数据库引用),当对于到多个collection时使用DBRef。
手动引用
文档的_id被存在另外一个文档,应用程序要通过另外一个查询来获取引用的数据。手动引用最大的缺点是无法对应到数据库和collection名。所以无法关联多个collection。若关联多个collection可以使用DBRef。
DBRef
DBRef会多一下几个字段:$ref 保存collection的名字,$id保存关联的_id,$db 可选用于保存db名。
3.4.3 GridFS Reference
GridFS保存2个collection,chunks,files,以fs为前缀。可以用非fs前缀,可以在一个数据库中创建多个前缀。
3.4.4 ObjectId
ObjectId长度12字节的BSON类型:4字节:自Unix以来的秒数,3字节:机器标识符,2字节进程id,3字节随机数。
Mongodb选用objectid是因为短,生成快,若客户端加了那么必须是唯一的,若不加就默认会用objectid。
1.可以用Objectid.getTimestamp()来查看Objectid的创建时间。
2._id字段存了objectid的排序和创建时间排序一样。
但是在并行状态下,Objectid不能严格的表示插入的顺序,客户端之间的时间并不同,objectid是client driver生成的。
3.4.5 BSON类型
- MinKey (internal type)
- Null
- Numbers (ints, longs, doubles)
- Symbol, String
- Object
- Array
- BinData
- ObjectID
- Boolean
- Date, Timestamp
- Regular Expression
- MaxKey (internal type)
Objectid:小,唯一,快速生成,顺序的
String:BSON字符串是UTF-8
Timestamps:Mongodb内部使用BSON有特定的timestamp数据类型而不是用Date类型来做辅助,在复制中oplog的ts字段也是timestamp类型。
Date:日期类型,64位毫秒级表示从Unix产生到现在。