在多个字段上使用mongodb索引的最佳做法

时间:2018-10-09 20:32:50

标签: c# mongodb mongodb-.net-driver

在mongodb中,我有一个特定的集合(发票),在我们的整个应用程序中,我们查询发票并按许多不同的字段进行过滤,如下所示:

public static List<Invoice> FindAll(string userId, Enums.InvoiceType? type = null, string propertyId = null, 
            string tenantId = null, string landlordId = null, string ownerUserId = null, 
            bool? isClosed = null, bool? autoGenerated = null, DateTime? startDate = null, DateTime? endDate = null,
            int? skip = null, int? take = null) {
            var builder = Filter;
            var filters = builder.Eq("UserId", userId.ToObjectId());
            if (type.HasValue)
                filters = filters & builder.Eq("Type", type.Value);
            if (!String.IsNullOrEmpty(propertyId))
                filters = filters & builder.Eq("PropertyId", propertyId.ToObjectId());
            if (!String.IsNullOrEmpty(tenantId))
                filters = filters & builder.Eq("TenantId", tenantId.ToObjectId());
            if (!String.IsNullOrEmpty(landlordId))
                filters = filters & builder.Eq("LandlordId", landlordId.ToObjectId());
            if (!String.IsNullOrEmpty(ownerUserId))
                filters = filters & builder.Eq("OwnerUserId", ownerUserId.ToObjectId());
            if (isClosed.HasValue)
                filters = filters & builder.Eq("IsClosed", isClosed.Value);
            if (autoGenerated.HasValue)
                filters = filters & builder.Eq("AutoGenerated", autoGenerated.Value);
            if (startDate.HasValue)
                filters = filters & builder.Gte("DueDate", startDate.Value);
            if (endDate.HasValue)
                filters = filters & builder.Lte("DueDate", endDate.Value);

            var result = Collection().Find(filters)
                .Sort(Builders<Invoice>.Sort.Descending("DueDate"));
            if (skip.HasValue)
                result.Skip(skip.Value);
            if (take.HasValue)
                result.Limit(take.Value);
            return result.ToList();
        }

因为我们要过滤的字段太多,所以我为每个可过滤的字段创建了单独的索引:

db.Invoice.ensureIndex( { UserId: 1 } );
db.Invoice.ensureIndex( { Type: 1 } );
db.Invoice.ensureIndex( { PropertyId: 1 } );
db.Invoice.ensureIndex( { TenantId: 1 } );
db.Invoice.ensureIndex( { LandlordId: 1 } );
db.Invoice.ensureIndex( { OwnerUserId: 1 } );
db.Invoice.ensureIndex( { DueDate: 1 } );
db.Invoice.ensureIndex( { AutoGenerated: 1 } );

但是,今天我注意到,按UserId,Type和TenantId(在一个查询中)进行过滤时,大约需要750毫秒。一旦创建以下索引:

db.Invoice.ensureIndex( { UserId: 1, Type: 1, TenantId: 1 } );

...查询开始耗时约15ms。很明显,当查询在多个字段上进行过滤(使用多个索引)时,在将要过滤的每个单独的字段上创建索引是不起作用的。但是,我不可能为将要执行的查询的每个排列都创建索引。

解决方案是什么?我应该在每个可过滤字段上只有一个索引吗? (我必须进行测试以查看其性能,但这似乎是错误的。)

1 个答案:

答案 0 :(得分:0)

进行任何Find操作时要牢记的一个好习惯是根据您的Find的操作编制索引。

您上面提到的您的Find最有可能进行集合扫描,因为它无法从集合中找到合适的索引来使用,并且必须遍历整个集合来满足您的请求操作,因此为什么性能会变慢。

我的建议是针对发票收据拖曳所有查找的代码库,并为其设置相关索引。这样,在Find操作运行时,mongodb将使用最合适的索引来满足该操作。根据Mongo的Indexing Strategies

的建议,

在您的情况下,我建议您使用Compound Index,因为您要查找多个字段。还要注意索引的顺序,mongo将查看索引中的第一项。我建议您将查询顺序与索引策略相匹配,以获得最佳性能。

如果要避免为Find专门设置索引,建议您使用复合索引,并仅对通常在代码库中查询的最常见属性进行索引。他们能够满足更广泛的查询。