EF Core优化技巧之预热处理

news2024/11/18 13:36:40

在这里插入图片描述

1. 前言

今天和听到同事们在讨论一个关于使用EFCore时,为什么第一次查询数据库总是很慢的原因。我们在工作中经常使用EFCore进行数据访问,但发现每次第一次查询都需要较长的时间,这给我们带来了困扰。因此,我们聚在一起,探讨了这个问题的原因和可能的解决方案。通过查询相关资料,于是就有了这篇博客,现在分享给有需要的你。

2. EFCore首次使用缓慢

在使用Entity Framework Core(EF Core)时,首次查询可能会比较慢,这是因为EF Core需要进行模型构建、元数据加载、数据库连接建立和查询计划生成等操作。那么有什么办法可以解决这个问题呢,答案之一就是使用EF Core的预热来处理问题。

3. 什么是EF Core的预热问题?

EF Core是一个轻量级、可扩展的ORM(对象关系映射)框架,用于在.NET应用程序中处理数据库操作。在应用程序启动时,EF Core需要进行一些初始化操作,如构建模型、加载元数据和建立数据库连接等。这些操作会导致首次查询的耗时增加,影响应用程序的性能。当使用Entity Framework Core(EF Core)进行数据库操作时,会涉及以下几个操作:

  1. 模型构建(Model Building):
    在使用EF Core之前,需要定义领域模型(Domain Model),即表示数据库表格的CLR对象。可以使用属性注解、Fluent API或实现IEntityTypeConfiguration接口等方式来配置模型。在运行时,EF Core会根据模型定义生成相应的数据库表结构。
  2. 元数据加载(Metadata Loading):
    EF Core通过反射和模型构建过程中的元数据提供程序来加载模型的元数据。这些元数据包括实体类型的属性、关系、索引等信息。在第一次创建DbContext实例时,EF Core会从模型构建器中加载元数据。
  3. 数据库连接建立(Database Connection Establishment):
    当执行数据库操作时,EF Core会根据配置连接字符串(Connection String)建立与数据库的连接。连接字符串包含数据库服务器的地址、身份验证方式、数据库名称等信息。EF Core会根据连接字符串选择合适的数据库提供程序来建立连接。
  4. 查询计划生成(Query Plan Generation):
    当执行查询操作时,EF Core会将LINQ查询表达式或查询方法转换为相应的SQL查询语句。这个过程称为查询翻译(Query Translation)。EF Core会根据查询表达式和模型的元数据生成查询计划,包括选择合适的表格、列、关联以及执行顺序等。

上述的操作都是在第一次进行数据库查询时执行的,因此首次查询可能会比较慢。这是因为EF Core需要进行模型构建、元数据加载、数据库连接建立和查询计划生成等操作。为了优化应用程序的性能,可以采取预热操作,提前执行这些操作,从而减少首次查询的耗时。

4. 预热EF Core的解决方案

为了解决EF Core的预热问题,我们可以采取以下措施来优化应用程序的性能:

  1. 显式调用EnsureCreated方法
    在EF Core 3.0及更高版本中,可以通过显式调用EnsureCreated方法来预先构建模型并加载元数据。这样,在第一次查询时,EF Core就不需要再执行这些操作,从而减少查询的耗时。示例代码如下:
using (var context = new MyDbContext())
{
    context.Database.EnsureCreated();
}
  1. 执行迁移操作
    在这里插入图片描述

如果应用程序使用了EF Core的迁移功能,我们可以在应用程序启动时执行迁移操作。这可以通过调用Database.Migrate方法来实现。该方法会执行所有的迁移操作,并初始化数据库中的表结构和数据。示例代码如下:

using (var context = new MyDbContext())
{
    context.Database.Migrate();
}
  1. 预热连接池
    EF Core使用连接池来管理数据库连接。在应用程序启动时,我们可以预先创建和配置一组数据库连接,以减少首次查询时连接建立的时间。这可以通过设置连接池的MinPoolSize属性和调用连接的Open方法来实现。示例代码如下:
var connectionString = "Server=(localdb)\\mssqllocaldb;Database=MyDatabase;Trusted_Connection=True;";
for (int i = 0; i < 10; i++)
{
    var connection = new SqlConnection(connectionString);
    connection.Open();
    connection.Close();
}

5. 测试预热与不预热的对比

