首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >EF Core性能翻车?3秒变0.3秒的优化秘籍大公开!

EF Core性能翻车?3秒变0.3秒的优化秘籍大公开!

作者头像
云中小生
发布2025-08-24 08:57:48
发布2025-08-24 08:57:48
13600
代码可运行
举报
运行总次数:0
代码可运行

大家好,我是.NET修仙日记的掌门人。今天要和大家分享的是EF Core查询性能优化的实战经验,这些都是我在实际项目中踩过的坑、趟过的雷,希望能帮助各位道友在数据访问层修炼出更高效的功法!

一、性能问题初现:一个慢查询引发的思考

上周,我们收到用户反馈:"洞府列表加载太慢,要等3-4秒才能显示!" 使用SQL Server Profiler抓取到的查询:

代码语言:javascript
代码运行次数:0
运行
复制
SELECT [p].[Id], [p].[Name], [p].[OwnerId], [p].[CreateTime], 
       [o].[Id], [o].[Name], [o].[Level], [o].[VipLevel]
FROM [Palaces] AS [p]
LEFT JOIN [Immortals] AS [o] ON [p].[OwnerId] = [o].[Id]
WHERE [p].[Type] = 1
ORDER BY [p].[CreateTime] DESC

思考:这个看似简单的查询为何如此之慢?

二、性能诊断三板斧

1. 使用EF Core的日志记录

代码语言:javascript
代码运行次数:0
运行
复制
// Program.cs中配置EF Core日志
builder.Services.AddDbContext<CultivationDbContext>(options =>
    options.UseSqlServer(connectionString)
           .LogTo(Console.WriteLine, LogLevel.Information));

2. 分析执行计划

在SSMS中执行SET STATISTICS IO ON 后运行查询,发现:

  • 逻辑读取次数高达1245次
  • 表扫描而非索引查找

3. 使用Application Insights监控

代码语言:javascript
代码运行次数:0
运行
复制
// 使用TelemetryClient记录查询耗时
var telemetry = new TelemetryClient();
var stopwatch = System.Diagnostics.Stopwatch.StartNew();

var palaces = await dbContext.Palaces
    .Include(p => p.Owner)
    .Where(p => p.Type == PalaceType.Normal)
    .OrderByDescending(p => p.CreateTime)
    .ToListAsync();

stopwatch.Stop();
telemetry.TrackMetric("PalaceQueryDuration", stopwatch.ElapsedMilliseconds);

三、六大优化神功

🚀 神功一:选择性加载 - 只取所需

优化前:

代码语言:javascript
代码运行次数:0
运行
复制
var palaces = await dbContext.Palaces
    .Include(p => p.Owner)  // 加载所有Owner信息
    .ToListAsync();

优化后:

代码语言:javascript
代码运行次数:0
运行
复制
var palaces = await dbContext.Palaces
    .Select(p => new PalaceDto
    {
        Id = p.Id,
        Name = p.Name,
        OwnerName = p.Owner.Name,  // 只取需要的字段
        CreateTime = p.CreateTime
    })
    .ToListAsync();

效果: 查询数据量减少60%,执行时间从1200ms降至400ms

⚡ 神功二:分页查询 - 化整为零

优化前:

代码语言:javascript
代码运行次数:0
运行
复制
// 加载全部数据
var allPalaces = await dbContext.Palaces.ToListAsync(); 

优化后:

代码语言:javascript
代码运行次数:0
运行
复制
var pageSize = 20;
var pageNumber = 1;

var palaces = await dbContext.Palaces
    .OrderBy(p => p.CreateTime)
    .Skip((pageNumber - 1) * pageSize)
    .Take(pageSize)
    .ToListAsync();

效果: 首次加载时间从3s降至200ms

🔍 神功三:索引优化 - 打通任督二脉

在DbContext中配置索引:

代码语言:javascript
代码运行次数:0
运行
复制
modelBuilder.Entity<Palace>()
    .HasIndex(p => new { p.Type, p.CreateTime })
    .IncludeProperties(p => new { p.Name, p.OwnerId });

生成的查询将使用复合索引,性能提升显著。

🧠 神功四:AsNoTracking - 无状态查询

