簡體   English   中英

極慢的EF啟動-15分鍾

[英]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倍:

  1. 將框架更新到6.2並啟用模型緩存

    公共類CachingContextConfiguration:DbConfiguration {public CachingContextConfiguration(){SetModelStore(new DefaultDbModelStore(Directory.GetCurrentDirectory())); }

    }

  2. ctx.Database.Initialize()從新線程中顯式調用ctx.Database.Initialize() 這仍然需要3-4秒,但由於它與其他事物同時發生,因此很有幫助。

  3. 以合理的順序將實體加載到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引擎就不會為相關對象調用數據庫,而只是從緩存中獲取它們。

  1. 我還在上下文構造函數中添加了此代碼:

      Configuration.LazyLoadingEnabled = false; Configuration.ProxyCreationEnabled = false; 

效果幾乎不明顯,但可能在龐大的數據集上發揮更大的作用。

我也看了這個由羅恩·米勒介紹EF核心的,我會切換到它的下一個版本-在某些情況下,它比EF6快5-6倍。

希望這可以幫助某人

暫無
暫無

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

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