为了验证预热操作对EF Core性能的影响,我们可以编写测试代码来比较预热与不预热的情况下的查询执行时间。具体步骤如下:

  1. 创建一个简单的测试应用程序,包含一个使用EF Core的查询操作。
  2. 在应用程序的入口点处,分别添加预热操作和不预热操作的代码。
  3. 编写测试方法,分别对应预热和不预热的情况。在每个测试方法中,创建一个新的DbContext实例,并执行相同的查询操作。
  4. 使用Stopwatch类来测量每个测试方法的执行时间,并比较两者之间的差异。

通过以上步骤,我们可以得出预热与不预热的情况下查询执行时间的对比结果,从而判断预热操作对EF Core性能的影响。

5.1 示例代码

在这里插入图片描述

  1. 初始化数据库,写入10万条数据
using System;
using Microsoft.EntityFrameworkCore;

namespace EFCoreWarmup
{
    // 定义实体类
    public class User
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    // 定义数据库上下文
    public class AppDbContext : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            string connectionString = "Data Source=mydb.db";
            optionsBuilder.UseSqlite(connectionString);
        }

        public DbSet<User> Users { get; set; }
    }

    class Program
    {
        // 初始化并添加10万条记录
	    static void InitializeAndAddRecords(AppDbContext context)
	    {
	        const int recordsToAdd = 100000;
	        for (int i = 1; i <= recordsToAdd; i++)
	        {
	            context.Users.Add(new User { Name = $"User_{i}" });
	            
	            // 每1000条保存一次以提高效率
	            if (i % 1000 == 0)
	            {
	                context.SaveChanges();
	                context.Dispose();
	                context = new AppDbContext(); // 重新创建上下文以确保内存管理
	            }
                Console.WriteLine(i);
	        }
	    }

        static void Main(string[] args)
        {
           using(var db = new AppDbContext())
           {
             db.Database.EnsureCreated();
             InitializeAndAddRecords(db); 
           }
        }
    }
}

2.预热前的执行查询

    /// <summary>
    /// 测试预热前执行查询
    /// </summary>
    static void TestEfCoreWithoutWarmup()
    {
        var stopwatch = Stopwatch.StartNew();
        using (var context = new AppDbContext())
        {
            var users = context.Users.ToList().Take(100);
        }
        stopwatch.Stop();
        Console.WriteLine($"预热前执行时间: {stopwatch.ElapsedMilliseconds} ms");
    }
    static void Main(string[] args)
    {
       TestEfCoreWithoutWarmup();
    }
  1. 运行结果

在这里插入图片描述
4. 预热后的执行查询

/// <summary>
        /// 测试预热后执行查询
        /// </summary>
        static void TestEfCoreWithWarmup()
        {
            var stopwatch = Stopwatch.StartNew();
            using (var context = new AppDbContext())
            {
                var users = context.Users.ToList();
            }
            stopwatch.Stop();
            Console.WriteLine($"预热后执行时间: {stopwatch.ElapsedMilliseconds} ms");
        }
        static void Main(string[] args)
        {
            // 执行预热
            using (var context = new AppDbContext())
            {
                context.Database.EnsureCreated();
            }
            TestEfCoreWithWarmup();
        }
  1. 运行结果
    在这里插入图片描述
  2. 运行结果对比

预热前后的对比,有接近1.2s的差距。由此可见预热的情况,在一定程度上提高了首次执行的效率。

6. EF Core 预热处理的优化技巧

提前执行一些查询操作,以便 EF Core 可以缓存查询计划、连接到数据库并建立连接池等资源,下面是一些 EF Core 预热处理的优化技巧:

  1. 在应用程序启动时进行预热处理:
    在应用程序启动时,执行一些常见的查询操作,以便 EF Core 可以缓存查询计划并建立数据库连接池。这样,在后续的请求中,EF Core 就可以直接使用已经建立好的连接和查询计划,提高性能和响应速度。
  2. 使用后台任务进行预热处理:
    可以将预热处理操作放在一个后台任务中,以避免应用程序启动时的阻塞。例如,可以使用 .NET Core 中的 Hosted Service 或者定时任务库(如 Hangfire)来执行预热处理。
  3. 选择适当的查询进行预热处理:
    根据应用程序的需求,选择一些常用的查询进行预热处理。这些查询通常是应用程序中频繁执行的查询,可以帮助 EF Core 建立查询计划并缓存结果,从而提高性能。
  4. 注意预热处理的时机和频率:
    预热处理不应该过于频繁,否则可能会对数据库造成额外的负担。根据应用程序的特点和数据库的性能,选择适当的时机和频率进行预热处理。
  5. 监控和调整预热处理的效果:
    在进行预热处理后,监控应用程序的性能和响应速度变化。如果发现预热处理没有达到预期的效果,可以考虑调整预热处理的查询内容或者时机。

