[英]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.