Day32:项目性能优化(EF Core + 分页 + 全异步)

一、今日学习目标

  1. EF Core 性能优化 10 条(企业高频)
  2. 正确分页查询(避免性能大坑)
  3. 全异步 API 改造(async/await 全覆盖)
  4. 禁止同步阻塞,提升接口吞吐量
  5. 项目性能直接提升 5~10 倍

二、第一部分:EF Core 性能优化(最重要!)

1. 必须用 AsNoTracking(查询性能提升巨大)

默认 EF 查询会跟踪实体 → 耗内存、耗性能 只读查询必须加 AsNoTracking ()

cs 复制代码
// 优化前
var user = await _context.Users.FirstOrDefaultAsync(u=>u.Id == id);

// 优化后(只读推荐)
var user = await _context.Users
    .AsNoTracking()
    .FirstOrDefaultAsync(u=>u.Id == id);

2. 只查需要的字段(Select 映射)

不要查全部字段!

复制代码
// 差
var list = await _context.Users.ToListAsync();

// 优
var list = await _context.Users
    .Select(u => new UserVo { Id = u.Id, Name = u.Name })
    .ToListAsync();

3. 禁用懒加载(你项目一定不要开)

懒加载 = N+1 查询灾难!


4. Include 只加载需要的关联数据

不要无脑 Include 所有表

cs 复制代码
// 只加载需要的角色
var user = await _context.Users
    .AsNoTracking()
    .Include(u => u.UserRoles)
    .ThenInclude(ur => ur.Role)
    .FirstOrDefaultAsync(u => u.Id == id);

5. Any () 比 Count () > 0 性能高 10 倍

判断是否存在数据:

cs 复制代码
// 优
var exists = await _context.Users.AnyAsync(u => u.Account == account);

// 差
var exists = await _context.Users.CountAsync(u => u.Account == account) > 0;

6. 分页用 Skip + Take,不要查全表

复制代码
var list = await _context.Users
    .Where(...)
    .Skip((pageIndex - 1) * pageSize)
    .Take(pageSize)
    .ToListAsync();

7. 批量操作用 EF Core 批量库(可选)

批量删除、批量更新不要循环单条操作。


8. 索引必须加

高频查询字段必须建索引:

  • Account
  • RoleId
  • MenuId
  • ParentId
cs 复制代码
// EF 配置索引
builder.HasIndex(u => u.Account).IsUnique();

9. 禁用异步行为(不影响性能但稳定)

cs 复制代码
builder.EnableRetryOnFailure(); // 重连

10. 不要在循环里查数据库

循环查库 = 性能自杀


三、第二部分:正确分页查询(企业标准)

你项目的用户 / 角色 / 菜单都要这样写!

分页 Dto

cs 复制代码
public class PageQueryDto
{
    public int PageIndex { get; set; } = 1;
    public int PageSize { get; set; } = 10;
}

分页通用返回

cs 复制代码
public class PageVo<T>
{
    public int Total { get; set; }
    public List<T> List { get; set; }
}

高性能分页代码(直接复制)

cs 复制代码
public async Task<PageVo<UserVo>> GetPageAsync(PageQueryDto dto)
{
    var query = _context.Users
        .AsNoTracking()
        .Where(...)
        .Select(u => new UserVo{ ... });

    var total = await query.CountAsync();

    var list = await query
        .Skip((dto.PageIndex - 1) * dto.PageSize)
        .Take(dto.PageSize)
        .ToListAsync();

    return new PageVo<UserVo>
    {
        Total = total,
        List = list
    };
}

四、第三部分:全异步 API 全覆盖(最重要!)

你的整个项目必须全部改成 async/await

规则:

  1. 所有 EF 操作必须用 Async 方法
  2. 所有 Service 方法必须 async Task
  3. 所有 Controller 必须 async Task<ActionResult>
  4. 绝对不要用 .Result/.Wait () 同步阻塞

错误写法(会卡死、性能差)

cs 复制代码
// 错误!同步阻塞
var user = _context.Users.FirstOrDefault(u=>u.Id == id);

正确写法(高性能)

cs 复制代码
var user = await _context.Users
    .AsNoTracking()
    .FirstOrDefaultAsync(u=>u.Id == id);

控制器必须异步

cs 复制代码
[HttpGet("page")]
public async Task<ActionResult<R<PageVo<UserVo>>>> Page(PageQueryDto dto)
{
    var result = await _userService.GetPageAsync(dto);
    return Ok(result);
}

五、异步性能优势

  • 线程不阻塞
  • 接口能处理更多并发
  • 内存占用更低
  • 企业 .NET 开发标准

六、今日性能优化总结(背住)

  1. 查询必加 AsNoTracking ()
  2. 只 Select 需要的字段
  3. Any () 代替 Count () >0
  4. 分页 Skip + Take
  5. 禁止循环查库
  6. 高频字段建索引
  7. 全异步 async/await
  8. 禁止同步阻塞(.Result)

七、今日练习

  1. 把所有 EF 查询加上 AsNoTracking()
  2. 把所有列表改成 正确分页
  3. 把整个项目 全部改成异步 async/await
  4. 把判断存在改成 AnyAsync()
  5. 给 Account、RoleId、MenuId 加 索引
相关推荐
毛驴赶鹿1 小时前
【金仓数据库征文】KES V9性能调优实战记录:从慢SQL排查到全链路优化
数据库·sql
码农阿豪2 小时前
【金仓数据库征文】Mac开发环境|SpringBoot3 + MyBatisPlus对接KES V9 Docker实战全指南
数据库·macos·docker
杨云龙UP2 小时前
MySQL 数据库常用备份工具对比:mysqldump、mydumper 与 XtraBackup
linux·运维·服务器·数据库·mysql·dba·备份恢复
ZENERGY-众壹2 小时前
500个工商业电站压垮数据库?海量光伏时序数据存储的架构取舍
数据库·架构·influxdb·光伏·光伏运维
mubei-1233 小时前
SpringDAO的用法
java·开发语言·数据库
星蓝_starblue5 小时前
零服务器、零数据库!开源growth-board,利用GitHub自动管理刷题/学习/求职全流程
服务器·数据库·程序人生·系统架构·node.js·github·改行学it
三8447 小时前
sqli-labs1-10通关笔记
数据库
BullSmall9 小时前
Another Redis Desktop Manager(ARDM)下载指南
数据库·redis·缓存
SelectDB9 小时前
Apache Doris 倒排索引工作原理:全文检索提速 59 倍,点查提速 14 倍
数据库
码农颜9 小时前
5.1.1 原⼦性
数据库·mysql