linq 提高查询速度?

cwtwac6a  于 2023-09-28  发布在  其他
关注(0)|答案(3)|浏览(157)

我正在尝试提高查询的性能。
我在考虑使用AsNoTracking(),但我不确定它的位置是否会产生差异,即(在Select()之前或之后,在Include()之前或之后,在ToList()之前或之后)。
我还读到了_context.ChangeTracker.Clear(),它可以阻止EF跟踪实体的更改。
那么使用它会使我的查询更快吗?

var checklist = await _context.SiteCategory
                              .Include(s => s.Sites)
                              .Include(s => s.TAudits)
                              .AsNoTracking()
                              .ToListAsync();

_context.ChangeTracker.Clear();
bxgwgixi

bxgwgixi1#

我还读到了_context.ChangeTracker.Clear(),它可以阻止EF跟踪实体的更改。
DbContext.ChangeTracker.Clear()本身不会直接提高查询性能。这将停止跟踪所有 * 现有的已跟踪 * 实体,但不会停止跟踪您从数据存储加载的 * 新 * 实体。它更快,并且是迭代 * 所有 * 跟踪实体并将其从上下文中分离的首选选项,并且如果您的代码已经丢弃了对先前实体的任何引用并且您正在重用上下文,则将改善内存消耗和一些逻辑流。
在大多数情况下,它不会提高对数据存储的查询性能,但是如果您已经加载了通过FK与您将要加载到上下文中的记录相关的实体,则会出现例外。
如果您不希望通过创建一个 new 上下文将现有记录链接到新查询的结果,则可以避免这种情况,或者您可以在执行新查询之前调用.Clear() *,在执行查询之后调用clear就太晚了,尽管它可能会改善将来针对相同上下文的查询。
避免这种情况的另一种方法是使用.AsNoTracking()加载前面的查询,但在查询中调用.AsNoTracking()具有相同的效果。

  • 如果没有以前的查询,因此没有以前跟踪的实体,则.Clear()将无效。
  • .AsNoTracking()加载记录,然后调用.Clear()将不会有任何效果,这也是因为没有要清除的跟踪实体。
  • .AsNoTracking()只能在IQueryable<T>表达式上调用,所以必须在.ToList()/.ToListAsync()之前调用。

重要的是要理解.AsNoTracking()不会影响查询执行对底层对象的性能,它将减少查询返回所需的时间,但只是通过跳过通常在ChangeTracker中跟踪返回对象引用的过程。
如果在清除变更跟踪器并使用.AsNoTracking()加载查询之后,您需要进一步优化,那么我们需要查看返回的相关项。
首先,您的查询是从SiteCategory加载ALL记录,以及相关SitesTAuditsALL。如果有很多记录,加载任何东西的ALL可能需要一些时间。性能将取决于底层存储和所包含关系的复杂性。
使用包含的导航路径,您可能会受益于通过.AsSlpitQuery()使用拆分查询。这不是一个银,但在许多情况下确实有帮助。
在扩展多个导航属性的方案中,拆分查询可以显著减少通过线路从数据存储区传输到运行时的字节数。这是因为如果没有它,EF会将您的表达式作为单个连接语句执行,从而导致所有相关表的笛卡尔积。当您使用.AsSplitQuery()拆分查询时,EF将对每个表执行单独的查询,返回较少的行,然后在内存中重新构造对象图。

vltsax25

vltsax252#

查找性能问题时要验证的第一件事是实际时间的去向。例如:

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;

