MongoDB 存储引擎和数据模型设计
标签: MongoDB NoSQL
1. 存储引擎
1.1 存储引擎是什么
存储引擎是位于持久化数据(通常是放在磁盘或者内存中)和数据库之间的一个操作接口,它负责数据的存储和读取方式。MongoDB数据库通过存储引擎在磁盘中读取数据,而假设我们的应用是ASP.NET MVC,我们可以使用官方的Mongo.Driver驱动,通过通信协议(如TCP)向MongoDB数据库发送各种请求。以下是一个简单的运行图示
1.2 MongoDB中的默认存储引擎
自MongoDB 3.2 Release版本起,MongoDB默认的存储引擎就成了WiredTiger。而在之前的版本中,它还是MMAPv1。但由于MongoDB架构支持可插拔的存储引擎,所以使用中即便要更换也是可以做到的。至于其他的功能比较大家可以参阅官方文档,如不再是In-Place Update,新增Compression等。
我们可以在开启mongod服务时输入相关参数调整存储引擎,如mongod --storageEngine MMAPv1|wiredTiger
我们也可以使用db.collections.stats()查看当前的引擎名称
MMAPv1
MMAPv1 提供集合级别锁(实际上称为collection-level locking)WiredTiger
WiredTiger 对于写操作提供文档级别并发控制(实际上称为document-level concurrency),因此,不同的客户端请求可以在同一时间针对一个集合中的不同文档进行修改
2. 数据模型设计
2.1 内嵌和引用
在MongoDB中,数据的表示方式有内嵌和引用两种。
“引用”我们比较好理解,是指将不同实体的数据分散不到不同的集合中,而在关系型数据库设计中就是将实体分别建立相应的模型表。如常见的“老师-学生”,“产品-标签”关系,只要实体间存在关系,就可以使用“引用”思想。
“内嵌”是一种反范式化的设计,指的是将每个文档所需的数据都嵌入到文档内部,我想举一个“用户-账户”的关系。我们知道在领域驱动设计中,“用户”是一个聚合根,每个用户对应一个账户,所以是“1对1”的一种关系,在关系型数据库设计中,大部分时候都会将这两者严格区分开来。但是在MongoDB中,却不然,我们可以直接选择将“用户”需要的“账户”数据内嵌到用户文档中,便于我们的增删改查。这是一种反范式化的设计。
设计MongoDB数据模型的时候,我们需要转变以往设计关系型数据模型时的思维。即便是针对一个关系中不同集合的数量规模,我们的模型也将有很大的不同。
2.2 设计原则
*
A. 1 - 1 或者 1 - (较少)
用户与账户,以及用户与收货地址都是这样情况,在这样的情况下,显而易见我们可以采取内嵌的方式来进行数据管理。
> db.person.findOne()
{
_id:ObjectId("cccc"),
name:"wddpct",
age:22,
location:"wenzhou",
addresses:[
{country:"china",city:"wenzhou",street:"chashan road"}
{country:"china",city:"wenzhou",street:"north center road"}
]
}
这也引伸出一个问题,除了“1”以外的另一端的实体是否有必要在数目较少的时候进行单独集合的储存。如用户和任务模块,任务是系统定期发布,分配给相应用户完成,这意味着我们对任务的操作也将比较复杂。这样的情况下,显然是分开不同集合进行存储,然后让person集合引用task_id数组。
> db.person.findOne()
{
_id:ObjectId("cccc"),
name:"wddpct",
age:21,
location:"wenzhou",
tasks:[
ObjectId("xxxx"),
ObjectId("yyyy"),
……
]
}
所以针对刚才提到的情况,我们大可以借鉴领域驱动模式中的“实体”和“值对象”的部分概念,主要还是看这些数据模型在系统中是否有较大较复杂的操作可能。
*
B. 1 - (较多)
博主之前负责过一个市级地区中小学眼视光筛查系统,里面的简化模型就比较适合拿来做例子。如学校与学生,数目多也不过数千。这样的情况下,自然也是使用引用的方式更容易接受
> db.school.findOne()
{
_id:ObjectId("cccc"),
name:"middle1",
location:"wenzhou",
students:[
ObjectId("xxxx"),
ObjectId("yyyy"),
……
]
}
这里同样也引伸出一个“冗余”的问题,我们知道大多时候我们需要查询的数据属性数目是比较少的,比如对于学生而言,我们可能只需要知道他的身高体重,所以我们可以使用“冗余”思想简单修改刚才的集合成以下格式来应付
> db.school.findOne()
{
_id:ObjectId("cccc"),
name:"middle1",
location:"wenzhou",
students:[
{ObjectId("xxxx"),name:"wddpct",height:233,weight:233},
{ObjectId("yyyy"),name:"wddmd",height:233,weight:233}
……
]
}
不过也要注意的一点是,这样每次更新student的信息时,不免又要对school中的冗余信息进行更新,所以也要结合具体场景使用
*
C. 1 - (非常多)
地区和车牌的关系勉强属于此类,一个地区可能有几十上百万车牌,我们不可能再像刚才那样在area中加入所有的license_id,不然可能光是单个文档大小就超过MongoDB的16MB限制了,而且对于查询也存在很大的负担。
这里我们可以直接套用关系型数据库中的外键思想,在license集合的末尾加入area_id就可以方便解决此类关系
> db.license.findOne()
{
_id:ObjectId("cccc"),
license:"middle1",
area:ObjectId("xxxx")
}
当然,我们也可以对area进行进一步冗余,所以就不额外说明了。
*
D. * -
对于多对多关系模型,可能又要祭出那句老话——“视具体情况而定”。不过一般情况下,它不过就是一对多关系的几个变种。一个基本的原则是考虑两边统一引用对方的ObjectId,适当冗余部分信息。
除此以外,我们还可以从以下几个原则去考虑
- 两边的数量比(较大方更适合引用)
- 两边的更新频率比(较大方更适合引用)
- 两边的读取频率比(较大方更适合内嵌)
……
E. 通用建议
以下给出一张较通用的建议表,仅供参考
内嵌 | 引用 |
---|---|
子文档较小 | 子文档较大 |
数据不会定期更改 | 数据经常改变 |
最终数据一致即可 | 中间阶段数据也必须一致 |
文档数据小额增加 | 文档数据大幅增加 |
数据通常需要执行二次查询 | 数据通常不包含在查询结果中 |
快速读取 | 快速写入 |
MongoDB 存储引擎和数据模型设计的更多相关文章
- MongoDB 存储引擎:WiredTiger和In-Memory
存储引擎(Storage Engine)是MongoDB的核心组件,负责管理数据如何存储在硬盘(Disk)和内存(Memory)上.从MongoDB 3.2 版本开始,MongoDB 支持多数据存储引 ...
- MongoDB存储引擎选择
MongoDB存储引擎选择 MongoDB存储引擎构架 插件式存储引擎, MongoDB 3.0引入了插件式存储引擎API,为第三方的存储引擎厂商加入MongoDB提供了方便,这一变化无疑参考了MyS ...
- mongodb存储引擎
存储引擎(Storage Engine)是MongoDB的核心组件,负责管理数据如何存储在硬盘(Disk)和内存(Memory)上.从MongoDB 3.2 版本开始,MongoDB 支持多数据存储引 ...
- MongoDB学习笔记(五、MongoDB存储引擎与索引)
目录: mongoDB存储引擎 mongoDB索引 索引的属性 MongoDB查询优化 mongoDB存储引擎: 目前mongoDB的存储引擎分为三种: 1.WiredTiger存储引擎: a.Con ...
- MongoDB 存储引擎选择
MongoDB存储引擎选择 MongoDB存储引擎构架 插件式存储引擎, MongoDB 3.0引入了插件式存储引擎API,为第三方的存储引擎厂商加入MongoDB提供了方便,这一变化无疑参考了MyS ...
- 重新学习MySQL数据库3:Mysql存储引擎与数据存储原理
重新学习Mysql数据库3:Mysql存储引擎与数据存储原理 数据库的定义 很多开发者在最开始时其实都对数据库有一个比较模糊的认识,觉得数据库就是一堆数据的集合,但是实际却比这复杂的多,数据库领域中有 ...
- SQLServer存储引擎——04.数据
4. SQL SERVER存储引擎之数据篇 (4.1)文件 (0)主数据文件.mdf初始文件大小至少为3MB,次要数据文件.ndf初始大小,同日志文件一样至少为512KB: (1)SQL SERVER ...
- SQL SERVER存储引擎——04.数据
4. SQL SERVER存储引擎之数据篇 (4.1)文件 (0)主数据文件.mdf初始文件大小至少为3MB,次要数据文件.ndf初始大小,同日志文件一样至少为512KB: (1)SQL SERVER ...
- [转帖]时序数据库技术体系 – InfluxDB TSM存储引擎之数据读取
时序数据库技术体系 – InfluxDB TSM存储引擎之数据读取 http://hbasefly.com/2018/05/02/timeseries-database-7/ 2018年5月2日 ...
随机推荐
- Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用(后续)
在[Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用]里面提到了Microsoft 身份认证,其实这也是一大块需要注意的地方,特作为后续补充这些知识点.上章是使用了Microsof ...
- Oracle学习之路-- 案例分析实现行列转换的几种方式
注:本文使用的数据库表为oracle自带scott用户下的emp,dept等表结构. 通过一个例子来说明行列转换: 需求:查询每个部门中各个职位的总工资 按我们最原始的思路可能会这么写: ...
- 3种web会话管理的方式
http是无状态的,一次请求结束,连接断开,下次服务器再收到请求,它就不知道这个请求是哪个用户发过来的.当然它知道是哪个客户端地址发过来的,但是对于我们的应用来说,我们是靠用户来管理,而不是靠客户端. ...
- Spring(三)__aop编程
aop( aspect oriented programming ) 面向切面编程,是对所有对象或者是一类对象编程 几个重要的概念: 1.切面(aspect):要实现的交叉功能,是系统模块化的一个切面 ...
- Android 中的mvvm
我们来了解一下MVVM模式与Databinding ,MVVM是一种模式,Databinding 是一种框架.DataBinding是一个实现数据和UI绑定的框架.而ViewModel和View可以通 ...
- DB2重启数据库实例
DB2重启数据库实例时,有时停止实例会失败,此时需要先确认没有应用链接数据库,然后再关闭数据库实例,并重新启动. 1.查看是否有活动的链接 命令:db2 list applications for d ...
- 基于java的分布式爬虫
分类 分布式网络爬虫包含多个爬虫,每个爬虫需要完成的任务和单个的爬行器类似,它们从互联网上下载网页,并把网页保存在本地的磁盘,从中抽取URL并沿着这些URL的指向继续爬行.由于并行爬行器需要分割下载任 ...
- 基于Cat的分布式调用追踪
Cat是美团点评出的一款APM工具,同类的产品也有不少,知名的开源产品如zipkin和pinpoint:国内收费的产品如oneapm.考虑到Cat在互联网公司的应用比较广,因此被纳入选型队列,我也有幸 ...
- python __globals__, __file__
Attribute Meaning __doc__ The function's documentationstring, or None ifunavailable; not inherited ...
- ASP.NET MVC系列:Model
1. Model任务 Model负责通过数据库.AD(Active Directory).Web Service及其他方式获取数据,以及将用户输入的数据保存到数据库.AD.Web Service等中. ...