MongoDB教程——第3天(性能——索引)

目录

介绍

背景

索引

默认索引

如何创建索引

MongoDB中不同类型的索引

1. 单字段索引

 2. 复合索引

3. 多键索引

4. 文字索引

MongoDB索引属性

1. Unique

2. Sparse

3. 部分索引

4. TTL索引

一些关键点

1. getIndexes()

2. dropIndex()

3. dropIndexes()

4.从集合中获取所有索引

5. 重建索引

限制

参考


介绍

欢迎来到MongoDB教程的第3天。这是MongoDB教程系列的第三部分。在这一部分,我们将看到性能的不同方面。性能始终是任何数据库的重要组成部分。在任何数据库中,无论是RDBMS还是No-SQL数据库,我们总是考虑如何增加查询响应时间,因为数据库的性能始终是应用程序整体性能的重要组成部分。每当我们谈论性能时,索引都是优先级的。在本文中,我们将介绍MongoDB中不同类型的索引。

背景

在阅读本文之前,最好先介绍一下本文的前两部分(第1天和第2天)。

  1. Mongo DB教程及SQL与Mongo DB查询的映射
  2. MongoDB教程 - 第2天

到目前为止,我们介绍的是:

  1. No-Sql介绍(不同类型的数据库归于No-SQL
  2. 如何在您的机器上安装和设置MongoDB
  3. RobomongoMongoDB开源管理工具)介绍
  4. MongoDb术语
  5. 如何在MongoDB中插入文档
  6. 如何在MongoDB中选择一个文档
  7. Where 子句,大于和小于
  8. Like, 逻辑AND逻辑OR
  9. MongoDb中的in运算符,MongoDB Count以及Sort的记录
  10. MongoDB中的UpdateDelete,RemoveDrop
  11. MongoDB中的TopDistinctBackup
  12. Schema less行为
  13. $exists$in$all,$nin
  14. MongoDB中的数据类型
  15. 嵌入式文档和点表示法

索引

我们可以把索引想象成书籍索引。假设我们正在搜索一本书中的一个主题并且我们没有索引,那么我们需要扫描每一页,直到我们到达该页。如果你的书有100页,那么你可以管理(如果你有足够的空闲时间)但假设你的书有100万页,那么通过翻阅每一页来搜索主题将是一项非常乏味的工作。我们在MongoDB中有相同的概念。

如果我们没有索引,那么mongodb会做一个完整的集合扫描来选择与query语句匹配的文档。如果集合中的文档数量很高,那将导致性能死亡。

假设我们在Names集合 中有以下文档:

db.Names.insert({"Name":"Ajay"})
db.Names.insert({"Name":"Manoj"})
db.Names.insert({"Name":"Preeti"})
db.Names.insert({"Name":"Anuj"})
db.Names.insert({"Name":"Tony"})
db.Names.insert({"Name":"Steve"})
db.Names.insert({"Name":"Smith"})
db.Names.insert({"Name":"David"})
db.Names.insert({"Name":"William"})

Names集合中的文档将以任意顺序存储。如果没有Index,并且我们会找到类似下面的文档,那么它将是完整的集合扫描,并且会降低性能:

db.Names.find({"Name":"Smith"})

如果我们想了解MongoDB对上述查询做了什么,只需使用explain()如下:

 这里有几点需要注意:

  • cursor : BasicCursor: 表示MongoDB会进行一次完整的集合扫描。
  • nscannedObjectsMongoDB扫描九个对象以匹配此查询。

那么现在问题来了:

什么是索引Index是项目的顺序集。Index以特定顺序存储值。

默认索引

当我们在MongoDB中创建集合时,MongoDB会自动在_id字段上创建一个unique Index。因为它是unique index,它可以防止我们在_id字段中输入重复的值。我们不能在MongoDB中删除index

如何创建索引

要在MongoDB中创建一个index,我们有两种方法:

1、createIndex()createIndex()方法的语法是:

db.Names.createIndex({"Name":1})

1是升序,-1是降序。所以如果我们想在Names集合中创建一个IndexoNam键上,那么我们将创建一个index如下:

db.Names.createIndex({"Name":1})

2、ensureIndex()ensureIndex()方法的语法是:

db.CollectionName.ensureIndex({"key": 1 or -1})

ensureIndex()方法自3.0.0版起已弃用。此方法是createIndex()的别名。

MongoDB中不同类型的索引

1. 单字段索引

