首页 > 其他分享 >关于EF Core 更新速度随时间越来越慢的解决办法

关于EF Core 更新速度随时间越来越慢的解决办法

时间:2023-05-26 19:58:00浏览次数:32  
标签:Core 解决办法 EF 更新 插入 DbContext context var entry

# 关于EF Core更新速度随时间越来越慢的解决办法 ## 背景 最近在做一些数据分析时,遇到了一个问题,当我把计算结果更新到数据库时,一开始速度会很快,但随着时间的推移,更新速度会越来越慢。 本篇博客就来说明这种现象的原因和解决办法。 我使用的是`.NET 7`和`EF Core 7`. ## 事例说明 我有1000W已处理好的数据需要更新到数据库,这些数据我也是从数据库中一次性查询出来的,这样可以只进行一次查询,并使用`AsNoTracking()`提高查询效率,然后我对这些数据进行了并行计算,最后将计算完的结果更新到数据库。最费时的操作就是更新到数据库。 请看以下代码示例: ```csharp var bc = new ConcurrentBag<list>(); // 并行计算 var computeTasks = group.AsParallel() .WithDegreeOfParallelism(Environment.ProcessorCount) .WithExecutionMode(ParallelExecutionMode.ForceParallelism) .Select(async g => { var computedData = await service.ComputeAsync(g.ToList()); if (computedData != null) { bc.Add(computedData); } }); await Task.WhenAll(computeTasks); // 数据插入 var batchSize = 5000; var items = bc.SelectMany(x => x).ToList(); left = items.Count; _logger.LogInformation($"need update {left} daily!"); foreach (var batch in items.Chunk(batchSize)) { context.AttachRange(batch); foreach (var entity in batch) { var entry = context.Entry(entity); entry.Property(e => e.A).IsModified = true; entry.Property(e => e.B).IsModified = true; entry.Property(e => e.C).IsModified = true; entry.State = EntityState.Modified; } var count = await context.SaveChangesAsync(); } await Console.Out.WriteLineAsync("[done] update all data"); ``` 并行计算速度非常快,几秒就能都完成了。 数据插入,我分批进行循环插入,每次5000条,通常不到1秒时间就能插入成功。但随着时间的推移,插入速度越来越慢。 > [!NOTE] > 由于我有1000W的数据插入,如果最终一次性提交,如果出现了异常,那么所有数据都不会插入成功,并且会等待很长的时间,并且在最终执行完成之前,你得不到任何信息,以预估可能花费的时间。所以我需要分批插入。 ## 原因 EF Core 会在上下文中跟踪所有已加载或附加的实体。随着循环的进行,上下文将追踪越来越多的实体,这可能会导致性能下降。 **也就是说在同一个`DbContext`上下文中,SaveChangesAsync()方法调用后,不会清除已更新的内容**,这意味着追踪的实体越来越多,最终多达1000W,并且这些都是已经标记为要更新的内容,也意味着你每次都会更新更多的内容到数据库。 ## 解决办法 ### 只进行一次SaveChanges 既然每次`saveChanges`不会清除,那么最后我只提交一次不就行了么?但这个方案不符合实际需求,上面已经提到过了。 ### 使用多个DbContext 既然 同一个`DbContext`下会出现这个问题,那么每次更新,我再创建一个新的DbContext不就可以了么? 这个方法虽然可行,但对于1000W的数据来说,即使我每次更新1W条数据,也需要创建1000+次`DbContext`,也有一定的消耗。 ### 清除追踪 既然问题是SaveChanges不会自动清除已追踪的更改,如果我可以手动去清除,不就可以了么?清除的操作比起创建新的`DbContext`实例,还是更快捷的。 那么我们修改代码: ```csharp foreach (var batch in items.Chunk(batchSize)) { context.AttachRange(batch); foreach (var entity in batch) { var entry = context.Entry(entity); entry.Property(e => e.A).IsModified = true; entry.Property(e => e.B).IsModified = true; entry.Property(e => e.C).IsModified = true; entry.State = EntityState.Modified; } var count = await context.SaveChangesAsync(); // ⚒️ add this line context.ChangeTracker.Clear(); } ``` > [!TIP] > `context.ChangeTracker.Clear()` 方法清除上下文中的所有已跟踪实体。这将重置更改跟踪器并清除其跟踪的所有实体,从而释放内存并提高性能。 ## 总结 `EF Core 7` 中已经添加了批量更新的方法,但这种方法也不适用于我遇到的场景,因为我不是按条件进行批量更新,而是每一条数据都需要更新。 `context.ChangeTracker.Clear()`可以在这样的场景下发挥作用,在一些关联插入或更新的场景,为避免追踪带来的冲突问题,也可以通过该方法清除追踪,然后再手动建立关系,进行提交。

