使用EF4在ToList()上出现高锁争用和性能降低

时间:2012-09-21 14:38:06

标签: c# performance entity-framework entity-framework-4

我有一个使用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()调用是绝对谋杀,几乎占所有延迟。

2 个答案:

答案 0 :(得分:2)

您需要记住IQueryableIEnumerable方法使用不同的执行方式。特别是IQueryable方法通常只是修改内存中的表达式树。从剖析器的角度来看,他们永远不会占用大量的时间。直到ToList调用才会实际执行查询(从先前的IQueryable方法生成),这意味着实际网络IO发生的位置。这样做的时间显着高于放在一起的所有查询生成。

简而言之,探查者在这里并没有真正帮助你。所有工作都通过ToList进行,但确定查询效率的代码基本上是其他任何地方。你想要的只是本来就是一项昂贵的操作,或者你没有有效地编写查询(在这种情况下,我们需要了解更多有关它的信息)。

关于它的并行化是否会有所帮助,那是不是真的知道。我的猜测可能不是。如果您的CPU最大化,那么您将保持繁忙,并且线程只会增加更多开销并减慢您的速度。如果CPU没有任务但速度很慢,那么线程更有可能帮助你。

答案 1 :(得分:0)

对于您的查询:如果您需要完整的fList个实体,请在执行查询后执行EL 投影:

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();