文章目录
-
- 索引的核心概念
-
- 单建索引
- 复合索引
- 索引的效率
- 索引实战
-
- 索引类型
-
- 唯一索引
- 稀疏索引
- 多建索引
- 哈希索引
- 地理空间索引
- 索引管理
-
- 创建和删除索引
- 后台索引
- 重建索引
- 查询优化
-
- 找出慢速查询
- mongo慢日志
- 执行计划
-
- hint 强制使用某个索引执行查询
- 总结
- 全文搜索
-
- mongo全文搜索的功能
- mongo全文搜索的使用步骤
-
- 创建全文索引
- 使用全文索引查询
索引可以大大减少查询时处理文档的数量。没有适当的索引,查询时通常会扫描整个集合。
索引的核心概念
单建索引
针对文档的一个字段建立索引,最好的单建索引的例子就是集合的_id字段
复合索引
如果在一个查询里使用多个查询条件,我们可以在这些条件上建立复合索引,这种情况下,复合索引的查询效率要高于两个单建索引。
索引的效率
- 索引虽然可以提高查询效率,但是索引的维护也是需要成本的,当插入或修改文档的时候,都需要维护索引结构。
- 当索引和数据没有加载到内存时,即使所有的索引都创建的恰当,也可能无法加快查询。当数据的大小超过内存很多时,每次查询都需要从磁盘读取数据导致性能变慢。这个情况的专业称呼为“颠簸”。
- 为了避免索引的数量太大,尽量不要创建不必要的索引
- 不要在选择性比较差的字段上创建索引,如性别字段。
- 我们可以通过索引的大小来预估mongo需要内存的大小。
索引实战
索引类型
唯一索引
如果要保证文档中的字段唯一,我们可以使用唯一索引。如果像集合中插入存在的唯一索引值,mongo会抛出异常
创建唯一索引
db.users.createIndex({name:1},{unique:true})
稀疏索引
索引默认是密集型的,意味着,对于索引集合中的文档,如果索引建值缺失,也会维护在索引的结构中。默认为null
不是每个文档都包含此字段时,不需要建立密集索引
多建索引
当对数据字段建立索引时,数组中每个元素都会出现在索引的结构中,既通过每个元素查询都可以提高查询效率
哈希索引
hash索引的限制
- 等值查询很快,不支持范围查询
- 不支持多建哈希
当使用分片部署时,hash索引可以将数据均匀分布到各个节点中
通过hashed指定为哈希索引
db.user.createIndex({name:'hashed'})
地理空间索引
当查询某个地点附近的文档时,可以使用地理空间索引提高查询效率
索引管理
创建和删除索引
db.collection.createIndex({open:1,close:1})
db.collection.dropIndex("indexName")
后台索引
db.collection.createIndex({open:1,close:1},{background:true})
重建索引
db.collection.reIndex()
查询优化
查询优化是找出慢速查询,发现问题解决问题的过程
找出慢速查询
mongodb日志会打印所有查询时间大于100ms的操作,这里是慢速查询的第一个入口.
启动时可以通过–slows 50修改慢查询日志参数
- 使用grep命令查询慢日志
grep -E '[0-9]+ms' mongod.log
-
使用PROFILER分析器
profiler设置方法
1.选择要监控的数据库
use dbName
2.设置profiler级别
db.serProfilingLevel(2)
0 关闭profile分析器
1 分析器只会记录超时的操作 传递毫秒作为第二个参数
2 记录所有的查询操作
db.serProfilingLevel(1,50)
监控结果会保存在一个system.profile的集合中
db.system.profile.find({millis:{gt:150}}) 获取大于150ms的操作
mongo慢日志
{
"op" : "query", # 操作类型,值可为command、count、distinct、geoNear、getMore、group、insert、mapReduce、query、remove、update
"ns" : "test.report", # 操作的数据库和集合
"command" : { # 命令
"find" : "report", # 操作的集合
"filter" : { "a" : { "$lte" : 500 } }, # 查询条件
"lsid" : {
"id" : UUID("5ccd5b81-b023-41f3-8959-bf99ed696ce9") #用户的会话id
},
"$db" : "test" # 操作的数据库
},
"cursorid" : 33629063128, # query和getmore 的游标id
"keysExamined" : 101, # MongoDB为执行操作而扫描的索引键的数量
"docsExamined" : 101, # MongoDB为了执行操作 而扫描的集合中的文档数。
"numYield" : 2, # 让步次数,操作时让其他的操作完成的次数。
"nreturned" : 101, # 操作返回的文档数
"queryHash" : "811451DD", # 查询的hash值
"planCacheKey" : "759981BA",
"locks" : { # 操作期间的锁和所的类型
"Global" : { #表示全局锁定
"acquireCount" : { #锁定的次数
"r" : NumberLong(3) # 表示共享锁
}
},
"Database" : { # 数据库锁
"acquireCount" : { "r" : NumberLong(1) },
"acquireWaitCount" : { "r" : NumberLong(1) },
"timeAcquiringMicros" : { "r" : NumberLong(69130694) }
},
"Collection" : { # 集合锁
"acquireCount" : { "r" : NumberLong(1) }
}
},
"storage" : { # 储存
"data" : {
"bytesRead" : NumberLong(14736), #操作 从磁盘放到缓存的数据的字节数
"timeReadingMicros" : NumberLong(17) # 操作 花费在磁盘读取的时间,以微妙为单位
}
},
"responseLength" : 1305014, # 操作返回结果的文档长度,单位为字节
"protocol" : "op_msg", # 消息的协议
"millis" : 69132, # 从 MongoDB 操作开始到结束耗费的时间
"planSummary" : "IXSCAN { a: 1, _id: -1 }", # 摘要
"execStats" : { # 操作执行过程中的详细信息
"stage" : "FETCH", # 操作形式 ,COLLSCAN 用于集合扫描,IXSCAN 用于扫描索引键,FETCH 用于检索文档
"nReturned" : 101, # 返回的文档数量
"executionTimeMillisEstimate" : 0,
"works" : 101,
"advanced" : 101,
"needTime" : 0,
"needYield" : 0,
"saveState" : 3,
"restoreState" : 2,
"isEOF" : 0,
"invalidates" : 0,
"docsExamined" : 101,
"alreadyHasObj" : 0,
"inputStage" : {
...
}
},
"ts" : ISODate("2019-01-14T16:57:33.450Z"), #操作的时间戳
"client" : "127.0.0.1", # 客户端的ip
"appName" : "MongoDB Shell", #客户端应用标识符
"allUsers" : [
{
"user" : "someuser", # 用户
"db" : "admin" # 验证的数据库
}
],
"user" : "[email protected]" # 经过验证的用户
}
执行计划
获取执行计划explain(true)
db.values.find({}).sort({close:-1}).limit(5).explain(true)
hint 强制使用某个索引执行查询
db.values.find({}).sort({close:-1}).limit(5).hint("_id_");
总结
- 索引非常有用,但是也有成本,他会让写入变慢
- mongoDB通常在一个查询里使用一个索引,所以多个字段的查询使用复合索引效率更高
- 当声明复合索引时顺序非常重要
- 避免慢速的查询,使用explain命令可以查看执行计划
- 为了避免影响应用程序,应该在流量增加之前尽早的优化查询
- 通过减少扫描的文档数量来优化查询
全文搜索
与其他的全文搜索引擎不同,mongo不允许我们编辑分词列表
mongo全文搜索的功能
- 基于词根分词的自动化实例索引
- 根据字段名可分配权重
- 多语言支持
- 分词
- 精确词汇匹配
- 根据给定短语或单词排出某些结果的能力
mongo全文搜索的使用步骤
- 首先定义全文搜索需要的索引
- 然后,在基于查询和聚合框架使用文本搜索
创建全文索引
在name、description、tags三个字段上创建全文索引
db.products.createIndex({
name:'text',
description:'text',
tags:'text'
})
创建索引并制定权重
db.books.createIndex(
{title:'text',
shortDescription:'text',
longDesctiption:'text',
authors:'text',
categories:'text'},
{weight:
{title:10,
shortDescription:1,
longDesctiption:1,
authors:1,
categories:5}
name:'book_text_index' 指定索引的名字
});
使用全文索引查询
db.products.find(
{$text:${search:'gardens'}},
{_id:0,name:1,description:1,tags:1}