今天將一個遷移至 ASP.NET Core 的項目放到一台 Linux 服務器上試運行。站點啟動后,瀏覽器打開一個頁面一直處於等待狀態。接着奇怪的事情發生了,整個 Linux 服務器響應緩慢,ssh命令行輸入都一頓一頓的,過了一會,直接停止響應,down機了,必須強制重啟服務器才行。再啟動站點,再訪問,問題依舊。換一台服務器,down機依然。
排查時在日志中發現了這樣的報警:
warn: Microsoft.EntityFrameworkCore.Query.Internal.SqlServerQueryCompilationContextFactory[8] The LINQ expression 'xxx' could not be translated and will be evaluated locally.
然后用 SQL Profiler 跟蹤了一下 EF Core 生成的 SQL 語句,發現了一條全表查詢的SQL語句,而這張表有80多萬條數據。罪魁禍首就是它,在內存中加載80多萬條數據進行查詢,結果把只有1核1G的Linux服務器搞掛了。
這個項目之前用的是基於 .NET Framework 的 Entity Framework ,並沒有出現這個問題。換成 Entity Framewor Core 怎么就變卦了呢?
后來在GitHub上發現了線索 —— The LINQ expression could not be translated and will be evaluated locally wrong warning:
If possible, you should always try to put Skip/Take/Distinct as the last operation when using EF Core (at least until we address the current limitations).
在我們這個項目中的確存在將 Skip/Take 放在 Select 之前的 LINQ 查詢代碼,修改之后問題立馬解決。而發生全表查詢的數據庫表所對應的實體,是在 LINQ 中 Include 進來的(Eager loading)。
這是目前版本的 EF Core 的一個坑,使用時需注意。建議使用 EF Core 時,一定要看日志中 EF Core 的報警信息。