猿教程 Logo

.Net连接MongoDb:关于查询计划的其他部分

介绍

在上一篇文章中,我们研究了创建索引时可以提供的几个选项。 使用唯一索引,我们可以确保只能在属性上插入唯一的值。 稀疏索引通常与唯一索引结合使用。 它使我们能够索引不是每个文档都有的字段,即字段为空的字段。 在这种情况下,这些文件将不会被索引。 最后我们来看前台与后台索引的创建。 如果在前台线程上创建了一个索引,那么它相对较快,但也阻止了数据库中的所有读写器和写入程序。 另一方面,后台索引创建速度较慢,但不阻止其他要读写的线程。

在这篇文章中,我们将介绍与说明函数相关的几件事情。

查询计划模式

现在我们已经看到了查询计划员的行动了。 我希望你开始明白如何读取返回的值。 如果执行查询需要多个步骤,那么我们开始读取描述第一阶段的最内部文档的输出,例如。 索引扫描。 然后我们按照阶段向外,我们将看到返回和扫描的文档数量减少。

我们来看一个例子。 我们在这个系列中创造了几次人物创作,但我们将再次创建一个:

use model
db.people.drop()
db.people.insert({"name" : "John", "age" : 34, "sports" : ["football", "badminton", "squash"], "grades" : [50, 60,45, 43]})
db.people.insert({"name" : "Mary", "age" : 28, "sports" : ["cricket", "badminton"], "grades" : [90, 60, 53]})
db.people.insert({"name" : "William", "age" : 29, "sports" : ["tennis", "basketball", "squash"], "grades" : [30, 40,64, 90, 78]})
db.people.insert({"name" : "Bridget", "age" : 45, "sports" : ["baseball", "athletics", "running"], "grades" : [50, 90,95, 40]})
db.people.insert({"name" : "Susan", "age" : 40, "sports" : ["football", "boxing", "karate"], "grades" : [70, 80,78]})
db.people.insert({"name" : "John", "age" : 39, "sports" : ["cyckling", "swimming", "basketball"], "grades" : [10, 90,60, 50]})
db.people.insert({"name" : "John", "age" : 31, "sports" : ["handball", "skating", "baseball"], "grades" : [55, 65]})
db.people.insert({"name" : "Joanna", "age" : 28, "sports" : ["judo", "karate", "running"], "grades" : [88, 77,66, 55]})
db.people.insert({"name" : "Christina", "age" : 27, "sports" : ["running", "swimming", "badminton"], "grades" : [51, 61,46, 44]})
db.people.insert({"name" : "Homer", "age" : 48, "sports" : ["football", "skating", "boxing"], "grades" : [45, 47,90, 99]})

我们在name属性上创建一个索引:

db.people.createIndex({"name" : 1})

我们现在将搜索约翰年龄超过32岁的所有人。 我们还将以默认模式运行explain功能,即没有任何参数。 该基本模式称为“查询计划器”模式:

db.people.explain().find({"name" : "John", "age" : {$gt : 32}})

这种基本模式不会给我们太多的信息。 我们至少知道我们的名字索引是在第一阶段使用的。 称为FETCH的第二个阶段必须在年龄域执行另一次扫描。 检查“过滤器”属性以验证:

"winningPlan" : {
                        "stage" : "FETCH",
                        "filter" : {
                                "age" : {
                                        "$gt" : 32
                                }
                        },
                        "inputStage" : {
                                "stage" : "IXSCAN",
                                "keyPattern" : {
                                        "name" : 1
                                },
                                "indexName" : "name_1",
                                "isMultiKey" : false,
                                "isUnique" : false,
                                "isSparse" : false,
                                "isPartial" : false,
                                "indexVersion" : 1,
                                "direction" : "forward",
                                "indexBounds" : {
                                        "name" : [
                                                "[\"John\", \"John\"]"
                                        ]
                                }
                        }
                }

第二级别的粒度称为执行统计模式。 当我们传递一个真正的布尔参数时,我们已经看到了它的输出。 我们也可以这样调用:

db.people.explain("executionStats").find({"name" : "John", "age" : {$gt : 32}})

这个级别的输出告诉我们在哪个阶段扫描了多少个文档。 索引扫描返回3个文档,即由名称索引覆盖的文档,因为有3个名称为“John”的文档。 然后,抓取阶段检查这3个文档,并在年龄字段上运行查询。 最初的3个中的2个由提取阶段转动。 以下是相关部分:

