【问题标题】:Using Linq to Entity for searching large data cause high memory usage使用 Linq to Entity 搜索大数据会导致高内存使用
【发布时间】:2015-11-23 09:48:22
【问题描述】:


我开发了一个 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的问题。
有没有更高效的解决方法?

【问题讨论】:

  • 尝试使用stored proceudre
  • 分页您的数据。使用 LINQ Take() 和 Skip()。
  • preVQuery 和 preQuery 之间有什么区别
  • 你也可以使用 ExecutedQuery
  • 我更正了 presQuery 和 prevQuery

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


【解决方案1】:

在使用 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 循环开始。当你看到一个混有查询代码的 for 循环时,你会发现你错过了对 Select、SelectMany、Join 或 GroupBy 等命令的一些巧妙使用。您永远不需要循环进行查询。

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

最后,如果你的最终结果有很多行,那么在获取数据的过程中不做任何改变会让你的应用使用更少的内存,唯一的补救办法就是让你的最终结果小paging your data with skip and take

关于使用存储过程的最后一条评论,procs 很好,因为它们保证数据库只会被访问一次,它运行在旨在执行此类计算的数据库上,以及最重要的好处(恕我直言) 您可以更好地控制查询,linq to entity/sql 在执行复杂查询时会生成非常奇怪且有时无效的 sql

【讨论】:

    猜你喜欢
    • 2011-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-31
    • 2014-07-03
    相关资源
    最近更新 更多