除了MongoDB创建的默认_id字段索引外,用户还可以在单​​个字段上创建升序或降序索引。

我们创建一个单一的键索引如下:

db.Names.createIndex({"Name":1})

现在,只需运行explain()

哇哦!现在我们定义了一个索引(BtreeCursor Name_1)而不是基本游标,最重要的是,看看nscannedObjects,它是1,这意味着MongoDB只扫描了1文档,我们在查询中提到过。

假设我们有另一个名为users的集合,其中包含以下文档:

db.Users.insert({"Name":"Ajay","Age":30})
db.Users.insert({"Name":"Manoj","Age":60})
db.Users.insert({"Name":"Preeti","Age":20})
db.Users.insert({"Name":"Anuj","Age":70})
db.Users.insert({"Name":"Tony","Age":25})
db.Users.insert({"Name":"Steve","Age":18})
db.Users.insert({"Name":"Smith","Age":33})
db.Users.insert({"Name":"David","Age":53})
db.Users.insert({"Name":"William","Age":65})

现在假设我们要找出Age大于30和小于60的所有文档。

 所以我们有了BasicCursor 所以它会进行一次完整的表扫描和查询所扫描的文件总数是9。现在我正在Age上定义一个索引:

db.Users.createIndex({"Age":1})

现在再次运行查询,它将找出Age大于30和小于60的所有文档。

Index之后,MongoDB不会做全表扫描,只会扫描4行。

 2. 复合索引

有时,我们想同时基于NameAge进行搜索。在这种情况下,我们将不得不申请对NameAge两个index,这将被称为复合索引。

语法: db.CollectionName({"Key1":1 or -1,"Key2": 1 or -1,"KeyN":1 or -1})

我们将在Users集合中创建一个关于NameAgeIndex,如下所示:

db.Users.createIndex({"Name":1,"Age":-1})

注意:只有当我们搜索NameNameAge时,复合索引才会起作用。如果我们只搜索Age,那么复合索引将不起作用。

假设我们正在搜索Name,那么我们可以看到复合索引正在使用中。

 如果我们搜索NameAge,那么我们可以再次看到复合索引正在使用中。

 但是如果只搜索Age,那么我们可以看到复合索引没有被使用:

 所以没有Index在这种情况下使用。

3. 多键索引

Users集合中删除现有文档并使用interestUsers集合中插入一些文档,如下所示:

db.Users.remove({})
db.Users.insert({"Name":"Ajay","Age":30,Interest : ["cricket","music"] })
db.Users.insert({"Name":"Manoj","Age":60,Interest : ["cricket","driving"]})
db.Users.insert({"Name":"Preeti","Age":20,Interest : ["music","driving"]})
db.Users.insert({"Name":"Anuj","Age":70,Interest : ["cooking","music"]})
db.Users.insert({"Name":"Tony","Age":25,Interest : ["swimming","cooking"]})
db.Users.insert({"Name":"Steve","Age":18,Interest : ["dancing","music"]})
db.Users.insert({"Name":"Smith","Age":33,Interest : ["tennis","tv"]})
db.Users.insert({"Name":"David","Age":53,Interest : ["music","swimming"]})
db.Users.insert({"Name":"William","Age":65,Interest : ["dancing","swimming"]})

现在如果我们想要Index一个数组的内容( 在我的例子中是Interest),那么我们将使用Multikey Index.

语法db.CollectionName.createIndex({"Array": 1 or -1})

我们将创建一个MultiKey Index兴趣如下:

db.Users.createIndex({Interest : 1})

4. 文字索引

如果我们正在执行文本搜索,那么为了获得更好的性能,我们可以在字符串内容上应用Text Index

我们只能在字符串字段上创建文本索引。

句法: db.CollectionName.createIndex({Field Name:"text"})

假设我们要创建一个IndexonName字段,那么我们将创建一个text index如下:

db.Users.createIndex({Name : "text"})

注意:一个集合最多可以有一个text index

除了这些索引之外,MongoDB还支持更多的索引,包括索引GeospatialHashed索引。

Geospatial索引用于更好地查询地理空间坐标数据,Hashed Index索引字段值的哈希值。

MongoDB索引属性

1. Unique

index字段上的unique属性允许MongoDB不接受索引字段的重复值。换句话说,unique属性将限制Index字段的insert重复值。

我在Users集合中添加另一个名为SSN的列,并在SSN字段上添加一个Unique属性,如下所示:

