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

概要

本篇主要介绍使用 context.ChangeTracker.Clear() 方法,在通过循环进行批量更新时,通过手动清除跟踪实体以提高性能的示例。

背景

最近在做一些数据分析时,遇到了一个问题,当我把计算结果更新到数据库时,一开始速度会很快,但随着时间的推移,更新速度会越来越慢。

本篇博客就来说明这种现象的原因和解决办法。

环境:ASP.NET Core 7EF Core 7.

事例说明

我有1000W已处理好的数据需要更新到数据库,这些数据我也是从数据库中一次性查询出来的,这样可以只进行一次查询,并使用AsNoTracking()提高查询效率,然后我对这些数据进行了并行计算,最后将计算完的结果更新到数据库。最费时的操作就是更新到数据库。

请看以下代码示例:

var bc = new ConcurrentBag<List<StockDailyKLineInfo>>();
// 并行计算
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实例,还是更快捷的。

那么我们修改代码:

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()可以在这样的场景下发挥作用,在一些关联插入或更新的场景,为避免追踪带来的冲突问题,也可以通过该方法清除追踪,然后再手动建立关系,进行提交。

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

  1. .NET 云原生架构师训练营(模块二 基础巩固 EF Core 更新和迁移)--学习笔记

    2.4.6 EF Core -- 更新 状态 自动变更检测 不查询删除和更新 并发 状态 Entity State Property State Entity State Added 添加 Uncha ...

  2. Ubuntu 14.04—无法修正错误,因为您要求某些软件包保持现状,就是它们破坏了软件包间的依赖关系 解决办法

    在Ubuntu中使用sudo apt-get install安装是有时候会出现: 无法修正错误,因为您要求某些软件包保持现状,就是它们破坏了软件包间的依赖关系   解决办法 这样的错误,这是因为更新源 ...

  3. C#项目间循环引用的解决办法,有图有真相

    C#项目间循环引用的解决办法,有图有真相 程序间的互相调用接口,c#禁止互相引用,海宏软件,20160315 /// c#禁止互相引用,如果项目[订单]中有一个orderEdit单元,要在项目[进销存 ...

  4. mongodb 更新数据时int32变为double的解决办法 & 教程

    https://www.runoob.com/mongodb/mongodb-mongodump-mongorestore.html mongodb 更新数据时int32变为double的解决办法   ...

  5. 使用EF Core更新与修改生产数据库

    使用EF Core的Code First,在设计阶段,直接使用Database.EnsureCreated()和EnsureDeleted()可以快速删除.更新最新的数据结构.由于没有什么数据,删除的 ...

  6. mongodb 更新数据时int32变为double的解决办法

       场景: 在命令手动的修改签到表的整型字段synState,multi参数是可以更新多条,如果是false则更新一条. db.getCollection("ClassRecordOneD ...

  7. myeclipse工程更新后java图标变为空心的解决办法

    今天用svn更新了工程发现目录结构改变了,同时所有的java文件的图标变成了空心的.解决办法如下 1.右键单击工程目录名,选择properties. 2.选择java bulid path,正常的应该 ...

  8. EF 表中中多次指定了列名解决办法

    这个问题是我们实际开发中遇到过的问题. 可能的原因:数据库在执行数据表迁移的时候,数据表执行成功,最后插入EF数据迁移表__MigrationHistory的时候,没有把所有的命令行完整插入,缺失了一 ...

  9. 关于访问Jira和Confluence服务越来越缓慢的解决办法阐述

    Jira和Confluence部署在同一台服务器上,跑一段时间后,发现访问jira和confluence时,打开越来越缓慢.这是因为根据主机物理内存不同,默认的java虚拟机内存也会不同(一个较低值) ...

  10. setTimeout代替setInterval的写法以及setInterval的弊端以及越来越快的解决办法

    平常经常遇到的一个问题,很多人想间隔时间执行一些事件的时候,第一时间就会想到用setInterval,但是setInterval村子啊不少弊端哦. 弊端1:setInterval会无视错误代码,即使代 ...

随机推荐

  1. OpenHarmony社区运营报告(2023年7月)

     本月快讯 • 2023年7月28日-29日,全球软件质量&效能大会(简称"QECon")圆满举行,OpenAtom OpenHarmony(简称"OpenHar ...

  2. 战码先锋直播预告丨参与ArkUI,共建OpenHarmony繁荣生态

    OpenAtom OpenHarmony(以下简称"OpenHarmony")工作委员会首度发起「OpenHarmony开源贡献者计划」,旨在鼓励开发者参与OpenHarmony开 ...

  3. 许北林:我为什么加入OpenHarmony生态?又为什么要做“启航KP”开发套件?

    许北林 软通动力 资深项目经理 在全球开源趋势下,中国正逐渐成为全球开源软件的主要使用者和核心贡献者.今天我们来认识一位接触 OpenHarmony 不到一年,便带领团队成功开发出一款"启航 ...

  4. Qt:MD5加密

    #include <QCryptographicHash> QString source_value = "123456"; // 待加密原始数据 QCryptogra ...

  5. Spring框架之IOC和AOP底层原理

    ​1.1简介 Spring:春天-->软件行业的春天 2002,首次推出了Spring框架的雏:interface21框架! Spring框架即以interface21框架为基础,经过重新设计, ...

  6. 实训篇-Html-表单练习

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  7. SSM整合项目中使用百度Ueditor遇到的问题。

    问题描述:无法上传图片,提示配置项加载有问题 大致情形:直接下载的ue编辑器,放在了/resources/   目录下,也就是静态资源路径,然后更改web.xml,将tomcat默认拦截器配置放到所有 ...

  8. gitee基于webhooks实现前端简单自动化部署

    1.为什么采用自动化部署 简而言之,程序员优秀传统:懒 =>高级生产力. 基于gitee进行的自动化部署,服务器环境为Ubuntu 基于webhooks进行的自动化部署更加轻快便捷 2.部署步骤 ...

  9. FastWiki一分钟本地离线部署本地企业级人工智能客服

    FastWiki一分钟本地离线部署本地企业级人工智能客服 介绍 FastWiki是一个开源的企业级人工智能客服系统,它使用了一系列先进的技术和框架来支持其功能. 技术栈 前端框架:React + Lo ...

  10. 剑指 Offer II 018(Java). 有效的回文(简单)

    题目: 给定一个字符串 s ,验证 s 是否是 回文串 ,只考虑字母和数字字符,可以忽略字母的大小写. 本题中,将空字符串定义为有效的 回文串 . 示例 1: 输入: s = "A man, ...