7. 总结

EF Core的预热问题是由于模型构建、元数据加载、数据库连接建立和查询计划生成等操作导致的。为了提高应用程序的性能,我们可以采取上述解决方案来优化EF Core的预热问题。通过显式调用EnsureCreated方法、执行迁移操作和预热连接池,我们可以减少首次查询的耗时,并提升应用程序的性能。

希望本文对你理解和解决EF Core的预热问题有所帮助!

8. 参考文档:

  • EF优化之启动预热-CSDN博客
  • C#中 EF(EntityFramework) 性能优化_c# ef core 预热-CSDN博客
  • C# EFCore学习总结-CSDN博客
  • 探究EFCore8.0同时更新万条记录的SQL生成和执行效率
  • 掌握EF Core:全方位面试指南,助你从初级到高级轻松晋级

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2169442.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis实战--Redis应用过程中出现的热门问题及其解决方案

Redis作为一种高性能的key-value数据库&#xff0c;广泛应用于缓存、消息队列、排行榜等场景。然而&#xff0c;在实际应用中&#xff0c;随着业务规模的不断扩大和访问量的持续增长&#xff0c;缓存系统也面临着诸多挑战&#xff0c;其中最为典型的便是缓存穿透、缓存击穿和缓…

Ansible集群服务部署案例

案例描述 本案例共讲述了多个节点部署Elk集群日志分析系统&#xff0c;分别在三个节点使用ansible部署Kibana、Logstash以及Elasticsearch服务。 案例准备 1. 规划节点 IP 主机名 节点 192.168.100.25 ansible Ansible节点 192.168.100.35 node1 Elasticsearch/Kiba…

shell用文件管理备份文件路径

从文件中读取文件路径 while IFS read -r s; do&#xff1a;这行代码启动一个 while 循环&#xff0c;逐行读取 001.sh 文件的内容。 IFS&#xff1a;将内部字段分隔符&#xff08;IFS&#xff09;设置为空&#xff0c;确保读取整行而不被默认的空格和换行符分隔。这样可以保…

知识管理数据库

知识管理数据库&#xff0c;可以分为几类&#xff1a; 灵感库、卡片库、作品库。 灵感库&#xff0c;通常是素材&#xff0c;想法。 片库&#xff0c;是完整的&#xff0c;成段落的文字。 作品库&#xff0c;是文章、专栏&#xff0c;或者书籍。 这三者的关系&#xff0c;好比…

java通过webhook给飞书发送群消息

现在使用飞书的人越来越多了&#xff0c;飞书有一个最大的好处&#xff0c;可以使用webhook简便的发送群消息。而在工作中&#xff0c;也经常会因为一些运维方面的工作&#xff0c;需要给飞书发送群消息&#xff0c;来实时提醒相关负责人,及时处理工作。 一、先看一下效果吧&a…

【计算机网络】网络层详解

文章目录 一、引言二、IP 基础知识1、IP 地址2、路由3、IP报文4、IP报文的分片与重组 三、IP 属于面向无连接型四、IP协议相关技术1、DNS2、ICMP3、NAT技术4、DHCP 一、引言 TCP/IP的心脏是网络层。这一层主要由 IP 和 ICMP 两个协议组成。网络层的主要作用是“实现终端节点之…

Visual Studio Code下载安装及汉化

官网&#xff1a;https://code.visualstudio.com/ 按照指示一步步操作即可&#xff1a; 汉化&#xff1a;

Stable Diffusion零基础学习

Stable Diffusion学习笔记TOP12 _插件篇之ControlNet功能篇 ControlNet目前支持的10多种预处理器&#xff0c;根据数据检测种类可分为两种类型&#xff1a; 1、功能型&#xff1a;拥有着不同的能力 2、构图型&#xff1a;控制着SD扩散图形的构图规则 Seg语义分割&#xff1a…

2025年第十届智能信息技术国际会议 (ICIIT 2025)即将召开!

第10届智能信息技术国际会议&#xff08;ICIIT 2025&#xff09;将于2025年2月20日至23日在越南河内举行。ICIIT系列会议将每年举行&#xff0c;为智能信息技术及相关领域提供互动论坛&#xff0c;除了越南的研究人员外&#xff0c;会议小组还欢迎来自世界各地的有兴趣与该地区…

SpringBoot框架快速入门

