当我在 LINQ 查询中切换 Distinct() 和 OrderBy() 时,性能会发生变化吗? [关闭]

Posted

技术标签:

【中文标题】当我在 LINQ 查询中切换 Distinct() 和 OrderBy() 时,性能会发生变化吗? [关闭]【英文标题】:Does the performance change when I switch Distinct() and OrderBy() in a LINQ Query? [closed] 【发布时间】:2021-12-14 21:06:15 【问题描述】:

当我在 LINQ 查询中同时使用 OrderBy()Distinct() 时,我只是在考虑哪个可以提供最佳性能。在我看来,它们的速度是相同的,因为 Distinct() 方法将在内存中使用哈希表,我假设任何 SQL 查询在执行之前都会先由 .NET 进行优化。 我的假设是否正确,或者这两个命令的顺序是否仍然会影响 LINQ 的性能? 至于它是如何工作的……当你构建一个 LINQ 查询时,你基本上是在构建一个表达式树,但还没有执行任何操作。所以调用MyList.Distinct().OrderBy() 只会创建这棵树,但不会执行它。 (它是延迟的。)只有当你调用另一个像ToList()这样的函数时,表达式树才会被执行,并且运行时可以在表达式树被执行之前对其进行优化。

【问题讨论】:

“我假设任何 SQL 查询在执行之前都会先由 .NET 进行优化”——你假设错了。 LINQ 查询将由提供程序进行翻译和优化。它们不会被运行时优化或重写,因为这可能会影响结果的保真度。如果查询以数据库提供程序结束,那么您可能从 LINQ to Objects 如何执行查询中得出的任何结论都是无效的,而数据库引擎的规则适用于该数据库提供程序。 即使使用 LINQ to Objects,.Distinct().OrderBy().OrderBy().Distinct() 也是具有不同结果和不同性能特征的不同操作序列。考虑将两者应用于 10 000 个相同值的集合,然后应用于 10 000 个不同值的集合,以了解原因。 首先定义比赛场地。 LINQ-to-object 和 LINQ to a SQL 后端是完全不同的东西。如果我们不知道我们在说什么,这个问题就无法回答。 延迟执行在这种情况下并不真正相关,除非您计划链接更多可能允许消除 OrderBy() 和/或 Distinct() 的操作(但 LINQ to Objects 没有不是那样工作的;它主要是按顺序执行操作,这里和那里只有一些巧妙的技巧来优化操作组合)。假设提供者以这样一种方式实现:.Distinct() 始终保证其结果已经排序;这样的提供者可以完全消除.OrderBy()。但这是不寻常的,一般无法保证。 底线很简单:如果问题通常适用于 LINQ 提供程序,出于性能原因,.OrderBy().Distinct() 应该始终优于 .Distinct().OrderBy() 是不正确的,反之亦然,因为它取决于提供者。只有当您选择特定的提供程序(无论是 LINQ to Objects、SQL Server 上的实体框架或其他任何东西)时,您才能开始回答有关性能的问题。至于正确性,这很简单:如果你没有在.OrderBy() 中结束事情,你可能没有有序的结果——这是否重要取决于你。 【参考方案1】:

对于 LINQ to 对象,即使我们假设 OrderBy(...).Distinct()Distinct().OrderBy(...) 将返回相同的结果 (which is not guaranteed),性能也将取决于数据。

如果您有大量重复数据 - 首先运行 Distinct 应该会更快。下一个基准测试表明(至少在我的机器上):

public class LinqBench

    private static List<int> test = Enumerable.Range(1, 100)
        .SelectMany(i => Enumerable.Repeat(i, 10))
        .Select((i, index) => (i, index))
        .OrderBy(t => t.index % 10)
        .Select(t => t.i)
        .ToList();

    [Benchmark]
    public List<int> OrderByThenDistinct() => test.OrderBy(i => i).Distinct().ToList();

    [Benchmark]
    public List<int>  DistinctThenOrderBy()=> test.Distinct().OrderBy(i => i).ToList();

在我的 .Net Core 3.1 机器上,它提供:

Method Mean Error StdDev
OrderByThenDistinct 129.74 us 2.120 us 1.879 us
DistinctThenOrderBy 19.58 us 0.384 us 0.794 us

【讨论】:

这个基准测试确实显示出很大的不同,这让我感到惊讶。但是您是否尝试过先使用 DistinctThenOrderBy,然后使用 OrderByThenDistinct 进行相同的基准测试?毕竟可能是数据库服务器一直在缓存数据…… @WimtenBrink 我展示的基准是针对 LINQ to Objects,没有数据库。至于数据库查询 - LINQ 代码将被转换为 SQL,它的转换方式将在很大程度上取决于具体的查询、ORM 和数据库提供程序。然后是数据库引擎,它也可以优化查询。许多未知数让我创建了一个基准。【参考方案2】:

首先,seq.OrderBy(...).Distinct()seq.Distinct().OrderBy(...) 不能保证返回相同的结果,因为 Distinct() may return an unordered enumeration。 MS 实现方便地保留了顺序,但如果您将 LINQ 查询传递给数据库,结果可能会以数据库引擎认为合适的任何顺序返回。

