EF启动速度极慢 - 15分钟

时间:2018-01-25 11:21:55

标签: c# sql-server entity-framework

前段时间我创建了一个系统,用户可以使用自定义文件为某些对象定义类别。然后,每个对象都具有基于其类别的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记录,第一个查询大约需要15秒。第一个查询是

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技术的技术我发现主要考虑视图创建缓存,ngening EF等等,但在这种情况下,启动时间会在添加更多记录后增加,而不是更多实体类型。

我意识到这是由架构的复杂性引起的,但有没有办法加速这个?幸运的是,这是Windows服务,因此一旦启动,它将持续数周,但仍然。

我应该在第一次加载时删除EF并在纯SQL中执行吗?我应该分批这样做吗?我应该将EF更改为nHibernate吗?或者是其他东西?在执行此行期间的虚拟化服务器上​​,此程序最大化CPU(不是SQL服务器,而是我的应用程序)。

我尝试过只加载对象,然后再加载它们的属性。这在小型数据库上要快一点(但不是很明显),但在较大的数据库上则要慢一些。任何帮助表示赞赏,即使答案是“吮吸并等待”。

1 个答案:

答案 0 :(得分:2)

我设法用这些技巧减少了EF 3倍的总开始时间:

  1. 将框架更新到6.2并启用model caching

    public class CachingContextConfiguration:DbConfiguration {     public CachingContextConfiguration()     {         SetModelStore(new DefaultDbModelStore(Directory.GetCurrentDirectory()));     }

    }

  2. 尽早从新主题中明确调用ctx.Database.Initialize()。这仍然需要3-4秒,但由于它与其他东西一起发生,它会有很大帮助。

  3. 以合理的顺序将实体加载到EF缓存中。

  4. 以前,我刚写了Inlude之后的Include,它转换为多个连接。我在一些博客文章中找到了一个“经验法则”,最多两个链接包括EF表现相当不错,但每一个都会大幅减慢一切。我还发现了一个显示EF缓存的blog post:一旦给定的实体加载了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;
      
    2. 这种影响几乎不可察觉,但可能在庞大的数据集中扮演更重要的角色。

      我还看过Rowan Miller对{1}} EF Core的介绍,我将在下一个版本中转发它 - 在某些情况下,它比EF6快5-6倍。

      希望这有助于某人