【发布时间】:2018-01-25 11:21:54
【问题描述】:
前段时间我创建了一个系统,用户可以在其中为某些对象定义具有自定义字段的类别。然后,每个对象都有基于其类别的 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 启动时间的技术都主要考虑视图创建缓存、生成 EF 等,但在这种情况下,添加更多记录而不是更多实体类型后启动时间会增加。
我知道这是由于架构的复杂性造成的,但有什么方法可以加快速度吗?幸运的是,这是 Windows 服务,因此一旦启动,它会持续数周,但仍然如此。
我应该在第一次加载时删除 EF 并在纯 SQL 中执行吗?我应该分批这样做吗?我应该将 EF 更改为 nHibernate 吗?或者是其他东西?在执行此行期间,在虚拟化服务器上,该程序最大限度地利用了 CPU(不是 SQL 服务器,而是我的应用程序)。
我试过只加载对象,然后再加载它们的属性。这在小型数据库上要快一些(但不明显),但在大型数据库上更慢。任何帮助表示赞赏,即使答案是“忍住等待”。
【问题讨论】:
-
.ToArray();是您的问题。为什么不直接将其用作IQueryable? -
因为我希望所有对象以后都可以在我们对 DB 的另一个查询中使用
-
您想要所有个对象?有没有更好的方法来构建你的逻辑,这样它就不会那么耗费资源和缓慢?
-
我想要所有对象,是的。如果稍后在工作站上一切都很快,我可以忍受缓慢的启动(因为它是一项服务)。但是16分钟是慢的。上面提到的带有过滤器的搜索必须检查每个 FieldValue,这意味着查询整个数据库(几乎,因为大多数搜索都没有过滤器)。目前,即使有 70k+ 条记录,搜索也是即时的。如果我每次都查询数据库,那么每次搜索都需要 15 分钟。它不是那么耗费资源,它在加载整个数据库的情况下远远低于 1GB 的 RAM
-
我认为你最好分别加载所有实体并依靠关系修复来设置引用和集合。所有包含在多个级别中的查询必须是巨大的。
标签: c# sql-server entity-framework