我有一个使用EF4(System.Data.Entities,没有nuget包)的项目,并直接为LINQ查询命中上下文。每当我通过让多个用户登录进行负载测试(使用VS测试负载测试)时,我的性能会非常糟糕,我的CPU会达到100%,并且VS.NET会在高.NET锁争用和高垃圾回收时发出警报。
在进行大量的分析和调整时,一切似乎都指向LINQ查询本身的执行(在某种程度上可以预期),以及我们在.ToList()调用中产生的令人难以置信的争用结果上有很多地方。
有没有人经历过这个?原因是什么,我该如何解决?我是否需要出于某种原因断开.ToList()调用?
更新: 有些人要求提供更多详细信息。以下是有问题的代码(稍微调整一下以删除我无法发布的内容)。
var query =
from f in context.fs
where f.usr.Any((u) => u.id == id)
select new
{
FS = f,
f.fList,
E = from e in f.fList select new { e.er },
L = from l in f.fList select new { l.id }
};
var res = query.ToList();
在重负载下,这个相同的代码在多个线程上运行(探测器说13)。 ToList()调用是绝对谋杀,几乎占所有延迟。
答案 0 :(得分:2)
您需要记住IQueryable
和IEnumerable
方法使用不同的执行方式。特别是IQueryable
方法通常只是修改内存中的表达式树。从剖析器的角度来看,他们永远不会占用大量的时间。直到ToList
调用才会实际执行查询(从先前的IQueryable
方法生成),这意味着实际网络IO发生的位置。这样做的时间显着高于放在一起的所有查询生成。
简而言之,探查者在这里并没有真正帮助你。所有工作都通过ToList
进行,但确定查询效率的代码基本上是其他任何地方。你想要的只是本来就是一项昂贵的操作,或者你没有有效地编写查询(在这种情况下,我们需要了解更多有关它的信息)。
关于它的并行化是否会有所帮助,那是不是真的知道。我的猜测可能不是。如果您的CPU最大化,那么您将保持繁忙,并且线程只会增加更多开销并减慢您的速度。如果CPU没有任务但速度很慢,那么线程更有可能帮助你。
答案 1 :(得分:0)
对于您的查询:如果您需要完整的fList
个实体,请在执行查询后执行E
和L
投影:
var query = from f in context.fs
where f.usr.Any((u) => u.id == id)
select new
{
FS = f,
f.fList
};
var res = query.ToList();
foreach (var x in res)
{
// runs in memory
var E = from e in x.fList select new e.er;
var L = from l in x.fList select new l.id;
// ...
}
如果你只需要E和L组合它们:
var query = from f in context.fs
where f.usr.Any((u) => u.id == id)
select new
{
FS = f,
EL = from x in f.fList select new { e.er, l.id }
};
var res = query.ToList();