其次,在极端情况下,当您有大量重复(例如,五个值随机重复 1,000,000 次)时,您最好在OrderBy() 之前执行Distinct

长话短说,如果您希望对结果进行排序,请使用Distinct().OrderBy(...),无论性能如何。

【讨论】:

这可能是一个疏忽,除了文档.. LINQ 中的 Distinct().OrderBy() 必须具体化 Distinct 结果并使用 OrderBy 对此进行选择(在 SQL SERver 上)确保结果是有序的。也就是说,你链接了wong文档。 您链接到错误的文档 - 正确的是 docs.microsoft.com/en-us/dotnet/api/…,它专门为 LINQ 处理此问题。您的参考谈论 Enumrable - 这是 Queryable。 @TomTom:Distinct().OrderBy() 不是问题,OrderBy().Distinct() 是。前者保证被订购,后者不是。在 SQL 方面当然也是如此(DISTINCT 没有 ORDER BY 后续没有内在顺序,即使对已经排序的数据进行操作)。 @TomTom LINQ 查询可以引用IEnumerable&lt;T&gt;IQueryable&lt;T&gt;。事实上,当他提到Distinct 的基于哈希的实现时,OP 甚至在他的问题中讨论了内存中的性能。 没有。他说他在内存中使用它并询问 SQL 这意味着 IQueryable 至少对于 SQL 部分,因为否则任何 SQL 优化都没有意义,因为查询已经实现了。【参考方案3】:

我假设任何 SQL 查询都会在 .NET 得到优化之前先进行优化 > 执行。

鉴于以下情况,您认为这将如何发挥作用:

只有 SQL 执行端(服务器)知道这方面的知识(即使用哪些索引)并且有一个查询优化器,它应该根据表的统计信息来优化执行的查询。 您必须非常确定您不会以任何方式更改结果。

抱歉,这没有任何意义 - 如果没有数据库的所有内部详细信息,您可以在 C# 中安全地进行任何优化,因此查询被发送到数据库进行分析。

因此,OrderBy 或 Distinct(尤其是 distinct)会影响性能 - 多少取决于 OrderBy 是否可以依赖索引。

或者这两个命令的顺序是否仍然影响LINQ的性能 一般?

这里变得有趣了(你没有举出例子)。

DISTINCT 和 ORDERBY 在 SQL 中是按特定顺序排列的,无论您在 LINQ 中如何表述它。根据 SQL 定义,只有一种允许的语法。 LINQ 将查询放在一起并对其进行优化。如果您查看语法,则 DISTINCT(至少是 SQL Server 的 SQL 术语)和 OrderBy 有一个特定的位置。

另一边……

.Distinct().OrderBy() 和 .OrderBy().Distinct()

有不同的结果。它们可以在 SQL 中完成(您可以将 Distinct 的输出用作然后订购的虚拟表),但它们具有不同的语义。除非你认为 LINQ 会神奇地读懂你的想法,否则编译器除了假设你有能力编写你所做的事情(只要它是合法的)并按照你给出的顺序执行这些步骤之外,没有任何上下文。

除了:Queryable 中 Distinct 的文档很明显这没有完成:

https://docs.microsoft.com/en-us/dotnet/api/system.linq.queryable.distinct?redirectedfrom=MSDN&view=net-5.0#System_Linq_Queryable_Distinct__1_System_Linq_IQueryable___0__

表示 Distinct 返回一个无序列表。

所以,有一个根本的区别,它们是不一样的。

【讨论】:

您确实意识到,虽然服务器会执行查询,但创建查询的仍然是 LINQ。当它收集数据进行查询时,如果需要,它可以交换两个命令的顺序以提高性能。 @WimtenBrink:执行翻译的是查询提供程序实现,而不是一般的“LINQ”,虽然从技术上来说,像 SQL 提供程序这样的东西可以批量重新安排操作,但实际上它们不会不要打扰,因为这不会加速任何事情,同时会引入相当多的潜在错误。在这种情况下,.Distinct().OrderBy()可能不会重新排列为.OrderBy().Distinct(),因为顺序不会被保留,而相反的操作可能会执行非常不同的操作,抢先猜测引擎是愚蠢的。 “您确实意识到,虽然服务器会执行查询,但创建查询的仍然是 LINQ” - 不。查询是移交给提供者的节点树,LINQ 除了生成过滤树之外不做任何事情。 “当它收集数据以进行查询时,” - 除了不收集数据之外,翻译器也不是,因为这会带来非常显着的开销 - 假设一般假设是它没有在服务器上运行,所以延迟是一个问题。没有“聚会”。

以上是关于当我在 LINQ 查询中切换 Distinct() 和 OrderBy() 时,性能会发生变化吗? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

为啥,当我在 WCF 服务中模拟时,当我尝试运行 LINQ to SQL 查询时,我的服务不能加载 System.Transactions?

LINQ to SQL语句之Select/Distinct和Count/Sum/Min/Max/Avg (转)

使用count distinct在postgres中进行慢速查询

仅基于表的一个字段在 Linq 中区分

linq中分组查询而且获取每个分组中的第一条记录,数据用于分页绑定

Linq 查询在代码中引发超时,但在 LinqPad 上工作正常