【问题标题】:MongoDB C# driver fast on take(1) but slow on take(2)MongoDB C# 驱动程序执行速度快(1) 但执行速度慢(2)
【发布时间】:2013-08-27 13:43:19
【问题描述】:

我第一次使用 MongoDB C# 驱动程序,我发现一些奇怪的性能结果。 当我查询一个包含 300 万条记录的集合时,排序和 .Take(1) 响应几乎是瞬时的(3 毫秒)。但是当我 .Take(2) 处理同一个查询时,最多需要 10 秒。 正确的索引已经到位,它是一个包含测试数据的非常简单的集合。

MongoClient client = new MongoClient();
MongoServer server = client.GetServer();

var database = server.GetDatabase("db_name");
var collection = database.GetCollection<MyType>("collection_name");

var query = from c in collection.AsQueryable<MyType>()
            where c.SearchString.Contains(searchString)
            orderby c.SearchString
            select c.SearchString;

List<string> results = query.Take(2).ToList();

【问题讨论】:

  • 我想知道为什么take(1) 很快:Contains() 转换为正则表达式,而无根正则表达式查询不能使用索引,所以它们非常慢。你能提供explain()的结果吗?
  • 我从未使用过 MongoDB,但我的猜测是:OrderBy+Take(1) 被翻译成类似Max(或Min)的东西。
  • 使用调试工具(不确定与 mongodb 有什么关系)查看在这两种情况下实际在数据库上执行的查询。如果有一个比另一个更有效的翻译(即,如果有一个操作需要第一个,但不是只需要两个,那么Take(2) 只是在 C# 应用程序中提取所有数据和过滤)那么这可能就是问题所在。
  • 访问docs.mongodb.org/manual/tutorial/manage-the-database-profiler页面,如上所述,尝试分析您的查询,以便在数据库上执行实际查询。
  • 尝试搜索集合中不存在的字符串。我敢打赌,Take(1) 在这种情况下会很慢。

标签: c# performance linq mongodb


【解决方案1】:

MongoDB C# Driver 会将 string.Contains 方法转换为正则表达式。所以c.SearchStringContains("abc") 会被翻译成:

{ SearchString : /abc/ }

但是,MongoDB 只能在“以”开头的正则表达式上使用索引。引用自documentation

$regex 只能在正则表达式时有效地使用索引 在字符串的开头(即 ^)有一个锚点,并且是 区分大小写的匹配。此外,虽然 /^a/、/^a./ 和 /^a.$/ 匹配等效字符串,它们具有不同的性能 特征。所有这些表达式都使用索引,如果 存在适当的索引;但是,/^a./ 和 /^a.$/ 速度较慢。 /^a/ 匹配前缀后可以停止扫描。

我怀疑如果您在查询中使用explain command,您会发现包含SearchString 字段的索引没有得到有效使用。

我认为Take(1)Take(2) 快的原因可能是您的SearchString 索引仅用于查询的排序部分,并且第一个匹配发生在很早之前B树步行。第二次出现可能发生在 B-Tree walk 的更晚时间,从而导致更高的nscan(服务器扫描以查找结果的文档数)。

要解决此问题并能够使用索引,我建议使用keyword search approach;或者,如果您有 v2.4+,您可以尝试text search 功能。

【讨论】:

    猜你喜欢
    • 2015-11-20
    • 1970-01-01
    • 1970-01-01
    • 2015-06-27
    • 1970-01-01
    • 1970-01-01
    • 2018-07-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多