有关某些背景,请参见以下帖子:MongoDB C# Driver - Return last modified rows only
在运行了将近两年的代码之后,最近我们一直遇到性能问题,而我一直说代码不是问题,基础设施坚持认为这是因为我正在进行全表扫描。
问题在于问题是特定于环境的。我们的质量保证环境始终如梦似幻,但Dev和Prod有时非常慢,而其他时候则很好-这非常不稳定。它们具有相同的数据和代码,但Dev和Prod具有另一个也在数据库上运行的应用程序。
我的数据有一个ID和一个_id(或AuditId)-我按ID对数据进行分组,然后返回该记录的最后一个_id,但未删除该记录。我们有多个具有相同ID的历史记录,我想返回最后一个(请参阅原始帖子)。
所以我有以下方法:
private static FilterDefinition<T> ForLastAuditIds<T>(IMongoCollection<T> collection) where T : Auditable, IMongoAuditable
{
var pipeline = new[] { new BsonDocument { { "$group", new BsonDocument { { "_id", "$Id" }, { "LastAuditId", new BsonDocument { { "$max", "$_id" } } } } } } };
var lastAuditIds = collection.Aggregate<Audit>(pipeline).ToListAsync().Result.ToList().Select(_ => _.LastAuditId);
var forLastAuditIds = Builders<T>.Filter.Where(_ => lastAuditIds.Contains(_.AuditId) && _.Status != "DELETE");
return forLastAuditIds;
}
此方法由以下方法调用,该方法接受一个表达式,该表达式将附加到由ForLastAuditIds创建的FilterDefinition。
protected List<T> GetLatest<T>(IMongoCollection<T> collection,
Expression<Func<T, bool>> filter, ProjectionDefinition<T, T> projection = null,
bool disableRoleCheck = false) where T : Auditable, IMongoAuditable
{
var forLastAuditIds = ForLastAuditIds(collection);
var limitedList = (
projection != null
? collection.Find(forLastAuditIds & filter, new FindOptions()).Project(projection)
: collection.Find(forLastAuditIds & filter, new FindOptions())
).ToListAsync().Result.ToList();
return limitedList;
}
现在,所有这些工作都非常好,并且可以被我所有调用集合的代码重复使用,但是这个特定集合比其他集合大很多,而我们在该集合上的运行速度越来越慢。
我的问题是:有没有办法让我将聚合器和Filter Builder合并起来,以返回单个FilterDefinition,而无需先运行全表扫描就可以使用它吗?
我真的希望我有道理。
答案 0 :(得分:1)
假设我完全了解您想要什么,这应该很简单:
首先,在LastAuditId
字段上放置一个降序索引:
db.collection.createIndex{ "LastAuditId": -1 /* for sorting */ }
甚至扩展索引以覆盖过滤器中的其他字段:
db.collection.createIndex{ "Status": 1, "LastAuditId": -1 /* for sorting */ }
但是,请确保您了解how indexes can/cannot support certain queries。并且始终使用explain()来查看实际情况。
下一步是要意识到,您必须始终在第一步中尽可能多地进行过滤,以减少所需的排序量。
因此,如果您需要如果您的业务需求允许,请首先使用Name
进行过滤,然后将其作为第一步。但是请注意,从一开始就进行过滤会改变您的语义,因为每个Id
都将通过先前的$match
阶段,从而获得每个Id
的最后修改的文档,而不是每个{ {1}}恰好也通过了以下$match
阶段。
无论如何,最重要的是,一旦您有了一个排序集,就可以通过将$group
与$first
一起使用来轻松,快速地获取最新的完整文档,该文档将具有正确的索引,不再进行集合扫描(现在将是索引扫描,因此速度更快)。
最后,您希望利用$$ROOT变量通过C#运行以下MongoDB查询的等效项,以避免再次查询(发布{{1后,我可以为您提供所需的代码}},Audit
和Auditable
类型,以及任何潜在的序列化器/约定):
IMongoAuditable
最后,请注意,迁移到最新版本的MongoDB可能是一个好主意,因为他们目前正在努力优化诸如您的聚合案例。这个:https://jira.mongodb.org/browse/SERVER-9507