提问人:AkshatSparrow 提问时间:9/25/2023 最后编辑:marc_sAkshatSparrow 更新时间:9/26/2023 访问量:109
提高查询速度?
Improving the speed of a query?
问:
我正在尝试提高查询的性能。
我正在考虑使用 ,但我不确定放置它的位置是否会有所不同,即(之前或之后、之前或之后、之前或之后)。AsNoTracking()
Select()
Include()
ToList()
我还读到了 ,它阻止了 EF 跟踪对实体的更改。_context.ChangeTracker.Clear()
那么使用它会让我的查询更快吗?
var checklist = await _context.SiteCategory
.Include(s => s.Sites)
.Include(s => s.TAudits)
.AsNoTracking()
.ToListAsync();
_context.ChangeTracker.Clear();
答:
和的位置可能会影响查询的性能以及实体框架 (EF) 管理跟踪实体的方式。但是,使用它们是否会使查询更快取决于具体方案和要求。AsNoTracking()
_context.ChangeTracker.Clear()
以下是对每种方法的解释以及它们如何影响性能:
AsNoTracking()
:此方法用于指示 EF 上下文不应跟踪从数据库中检索到的实体。这意味着 EF 不会跟踪对这些实体所做的更改,也不会在保存更改时为它们生成更新语句。它可以提高查询性能,因为 EF 不需要维护这些实体的状态。放在 、 或 之前会影响查询检索到的所有实体。它通常应用于整个查询结果。
AsNoTracking()
Select()
Include()
ToList()
当您不打算修改实体并希望提高查询性能时使用。
AsNoTracking()
_context.ChangeTracker.Clear()
:此方法从 EF 上下文的更改跟踪器中清除所有跟踪的实体。当您想要将实体从上下文中分离以防止意外更改或释放内存时,它可能很有用。在完成对实体的处理之后放置(例如,after )是将它们从上下文中分离出来的好做法。这有助于减少内存使用量并防止跟踪更改。
_context.ChangeTracker.Clear()
ToList()
它通常不用于查询性能改进,而是用于内存管理和防止对实体的意外更改。
使用这些方法是否会使查询速度更快取决于您的特定用例:
如果不需要跟踪对实体的更改,并且只读取数据,则使用 可以通过避免更改跟踪的开销来提高查询性能。
AsNoTracking()
_context.ChangeTracker.Clear()
更多的是关于内存管理和避免意外更改,而不是查询性能。在完成实体操作后,最好将实体从上下文中分离出来。
总之,您可以同时使用 and 来提高查询性能和管理实体,但它们的位置应符合有关更改跟踪和内存管理的特定要求。AsNoTracking()
_context.ChangeTracker.Clear()
评论
我还读到了 ,它阻止了 EF 跟踪对实体的更改。
_context.ChangeTracker.Clear()
DbContext.ChangeTracker.Clear()
本身不会直接提高查询性能。这将停止跟踪所有现有的跟踪实体,也不会停止跟踪从数据存储加载的新实体。它速度更快,是循环访问所有跟踪实体并将它们与上下文分离的首选选项,如果您的代码已放弃对先前实体的任何引用,并且您正在重用上下文,则将改善内存消耗和某些逻辑流。
在大多数情况下,它不会提高针对数据存储的查询的性能,但是如果您已经加载了通过 FK 与将要加载到上下文中的记录相关的实体,则会出现例外情况。
如果您不希望通过创建新上下文将现有记录链接到新查询的结果,则可以避免这种情况,或者您可以在执行新查询之前调用,但在执行查询后调用 clear 为时已晚,尽管它可能会改进将来针对同一上下文的查询。.Clear()
避免这种情况的另一种方法是使用 加载前面的查询。AsNoTracking(),
但在查询中调用具有相同的效果。.AsNoTracking()
- 如果没有以前的查询,因此没有以前跟踪的实体,则不会产生任何影响。
.Clear()
- 加载记录,然后调用将不起作用,因为没有要清除的跟踪实体。
.AsNoTracking()
.Clear()
.AsNoTracking()
只能在表达式上调用,因此必须在 / 之前调用它。IQueryable<T>
.ToList()
.ToListAsync()
请务必了解,这不会影响针对基础数据存储的查询执行性能,它将减少查询返回所需的时间,但前提是跳过通常会跟踪 ChangeTracker 中返回对象的引用的过程。.AsNoTracking()
如果在清除更改跟踪器并加载查询后,您需要进一步优化,那么我们需要查看返回的相关项。.AsNoTracking()
首先,您的查询正在加载 ALL 记录 from 和 所有相关的 和 。如果有很多记录,加载所有内容可能需要一些时间。性能将取决于基础存储和所包含关系的复杂性。SiteCategory
Sites
TAudits
使用包含的导航路径,您可能会受益于通过 使用拆分查询。这不是灵丹妙药,但在许多情况下确实有帮助。.AsSlpitQuery()
在扩展多个导航属性的情况下,拆分查询可以显著减少从数据存储到运行时的传输字节数。这是因为如果没有它,EF 会将您的表达式作为单个联接语句执行,从而生成所有相关表的笛卡尔乘积。使用 EF 拆分查询时,将对每个表执行单独的查询,返回较少的行,然后它会在内存中重新构造对象图。.AsSplitQuery()
评论
AsSplitQuery()
查找性能问题时,首先要验证的是实际时间的去向。例如:
Stopwatch timer = Stopwatch.StartNew();
var checklist = await _context.SiteCategory
.Include(s => s.Sites)
.Include(s => s.TAudits)
.AsNoTracking()
.ToListAsync();
timer.Stop();
int ms = timer.Elapsed.TotalMilliseconds;
这将为您提供此特定查询的数字。如果这个数字不能解释你正在执行的操作(如渲染页面)的总时间,那么你可能会犯延迟加载的错误。例如,您有一个页面应该呈现这些网站类别的列表,加载需要很长时间,因此您在此查询上放置了一个断点,这几乎是操作中唯一确定它是可疑的查询,但查询会在一秒钟左右返回, 但是页面需要一分钟+才能加载。很有可能,当视图引擎遍历每个类别时,它正在“触摸”一个不急于加载的导航属性,从而触发延迟加载往返数据库。将此乘以行数(延迟加载将单独加载结果集中每一行的相关记录),就会有很大的延迟。
在查找 EF 的性能问题时,最好对数据库运行探查器,以准确查看正在运行的查询。这可以突出显示延迟加载之类的事情,通过这样的查询加载页面,您希望看到一个查询,如果您开始看到 10 个或 100 个或更多查询被启动相关表,那么您已经绊倒了放置加载。代码/视图引擎“触及”的导航属性比你预先加载的要多。
如果此查询速度明显较慢,那么接下来要查看的是您加载了多少数据。您实际需要从站点类别、站点和审核中获得哪些数据?您是否需要这 3 个表的所有行中的所有列?在加载单个顶级实体时,通常使用预先加载实体图(实体及其亲属),但在加载集合(如列出所有项或搜索结果)时,可能会非常昂贵/浪费。您的语句正在加载整个 SiteCategories 表,因此所有站点和审核可能有很多行。当 JOINing 表时,数据库会跨所有列生成笛卡尔积,从而快速生成广泛而深入的结果集,供 EF 筛选以组成实体。在绝对需要此数据的情况下,可以使用 (EF Core) 缓解此问题,但是,在许多情况下,并非所有数据都需要,因此应考虑将数据投影到仅使用所需数据的 ViewModel。这很大程度上取决于您如何使用这些结果。无论调用此查询,请确保它没有像子句那样进行任何进一步的筛选,该子句最好向上移动到查询中。如果只需要网站类别中的几列,例如网站计数,以及最新审核记录的日期等,则可以在 Linq 查询中投影这些列。如需帮助,请随时使用使用此清单结果的视图或代码更新问题。预测结果是解决延迟加载等问题后可以提高性能的最大障碍之一。AsSplitQuery()
Select
Where()
加载整个集合的下一个考虑因素是,您的解决方案是否正在使用或应该使用分页和服务器端分页,而不是客户端分页。许多数据网格控件等都提供客户端分页,即它们将采用数千行,并以 10 或 25 行左右的批次呈现它们。客户端分页的问题在于,您需要加载所有数据,以便客户端一次显示几个数据。服务器端分页具有将当前页面和页面大小发送到服务器的控件,以便查询可以告诉数据库只返回一页结果。每次客户端控件更改页面或页面大小时,它都会从服务器请求一个新页面。这需要更多的编码和客户端和服务器之间的协调,但它可以大大提高加载性能。
最后要检查的可能是启动此操作时 DbContext 的当前状态?例如,这是一个 Web 应用程序,_context引用生存期是否限定为请求?或者它是否使用生存期较长的 DbContext 实例,如 Singleton?如果 DbContext 碰巧正在跟踪大量实体引用,这些引用可能与正在加载的数据相关,也可能不相关,那么无论您是否急于加载,它都可以花时间检查所有跟踪的引用以插入到查询结果中。在这种情况下,在查询之前可能会加快查询速度,但实际上,您应该确保 DbContext 实例的生存期很短,以避免这些类型的问题以及更新数据时的“中毒”。_context.ChangeTracker.Clear();
这应该希望能给你一些想法,一旦你确认了实际收取性能成本的位置,就要检查的事情。
评论
.AsNoTracking
ChangeTracker.Clear()
.Clear()
在这种情况下,对之前的代码没有性能影响,已经加载,并且由于指定了,因此跟踪器无论如何都不会跟踪其中的记录。checklist
.AsNoTracking()
checklist
AsNoTracking