c# - EF Core 内存使用和 QueryTrackingBehavior.NoTracking
问题描述
我有一个 ASP.NET Core 3 网站,该网站经常在 Azure 上耗尽内存。
繁重(但经常使用)的功能之一是生成报告。所以我想我会用一个这样的报告作为测试用例来看看发生了什么。
这是应用程序加载后的内存快照,然后是对其中一个报告的 9 次后续请求。
查看诊断信息,EF 更改跟踪对象消耗了大量内存。
我发现如果我options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
在启动时使用,那么相同活动的快照会产生以下内容:
这是一个巨大的改进——为每个请求增加 2 MB 是不可行的。这是否正常 - 我会认为即使启用更改跟踪,GC 也不会让它变得如此糟糕?或者我的报告代码中是否有一些东西使它保持引用或其他东西 - 我读到类中的静态变量可能导致 GC 不释放这些实例,这是一种可能性吗?我不确定关闭某些默认功能是否只是对我在根本上做错的其他事情的创可贴(我很确定我正在使用using
语句等处理所有内容)。
解决方案
我想说,将所有 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();
这样,您将大大提高只读查询的性能,而不会影响应用程序的其余行为。
推荐阅读
- apache-spark - 查找pyspark数组的平均值
- c# - 带有子模型列表的模型如何添加、删除
- jestjs - 堆栈:语法错误:'import' 和 'export' 可能只出现在 'sourceType: "module"'
- json - 如何遍历每个 JSON 对象并访问 API?
- python - 从列中过滤掉非数字值
- java - Java:构造函数变量与在类级别可见的变量
- dart - 使用 API 获取的 Flutter SliverGrid 填充
- php - 使用时间截止计算“下一个工作日”
- c++ - 为什么这会收敛到 3 而不是 pi?
- javascript - 使用 navLInk 的路由组件反应