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

🏰 365bet现场走地盘 📅 2026-07-18 09:17:52 👤 admin 👁️ 2507 👑 446
EF Core性能翻车?3秒变0.3秒的优化秘籍大公开!

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

一、性能问题初现:一个慢查询引发的思考上周,我们收到用户反馈:"洞府列表加载太慢,要等3-4秒才能显示!" 使用SQL Server Profiler抓取到的查询:

代码语言:javascript复制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复制// Program.cs中配置EF Core日志

builder.Services.AddDbContext(options =>

options.UseSqlServer(connectionString)

.LogTo(Console.WriteLine, LogLevel.Information));

2. 分析执行计划在SSMS中执行SET STATISTICS IO ON 后运行查询,发现:

逻辑读取次数高达1245次表扫描而非索引查找3. 使用Application Insights监控代码语言:javascript复制// 使用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复制var palaces = await dbContext.Palaces

.Include(p => p.Owner) // 加载所有Owner信息

.ToListAsync();

优化后:

代码语言:javascript复制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复制// 加载全部数据

var allPalaces = await dbContext.Palaces.ToListAsync();

优化后:

代码语言:javascript复制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复制modelBuilder.Entity()

.HasIndex(p => new { p.Type, p.CreateTime })

.IncludeProperties(p => new { p.Name, p.OwnerId });

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

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

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

优化后:

代码语言:javascript复制var palace = await dbContext.Palaces

.AsNoTracking()

.FirstAsync(p => p.Id == id);

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

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

代码语言:javascript复制foreach (var item in items)

{

dbContext.Add(item);

await dbContext.SaveChangesAsync(); // 每次保存

}

优化后:

代码语言:javascript复制dbContext.AddRange(items);

// 批量保存

await dbContext.SaveChangesAsync();

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

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

代码语言:javascript复制// 每次执行都会重新编译

var palace = await dbContext.Palaces

.FirstOrDefaultAsync(p => p.Id == id);

优化后:

代码语言:javascript复制private static readonly Func> 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复制// 传统方式

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复制// 查询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天道玄机。修真之路漫漫,我们以代码为符,共绘仙途!

皇家推荐

「悼」字组词
365bet现场走地盘

「悼」字组词

📅 11-02 👁️ 7483
[世界杯]秀萌娃 宝宝助阵俄罗斯世界杯
日博best365

[世界杯]秀萌娃 宝宝助阵俄罗斯世界杯

📅 09-21 👁️ 2757
在日本只卖23万的普拉多,为什么在国内卖到了43万?
365bet现场走地盘

在日本只卖23万的普拉多,为什么在国内卖到了43万?

📅 01-11 👁️ 6172
穫的解释
365bet现场走地盘

穫的解释

📅 07-12 👁️ 2842
红黄牌纪录(揭秘犯规之战——记录罚牌数量最多的比赛与球员)
为什么男人硬了就会流水?
日博best365

为什么男人硬了就会流水?

📅 01-05 👁️ 7879