0

作为概念证明,我已将约 5400 万条记录加载到 mongodb 中。目的是考察mongodb的查询速度。

我使用以下类来存储数据:

[BsonDiscriminator("Part", Required = true)]
public class Part
{
   [BsonId]
    public ObjectId Id { get; set; }
   [BsonElement("pgc")]
   public int PartGroupCode { get; set; }
   [BsonElement("sc")]
   public int SupplierCode { get; set; }
   [BsonElement("ref")]
   public string ReferenceNumber { get; set; }

   [BsonElement("oem"), BsonIgnoreIfNull]
   public List<OemReference> OemReferences { get; set; }

   [BsonElement("alt"), BsonIgnoreIfNull]
   public List<AltReference> AltReferences { get; set; }

   [BsonElement("crs"), BsonIgnoreIfNull]
   public List<CrossReference> CrossReferences { get; set; }

   [BsonElement("old"), BsonIgnoreIfNull]
   public List<FormerReference> FormerReferences { get; set; }

   [BsonElement("sub"), BsonIgnoreIfNull]
   public List<SubPartReference> SubPartReferences { get; set; }
}

我创建了以下索引:

  • ref、sc、pgc 上的复合索引
  • oem.refoem 上的升序索引
  • alt.refalt 上的升序索引
  • crs.refcrs 上的升序索引
  • old.refold 上的升序索引
  • sub.refsub 上的升序索引

我执行以下查询来测试性能:

var searchValue = "345";
var start = DateTime.Now;
var result1 = collection.AsQueryable<Part>().OfType<Part>().Where(part => part.ReferenceNumber == searchValue);
long count = result1.Count();
var finish = DateTime.Now;

start = DateTime.Now;
var result2 = collection.AsQueryable<Part>().OfType<Part>().Where(part =>
    part.ReferenceNumber.Equals(searchValue) ||
    part.OemReferences.Any(oem => oem.ReferenceNumber.Equals(searchValue)) ||
    part.AltReferences.Any(alt => alt.ReferenceNumber.Equals(searchValue)) ||
    part.CrossReferences.Any(crs => crs.ReferenceNumber.Equals(searchValue)) ||
    part.FormerReferences.Any(old => old.ReferenceNumber.Equals(searchValue))
    );
count = result2.Count();
finish = DateTime.Now;

start = DateTime.Now;
var result3 = collection.AsQueryable<Part>().OfType<Part>().Where(part =>
    part.ReferenceNumber.StartsWith(searchValue) ||
    part.OemReferences.Any(oem => oem.ReferenceNumber.StartsWith(searchValue)) ||
    part.AltReferences.Any(alt => alt.ReferenceNumber.StartsWith(searchValue)) ||
    part.CrossReferences.Any(crs => crs.ReferenceNumber.StartsWith(searchValue)) ||
    part.FormerReferences.Any(old => old.ReferenceNumber.StartsWith(searchValue))
    );
count = result3.Count();
finish = DateTime.Now;

var regex = new Regex("^345"); //StartsWith regex
start = DateTime.Now;
var result4 = collection.AsQueryable<Part>().OfType<Part>().Where(part =>
    regex.IsMatch(part.ReferenceNumber) ||
    part.OemReferences.Any(oem => regex.IsMatch(oem.ReferenceNumber)) ||
    part.AltReferences.Any(alt => regex.IsMatch(alt.ReferenceNumber)) ||
    part.CrossReferences.Any(crs => regex.IsMatch(crs.ReferenceNumber)) ||
    part.FormerReferences.Any(old => regex.IsMatch(old.ReferenceNumber))
    );
count = result4.Count();
finish = DateTime.Now;

结果不是我所期望的:

  • 在 345 条上搜索 1 条结果:3 条记录 (00:00:00.3635937)
  • 在 345 条结果中搜索 2:58 条记录 (00:00:00.0671566)
  • 在 345 条结果中搜索 3:6189 条记录 (00:01:17.6638459)
  • 在 345 条上搜索 4 条结果:6189 条记录 (00:01:17.0727802)

为什么 StartsWith 查询(3 和 4)这么慢?StartsWith 查询性能决定成败。

我是否创建了错误的索引?任何帮助表示赞赏。

将 mongodb 与 10gen C# 驱动程序一起使用

更新:查询从 Linq 转换为 MongoDB 查询的方式对性能非常重要。我再次构建相同的查询(如 3 和 4),但使用 Query 对象:

var query5 = Query.And(
    Query.EQ("_t", "Part"),
    Query.Or(
    Query.Matches("ref", "^345"),
    Query.Matches("oem.refoem", "^345"),
    Query.Matches("alt.refalt", "^345"),
    Query.Matches("crs.refcrs", "^345"),
    Query.Matches("old.refold", "^345")));

start = DateTime.Now;
var result5 = collection.FindAs<Part>(query5);
count = result5.Count();
finish = DateTime.Now;

此查询的结果在 00:00:00.4522972 返回

查询翻译为 command: { count: "PSG", query: { _t: "Part", $or: [ { ref: /^345/ }, { oem.refoem: /^345/ }, { alt.refalt: /^345/ }, { crs.refcrs: /^345/ }, { old.refold: /^345/ } ] } }

与查询 3 和 4 相比,差异很大: command: { count: "PSG", query: { _t: "Part", $or: [ { ref: /^345/ }, { oem: { $elemMatch: { refoem: /^345/ } } }, { alt: { $elemMatch: { refalt: /^345/ } } }, { crs: { $elemMatch: { refcrs: /^345/ } } }, { old: { $elemMatch: { refold: /^345/ } } } ] } }

那么为什么查询 3 和 4 不使用索引呢?

4

1 回答 1

1

索引文档

每个查询(包括更新操作)都使用一个且仅一个索引。

换句话说,MongoDB 不支持索引交集。因此,创建大量索引是没有意义的,除非有查询使用这个索引并且只使用这个索引。另外,请确保您在Count()这里调用了正确的方法。如果您调用 linq-to-object 扩展(IEnumerable'Count()扩展而不是MongoCursor' Count,它实际上必须获取和水合所有对象)。

将它们放入单个多键索引中可能更容易,如下所示:

{ 
    "References" : [ { id: new ObjectId("..."), "_t" : "OemReference", ... }, 
                     { id: new ObjectId("..."), "_t" : "CrossReferences", ...} ],
    ...
}

在哪里References.id被索引。现在,查询db.foo.find({"References.id" : new ObjectId("...")})将自动搜索引用数组中的任何匹配项。由于我假设必须区分不同类型的引用,因此使用鉴别器是有意义的,因此驱动程序可以支持多态反序列化。在 C# 中,您可以这样声明

[BsonDiscriminator(Required=true)]
[BsonKnownTypes(typeof(OemReference), typeof(...), ...)]
class Reference { ... }

class OemReference : Reference { ... }

驱动程序将自动序列化名为 的字段中的类型名称_t。如果需要,可以根据您的需要调整该行为。

另请注意,缩短属性名称将减少存储需求,但不会影响索引大小。

于 2013-06-05T08:54:48.757 回答