暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MongoDB优化

原创 逆风飞翔 2021-11-02
371

Table of Contents
一.查询分析器
1.1 启用查询分析器
1.2 禁用查询分析器
1.3 记录慢查询
1.4 查找慢查询
1.5 增大分析器集合的大小
二.explain
三.使用索引优化查询
3.1 管理索引
3.1.1 显示索引
3.1.2 创建索引
3.1.3 删除索引
3.1.4 重建索引
3.2 索引选择三步法
3.3 指定索引选项
3.3.1 后台创建索引
3.3.2 创建唯一索引
3.3.3 创建稀疏索引
3.3.4 创建部分(条件)索引
3.4 使用hint()
参考:
一.查询分析器
1.1 启用查询分析器
> use test
s
switched to db test
>
> db.setProfilingLevel(1);
{
{ "was" : 0, "slowms" : 100, "sampleRate" : 1, "ok" : 1 }
>
>


1.2 禁用查询分析器
> use test
s
switched to db test
>
> db.setProfilingLevel(0);
{
{ "was" : 1, "slowms" : 100, "sampleRate" : 1, "ok" : 1 }
>
>


1.3 记录慢查询
-- 只记录执行超过半秒钟的查询

db.setProfilingLevel(1,500);
-
-- 所有查询启用分析器

db.setProfilingLevel(2);


测试记录:
> use test
s
switched to db test
>
> db.setProfilingLevel(1,500);
{
{ "was" : 0, "slowms" : 100, "sampleRate" : 1, "ok" : 1 }
>
> db.setProfilingLevel(2);
{
{ "was" : 1, "slowms" : 500, "sampleRate" : 1, "ok" : 1 }
>
>
>
>
>
>
>
> db.setProfilingLevel(1,500);
{
{ "was" : 2, "slowms" : 500, "sampleRate" : 1, "ok" : 1 }
>
>


1.4 查找慢查询
use test;

db.system.profile.find();

-- 查询所有执行时间长于10毫秒的查询,然后按执行时间对结果进行降序排序:

db.system.profile.find({millis:{$gt:10}}).sort({millis:-1});


每条记录返回的字段含义和作用:
op:操作类型,可以是query、insert、update、command或delete。
query:正在运行的查询。
ns:查询所在的完整命名空间。
ntoreturn:返回文档的数目。
nscanned:为返回该文档而扫描的索引条目数目。
ntoskip:被忽略的文档数目。
keyUpdates:该查询更新的索引键数目。
numYields:该查询为其它查询让出锁的次数。
lockStats:数据库花费在获取读写锁上的毫秒数。
nreturned:返回文档的数目。
responseLength:响应的字节长度。
millis:执行查询所花费的毫秒数。
ts:以UTC格式显示查询执行时的时间戳。
client:运行该查询的客户端连接信息。
user:运行该操作的用户。
图形化界面看起来更直关

1.5 增大分析器集合的大小
use test;
d
db.setProfilingLevel(0);

db.system.profile.drop();

db.createCollection( "system.profile", { capped: true, size: 50 * 1024 * 1024 } );

db.setProfilingLevel(2);


测试记录:
> use test;
s
switched to db test
>
> db.setProfilingLevel(0);
{
{ "was" : 1, "slowms" : 500, "sampleRate" : 1, "ok" : 1 }
>
> db.system.profile.drop();
t
true
>
> db.createCollection( "system.profile", { capped: true, size: 50 * 1024 * 1024 } );
{
{ "ok" : 1 }
>
> db.setProfilingLevel(2);
{
{ "was" : 0, "slowms" : 500, "sampleRate" : 1, "ok" : 1 }
>
>


二.explain
> db.t2.find().explain(true)
{
{

"queryPlanner" : {

"plannerVersion" : 1,

"namespace" : "test.t2",

"indexFilterSet" : false,

"parsedQuery" : {



},

"winningPlan" : {

"stage" : "COLLSCAN",

"direction" : "forward"

},

"rejectedPlans" : [ ]

},

"executionStats" : {

"executionSuccess" : true,

"nReturned" : 1000000,

"executionTimeMillis" : 366,

"totalKeysExamined" : 0,

"totalDocsExamined" : 1000000,

"executionStages" : {

"stage" : "COLLSCAN",

"nReturned" : 1000000,

"executionTimeMillisEstimate" : 0,

"works" : 1000002,

"advanced" : 1000000,

"needTime" : 1,

"needYield" : 0,

"saveState" : 7812,

"restoreState" : 7812,

"isEOF" : 1,

"direction" : "forward",

"docsExamined" : 1000000

},

"allPlansExecution" : [ ]

},

"serverInfo" : {

"host" : "10-31-1-122",

"port" : 27017,

"version" : "4.2.10",

"gitVersion" : "88276238fa97b47c0ef14362b343c5317ecbd739"

},

"ok" : 1
}
}
>
>


explain()以下字段:
queryPlanner:执行查询的细节,包括计划的细节。
queryPlanner.indexFilterSet:表明是否使用索引过滤器来实现这个查询。
queryPlanner.parsedQuery:正在运行的查询。这是查询修改后的形式,显示了如何在内部评估它。
queryPlanner.winningPlan:被选中来执行查询的计划。
executionStats.keysExamined:表示为找到查询中的所有对象二扫描的索引条目数。
executionStats.docsExamined:显示实际扫描的对象数量,而不仅是它们的索引条目。
executionStats.nReturned:显示游标上的条目数量(即返回的条目数量)。
executionStages:提供执行计划的细节。
serverInfo:执行该查询的服务器。
indexFilterSet表示没有使用索引;COLLSCAN表示集合扫描。如果docsExamined显著高于nReturned,那么查询可能需要添加索引。
三.使用索引优化查询
3.1 管理索引
MongoDB的索引用于查询(find、findOne)和排序。如果倾向于在集合中大量使用排序,那么应该根据排序的需求添加索引。
索引最好用在主要为读访问的集合中。如果集合中有过多的索引,它们有可能会对写操作的性能造成负面影响。
目前每个集合最多可以拥有64个索引。
一个查询中只会使用一个索引,所以添加许多小索引通常并不会改善查询性能。复合索引提供了一种减少集合中索引数目的方法,它允许将多个字段结合在一起创建一个索引,所以应该尽量使用符合索引。
除非数据项的列表和排序与索引结构匹配,否则排序不利用复合索引。
MongoDB indexes use a B-tree data structure
这个现实MongoDB索引同关系型数据库一样,也是 B-tree索引
3.1.1 显示索引
> use test
s
switched to db test
>
>
>
> db.t2.getIndexes()
[
[

{

"v" : 2,

"key" : {

"_id" : 1

},

"name" : "_id_",

"ns" : "test.t2"

}
]
]
>
>


3.1.2 创建索引
创建索引语法:
-- 语法
d
db.collection.createIndex( <key and index type specification>, <options> )
-
-- 降序索引
d
db.collection.createIndex( { name: -1 } )

-- t2集合 id文档 创建索引
>
> db.t2.createIndex({id:1})
{
{

"createdCollectionAutomatically" : false,

"numIndexesBefore" : 1,

"numIndexesAfter" : 2,

"ok" : 1
}
}
>
>
>
> db.t2.getIndexes()
[
[

{

"v" : 2,

"key" : {

"_id" : 1

},

"name" : "_id_",

"ns" : "test.t2"

},

{

"v" : 2,

"key" : {

"id" : 1

},

"name" : "id_1",

"ns" : "test.t2"

}
]
]
>
>



-
-- t2集合 name、date两个文档创建复合索引
>
> db.t2.getIndexes()
[
[

{

"v" : 2,

"key" : {

"_id" : 1

},

"name" : "_id_",

"ns" : "test.t2"

},

{

"v" : 2,

"key" : {

"id" : 1

},

"name" : "id_1",

"ns" : "test.t2"

},

{

"v" : 2,

"key" : {

"name" : 1,

"date" : 1

},

"name" : "联合索引",

"ns" : "test.t2"

}
]
]
>
>


3.1.3 删除索引
# 删除一个集合上的所有索引
d
db.t2.dropIndexes();
#
# 删除单个索引(对应于createIndex()函数创建索引的语法)
d
db.t2.createIndex({name:1,date:1},{name : "联合索引"})
d
db.t2.dropIndex({name:1,date:1},{name : "联合索引"})


测试记录:
> db.t2.dropIndexes();
{
{

"nIndexesWas" : 3,

"msg" : "non-_id indexes dropped for collection",

"ok" : 1
}
}
>
> db.t2.getIndexes()
[
[

{

"v" : 2,

"key" : {

"_id" : 1

},

"name" : "_id_",

"ns" : "test.t2"

}
]
]
>
>
>
> db.t2.createIndex({name:1,date:1},{name : "联合索引"})
{
{

"createdCollectionAutomatically" : false,

"numIndexesBefore" : 1,

"numIndexesAfter" : 2,

"ok" : 1
}
}
>
> db.t2.dropIndex({name:1,date:1},{name : "联合索引"})
{
{ "nIndexesWas" : 2, "ok" : 1 }
>
>


3.1.4 重建索引
-- 重建集合上所有索引
d
db.t2.reIndex();


3.2 索引选择三步法
(1)相等测试:按任意顺序把所有相等测试的字段添加到复合索引中。
(2)排序字段:(只有存在多个排序字段,升序/降序才重要)在索引中添加排序字段,其顺序和方向与查询的排序相同。
(3)范围过滤器:首先,为基数最低的字段添加范围过滤器(集合中不同的值最少),接着添加基数次低的范围过滤器,直到基数最高的范围过滤器为止。
如果相等测试或范围过滤器字段没有选择性,就可以省略它们,以减少索引的大小。经验法则是,如果字段没有过滤掉集合中至少90%的文档,就最好在索引中省略它。如果集合上有几个索引,就可能需要提示MongoDB使用正确的索引。
3.3 指定索引选项
3.3.1 后台创建索引
db.t2.createIndex({name:1}, {background:true})



#
# 终止索引进程
d
db.currentOp();
d
db.killOp(<opid>);


测试记录:
> db.t2.createIndex({name:1}, {background:true})



{
{

"createdCollectionAutomatically" : false,

"numIndexesBefore" : 2,

"numIndexesAfter" : 3,

"ok" : 1
}
}
>
>


3.3.2 创建唯一索引
> db.t2.createIndex({id:1}, {unique:true})
{
{

"createdCollectionAutomatically" : false,

"numIndexesBefore" : 3,

"numIndexesAfter" : 4,

"ok" : 1
}
}


3.3.3 创建稀疏索引
> db.t2.createIndex({name:1}, {sparse:true})
{
{

"createdCollectionAutomatically" : false,

"numIndexesBefore" : 1,

"numIndexesAfter" : 2,

"ok" : 1
}
}
>
>


3.3.4 创建部分(条件)索引
> db.t2.createIndex({id:1}, {partialFilterExpression:{ cost: { $gt: 10 } } })
{
{

"createdCollectionAutomatically" : false,

"numIndexesBefore" : 2,

"numIndexesAfter" : 3,

"ok" : 1
}
}
>
>


3.4 使用hint()
hint测试
# 创建索引
d
db.t2.createIndex({"id":1, "name":1});
#
# 加hint使用索引
d
db.t2.find({id: 1, name: 'aaa'}).hint({"id":1, "name":1});
#
# 不使用任何索引
d
db.t2.find({id: 1, name: 'aaa'}).hint({$natural:1})


测试记录:
> db.t2.createIndex({"id":1, "name":1});
{
{

"createdCollectionAutomatically" : false,

"numIndexesBefore" : 3,

"numIndexesAfter" : 4,

"ok" : 1
}
}
>
>
>
>
-
-- 强制不走索引的,明显慢一些
>
> db.t2.find({id: 1, name: 'aaa'}).hint({$natural:1})
{
{ "_id" : ObjectId("5facab82677cc70f7ab9358e"), "id" : 1, "name" : "aaa", "date" : ISODate("2020-11-12T03:26:58.366Z") }
>
>


参考:
1.https://docs.mongodb.com/v4.2/indexes/#default-id-index
2.https://blog.csdn.net/wzy0623/article/details/83060759

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论