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日 ...
随机推荐
- 谈谈一些有趣的CSS题目(四)-- 从倒影说起,谈谈 CSS 继承 inherit
开本系列,讨论一些有趣的 CSS 题目,抛开实用性而言,一些题目为了拓宽一下解决问题的思路,此外,涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题中有你感觉 ...
- H5坦克大战之【画出坦克】
今天是个特殊的日子,圣诞节,也是周末,在这里先祝大家圣诞快乐!喜庆的日子,我们可以稍微放松一下,扯一扯昨天雷霆对战凯尔特人的比赛,这场比赛大威少又双叒叕拿下三双,而且是一个45+11+11的超级三双, ...
- Velocity初探小结--Velocity在spring中的配置和使用
最近正在做的项目前端使用了Velocity进行View层的数据渲染,之前没有接触过,草草过了一遍,就上手开始写,现在又回头细致的看了一遍,做个笔记. velocity是一种基于java的模板引擎技术, ...
- c#语言规范
0x00 分类 C#语言规范主要有两个来源,即我们熟知的ECMA规范和微软的规范.尽管C#的ECMA规范已经前后修订4次,但其内容仅仅到C# 2.0为止.所以慕容为了方便自己和各位方便查询,在此将常见 ...
- [BootStrap] 富编辑器,基于wysihtml5
在我的周围,已经有很多人在使用BootStrap,但对于任何一个带留言.评论.提问.文章编辑功的网站,编辑器永远是重中之重,显然,早期的编辑器完全没考虑过BootStrap的出现,或皮肤跟网站不匹配, ...
- Effective前端2:优化html标签
div { float: left; } .keyboard > div + div { margin-left: 8px; } --> div{display:table-cell;ve ...
- 记录一次bug解决过程:数据迁移
一 总结 不擅长语言表达,勤于沟通,多锻炼 调试MyBatis中SQL语法:foreach 问题:缺少关键字VALUES.很遗憾:它的错误报的让人找不着北. 二 BUG描述:MyBatis中批量插入数 ...
- 分布式学习系列【dubbo入门实践】
分布式学习系列[dubbo入门实践] dubbo架构 组成部分:provider,consumer,registry,monitor: provider,consumer注册,订阅类似于消息队列的注册 ...
- 热修复-Nuwa学习篇
nuwa热修复是基于qq空间团队的思路,最近的热度话题了,很多种方案,自己先研究几种方案,基本上都各有优势,学习肯定得先挑个软柿子捏了,自己对比了一下,发现nuwa代码量少点,所以就决定了,先研究nu ...
- Linux初识
在这篇文章中你讲看到如下内容: 计算机的组成及功能: Linux发行版之间的区别和联系: Linux发行版的基础目录及功用规定: Linux系统设计的哲学思想: Linux系统上获取命令帮助,及man ...