优化前:

代码语言:javascript
代码运行次数:0
运行
复制
var palace = await dbContext.Palaces.FirstAsync(p => p.Id == id);

优化后:

代码语言:javascript
代码运行次数:0
运行
复制
var palace = await dbContext.Palaces
    .AsNoTracking()
    .FirstAsync(p => p.Id == id);

适用场景: 只读查询,不需要更新实体时使用

🧩 神功五:批量操作 - 一气呵成

优化前:

代码语言:javascript
代码运行次数:0
运行
复制
foreach (var item in items)
{
    dbContext.Add(item);
    await dbContext.SaveChangesAsync(); // 每次保存
}

优化后:

代码语言:javascript
代码运行次数:0
运行
复制
dbContext.AddRange(items);
// 批量保存
await dbContext.SaveChangesAsync(); 

效果: 1000条数据插入从30s降至1s

🎯 神功六:编译查询 - 预编译功法

优化前:

代码语言:javascript
代码运行次数:0
运行
复制
// 每次执行都会重新编译
var palace = await dbContext.Palaces
    .FirstOrDefaultAsync(p => p.Id == id);

优化后:

代码语言:javascript
代码运行次数:0
运行
复制
private static readonly Func<CultivationDbContext, int, Task<Palace>> GetPalaceById =
    EF.CompileAsyncQuery((CultivationDbContext db, int id) => 
        db.Palaces.FirstOrDefault(p => p.Id == id));

// 使用预编译查询
var palace = await GetPalaceById(dbContext, id);

效果: 高频查询性能提升40%

四、高级心法:EF Core 8新特性

1. 批量更新/删除

代码语言:javascript
代码运行次数:0
运行
复制
// 传统方式
var palaces = await dbContext.Palaces
    .Where(p => p.Level < 5)
    .ToListAsync();

foreach (var p in palaces)
{
    p.Level = 5;
}
await dbContext.SaveChangesAsync();

// EF Core 7+ 批量更新
await dbContext.Palaces
    .Where(p => p.Level < 5)
    .ExecuteUpdateAsync(setters => setters.SetProperty(p => p.Level, 5));

2. JSON列查询

代码语言:javascript
代码运行次数:0
运行
复制
// 查询JSON列中的属性
var palaces = await dbContext.Palaces
    .Where(p => p.MetadataJson.Contains("\"VIP\": true"))
    .ToListAsync();

五、性能优化前后对比

指标

优化前

优化后

提升幅度

洞府列表加载

3200ms

450ms

7倍

批量插入1000条

30s

1.2s

25倍

CPU占用峰值

85%

25%

大幅降低

内存消耗

420MB

180MB

57%减少

六、修仙感悟

经过这一轮优化,我深刻体会到EF Core性能优化的几个关键点:

  • 知彼知己: 先测量,再优化,不要凭猜测
  • 有所取舍: 不是所有场景都需要极致优化
  • 与时俱进: 善用EF Core的新特性
  • 综合调理: 数据库设计、索引、查询方式要协同优化

(点击关注,修炼不迷路👇

▌转载请注明出处,渡人渡己

🌟 感谢道友结缘! 若本文助您突破修为瓶颈,不妨[打赏灵丹]或[转发功德],让更多.NET道友共参CLR天道玄机。修真之路漫漫,我们以代码为符,共绘仙途!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-07-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 .NET修仙日记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、性能问题初现:一个慢查询引发的思考
  • 二、性能诊断三板斧
    • 1. 使用EF Core的日志记录
    • 2. 分析执行计划
    • 3. 使用Application Insights监控
  • 三、六大优化神功
    • 🚀 神功一:选择性加载 - 只取所需
    • ⚡ 神功二:分页查询 - 化整为零
    • 🔍 神功三:索引优化 - 打通任督二脉
    • 🧠 神功四:AsNoTracking - 无状态查询
    • 🧩 神功五:批量操作 - 一气呵成
    • 🎯 神功六:编译查询 - 预编译功法
  • 四、高级心法:EF Core 8新特性
    • 1. 批量更新/删除
    • 2. JSON列查询
  • 五、性能优化前后对比
  • 六、修仙感悟
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档