【问题标题】:CosmosDB: Linq vs SqlQuerySpec Performance when querying CosmosDBCosmosDB:查询 CosmosDB 时的 Linq 与 SqlQuerySpec 性能
【发布时间】:2018-08-29 15:37:24
【问题描述】:

在查询 CosmosDB 特定文档时,我经常执行 LINQ 谓词。但是,今天我用 10 万多个文档填充了我的 CosmosDB。性能非常缓慢。由于 Azure 门户中的 SQL 查询明显更快,我尝试使用 SqlQuerySpec。瞧!它的工作速度要快得多。

谁能告诉我在 CosmosDB 中使用 Linq 谓词时幕后发生了什么以及为什么它会减慢我的查询速度?

以下代码用于我获取文档的方法中。 注意:在这种情况下,id 是分区键。

        var collectionUri = UriFactory.CreateDocumentCollectionUri(CDBdatabase, CDBcollection);

        var sqlStatement = new SqlQuerySpec
        {
            QueryText = "SELECT * FROM c where c.id = @id",
            Parameters = new SqlParameterCollection()
                {
                          new SqlParameter("@id", consumerId),
                },
        };

        IDocumentQuery<T> query = documentClient.CreateDocumentQuery<T>(
            collectionUri,
            sqlStatement,
            .AsDocumentQuery();

        List<ConsumerDetails> results = new List<ConsumerDetails>();
        while (query.HasMoreResults)
        {
            results.AddRange(await query.ExecuteNextAsync<ConsumerDetails>());
        }

        return results.FirstOrDefault();

对比,较慢的代码:

        return documentClient.CreateDocumentQuery<ConsumerDetails>(
            collectionUri,
            .Where(f => f.Id == consumerId).AsEnumerable().FirstOrDefault();

【问题讨论】:

  • 与您的问题稍微无关,但是,如果您真的是通过文档的 id 查询,那么 ReadDocument() 总是比查询快(例如,1 RU 用于 1KB 文档),因为它不需要经过查询引擎。
  • 您可以在从 LINQ 链获得的 IQueryable 上调用 ToString() 并检查手动构建的查询与来自 CosomsDB linq 提供程序的查询之间的区别。
  • 好吧,有几件事缺少给出一个好的答案。你能发布你的ConsumerDetails DTO 的样子吗?您是否尝试在 LINQ 方法中将分区键值作为 FeedOptions 对象的一部分提供?您是否尝试在 linq 查询上执行 .AsDocumentQuery 和 ExecuteNextAsync?

标签: c# linq azure azure-cosmosdb


【解决方案1】:

答案在于你的查询方式。

您发布的两个代码段不一样。

为了使它们相同,第二个必须如下所示:

var collectionUri = UriFactory.CreateDocumentCollectionUri(CDBdatabase, CDBcollection);

var query = documentClient.CreateDocumentQuery<ConsumerDetails>(
    collectionUri)
    .Where(f => f.Id == consumerId)
    .AsDocumentQuery();

List<ConsumerDetails> results = new List<ConsumerDetails>();
while (query.HasMoreResults)
{
    results.AddRange(await query.ExecuteNextAsync<ConsumerDetails>());
}

return results.FirstOrDefault();

在这两种情况下,您将对 CosmosDB 执行 SQL。 但是,在 LINQ 的情况下,LINQ 转换器将启动将您的表达式转换为 SQL 查询。

同样在 SQL 示例中,因为您直接指向也是分区键的小写 id(即 CosmosDB id),所以 CosmosDB 将识别这一点并将跨分区一的查询限制为特定于分区的分区,使其更快,更便宜。 .Where(f =&gt; f.Id == consumerId)(带有大写的 Id)一旦通过 LINQ 提供程序,将被转换为 SELECT * FROM c where c.Id = consumerId,除非有 JsonAttribute("id") 装饰 Id 属性。这意味着您需要在FeedOptions 中为查询提供PartitionKey 值。

【讨论】:

  • 嗨,我已经根据您和@David Mokogon(在上面的 cmets 中)的反馈更新并比较了我的代码。我现在意识到这不是完全一样的逻辑。是的,我也忘了提到我确实使用 JsonAttributes 作为 ID。但是,这使我想到了以下问题:对 AsEnumerable() 执行 LINQ 查询与执行 AsDocumentQuery + ExecuteNextAsync 是否存在任何性能问题。关于我如何/何时向 CosmosDB 发出请求,AsEnumerable 的幕后发生了什么?
  • Ofc 有。 AsEnumerable().FirstOrDefault(); 会先同步获取与谓词匹配的所有文档,然后获取第一个或默认值。 AsDocumentQuery + ExecuteNextAsync 将使用 CosmosDB 的正确异步分页,因此您的应用程序不会被锁定。
猜你喜欢
  • 1970-01-01
  • 2022-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多