"executionStats" : {
                "executionSuccess" : true,
                "nReturned" : 2,
                "executionTimeMillis" : 0,
                "totalKeysExamined" : 3,
                "totalDocsExamined" : 3,
                "executionStages" : {
                        "stage" : "FETCH",
                        "filter" : {
                                "age" : {
                                        "$gt" : 32
                                }
                        },
                        "nReturned" : 2,

"inputStage" : {
                                "stage" : "IXSCAN",
                                "nReturned" : 3,

第三级的粒度称为所有计划执行模式。 如果最初有多个可能的查询计划才能得到结果集,这是有用的。 在这种情况下,将有一个获胜计划和一个或多个被拒绝的计划。 MongoDb并行运行这些选项,并决定哪个计划将最快地提取结果。

为了产生一些被拒绝的计划,我们在运动阵列上插入一个索引:

db.people.createIndex({"sports" : 1})

现在我们来运行一个具有修改查询的完整查询计划程序:

db.people.explain("allPlansExecution").find({"name" : "John", "age" : {$gt : 32}, "sports" : "swimming"})

将有一个长的文件与获胜计划,应该有2个被拒绝的计划。 获胜计划使用体育阵列指数。 第一个拒绝的索引使用名称索引。 第二个被拒绝的计划在称为“AND_SORTED”的阶段执行2次索引搜索。

获奖计划的执行情况表明,体育指数扫描返回2个文件,其中2个文件,其中运动阵列包括“游泳”,约翰和克里斯蒂娜。 然后,抓取阶段检查这两个文档,并返回其中的1个文件:

"executionStats" : {
                "executionSuccess" : true,
                "nReturned" : 1,
                "executionTimeMillis" : 2,
                "totalKeysExamined" : 2,
                "totalDocsExamined" : 2,


"inputStage" : {
                                "stage" : "IXSCAN",
                                "nReturned" : 2,

为什么其他2个计划被拒绝? 这里最重要的属性是一个称为totalKeysExpected的属性。 这是为了执行查询而必须检查的索引条目的数量。 体育指数计划只需检查2个键。 输出中名为“allPlansExecution”的部分显示被拒绝的计划中检查的密钥数:

"allPlansExecution" : [
                        {
                                "nReturned" : 1,
                                "executionTimeMillisEstimate" : 0,
                                "totalKeysExamined" : 3,
                                "totalDocsExamined" : 3,

 {
                                "nReturned" : 1,
                                "executionTimeMillisEstimate" : 0,
                                "totalKeysExamined" : 3,
                                "totalDocsExamined" : 1,

顶部显示名称索引计划,底部显示具有2个索引搜索的路径。 两人都必须检查3个索引键,与获胜计划中的2个相比。 然后,MongoDb决定,体育指数路径是最快的,而另外两个则被拒绝。

我们最后一件事可以在这里查看与提供结果集的扫描文档数量有关。 用explain函数运行名称查询...

db.people.explain("executionStats").find({"name" : "John"})

...会告诉我们,共扫描3份文件:

"executionStats" : {
                "executionSuccess" : true,
                "nReturned" : 3,
                 "totalDocsExamined" : 3,

索引扫描返回3个键,MongoDb检查索引键指向的三个文档。 没有索引MongoDb应该检查所有10个文档的集合,所以这是一个很大的改进。 但是,有些情况下我们可以做更多的事情。 这就是索引本身覆盖整个查询的时候。

在上述查询中,MongoDb必须检查该集合中的3个文档,因为我们没有为要返回的属性指定任何过滤器。 MongoDb不得不从文件中收集姓名,年龄,体育,成绩和_id字段。

说我们只想看到结果集中的名称字段。 我们将修改我们的搜索,并提取名称字段包含“r”的那些文档:

db.people.find({"name" : /r/}, {"_id" : 0, "name" : 1})

我们得到4个文件:

{ "name" : "Bridget" }
{ "name" : "Christina" }
{ "name" : "Homer" }
{ "name" : "Mary" }

让我们看看查询策划人员告诉我们的内容:

db.people.explain("executionStats").find({"name" : /r/}, {"_id" : 0, "name" : 1})

我们可以看到,MongoDb不需要检查单个文档。 名称索引足以覆盖查询本身,因为我们只想查看名称字段:

"executionStats" : {
                "executionSuccess" : true,
                "nReturned" : 4,
                "executionTimeMillis" : 0,
                "totalKeysExamined" : 10,
                "totalDocsExamined" : 0,

所以真的值得检查你需要哪些属性。 它可以使索引完全覆盖查询,这是绝对最快的检索结果集的方法。 这些查询在MongoDb中被称为覆盖查询。

让我们完成这篇文章,并提供一个关于如何使用totalIndexSize()函数提取集合的总索引大小的提示:

db.people.totalIndexSize()

它给出49152个字节。 如果要查看详细信息,请调用stats功能:

db.people.stats()

...有一个索引大小的部分:

"nindexes" : 3,
        "totalIndexSize" : 49152,
        "indexSizes" : {
                "_id_" : 16384,
                "name_1" : 16384,
                "sports_1" : 16384
        },

我们将在下一篇文章中查看文本索引。


版权声明:本站所有教程均为本站原创或翻译,转载请注明出处,请尊重他人劳动果实。请记住本站地址:www.yuanjiaocheng.net (猿教程) 作者:卿文刚
本文标题: C#环境
本文地址:http://www.yuanjiaocheng.net/CsharpMongo/36.html