标签:Core,解决办法,EF,更新,插入,DbContext,context,var,entry
From: https://www.cnblogs.com/msdeveloper/p/17435661.html

相关文章

  • OneForAll下载安装以及环境配置
    python-3.9.7-amd64OneForAll-masterpython安装以及插件安装首先下载python解压到电脑c盘在c盘中创建一个工具文件夹,然后下载OneForAll-master下载好之后找到安装包点击安装勾选下面两个得点击上面的,上面的是自定义安装出现这个就代表的安装完成了,但是一定要记得文件路径然后下载......
  • Educational Codeforces Round 149 (Rated for Div. 2) 题解
    https://codeforces.com/contest/1837https://codeforces.com/contest/1837/problems利益相关:上紫祭。真的不要以为这道题放在F就不敢做。压线过题的感觉真好。ABC题都过水,就不写了。代码丢在这里:A:https://codeforces.com/contest/1837/submission/207156920B:https:......
  • Coremail与中科曙光达成战略合作 紧抓数字经济大机遇
    5月12日,广东盈世计算机科技有限公司(以下简称:Coremail)与曙光信息产业股份有限公司(以下简称:中科曙光)正式签约合作协议、达成战略合作伙伴关系。Coremail技术副总裁林延中、中科曙光副总裁郭莹等领导出席签约活动,并就未来合作进行了深入探讨。双方将在计算、存储、安全、数据中心等领......
  • EntityFramework Core 删除迁移
    EFCore删除迁移的命令是Remove-Migration。一次只删除一个迁移,并且仅删除尚未应用到数据库的最新迁移。如果强行删除已经应用到数据库的迁移,会抛出异常。删除尚未应用到数据库的最新迁移直接运行Remove-Migration命令即可。删除已经应用到数据库的迁移假设我们已经按顺序应用......
  • ssh远程登录服务器时提示'Permission denied (publickey)'的解决办法
    scp远程拷贝文件时提示错误:Warning:Permanentlyadded'10.0.0.182'(RSA)tothelistofknownhosts.Permissiondenied(publickey).解决:登录10.0.0.182,将/etc/ssh/sshd_config文件中的PasswordAuthenticationno改为PasswordAuthenticationyes重启sshd服务:/etc/init.......
  • 开源工作流WorflowCore学习之工作流简单审核
    在开源趋势下,很多开源的组件在国内,乃至全网都少有案例。为了做这个工作流翻了许多帖子和github的帖子在这里对github ZL.WorflowCoreDemo,和PizzaRestaurantWorkflow-main表示感谢,同时也感谢给博客园的帖子。本案例再利用ZL.WorflowCoreDemo中的项目直接进行新加的。关于如何......
  • CodeForces 1107B Digital root(找规律)
    传送门每个数字都有个数位和,就是把数字的每一位相加直到数位和是一个个位数。然后题目就要你求第K个数位和为X的数字是多少。写一些数字出来就很容易发现规律了可以看出每一竖列的数位和是相等的,然后就找到规律是9*(k-1)+x,注意数据范围是1e12,是longlong,然后就这么多,就可以直......
  • CodeForces 1107A Digits Sequence Dividing(思维)
    传送门唉,题目讲的天花乱坠的,花里胡哨,一上来真是把我唬住了。愣了半天也没看出来到底咋做,后来借助翻译明白了这个题就是让你把一串字符分成两串,然后第一串要比第二串小,就这样,然后又是个SpecialJudge。做的时候就把第一个数作为第一个串,然后串长如果为2,就判断一下后面的串要比第一个......
  • CodeForces 1108B Divisors of Two Integers(思维)
    传送门题目大意就是给你由X,Y两个数的所有因子(包括一和数本身)组成的序列,然后通过这个序列找出这两个数。由此可见,序列里最大的数一定是X或Y其中的一个,然后我们的任务就是找另一个了,我找的是剩下的因子里不能被已找到的那个数整除的数中最大的数,且没有和这个数相同的数。#include<std......
  • Educational Codeforces Round 63 (Rated for Div. 2) A,B,C
    A.ReverseaSubstring传送门就是找不满足升序排列的字母,输出就行了。#include<bits/stdc++.h>#definelllonglongusingnamespacestd;constintmaxn=3e5+10;chars[maxn];intmain(){#ifndefONLINE_JUDGEfreopen("in","r",stdin);#endif//ONL......