[英]Extremely slow EF startup - 15 minutes
前一段時間,我創建了一個系統,用戶可以在其中定義帶有某些對象的自定義字段的類別。 然后,每個對象都有基於其類別的FieldValue。 以下課程:
public class DbCategory
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public TextDbField MainField { get; set; }
public List<DbField> Fields { get; set; }
}
public class DbObject
{
public int Id { get; set; }
public byte[] Bytes { get; set; }
[Required]
public DbCategory Category { get; set; }
public TextDbFieldValue MainFieldValue { get; set; }
public List<DbFieldValue> FieldsValues { get; set; }
}
public abstract class DbField
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public bool Required { get; set; }
}
public class IntegerDbField : DbField
{
public int? Minimum { get; set; }
public int? Maximum { get; set; }
}
public class FloatDbField : DbField
{
public double? Minimum { get; set; }
public double? Maximum { get; set; }
}
//... few other types
public abstract class DbFieldValue
{
[Key]
public int Id { get; set; }
[Required]
public DbField Field { get; set; }
[JsonIgnore]
public abstract string Value { get; set; }
}
public class IntDbFieldValue : DbFieldValue
{
public int? IntValue { get; set; }
public override string Value
{
get { return IntValue?.ToString(); }
set
{
if (value == null) IntValue = null;
else IntValue = int.Parse(value);
}
}
}// and other FieldValue types
在我的開發機(i5、16bg ram和ssd驅動器)上,數據庫(在SqlExpress中)具有4個類別,每個類別具有5-6個字段,10k記錄,第一次查詢大約需要15s。 第一個查詢是
var result = db.Objects
.Include(s => s.Category)
.Include(s => s.Category.MainField)
.Include(s => s.MainFieldValue.Field)
.Include(s => s.FieldsValues.Select(f => f.Field))
.Where(predicate ?? AlwaysTrue)
.ToArray();
我這樣做是為了將所有內容加載到內存中。 然后,我處理緩存列表,然后將更改寫入數據庫。 我這樣做是因為用戶可以對每個FieldValue使用過濾器執行搜索。 事實證明,每次查詢數據庫都非常慢-但是,這一部分工作得很好。
問題稍后出現。 一些客戶定義了6個類別,每個類別有20多個字段,並存儲70k +記錄,有時啟動時間超過15分鍾。 此后,速度在5k和50k之間沒有差異。
我發現每種改善EF Code First啟動時間的技術都主要考慮視圖創建緩存,增強EF等功能,但是在這種情況下,添加更多記錄而不是添加更多實體類型后,啟動時間就會增加。
我意識到這是由模式的復雜性引起的,但是有什么方法可以加快速度嗎? 幸運的是,這是Windows服務,因此一旦啟動,它將持續數周,但仍然可以。
我應該在首次加載時放棄EF並在純SQL中執行嗎? 我應該分批這樣做嗎? 我應該將EF更改為nHibernate嗎? 或者是其他東西? 在執行此行的過程中,在虛擬服務器上,此程序使CPU(不是SQL Server,而是我的應用程序)的內存最大。
我嘗試僅加載對象,然后稍后加載它們的屬性。 在小型數據庫上,這要快一些(但不是很明顯),但在大型數據庫上則要慢一些。 任何幫助表示贊賞,即使答案是“等待並解決”。
通過這些技巧,我設法將EF導致的總啟動時間減少了3倍:
將框架更新到6.2並啟用模型緩存 :
公共類CachingContextConfiguration:DbConfiguration {public CachingContextConfiguration(){SetModelStore(new DefaultDbModelStore(Directory.GetCurrentDirectory())); }
}
ctx.Database.Initialize()
從新線程中顯式調用ctx.Database.Initialize()
。 這仍然需要3-4秒,但由於它與其他事物同時發生,因此很有幫助。
以合理的順序將實體加載到EF緩存中。
以前,我只是在Inlude之后編寫了Include,將其轉換為多個聯接。 我在一些博客文章中發現了一個“經驗法則”,其中最多兩個鏈式的Includes EF表現不錯,但是每增加一個都會大大降低一切。 我還發現了一個博客文章 ,其中顯示了EF緩存:給定實體使用Include或Load加載后,它將自動置於適當的屬性中(博客作者對對象的並集是錯誤的)。 所以我這樣做:
using (var db = new MyContext())
{
db.Fields.Load();
db.Categories.Include(c => c.MainField).Include(x => x.Fields).Load();
db.FieldValues.Load();
return db.Objects.Include(x => x.MainFieldValue.Field).ToArray();
}
這比從問題中提取數據快6倍。 我認為,一旦先前加載了實體,EF引擎就不會為相關對象調用數據庫,而只是從緩存中獲取它們。
我還在上下文構造函數中添加了此代碼:
Configuration.LazyLoadingEnabled = false; Configuration.ProxyCreationEnabled = false;
效果幾乎不明顯,但可能在龐大的數據集上發揮更大的作用。
我也看了這個由羅恩·米勒介紹EF核心的,我會切換到它的下一個版本-在某些情況下,它比EF6快5-6倍。
希望這可以幫助某人
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.