Lee*_*ary 18 c# performance entity-framework
我正在尝试在我自己的时间制作一个stackoverflow克隆来学习EF6和MVC5,我目前正在使用OWin进行身份验证.
当我有50-60个问题的时候一切正常,我使用Red Gate数据生成器并尝试将其增加到100万个问题,并且几千个子表行没有关系只是为了"压力"ORM一点.这是linq的样子
var query = ctx.Questions
.AsNoTracking() //read-only performance boost.. http://visualstudiomagazine.com/articles/2010/06/24/five-tips-linq-to-sql.aspx
.Include("Attachments")
.Include("Location")
.Include("CreatedBy") //IdentityUser
.Include("Tags")
.Include("Upvotes")
.Include("Upvotes.CreatedBy")
.Include("Downvotes")
.Include("Downvotes.CreatedBy")
.AsQueryable();
if (string.IsNullOrEmpty(sort)) //default
{
query = query.OrderByDescending(x => x.CreatedDate);
}
else
{
sort = sort.ToLower();
if (sort == "latest")
{
query = query.OrderByDescending(x => x.CreatedDate);
}
else if (sort == "popular")
{
//most viewed
query = query.OrderByDescending(x => x.ViewCount);
}
}
var complaints = query.Skip(skipCount)
.Take(pageSize)
.ToList(); //makes an evaluation..
Run Code Online (Sandbox Code Playgroud)
不用说我得到SQL超时并且在安装Miniprofiler之后,看看生成的sql语句,它是一个可怕的几百行.
我知道我加入/包括太多桌子,但现实生活中有多少项目,我们只需加入1或2个桌子?可能有些情况我们必须用数百万行来做这么多连接,是唯一的存储过程吗?
如果是这样的话,EF本身是否只适合小规模项目?
Eri*_*ips 17
您遇到的问题很可能是笛卡尔积.
仅基于一些样本数据:
var query = ctx.Questions // 50
.Include("Attachments") // 20
.Include("Location") // 10
.Include("CreatedBy") // 5
.Include("Tags") // 5
.Include("Upvotes") // 5
.Include("Upvotes.CreatedBy") // 5
.Include("Downvotes") // 5
.Include("Downvotes.CreatedBy") // 5
// Where Blah
// Order By Blah
Run Code Online (Sandbox Code Playgroud)
这将返回向上的行数
50 x 20 x 10 x 5 x 5 x 5 x 5 x 5 x 5 = 156,250,000
Run Code Online (Sandbox Code Playgroud)
说真的......这是要返回的INSANE行数.
如果您遇到此问题,您真的有两种选择:
第一:简单的方法,依靠实体框架在进入上下文时自动连接模型.然后,使用实体AsNoTracking()并处理上下文.
// Continuing with the query above:
var questions = query.Select(q => q);
var attachments = query.Select(q => q.Attachments);
var locations = query.Select(q => q.Locations);
Run Code Online (Sandbox Code Playgroud)
这将为每个表发出一个请求,但不是156百万行,而是只下载110行.但很酷的部分是它们都连接在EF Context Cache内存中,所以现在questions变量已完全填充.
ken*_*n2k 11
我没有看到您的LINQ查询有任何明显错误(.AsQueryable()不应该是强制性的,但如果删除它不会改变任何内容).当然,不要包含不必要的导航属性(每个都添加一个SQL JOIN),但如果一切都是必需的,那就应该没问题.
现在,当C#代码看起来没问题时,是时候看看生成的SQL代码了.正如您所做的那样,第一步是检索执行的SQL查询.有.Net方式,对于SQL Server我个人总是启动SQL Server分析会话.
获得SQL查询后,尝试直接对数据库执行它,并且不要忘记包含实际的执行计划.这将在大多数情况下准确显示查询的哪个部分.它甚至会指示您是否有明显的缺失索引.
现在的问题是,如果您添加所有这些索引,您的SQL Server会告诉您它们丢失了吗?不必要.例如,请参阅不要盲目地创建缺少的索引.您必须选择应添加哪些索引,哪些不应该添加.
由于代码优先方法为您创建索引,我假设它们只是主键和外键的索引.这是一个好的开始,但这还不够.我不知道表中的行数,但是只有你可以添加的明显索引(没有代码生成工具可以这样做,因为它与业务查询相关),例如是CreatedDate列上的索引,因为您按此值订购商品.如果不这样做,SQL Server将不得不在1M行上执行表扫描,这在性能方面当然是灾难性的.
所以:
Include如果可以,尝试删除一些如您所知,Include方法生成巨大的SQL。
免责声明:我是Entity Framework Plus(EF +)项目的所有者
EF + Query IncludeOptimized方法允许完全像EF Core一样优化生成的SQL。
不是生成一个怪异的SQL,而是生成多个SQL(每个包含一个)。此功能还作为奖励,它允许过滤相关实体。
文件:EF +查询包含最佳化
var query = ctx.Questions
.AsNoTracking()
.IncludeOptimized(x => x.Attachments)
.IncludeOptimized(x => x.Location)
.IncludeOptimized(x => x.CreatedBy) //IdentityUser
.IncludeOptimized(x => x.Tags)
.IncludeOptimized(x => x.Upvotes)
.IncludeOptimized(x => x.Upvotes.Select(y => y.CreatedBy))
.IncludeOptimized(x => x.Downvotes)
.IncludeOptimized(x => x.Downvotes.Select(y => y.CreatedBy))
.AsQueryable();
Run Code Online (Sandbox Code Playgroud)