【问题标题】:Why is my .NET MongoDb Driver Query Horribly Slow?为什么我的 .NET MongoDb 驱动程序查询非常慢?
【发布时间】:2016-05-24 04:46:28
【问题描述】:

我直接在 Mongo(Robomongo、CLI 等)中对 select all items where field1=x, field2 =y, and field3=z 运行查询,并且对几十万个项目只用了不到一秒钟的时间:

db.items.find( { 
    $and: [ 
        { CreatingOrgId: 1 }, 
        { LocationId: 941 },
        { StatusId: 1}
    ] 
} )

然后我尝试从 C# 驱动程序运行完全相同的东西,但它滞后了(只有“聚合过滤器”代码是相关的,其他一切都是为了上下文):

FilterDefinition<BsonDocument> locationsFilter;  = Builders<BsonDocument>.Filter.Eq("LocationId", 941);
FilterDefinition<BsonDocument> orgFilter = Builders<BsonDocument>.Filter.Eq("CreatingOrgId", 1);
FilterDefinition<BsonDocument> statusFilter = Builders<BsonDocument>.Filter.Eq("StatusId", 1);

FilterDefinition<BsonDocument> aggregateFilter = locationsFilter & statusFilter & orgFilter;

        List<ItemViewModel> stuffList = mongoItemsCollection
                                             .Find(aggregateFilter)
                                             .Project(x => Mapper.Map<BsonDocument, StuffViewModel>(x))
                                             .ToListAsync().Result;

我的错误是什么?以下是 mongo 看到的查询:

编辑:看起来将项目映射到项目对象在某种程度上扼杀了我的查询。如果没有映射,它相当快(大量记录只需几秒钟),如下所示:

    var rawItems = mongoItemsCollection
                             .Find(aggregateFilter)
                             .ToListAsync().Result;

编辑 2:看起来 automapper 是问题的重要部分(获取 bson“项目”对象并将其转换为 .NET 视图模型)。如果有人想回答,我仍然对 .NET --> mongo 查询本身的优化感兴趣(忽略自动映射器部分)。

【问题讨论】:

  • 旁注:完全没有必要使用$anddb.items.find({CreatingOrgId: 1, LocationId: 941, StatusId: 1})。据我所知,这些查询之间的字段顺序是不同的。假设您有一个索引:Order matters。因此,您应该根据索引的顺序重新排列过滤器。
  • 感谢您的回复。在实践中,我实际上必须按 5 个字段进行过滤,所有这些字段都是可选的。所以像 ABCDE、A、BCD、CDE、D 等。我对细节很好奇,因为我不明白 ABCDE 上的索引是否可以处理字段 CD 中的搜索,例如。或者我是否需要每个可能组合的索引。但是,如果我正确理解您的链接,该示例非常清楚地表明 ABCDE 上的索引将处理所有子查询(例如前面提到的“CD”),只要它们按顺序排列即可。另外,是否存在需要 $and 的情况?
  • 您的 C# 代码正在执行 ToListAsync,因此它正在访问所有结果并返回它们。在 CLI 上比较的更等效的测试是.find().toArray() ,否则它只会访问和返回 20 个结果(默认情况下)。如果结果集是“几十万个项目”,那将是一个很大的区别,尤其是如果结果不是仅索引并且必须从磁盘中检索。
  • 看起来映射是杀死它的一部分,你说得有道理,会更新问题。
  • @sheilak 你能详细说明你的答案吗?我的控制器不是异步的,这就是我使用 .Result() 的原因 - 当我查看方法列表时,.toArrayAsync() 不是一个选项。我正在使用 C# 驱动程序 2.0。

标签: mongodb mongodb-query mongodb-.net-driver


【解决方案1】:

假设你在 3 个字段上有一个索引,那么问题是 mongo 和 C# 查询中字段的顺序不同。

C#查询为:LocationId、StatusId、CreatingOrgId

Mongo 查询为:CreatingOrgId、LocationId、StatusId

您可以通过首先启用分析来验证 MongoDB 中的确切查询:

db.setProfilingLevel(2); // Profiles all queries.

然后,使用以下命令查找确切的查询:

db.system.profile.findOne();

【讨论】:

  • 感谢双方的回复。我将让赏金运行一段时间,但我非常感谢关于这个通用 Q 的答案。在这个 iirc 之后做了它,因为我意识到我以后需要理解并且很惊讶它还没有被问到。编辑:我启用了分析 - 这就是我获得屏幕截图的方式。我认为也有一些方法可以直接从 .NET 进行分析,但我无法找到如何轻松地做到这一点,尽管我们在这里有一些回复使其过于复杂(我认为)。
【解决方案2】:

当您在 C# 中运行 .ToListAsync() 时,将访问并返回整个查询结果。

而当您在命令行上运行 .find() 时,仅返回 20(默认情况下)。

在命令行上更等效的测试是.find().toArray(),它也将访问并返回所有结果。或者你可以在你的 C# 查询中设置一个限制。

如果您的完整结果集比 shell 批量大小大很多,这可能会导致您的结果存在一些差异。如果不覆盖查询(即查询中的所有字段和返回的所有字段不在同一个索引中),并且如果访问的数据不在内存中而是从磁盘访问,则这种差异会更糟。

【讨论】:

    猜你喜欢
    • 2011-10-19
    • 2015-11-01
    • 1970-01-01
    • 2013-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-14
    • 2021-03-12
    相关资源
    最近更新 更多