首页 > 解决方案 > EF Core 内存使用和 QueryTrackingBehavior.NoTracking

问题描述

我有一个 ASP.NET Core 3 网站,该网站经常在 Azure 上耗尽内存。

在此处输入图像描述

繁重(但经常使用)的功能之一是生成报告。所以我想我会用一个这样的报告作为测试用例来看看发生了什么。

这是应用程序加载后的内存快照,然后是对其中一个报告的 9 次后续请求。

在此处输入图像描述

查看诊断信息,EF 更改跟踪对象消耗了大量内存。

在此处输入图像描述

我发现如果我options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);在启动时使用,那么相同活动的快照会产生以下内容:

在此处输入图像描述

这是一个巨大的改进——为每个请求增加 2 MB 是不可行的。这是否正常 - 我会认为即使启用更改跟踪,GC 也不会让它变得如此糟糕?或者我的报告代码中是否有一些东西使它保持引用或其他东西 - 我读到类中的静态变量可能导致 GC 不释放这些实例,这是一种可能性吗?我不确定关闭某些默认功能是否只是对我在根本上做错的其他事情的创可贴(我很确定我正在使用using语句等处理所有内容)。

标签: c#entity-frameworkasp.net-corememoryentity-framework-core

解决方案


我想说,将所有 EF 查询切换为 时,这种结果是预期的NoTracking,特别是在报告您最有可能正在读取然后跟踪内存中大量对象的场景中。

官方文档中,您可以找到有关此主题的详细信息。在那里,您还可以看到比较两个查询的性能的基准,一个使用更改跟踪器,另一个不使用更改跟踪器,使用一个小数据集(10 个博客,每个 20 个帖子)。尽管数据量很小,但结果与您的相似:性能提高了近 40%,分配的内存也减少了。

统计数据

因此,关于I'm not sure if switching off some default functionality is just a band-aid to something else I'm doing fundamentally wrong,我肯定会说这根本不是一个创可贴的解决方案,只是为了报告功能。在这些需要提高性能的只读场景中,实际上建议使用非跟踪查询。

但是,我唯一要知道的是,您可能不想为应用程序中的所有查询关闭跟踪行为。通过这样做,如果您依赖更改跟踪器在应用程序的其他位置执行实体更新,这些更新将停止工作。

例如:

var blog = context.Blogs
    .Where(blog => blog.Id = blogId)
    .SingleOrDeafult();

blog.Name = "Another Name";

context.SaveChanges() // If the default query behaviour is 'NoTracking', the Blog's name won't be updated since it wasn't in the ChangeTracker.

我要做的是将默认行为保持为跟踪,但随后我会将仅在报告中使用的所有查询更改为以非跟踪方式完成。为此,您必须添加.AsNoTracking()所有报告 EF 查询。

例如:

var blogs = context.Blogs
    .AsNoTracking()
    .ToList();

这样,您将大大提高只读查询的性能,而不会影响应用程序的其余行为。


推荐阅读