使用Linq to Entity搜索大数据会导致内存使用率过高

时间:2015-11-23 09:48:22

标签: c# asp.net entity-framework linq


我开发了一个ASP.NET MVC Web应用程序并使用Linq to Entity
而我在内存使用方面遇到了麻烦

这是我的代码:

//Search for about 0.6 million transaction data
var presQuery=(from A in db.TransDetail
               where A.TRAN_DATE.CompareTo("2015/02/01")>=0 && A.TRAN_DATE.CompareTo("2015/02/28")<=0
               group A by A.MBR_ID into grp
               select new {
                MBR_ID=grp.Key,
                Price=grp.Sum(s=>s.Price)}).ToList();

//Search for about 0.6 million transaction data
var preVQuery=(from A in db.TransDetail
               where A.TRAN_DATE.CompareTo("2015/01/01")>=0 && A.TRAN_DATE.CompareTo("2015/01/31")<=0
               group A by A.MBR_ID into grp
               select new {
                MBR_ID=grp.Key,
                Price=grp.Sum(s=>s.Price)}).ToList();

int[] rankValue=new int(){2000,1000,500,250,100};
TransDetailViewModel model;
List<TransDetailViewModel> ls=new List<TransDetailViewModel>();
double CountRatio;
int TotalPrice;
int TotalCnt;




//start calculate 
for(int i=0;i<5;i++){
    for(int j=0;j<5;j++){
    if (i < 5  && j < 5)
    {
        var query1 = presQuery.Where(w => w.Price >= rankValue[i]);
        var query2 = prevQuery.Where(w => w.Price >= rankValue[j]);
        var query3 = from q1 in query1
                     join q2 in query2 on q1.MBR_ID equals q2.MBR_ID
                     select q1;
        TotalCnt = query3.Count();
        TotalPrice = Convert.ToInt32(query3.Sum(s => s.Price));
        CountRatio = (Convert.ToDouble(query1.Count()) / Convert.ToDouble(query2.Count())) * 100;
        model = new TransDetailViewModel()
        {
            RankLevel = "R" + i.ToString() + j.ToString(),
            CountRatio = CountRatio,
            TotalCount = TotalCnt,
            TotalPrice = TotalPrice
        };
        ls.Add(model);
        }      

    }   
}

当有四个人同时开始搜索时,上面的代码会导致应用程序内存使用量增加到1G的问题。
有没有更有效的方法来解决?

1 个答案:

答案 0 :(得分:2)

当使用linq时,有一个叫做物化的概念,像ToList这样的命令会导致你的查询“物化”,这意味着,数据会从数据库中提取到内存中,现在来看看这段代码

//Search for about 0.6 million transaction data
var presQuery=(from A in db.TransDetail
               where A.TRAN_DATE.CompareTo("2015/02/01")>=0 && A.TRAN_DATE.CompareTo("2015/02/28")<=0
               group A by A.MBR_ID into grp
               select new {
                MBR_ID=grp.Key,
                Price=grp.Sum(s=>s.Price)}).ToList();

“ToList”实现了查询,所以如果这个查询实际上加载了60万行...你刚刚在这里炸掉你的内存(我甚至没有提到第二个查询是相同的,所以你加载相同的数据两次),所以只需从查询末尾删除“ToList”就可以解决内存问题,直到这部分代码。

现在,对于第二部分,您将从双循环开始。当你看到一个与查询代码混合的for循环时,你会发现一些强烈的气味,你会错过使用Select,SelectMany,Join或GroupBy这样的命令。你永远不需要循环来查询。

这些循环将成为一个问题,因为现在我们删除了“ToList”并且我们的查询没有实现,在for循环中调用“Count()”将导致代码多次访问数据库并且这很有趣问题,因为运行此代码所需的时间大约是从服务器到数据库的ping乘以for循环中的迭代次数,因此如果您的数据库是本地的,它运行速度很快,但是当您将其部署到生产环境时,它很慢!我们需要摆脱for循环。 (我没时间,所以我现在不打算这样做)

最后,如果您的最终结果有很多行,那么获取数据的过程中的任何更改都不会使您的应用使用更少的内存,唯一的解决方法就是使paging your data with skip and take的最终结果缩小

关于使用存储过程的最后一条评论,procs是好的,因为它们保证数据库只会被命中一次,它运行在设计用于执行此类计算的数据库上,并且最重要的是好处(imho) )你可以更好地控制你的查询,linq to entities / sql在执行复杂查询时可能会生成非常奇怪且有时无效的sql