这将为您提供此特定查询的数据。如果这个数字没有解释您正在执行的操作(例如呈现页面)的总时间,那么您可能会与延迟加载发生冲突。例如,你有一个页面,它应该呈现这些站点类别的列表,它需要很长时间来加载,所以你在这个查询上设置了一个断点,这个查询几乎是Action中唯一的查询,认为它是可疑的,但是查询在一秒钟左右就回来了,但是页面需要一分钟以上的时间来加载。视图引擎在遍历每个类别时,可能会“触及”一个不急于加载的导航属性,从而触发一个返回数据库的延迟加载往返。乘以行数(延迟加载将分别为结果集中的每一行加载相关记录),您将得到一个显著的延迟。
在查找EF的性能问题时,最好对数据库运行一个分析器,以确切地查看正在运行的查询。这可以突出显示延迟加载,其中通过这样的查询加载页面,您希望看到一个查询,如果您开始看到10个或100个或更多的查询被相关表启动,那么您已经触发了延迟加载。代码/视图引擎正在“接触”比您已经加载的更多的导航属性。
如果这个查询非常慢,那么接下来要看的是您加载了多少数据。您实际上需要网站类别、网站和审核中的哪些数据?你需要这3个表的所有行的所有列吗?当加载单个顶级实体时,通常使用急切加载实体图(实体及其相关实体),但是当加载诸如列出所有项目或搜索结果的集合时,可能是极其昂贵/浪费的。您的语句将加载SiteCategories的整个表,因此可能会有很多包含所有站点和审计的行。当JOINing表时,数据库在所有列上产生笛卡尔乘积,快速产生广泛而深入的结果集,供EF筛选以组成实体。在绝对需要这些数据的情况下,您可以使用AsSplitQuery()(EF Core)来缓解这种情况,但是,在许多情况下,并不需要所有数据,因此您应该考虑使用Select将所需数据向下投影到ViewModel。这在很大程度上取决于您如何使用这些结果。无论调用这个查询的是什么,都要确保它没有做任何进一步的过滤,比如Where()子句,它最好被移到查询中。如果您只需要网站类别中的几列和网站计数之类的内容,可能还需要最新审核记录的日期等。这些可以在Linq查询中进行投影。要获得帮助,请随时使用正在使用此检查表结果的视图或代码更新问题。在解决了延迟加载等问题后,向下投影结果无疑是您可以给予的最大性能提升之一。
加载整个集合的下一个考虑因素是,您的解决方案是否正在使用或应该使用分页,以及服务器端分页而不是客户端分页。许多数据网格控件等提供客户端分页,这是他们将采取数千行,并提出他们在一批10或25左右。客户端分页的问题是,您需要加载 * 所有 * 数据,以便客户端一次显示一些数据。服务器端分页控制将当前页面和页面大小发送到服务器,以便查询可以告诉数据库仅返回一页结果。每次客户端控件更改页或页大小时,它都会向服务器请求一个新页。这需要在客户端和服务器之间进行更多的编码和协调,但它会导致更快的加载性能。
最后要检查的可能是在启动此操作时DbContext的当前状态。例如,这是一个Web应用程序吗?_context引用的生命周期范围是否针对请求?或者它是使用一个更长寿的DbContext示例,比如Singleton?如果DbContext碰巧跟踪了许多实体引用,这些引用可能与您正在加载的数据相关,也可能与您正在加载的数据无关,那么无论您是否急于加载,它都可以花时间检查所有跟踪的引用以插入查询结果。在这种情况下,_context.ChangeTracker.Clear(); * 优先于查询 * 可能会加快查询速度,但实际上,您应该确保DbContext示例保持短暂状态,以避免这些类型的问题以及更新数据时的“中毒”。
这应该希望给予你一些想法的事情,以检查一旦你确认的性能成本实际上是被收取。

cu6pst1q

cu6pst1q3#

AsNoTracking()_context.ChangeTracker.Clear()的位置可能会影响查询的性能以及Entity Framework(EF)管理跟踪实体的方式。但是,使用它们是否会使查询更快取决于特定的场景和要求。
以下是对每一项的解释,以及它们如何影响性能:

  • AsNoTracking():此方法用于指示从数据库检索的实体不应被EF上下文跟踪。这意味着EF不会跟踪对这些实体所做的更改,并且在保存更改时不会为它们生成update语句。它可以提高查询性能,因为EF不需要维护这些实体的状态。
  • AsNoTracking()放在Select()Include()ToList()之前会影响查询检索到的所有实体。它通常应用于整个查询结果。
  • 当您不打算修改实体并希望提高查询性能时,请使用AsNoTracking()
  • _context.ChangeTracker.Clear():此方法从EF上下文的更改跟踪器中清除所有跟踪的实体。当您要从上下文分离实体以防止意外更改或释放内存时,它可能很有用。
  • _context.ChangeTracker.Clear()放在处理完实体之后(例如,放在ToList()之后)是将它们从上下文中分离出来的一种好做法。这可以帮助减少内存使用并防止跟踪更改。
  • 它通常不用于提高查询性能,而是用于内存管理和防止对实体的无意更改。

使用这些方法是否会使查询更快取决于您的具体用例:

  • 如果您不需要跟踪实体的更改,而只是阅读数据,那么使用AsNoTracking()可以避免更改跟踪的开销,从而提高查询性能。
  • _context.ChangeTracker.Clear()更多的是关于内存管理和避免意外更改,而不是查询性能。当您处理完实体时,将它们从上下文中分离出来是一个很好的做法。

总之,您可以同时使用AsNoTracking()_context.ChangeTracker.Clear()来提高查询性能和管理实体,但是它们的位置应该与更改跟踪和内存管理方面的特定需求相一致。

相关问题