为什么运行泛型聚合方法要慢很多倍?

时间:2013-08-15 16:27:14

标签: .net sql-server performance entity-framework entity-framework-5

我在使用通用方法进行数据聚合时遇到了Entity Framework的性能问题。当使用泛型方法查询具有几十万行的表的(索引的)Id列的最大值时,我的性能下降很大。我使用代码生成的int键而不是sql身份,此代码用于获取下一个新ID。

这是示例说明。 MaxTyped使用Max上的db.PostsDbSet<Post>),而MaxGeneric使用通用方法max来执行此操作。

static int MaxTyped()
{
    using (var db = new BloggingContext())
    {                    
        return db.Posts.Max(p => p.PostId);
    }
}

static int MaxGeneric()
{
    using (var db = new BloggingContext())
    {
        return max(db.Posts, p => p.PostId);
    }
}

static int max<T>(DbSet<T> set, Func<T, int> func) where T : class
{
    // intellisense says its IEnumerable.Max
    return set.Max(func);
}

在我不太老的家用台式机上使用相当快的硬盘,100k行的MaxTyped运行0.5秒,而MaxGeneric大约6.5s。这慢了一个数量级。

在我办公室旧的测试服务器上,我们有几秒钟的时间,差不多10分钟。

我发现问题的唯一痕迹是两种情况下Max方法的Intellisense输出的差异:在MaxTyped中,它将方法识别为IQueryable.Max,而在{{1它表示它的MaxGeneric,这可能意味着EF在所有加载的实体上而不是在数据库中执行Max。我尝试将IEnumerable.Max投射到set,但没有任何改变。为什么会有这样的差异?如何解决?

1 个答案:

答案 0 :(得分:3)

因为MaxTyped执行“服务器端”,所以只返回一行,而MaxGeneric执行“客户端”,因此返回表的所有行,“反序列化“到int和”max(ed)“(最后一步是最快的一步)

实际成本在于SQL Server和.NET应用程序之间的数据传递:必须传递的数据越少,就越好。

现在尝试

static int max<T>(DbSet<T> set, Expression<Func<T, int>> func) where T : class
{
    // intellisense says its IEnumerable.Max
    return set.Max(func);
}

并查看set.Max是否正在使用IEnumerable<T>.Max()IQueriable<T>.Max()。如果它正在使用第二个,它的速度应该是大约0.5秒。

不同之处在于IQueriable<T>.Max()仅接受Expression<Func<T, int>>,而IEnumerable<T>.Max()接受Func<T, int>,而当您撰写p => p.PostId时,这可能是Expression<Func<>>或者Func<>,取决于它所放入的变量的类型。