MongoDB 存储引擎和数据模型设计

MongoDB 存储引擎和数据模型设计

1. 存储引擎

1.1 存储引擎是什么

1.2 MongoDB中的默认存储引擎

2. 数据模型设计

2.1 内嵌和引用

2.2 设计原则

A. 1 - 1 或者 1 - *(较少)

B. 1 - *(较多)

C. 1 - *(非常多)

D. * - *

E. 通用建议

1. 存储引擎 1.1 存储引擎是什么

存储引擎是位于持久化数据(通常是放在磁盘或者内存中)和数据库之间的一个操作接口,它负责数据的存储和读取方式。MongoDB数据库通过存储引擎在磁盘中读取数据,而假设我们的应用是ASP.NET MVC,我们可以使用官方的Mongo.Driver驱动,通过通信协议(如TCP)向MongoDB数据库发送各种请求。以下是一个简单的运行图示

1.2 MongoDB中的默认存储引擎

自MongoDB 3.2 Release版本起,MongoDB默认的存储引擎就成了WiredTiger。而在之前的版本中,它还是MMAPv1。但由于,ongoDB架构支持可插拔的存储引擎,所以使用中即便要更换也是可以做到的。至于其他的功能比较大家可以参阅官方文档,如不再是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"), …… ] }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/c6c9d36592d170eb0c38fabcfa2a6fdc.html