关于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. 战码先锋直播预告丨参与ArkUI,共建OpenHarmony繁荣生态

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

  2. 聊聊ChatGLM3多用户并发API调用的问题

    转载请备注出处:https://www.cnblogs.com/zhiyong-ITNote 背景 目前在公司内部4张A10的GPU服务器上部署了ChatGLM3开源模型:然后部署了官方默认的web_ ...

  3. 华为会员开放服务(Membership Kit),助力移动应用快速建设会员生态

    会员开放服务(Membership Kit)是华为面向开发者提供的券码开放能力,开发者可以通过Membership Kit开展灵活多样的营销活动,助力开发者建设会员生态,实现用户运营与增量创收的目标. ...

  4. SMOKE多模式排放清单处理技术及EDGAR/MEIC清单制作与VOCs排放量核算

    大气污染问题既是局部.当地的,也是区域的,甚至是全球的.本地的污染物排放除了对当地造成严重影响外,同时还会在动力输送作用下,极大地影响下风向地区的大气环境状况.数值模式模拟是分析大气污染物时空分布和成 ...

  5. CENTOS 6.4 编译安装APACHE PHP MYSQL ZEND【转载未测试】

    CENTOS 6.4 编译安装APACHE PHP MYSQL ZEND 由 cache 发布于 WWW 2013-07-21 [ 5560 ] 次浏览 [ 0 ] 条评论 标签: LAMP 搞网站跑 ...

  6. maven 创建spring boot 需要的配置[一]

    前言 之所以写这个是因为现在官方推荐云创建: 所以标注一下maven project,创建后,如何导入spring boot. 正文 1.步骤一 在pom.xml 中加入: <dependenc ...

  7. 力扣74(java&python)-搜索二维矩阵(中等)

    题目: 编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值.该矩阵具有如下特性: 每行中的整数从左到右按升序排列.每行的第一个整数大于前一行的最后一个整数. 示例 1: 输入:matri ...

  8. KubeVela 1.5:灵活框选 CNCF 原子能力打造独特的企业应用发布平台

    简介: KubeVela 1.5 于近日正式发布.在该版本中为社区带来了更多的开箱即用的应用交付能力,包括新增系统可观测:新增 Cloud Shell 终端,将 Vela CLI 搬到了浏览器:增强的 ...

  9. 玩转DB里的数据 — 阿里云DMS任务编排之简介和实操

    1.任务编排介绍 数据库是企业IT系统里的重要基础设施,里面存储了大量有价值的数据资产,如:交易数据.客户数据.订单数据,等等.其实,数据库在企业里一直扮演着一个数据生产者(Producer)的角色, ...

  10. 实时化或成必然趋势?新一代 Serverless 实时计算引擎

    作者:高旸(吾与),阿里巴巴高级产品专家 本文由阿里巴巴高级产品专家高旸(吾与)分享,主要介绍新一代Serverless实时计算引擎的产品特性及核心功能. 一.实时计算 Flink 版 – 产品定位与 ...