db.Users.drop()
db.Users.createIndex({SSN:1},{unique:true})
db.Users.insert({"Name":"Ajay","Age":30,
Interest : ["cricket","music"] ,"SSN" : "12345"})
db.Users.insert({"Name":"Manoj","Age":60,
Interest : ["cricket","driving"],"SSN" : "54321"})

我从Users集合中删除了所有记录,并在SSN字段上创建了一个具有唯一属性的索引。因此,如果我尝试在SSN中插入重复值,则会出现错误。让我们试试看:

db.Users.insert({"Name":"Preeti","Age":20,
Interest : ["music","driving"],"SSN" : "54321"})

错误是:

insertDocument :: caused by :: 11000 E11000 duplicate key error index: 
                 Test.Users.$SSN_1  dup key: { : "54321" }

因此,我们不能在具有unique属性的index字段中插入重复值。

2. Sparse

我正在删除Users集合并将插入一些文档,如下所示:

db.Users.drop()
db.Users.insert({"Name":"Ajay","Age":30,
Interest : ["cricket","music"] ,"SSN" : "12345"})
db.Users.insert({"Name":"Manoj","Age":60,
Interest : ["cricket","driving"],"SSN" : "54321"})
db.Users.insert({"Name":"Preeti","Age":20,
Interest : ["music","driving"]})
db.Users.insert({"Name":"Anuj","Age":70,
Interest : ["cooking","music"]})

现在,如果我尝试使用unique属性在SSN字段上创建一个Index,会发生什么 。如果我尝试创建一个unique属性的index,如下所示,那么我会收到一个错误,因为SSN包含null最后两个文档,因此SSN不是唯一的。

db.Users.createIndex({SSN:1},{unique:true})

错误是:

E11000 duplicate key error index: Test.Users.$SSN_1  dup key: { : null }

那么解决方案是什么呢?我不能在这样的记录上创建unique索引吗?

等等……我们有一个解决方案……我们有适用于这种情况的sparse属性。

Sparse会告诉数据库这些文件不应该包括在缺少SSNindex中。

太酷了。是时候来创建一个具有sparseunique属性的index

db.Users.createIndex({SSN:1},{unique:true,sparse:true})

这一次,index使用unique属性创建时不会出错,因为sparse在那里。

3. 部分索引

这是MongoDB 3.2附带的一个新概念。有时,我们想创造index一些特定的条件。如果我们使用某些条件创建index,那么它是部分Index.

假设我只想在Age大于30时在Name字段上创建一个Index。我们需要在创建一个Index时需要指定一个条件,如下所示:

db.Users.createIndex(
   { Name: 1},
   { partialFilterExpression: { Age: { $gt: 30 } } }
)

要应用条件,我们使用partialFilterExpression

4. TTL索引

MongoDB有一种特殊类型的单一字段,Index名为TTL IndexMongoDB使用这种类型Index在一段时间后自动删除文档。我们使用expireAfterSeconds选项来提供到期时间。

我正在Age上创建一个的TTL索引,过期时间为60秒,如下所示:

db.Users.createIndex( { "Age": 1 }, { expireAfterSeconds: 60 } ) 

它会在60秒后自动删除此文档。后台任务每60秒运行一次,删除所有过期文档。因此,从集合中删除此文档可能需要一些额外的时间。它还取决于mongod实例的工作量,因此过期的文档可以在指定的时间后收集。

一些关键点

1. getIndexes()

如果我们想查看一个集合上所有创建的索引,我们使用getIndexes()方法。

句法: db.CollectionName.getIndexes()

2. dropIndex()

要删除Index,我们使用dropIndex()方法。

句法: db.CollectionName.dropIndex({"Key":1 or -1})

传递带有1-1的键,这是我们在创建index时传递的。

3. dropIndexes()

要从集合中删除所有Index,我们使用dropIndexes()方法。

db.CollectionName.dropIndexes() 

4.从集合中获取所有索引

db.getCollectionNames().forEach(function(collection) {
   index = db[collection].getIndexes();
   print("Indexes for " + collection + ":");
   printjson(index);
});

5. 重建索引

要重建一个Index,我们使用reIndex()如下方法:

db.CollectioName.reIndex()

限制

  1. 单个集合不能有超过64个索引。
  2. 复合索引不能超过31个字段。

参考

https://www.codeproject.com/Articles/1091645/MongoDB-Tutorial-Day-Performance-Indexing

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值