探究EFCore8.0同时更新万条记录的SQL生成和执行效率

1. 前言

EF Core 8.0.3是当前最新的版本,针对EFCore ORM(对象关系映射)框架的测试就从来没有停止过。它能够将数据库中的数据映射到应用程序中的对象模型,从而简化了对数据库的操作。通过 EF Core,可以使用面向对象的方式来进行数据库操作,而不必编写复杂的 SQL 查询语句。

随着版本的不断更新,EF Core 在性能和功能方面都得到了持续的优化和增强。新版本不仅修复了 bug,还引入了更多的特性和改进,以提升用户体验和开发效率。微软开发团队也在不断努力优化 EF Core 的性能,使其能够更好地应对各种数据库操作场景。

在处理大数据量下的数据库操作时,性能优化成为开发人员关注的焦点之一。针对大数据量的查询、插入、更新和删除操作,需要考虑如何有效利用 EF Core 提供的功能和优化策略,以确保操作的效率和性能。通过合理的设计和优化,充分发挥 EF Core 在大数据量操作下的优势,提升系统的性能和响应速度。

为此,我们今天来试验:探究EFCore8.0同时更新万条记录的SQL生成和执行效率

2. 启动docker数据库

1、启动docker Mysql 容器
在这里插入图片描述
2、测试数据库是否正常连接,我这里使用的是vscode插件Database Client,可以查看数据库运行状态。
在这里插入图片描述

3. 构建配置项目

1、新增控制台项目

在这里插入图片描述

2、引入EFCore相关包,引入日志包的目的是为了查看生成的sql语句

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>disable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.3" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.3">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.3">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
    <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
  </ItemGroup>

</Project>

3、配置Entity和Context

  [Table("tb_point")]
    public class Point
    {
        [Key]
        public string Uid { get; set; }
        public double X { get; set; }
        public double Y { get; set; }
        public double Z { get; set; }
    }

public class MyDbContext : DbContext
{
    private static ILoggerFactory loggerFactory = LoggerFactory.Create(b=>b.AddConsole());
    public DbSet<Point> Points { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseMySql("server=localhost;port=3306;database=mydb;user=root;password=root;", new MariaDbServerVersion(new Version(8, 0, 27)));
        optionsBuilder.UseLoggerFactory(loggerFactory);
    }
}

注意这里的日志配置。在Context文件中调用日志组件.

4、使用的是Code first 模式。当数据库不存在是,自动构建数据库和表结构

  if (!context.Database.CanConnect())
  {
    context.Database.EnsureCreated();
  }

4. 插入万条数据

构造并插入万条记录

static List<Point> GeneratePoints(int count)
{
    Random random = new Random();
    List<Point> points = new List<Point>();

    for (int i = 0; i < count; i++)
    {
        Point point = new Point
        {
            Uid = Guid.NewGuid().ToString(),
            X = random.NextDouble(),
            Y = random.NextDouble(),
            Z = random.NextDouble()
        };

        points.Add(point);
    }

    return points;
}
private static void Insert()
{
    using (var context = new MyDbContext())
    {
        if (!context.Database.CanConnect())
        {
            context.Database.EnsureCreated();
        }

        List<Point> points = GeneratePoints(10000);

        context.Points.AddRange(points);
        context.SaveChanges();
    }
}

这个时候,我们运行程序,将生成的万条记录,写入数据库中,这个时候,我们可以查看这万条记录是如何由efcore转换为sql语句的。
在这里插入图片描述
从输出的图中,我们可以看出,生成了42条记录为一个Command命令。同时插入1万条记录。生成了接近238个Command指令,每条个Command执行耗时1ms。

5. 查询万条数据

查询万条数据的代码如下

 private static List<Point> Query()
 {
     using (var context = new MyDbContext())
     {
         return context.Points.Take(10000).ToList();
     }
 }

我们看到的查询语句是:
在这里插入图片描述
EFCore 提示我们需要加入Limiting配置或者Orderby等排序操作。执行耗时27ms。生成sql语句

SELECT `t`.`Uid`, `t`.`X`, `t`.`Y`, `t`.`Z`
FROM `tb_point` AS `t`
LIMIT @__p_0 //是我们Take(10000)的参数

6. 更新万条数据

更新万条数据代码如下

static void RefreshPoints(List<Point> points)
{
    Trace.WriteLine(points.Count.ToString());
    Random random = new Random();

    foreach (var point in points)
    {
        // 模拟刷新数据,这里使用随机数
        point.X = random.NextDouble();
        point.Y = random.NextDouble();
        point.Z = random.NextDouble();
    }
}
private static void Update(List<Point> points)
{
    RefreshPoints(points);
    Stopwatch st = new Stopwatch();
    st.Start();
    using (var context = new MyDbContext())
    {
        context.Points.UpdateRange(points);
        context.SaveChanges();
    }
    st.Stop();
    Trace.WriteLine(st.ElapsedMilliseconds.ToString());
}

我们看到的Update语句:

info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (1ms) [Parameters=[@p3='?' (Size = 95), @p0='?' (DbType = Double), @p1='?' (DbType = Double), 
                                            。。。。太长省略。。。。
                                            @p166='?' (DbType = Double)], CommandType='Text', CommandTimeout='30']
      UPDATE `tb_point` SET `X` = @p0, `Y` = @p1, `Z` = @p2
      WHERE `Uid` = @p3;
      SELECT ROW_COUNT();

      UPDATE `tb_point` SET `X` = @p4, `Y` = @p5, `Z` = @p6
      WHERE `Uid` = @p7;
      SELECT ROW_COUNT();
		  。。。。太长省略。。。。
      UPDATE `tb_point` SET `X` = @p164, `Y` = @p165, `Z` = @p166
      WHERE `Uid` = @p167;
      SELECT ROW_COUNT();

这里我们就可知,生成了42条记录为一个Update命令。生成了接近238个Update Command指令,每条个Command执行耗时1ms。
万条记录的刷新耗时为1263ms
在这里插入图片描述

总结

通过Microsoft.Extensions.Logging.Debug在控制台输出完整调试日志,可以直观的看到生成的sql语句,以及数据库在插入和更新大量数据时,sql语句是如何生成的。这对我们观察和使用EFCore,优化性能等指明了优化方向。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dotnet研习社

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值