Linq 查询运行缓慢,但查询单独运行很快

Posted

技术标签:

【中文标题】Linq 查询运行缓慢,但查询单独运行很快【英文标题】:Linq query runs slow but query runs fast in isolation 【发布时间】:2015-04-01 06:25:42 【问题描述】:

我们有一个 REST 服务来获取给定 ID 的成员。 SQL 单独运行很快(5 毫秒),但从 Linq 运行(使用实体框架 6)时,运行速度非常慢(230 毫秒)。

我不认为这个问题与 this、this 或 this 重复,因为它感觉与 Linq/EntityFramework 相关。

以下是统计数据: 客户端调用member get的时间约为360ms 从 C# 代码执行 Linq 查询所用的时间约为 230ms 在 SQL Server 上执行 SQL 的时间约为 228ms

生产中的 SQL 跟踪具有相似的性能(在 SQL 服务器上执行 SQL 需要 141 毫秒),因此数字感觉真实。

我尝试连续六次运行 Linq 查询,以查看从数据上下文建立连接的成本是否存在问题。这些 Linq 查询中的每一个都花费了相同的时间来运行。

如果我使用相同的数据上下文直接运行 SQL(即:Linq 生成的),运行时(从 C# 测量)从 230ms 下降到 19ms。

直接在服务器上运行 SQL(SQL Server management Studio)大约需要 5ms。

C# 代码(都在同一个例程中,使用相同的数据上下文,没有 using 块)产生这些数字:

Linq original query run =227ms
Raw SQL query: 19ms
Linq run 0=228
Linq run 1=227
Linq run 2=229
Linq run 3=229
Linq run 4=232

Linq 查询如下所示:

DateTime start = DateTime.Now;
        var memberDetail = await (from member in DataContext.Members.AsNoTracking()
                                  join memberName in DataContext.MemberNames.AsNoTracking() on member.UID equals memberName.MemberID into nameOutput
                                  from mn in nameOutput.DefaultIfEmpty()
                                  join memberProperty in DataContext.Properties.AsNoTracking() on member.PropertyID equals memberProperty.UID
                                  join membershipCycle in DataContext.MembershipCycleHistories.AsNoTracking() on member.UID equals membershipCycle.MemberID into cycleOutput
                                  from co in cycleOutput.DefaultIfEmpty()
                                  where member.ReferenceNumber.Equals(memberNumber) &&
                                        memberProperty.ExternalID.Equals(property, StringComparison.InvariantCultureIgnoreCase)
                                  select new
                                  
                                      member.UID,
                                      member.Created,
                                      member.LastUpdated,
                                      PropertyName = memberProperty.ExternalID,
                                      member.ReferenceNumber,
                                      member.Active,
                                      member.IsAwaitingSync,
                                      member.Class,
                                      mn.FirstName,
                                      mn.LastName,
                                      mn.PreferredName,
                                      MembershipCreditBalance = co.MembershipCredits,
                                      member.DOB
                                  
                                 ).FirstOrDefaultAsync();
        System.Diagnostics.Trace.WriteLine(String.Format("linq run original=0", (DateTime.Now - start).TotalMilliseconds));

而连接字符串是:

Data Source=SQLServer123;Initial Catalog=DB123;Persist Security Info=True;User ID=User123;Password=PWD123;MultipleActiveResultSets=True

【问题讨论】:

不要使用 DateTime.Now 来执行 500 毫秒以下的计时,请使用 Stopwatch 类重新运行您的测试。另外,您是如何让原始 SQL 对服务器运行的,以及您是如何运行它的?最后,如果您使用 Sql Profiler,linq 查询执行服务器端需要多长时间? @ScottChamberlain - 我用秒表重新运行这些。结果在所提供数字的 5% 以内。 SQL 是从 linq 查询 (tostring()) 和 SQL 跟踪中提取的。直接在 management studio 中运行查询的运行时间为 5ms。 【参考方案1】:

经过更多调查后,我发现了问题所在。数据库对其所有字符串都使用 varchars。当 Linq 将其参数传递给 SQL Server 时,它会将它们作为 Unicode 传递。 SQL Server 查看数据类型并确定它不能使用 varchar 索引,因此回退到线性扫描。

通过将我的数据库从 varchar 更改为 nvarchar,我的查询速度从 258 毫秒变为 3 毫秒。

【讨论】:

在这种情况下,是只有 where 子句中的参数导致了这个问题,还是返回的行本身就是字符串也导致了问题? @Gilles - 问题是类型不匹配。只要参数与实际的数据库列类型匹配,就应该没问题。话虽如此,.Net 将所有字符串作为 unicode 传递,因此 DB 字符串列应始终为 nvarchar。 如果无法控制 db 列的类型怎么办?

以上是关于Linq 查询运行缓慢,但查询单独运行很快的主要内容,如果未能解决你的问题,请参考以下文章

Postgres 运行缓慢的删除查询

非常简单的 MySQL 索引查询运行非常缓慢

Postgres 查询不会在功能中完成,但如果单独运行它可以工作

SQL Developer 中没有参数的查询运行缓慢

Impala 分区查询运行缓慢

mysql内部连接查询运行缓慢