MongoDB学习笔记8——优化
“如果MongoDB查询运行超过0毫秒,那它一定是出什么问题了。”
MongoDB与任何数据存储系统一样,如果使用了错误的数据结构,或者并未在集合中创建正确的索引,MongoDB的速度可能急剧下降。
1)优化服务器硬件以提高性能
通常对数据库服务器进行的最快捷也是最便宜的优化就是为之选择正确大小的硬件。如果数据库服务器的内存太小或者驱动器速度太低,就可能会对数据库性能产生巨大影响。
理解MongoDB的存储引擎
MongDB近期历史上最大的改变是增加了新的存储引擎的API。随着这个API还发布了几个存储引擎,其中,WiredTiger常出现在大标题中。第一次启动MongoDB实例时,存储引擎是固定的。为了改变存储引擎,应转储系统的所有数据,删除数据库内容,再次导入数据。大多数情况下,最好使用默认的存储引擎,在MongoDB3.2中,就是WiredTiger。WiredTiger还能使用压缩算法之一自动压缩数据,这大大节省了数据所需的存储空间。
2)评估查询性能
MongoDB有两个用于优化查询性能的工具:explain()和MongoDB分析器。MongoDB分析器是查找性能不佳的查询以及选择查询用于进一步检查的好工具,而explain()是研究单个查询的好工具,通过它可以判断查询的执行性能。此外,MongoDB总是将超过一定执行时间的查询写到日志文件中(默认100ms)。设置slowMS值可以改变这个默认的100ms。
2-1)MongoDB分析器:
启用MongoDB分析器
$mongo
use blog
db.setProfilingLevel(1)
禁用分析器
db.setProfilingLevel(0)
只针对超过了特定执行时间的查询启用分析器
db.setProfilingLevel(1,500)
为所有查询启用分析器
db.setProfilingLevel(2)
2-2)查找慢速查询
system.profile集合中的典型文档
db.system.profile.find()
里面可以查找一些关键字段:
例如:
db.system.profile.find({millis:{$gt:10}}).sort({millis:-1})
2-3)增大分析器集合的大小
出于某种原因,发现分析器集合太小,那么可以增加它的大小
首先禁止目标数据库上的分析器,保证执行下列操作时不会有写入操作发生:
use blog
db.setProfilingLevel(0)
接下来需要删除现有的system.profile集合
db.system.profile.drop()
删除该集合之后,可以使用createCollection命令创建新的分析器集合,并使用目标字节大小作为参数。以创建一个大小为50MB的固定大小集合。它通过乘以1024的方式将50字节转换成KB,然后转换成MB:
db.createCollection("system.profile",{capped:true,size:50*1024*1024})
成功创建更大的固定大小集合之后,可以重新启用分析器:
db.setProfilingLevel(2)
3)使用explain()分析特定的查询
use blog
db.posts.find().explain(true)
之后会显示一系列的指标。
4)管理索引
索引最好用在主要访问为读访问的集合中。每个集合中最多可拥有64个索引。
4-1)显示索引
使用辅助函数getIndexes(),可以显示指定集合中的索引。
use blog
db.posts.getIndexes()
当在某个元素上定义索引时,MongoDB构造一个二叉树索引,并用它高效地定位文档。如果未找到合适的索引,MongoDB将扫描集合中所有文档,搜索满足查询的记录。
4-2)创建简单的索引
使用升序二叉树索引 db.posts.createIndex({Tags:1})
使用降序二叉树索引 db.posts.createIndex({Tags:-1})
为索引嵌入式文档中的字段,可使用普通的点标记寻址模式:
db.posts.createIndex({"comments.count":1})
MongoDB提供了一个特殊的操作符$all,用于选择包含了参数所提供的所有数据的文档:
db.posts.find({Tags:{$all:['sailor','moon']}})
5)指定索引选项
5-1)创建部分索引:
db.restaurants.createIndex(
{name:1},
{partialFilterExpression:{cost:{$gt:10}}})
5-2)TTL索引
在计算机术语中,TTL是一种为特定数据块或者请求赋予时间范围的方式,它将指定一个时间点,一旦超过该时间点,数据将失效。为创建TTL索引,必须在单索引中添加expireAfterSeconds标志和对应的秒数。这将表示任何索引字段值大于指定TTL值文档,都将在TTL删除任务下一次执行时被删除。因为删除任何任务每60秒运行一次。
假设希望将创建时间大于28天的评论全部删除。计算出28天为2419200秒。然后创建下面的索引:
db.comments.createIndex({ts:1},{expireAfterSeconds:2419200})
在该文档存在的时间超过2419200秒后,它将被删除。可使用下面的语法创建一个时间超过2419200秒的旧文档:
date = new Date(new Date().getTime()-2419200);
db.comments.insert({});(与comments的设置有关)
5-3)删除索引
db.posts.dropIndexes()
删除单个索引:
db.posts.dropIndexes({"author.name":1,"author.email":1})
5-4)重建集合索引
db.posts.reIndex()
6)通过hint()强制使用特定的索引
db.posts.createIndex({author.name:1,author.email:1})
强制查询优化器使用已定义的索引:
db.posts.find({author:{name:'joe',email:'joe@mongodb.com'}}).hint({author.name:1.author.email:1})
如果处于某种原因不希望使用任何索引,即希望使用集合文档扫描作为选择记录的方式,那么可以使用下面的提示:
db.posts.find({author:{name:'joe',email:'joe@mongodb.com'}}).hint($natural:1})
7)使用索引过滤器
命令是planCacheSetFilter
db.runCommand(
{
planCacheSetFilter:"stuff", (stuff是一个集合名)
query:{letter:{$gt:"B"},shape:"circle"},
sort:{number:1},
projection:{},
indexes:[{letter:1,shape:1}]
}
)
db.stuff.find({letter:{$gt:"B"},shape:"circle"}).sort({number:1})
设置了索引过滤器后,还有两个推论函数需要注意。首先是planCacheListFilters函数,它列出了给定集合上当前的过滤器,调用和输出如下:
db.runCommand({planCacheListFilters:"stuff"})
第二个函数planCacheClearFilter可以删除索引过滤器,它的调用几乎与planCacheSetFilter相同
db.runCommand(
{
planCacheClearFilter:"stuff",
query:{letter:{$gt:"B"},shape:"circle"},
sort:{number:1},
projection:{},
indexes:[{letter:1,shape:1}]
}
)