1、起步依赖 1.概述 在入门案例中&#xff0c;我们引入了web依赖和test依赖&#xff0c;这两个依赖是SpringBoot中的starter依赖&#xff0c;starter依赖也被称为起步依赖 SpringBoot 在配置上相比Spring要简单许多&#xff0c;其核心在于spring-boot-starter&#xff0c;在使…

Django Web开发基础介绍

概述 Django 是后端Python的 Web 开发框架&#xff0c;主要任务是处理与服务器和数据库相关的事务&#xff0c;模板渲染动态生成网页内容只是其中一部分。 Django 内置了的模板引擎&#xff0c;可以将 HTML 页面与 Python 代码进行分离。模板引擎提供了丰富的模板标签和过滤器…

???Ansible-使用roles

文章目录 一、Ansible的内置的或官方推荐创建的目录及文件介绍roles目录解释1、roles/自定义角色名目录下2、roles/自定义角色名目录/tasks目录下3、roles/自定义角色名目录/handlers目录下4、roles/自定义角色名目录/templates目录下5、roles/自定义项目名目录/files目录下6、…

vscode【实用插件】Notes 便捷做笔记

安装 在 vscode 插件市场的搜索 Notes点 安装 安装成功后&#xff0c;vscode 左侧栏会出现 使用 初次使用 需先选择一个本地目录 重启 vscode 后&#xff0c;得到 切换笔记目录 新建笔记 快捷键为 Alt N 默认会创建 .md 文件 配合插件 Markdown Preview Enhanced 预览 .md…

一文讲清CSS基础之浮动float原理

文章目录 前言1、演示基础代码2、块级元素和行级元素都可以开启浮动&#xff0c;开启后不会独占一行3、块级元素独占一行&#xff0c;如果上方有行级元素则空出行级元素的高度4、开启浮动后元素会脱离文档流&#xff0c;浮动流宽高由内容撑开5、浮动元素的宽高也可以设定6、行内…

华为GaussDB数据库(单机版)在ARM环境下的安装指南

一、软件版本 机器配置&#xff1a;8核16G&#xff0c;CPU: Huawei Kunpeng 920 2.9GHz操作系统&#xff1a;EulerOS 2.8 64bit with ARM数据库版本&#xff1a;GaussDB Kernel 505.1.0 build 44f4fa53 二、部署流程 2.1 新建用户 ① 以omm用户为例&#xff0c;添加一个omm用…

使用C#,MSSQL开发的钢结构加工系统

很久以前的项目&#xff0c;上位机使用C#开发。数据库使用mssql。控制系统选用了三菱PLC&#xff0c;上位机和PLC之间走ModbusTCP通讯协议。 主要功能&#xff1a;读取加工文件&#xff08;csv格式&#xff09;&#xff0c;导入到数据库&#xff0c;并根据机床刀具规则&#x…

Python | Leetcode Python题解之第438题找到字符串中所有字母异位词

题目: 题解&#xff1a; class Solution:def findAnagrams(self, s: str, p: str) -> List[int]:s_len, p_len len(s), len(p)if s_len < p_len:return []ans []count [0] * 26for i in range(p_len):count[ord(s[i]) - 97] 1count[ord(p[i]) - 97] - 1differ [c !…

Unity 热更新(HybridCLR+Addressable)-创建Addressable资源

三、创建Addressable资源 创建三个文件夹&#xff0c;放Addressable资源&#xff0c;里面对应放程序集&#xff0c;预制体以及场景 拖拽到Addressable Groups对应组中 其中文件名太长&#xff0c;带着路径&#xff0c;可以简化名字 创建一个脚本&#xff0c;对于这个脚本进行一…

在实时语音交互上超过GPT-4o,端到端语音模型Mini-Omni部署

Mini-Omni是清华大学开源的多模态大型语言模型&#xff0c;具备实时语音输入和流式音频输出的能力。 Mini-Omni模型能够一边听、一边说&#xff0c;一边思考&#xff0c;类似于ChatGPT的语言对话模式。 Mini-Omni模型的主要特点是能够直接通过音频模态进行推理&#xff0c;并…

python全栈学习记录(十六)模块与包

模块与包 文章目录 模块与包一、模块1.模块的导入方式2.模块的循环导入问题3.搜索路径与优先级 二、包1.包的使用2.绝对导入与相对导入 三、一般工程的开发目录规范 一、模块 模块是一系列功能的集合体&#xff0c;常见的模块形式&#xff08;自定义模块、第三方模块、内置模块…