[英]Entity Framework Performance Issue
我遇到了Entity Framework的一個有趣的性能問題。 我正在使用Code First。
這是我的實體的結構:
一本書可以有很多評論。 評論與單本書相關聯。 評論可以有一個或多個評論。 評論與一篇評論相關聯。
public class Book
{
public int BookId { get; set; }
// ...
public ICollection<Review> Reviews { get; set; }
}
public class Review
{
public int ReviewId { get; set; }
public int BookId { get; set; }
public Book Book { get; set; }
public ICollection<Comment> Comments { get; set; }
}
public class Comment
{
public int CommentId { get; set; }
public int ReviewId { get; set; }
public Review Review { get; set; }
}
我用大量數據填充了我的數據庫並添加了適當的索引。 我正在嘗試使用此查詢檢索一本包含10,000條評論的圖書:
var bookAndReviews = db.Books.Where(b => b.BookId == id)
.Include(b => b.Reviews)
.FirstOrDefault();
這本特別的書有10,000條評論。 此查詢的性能約為4秒。 運行完全相同的查詢(通過SQL事件探查器)實際上很快就會返回。 我使用相同的查詢和SqlDataAdapter以及自定義對象來檢索數據,它發生在500毫秒以內。
使用ANTS Performance Profiler看起來大部分時間花在做一些不同的事情上:
Equals方法被稱為5000萬次。
有誰知道為什么需要調用這5000萬次以及如何才能提高性能呢?
為什么Equals被稱為50M次?
這聽起來很可疑。 您有10,000條評論和50.000.000次調用Equals
。 假設這是由EF內部實現的身份映射引起的。 身份映射確保上下文僅跟蹤具有唯一鍵的每個實體一次,因此如果上下文已經具有與來自數據庫的加載記錄具有相同鍵的實例,則它將不實現新實例而是使用現有實例。 現在這又如何與這些數字相吻合? 我可怕的猜測:
=============================================
1st record read | 0 comparisons
2nd record read | 1 comparison
3rd record read | 2 comparisons
...
10.000th record read | 9.999 comparisons
這意味着將每個新記錄與身份映射中的每個現有記錄進行比較。 通過應用數學計算所有比較的總和,我們可以使用稱為“算術序列”的東西:
a(n) = a(n-1) + 1
Sum(n) = (n / 2) * (a(1) + a(n))
Sum(10.000) = 5.000 * (0 + 9.999) => 5.000 * 10.000 = 50.000.000
我希望我的假設或計算沒有錯。 等待! 我希望我錯了,因為這似乎並不好。
嘗試關閉更改跟蹤=希望關閉身份地圖檢查。
這可能很棘手。 從...開始:
var bookAndReviews = db.Books.Where(b => b.BookId == id)
.Include(b => b.Reviews)
.AsNoTracking()
.FirstOrDefault();
但是很有可能您的導航屬性不會被填充(因為它由變更跟蹤處理)。 在這種情況下使用這種方法:
var book = db.Books.Where(b => b.BookId == id).AsNoTracking().FirstOrDefault();
book.Reviews = db.Reviews.Where(r => r.BookId == id).AsNoTracking().ToList();
無論如何,你能看到哪些對象類型傳遞給Equals? 我認為它應該只比較主鍵,甚至50M整數比較不應該是這樣的問題。
作為旁注,EF很慢 - 這是眾所周知的事實。 在實現實體時,它還在內部使用反射,因此簡單的10.000記錄可能需要“一些時間”。 除非您已經這樣做,否則還可以關閉動態代理創建( db.Configuration.ProxyCreationEnabled
)。
我知道這聽起來有點蹩腳,但你是否嘗試過相反的方式,例如:
var reviewsAndBooks = db.Reviews.Where(r => r.Book.BookId == id)
.Include(r => r.Book);
當你以這種方式處理你的查詢時,我注意到EF有時會有更好的表現(但我沒有時間弄清楚原因)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.