MongoDB进阶“正反”范式设计讲解

原文地址:https://www.tsanyang.top/share-detail/739784336139616256.html


命名规范

数据库名称使用全小写以’_’隔开,长度最多为64个字符。

集合名称使用全小写以’_’隔开,长度最好不超三个英文单词。

文档key的名称使用全小写以’_’隔开,不能以$开头;不能包含.(点号),长度最好不超三个英文单词。

索引命名:idx_<构成索引的字段名>。

注:以上如果字段名字过长,可采用字段缩写,缩写需加说明。

索引

建立“适当”索引可以提高查询的执行效率,MongoDB可以使用索引来限制它必须检查的文档数。如果没有索引,MongoDB必须执行集合扫描,即扫描集合中的每个文档,以选择与查询语句匹配的文档。

默认_id 索引

MongoDB 在创建集合期间在_id字段上创建了唯一索引。该索引可防止客户端插入两个_id字段值相同的文档。_id字段的索引不能删除,在分片群集中,如果不将该_id字段用作分片键,则应用程序必须确保_id字段值的唯一性以防止出错。通常使用标准的自动生成的ObjectId来完成。

单索引

单键索引顾名思义就是单个字段作为索引列,mongoDB的所有collection默认都有一个单键索引_id,我们也可以对一些经常作为过滤条件的字段设置索引,如给level字段添加一个索引,语法很简单

db.userinfos.createIndex({level:1})

复合索引

MongoDB支持用户在多个字段上定义索引,即 复合索引。

复合索引中字段的顺序很重要。例如,如果复合索引为{ userid: 1, score: -1 },则索引首先以userid字段进行排序,然后在每个userid 值以score字段进行排序。

对于复合索引和排序操作,索引键的排序顺序(即升序或降序)可以确定索引是否可以支持排序操作。有关索引顺序对复合索引中结果的影响的详细信息,请参阅 排序顺序。

多键索引

多键索引是建在数组上的索引,在mongoDB的document中,有些字段的值为数组,多键索引就是为了提高查询这些数组的效率。看一个栗子:准备测试数据,product集合中添加产品,每个产品都有一个imgs数组

db.product.insertMany([

     {

         "product_name":"产品1",

         "imgs":[{url:'www.baidu.com',size:12},

                    {url:'www.baidu.com',size:68},

                    {url:'www.baidu.com',size:102}]

      },

      {

         "product_name":"产品2",

         "imgs":[{url:'www.baidu.com',size:20},

                    {url:'www.baidu.com',size:22},

                    {url:'www.baidu.com',size:25}]

      }]

  )

为了提高查询product的效率,我们使用  db.classes.createIndex({'imgs.size':1}) 给students的age字段添加索引。

索引属性

唯一索引(unique indexes)用于为collection添加唯一约束,即强制要求collection中的索引字段没有重复值。

局部索引(Partial Indexes)顾名思义,只对collection的一部分添加索引。创建索引的时候,根据过滤条件判断是否对document添加索引,对于没有添加索引的文档查找时采用的全表扫描,对添加了索引的文档查找时使用索引。

稀疏索引(sparse indexes)在有索引字段的document上添加索引,如在address字段上添加稀疏索引时,只有document有address字段时才会添加索引。而普通索引则是为所有的document添加索引,使用普通索引时如果document没有索引字段的话,设置索引字段的值为null。

TTL索引(TTL indexes)是一种特殊的单键索引,用于设置document的过期时间,mongoDB会在document过期后将其删除,TTL非常容易实现类似缓存过期策略的功能。

嵌入

嵌入意味着要把某一类型的数据,如包含更多数据的数组,嵌入到文档本身。

例如:针对产品需要保存多个图片地址进行建模的场景下使用内嵌文档是很合适,可以在product文档中嵌入imgs数组文档。

db.product.findOne();

     {

          _id:ObjectID('1'),

          product_name:"产品1",

          imgs:[{url:'www.baidu.com',size:12},

                    {url:'www.baidu.com',size:68},

                    {url:'www.baidu.com',size:102}]

      },

引用

引用意味着创建一个引用,包含另一个文档的数据。

例如:以产品零件订货系统为例。每个商品有数百个可替换的零件,但是不会超过数千个。这个用例很适合使用间接引用---将零件的objectid作为数组存放在商品文档中(在这个例子中的ObjectID我使用更加易读的2字节,现实世界中他们可能是由12个字节组成的)。

db.parts.findOne();

{

_id:ObjectID('1'),

name:"零件1",

......

}

每个产品的文档对象中parts数组中将会存放多个零件的ObjectID

db.product.findOne();

     {

          _id:ObjectID('1'),

          product_name:"产品1",

          imgs:[{url:'www.baidu.com',size:12},

                    {url:'www.baidu.com',size:68},

                    {url:'www.baidu.com',size:102}],

           parts:[

           ObjectID('1'),

           ObjectID('2'),

           ObjectID('3'),

           ObjectID('4')

          ]

      },

你可以采取内嵌,或者建立one端或者N端的引用,也可以三者兼而有之。

你可以在one端或者N端冗余多个字段

下面这些是你需要谨记的:

1、优先考虑内嵌,除非有什么迫不得已的原因。

2、需要单独访问一个对象,那这个对象就不适合被内嵌到其他对象中。

3、数组不应该无限制增长。如果many端有数百个文档对象就不要去内嵌他们可以采用引用ObjectID的方案;如果有数千个文档对象,那么就不要内嵌ObjectID的数组。该采取哪些方案取决于数组的大小。

4、不要害怕应用层级别的join:如果索引建的正确并且通过投影条件(第二章提及)限制返回的结果,那么应用层级别的join并不会比关系数据库中join开销大多少。

5、在进行反范式设计时请先确认读写比。一个几乎不更改只是读取的字段才适合冗余到其他对象中。

6、在mongodb中如何对你的数据建模,取决于你的应用程序如何去访问它们。数据的结构要去适应你的程序的读写场景。

设计指南

当你在MongoDB中对“一对多”关系进行建模,你有很多的方案可供选择,所以你必须很谨慎的去考虑数据的结构。下面这些问题是你必须认真思考的:

关系中集合的规模有多大:是一对很少,很多,还是非常多?

对于一对多中”多“的那一端,是否需要单独的访问它们,还是说它们只会在父对象的上下文中被访问。

被冗余的字段的读写的比例是多少?

数据建模设计指南

在一对很少的情况下,你可以在父文档中内嵌数组。

在一对很多或者需要单独访问“N”端的数据时,你可以采用数组引用ObjectID的方式。如果可以加速你的访问也可以在“N”端使用父引用。

在一对非常多的情况下,可以在“N”端使用父引用。

如果你打算在你的设计中引入冗余等反范式设计,那么你必须确保那些冗余的数据读取的频率远远大于更新的频率。而且你也不需要很强的一致性。因为反范式化的设计会让你在更新冗余字段时付出一定的代价(更慢,非原子化)。

你可能感兴趣的:(MongoDB进阶“正反”范式设计讲解)