为什么我的 .NET MongoDb 驱动程序查询非常慢?
Why is my .NET MongoDb Driver Query Horribly Slow?
我 运行 直接在 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# 驱动程序的完全相同的东西,但它滞后了(只有 "Aggregate filter" 代码是相关的,其他一切都是为了上下文):
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 "item" 对象并将其转换为 .NET 视图模型)。我仍然对 .NET 的优化感兴趣 --> mongo 查询本身(忽略自动映射器部分),如果有人想回答的话。
假设您有 3 个字段的索引,那么问题是 mongo 和 C# 查询之间的字段顺序不同。
C# 查询是:LocationId、StatusId、CreatingOrgId
Mongo 查询是:CreatingOrgId、LocationId、StatusId
您可以通过首先启用分析来验证 MongoDB 中的确切查询:
db.setProfilingLevel(2); // Profiles all queries.
然后,使用以下方法找到确切的查询:
db.system.profile.findOne();
当您在 C# 中 运行 .ToListAsync()
时,将访问和 return 编辑查询的全部结果。
而当您在命令行上 运行 .find()
时,只有 20(默认)被 returned。
命令行上更等效的测试是 .find().toArray()
,它也将访问和 return 所有结果。或者您可以在 C# 查询中设置一个限制。
如果您的完整结果集比 shell 批次大小大很多,这可能是您的结果存在差异的部分原因。如果查询未被覆盖(即查询中的所有字段和 returned 的所有字段不在同一索引中)并且如果访问的数据不在内存中但访问从磁盘。
我 运行 直接在 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# 驱动程序的完全相同的东西,但它滞后了(只有 "Aggregate filter" 代码是相关的,其他一切都是为了上下文):
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 "item" 对象并将其转换为 .NET 视图模型)。我仍然对 .NET 的优化感兴趣 --> mongo 查询本身(忽略自动映射器部分),如果有人想回答的话。
假设您有 3 个字段的索引,那么问题是 mongo 和 C# 查询之间的字段顺序不同。
C# 查询是:LocationId、StatusId、CreatingOrgId
Mongo 查询是:CreatingOrgId、LocationId、StatusId
您可以通过首先启用分析来验证 MongoDB 中的确切查询:
db.setProfilingLevel(2); // Profiles all queries.
然后,使用以下方法找到确切的查询:
db.system.profile.findOne();
当您在 C# 中 运行 .ToListAsync()
时,将访问和 return 编辑查询的全部结果。
而当您在命令行上 运行 .find()
时,只有 20(默认)被 returned。
命令行上更等效的测试是 .find().toArray()
,它也将访问和 return 所有结果。或者您可以在 C# 查询中设置一个限制。
如果您的完整结果集比 shell 批次大小大很多,这可能是您的结果存在差异的部分原因。如果查询未被覆盖(即查询中的所有字段和 returned 的所有字段不在同一索引中)并且如果访问的数据不在内存中但访问从磁盘。