簡體   English   中英

在具有多個 JOIN 的 EF Core 中執行極慢的查詢,而在 SSMS 中卻快速執行完全相同的查詢

[英]Extremely slow query execution in EF Core with several JOINs while exactly the same query is executing fast in SSMS

我有一個包含多個導航屬性的實體,我需要加載其中的三個。 我寫的最初的 Linq 查詢是:

await _context.EventParticipant
              .Include(x => x.Employee)
              .Include(x => x.ExcuseDescription)
              .Include(x => x.RegistrationMethod)
              .Where(x => x.EventId == eventId)
              .ToListAsync();

這導致了大量的性能問題,導致查詢似乎永遠無法完成,當有大約 300 行要獲取時。 我首先看的是 LINQ 生成的查詢,它是這樣的:

SELECT 
    [e].[EmployeeNumber], [e].[EventId], [e].[ExcuseDescriptionId], 
    [e].[IsInvited], [e].[RegistrationMethodId], 
    [v].[employee_number], [v].[company_email], 
    [v].[department_name], [v].[employee_email], 
    [v].[employeecard_rfid], [v].[firstname], [v].[lastname], 
    [v].[status], [v].[mandant], [v].[upn], [v].[userid], 
    [e0].[Id], [e0].[Description], [r].[Id], [r].[Description]
FROM 
    [EventParticipant] AS [e]
INNER JOIN 
    [V_Employee] AS [v] ON [e].[EmployeeNumber] = [v].[employee_number]
LEFT JOIN 
    [ExcuseDescription] AS [e0] ON [e].[ExcuseDescriptionId] = [e0].[Id]
LEFT JOIN 
    [RegistrationMethod] AS [r] ON [e].[RegistrationMethodId] = [r].[Id]
WHERE 
    [e].[EventId] = @__eventId_0

現在,這個查詢在 SSMS 中運行不到一秒鍾,所以我的下一個猜測是問題是由實體跟蹤引起的,但是在實體化實體之前添加AsNoTracking()並沒有帶來任何改進。

有趣的是,調試 output 將顯示查詢在大約 16 秒后執行(這仍然非常慢),但實體仍然永遠不會實現。 該查詢確實加載了一些額外的列,所以我嘗試的下一件事是 select 僅我需要的列,所以我最終得到以下內容:

await _context.EventParticipant
              .Where(x => x.EventId == eventId).AsNoTracking()
              .Select(participant => new EventParticipantViewModel()
                                     {
                                          EventId = eventId,
                                          EmployeeNumber = participant.EmployeeNumber,
                                          Department = participant.Employee.DepartmentName,
                                          FirstName = participant.Employee.Firstname,
                                          LastName = participant.Employee.Lastname,
                                          IsInvited = participant.IsInvited,
                                          ExcuseDescription = participant.ExcuseDescription.Description ?? null,
                                          RegisterMethod = participant.RegistrationMethod.Description ?? null,
                                      })
              .ToListAsync();

現在,這生成了以下查詢:

SELECT @__eventId_0 AS [EventId], [e].[EmployeeNumber], [v].[department_name] AS [Department], [v].[firstname] AS [FirstName], [v].[lastname] AS [LastName], [e].[IsInvited], COALESCE([e0].[Description], NULL) AS [ExcuseDescription], COALESCE([r].[Description], NULL) AS [RegisterMethod]
  FROM [EventParticipant] AS [e]
  INNER JOIN [V_Employee] AS [v] ON [e].[EmployeeNumber] = [v].[employee_number]
  LEFT JOIN [ExcuseDescription] AS [e0] ON [e].[ExcuseDescriptionId] = [e0].[Id]
  LEFT JOIN [RegistrationMethod] AS [r] ON [e].[RegistrationMethodId] = [r].[Id]
  WHERE [e].[EventId] = @__eventId_0

盡管查詢本身與原始查詢沒有太大區別,但對性能的影響是巨大的:上面的查詢在不到 2 秒的時間內以相同的 300 行來執行,實體實現!

誰能解釋一下為什么會這樣? EF Core 在后台做了什么樣的魔法會影響這樣的性能?

正如@Stu 提到的,很難確定不知道所涉及的索引或所涉及的子集合記錄的數量,但要意識到這一點。Include 本質上是執行 select * (使用子表中的所有列),這可能會拉出不同的索引比您的自定義投影。 自定義投影可能正在使用覆蓋索引,從而提高您的性能。

您沒有提到的另一件事是您使用的是哪個版本的 EF(我假設是核心)。 某些版本從相同的 LINQ 表達式生成了截然不同的查詢,其中一些可能導致更好或更差的性能。 例如,早期版本的 EF 核心可能會將此查詢拆分為多個延遲加載的表達式,或者在子表 Id 上注入 order by,如果不包含在索引中,可能會導致性能問題(尤其是具有唯一標識符 Id)。

在這兩個查詢中,大部分時間都花在等待您的鏈接服務器上。 分布式查詢的計划更改可能非常痛苦。 因此,雖然您還沒有捕獲任何真正長時間運行的執行的實際執行計划,但它可能是由您的鏈接服務器引起的。

        <WaitStats>
          <Wait WaitType="OLEDB" WaitTimeMs="939" WaitCount="16" />
        </WaitStats>

首先,您真的必須使用鏈接服務器嗎? 你不能把數據復制到本地數據庫嗎?

如果您確實使用鏈接服務器,請盡量不要將遠程表與本地表連接起來。 使用鏈接服務器中的數據加載臨時表,然后將其加入本地表會更可靠。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM