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 加 索引
相关推荐
掉头发的王富贵3 小时前
【StarRocks】极限十分钟入门StarRocks
数据库·sql·mysql
Nturmoils3 小时前
WHERE 条件别凭习惯写,常用查询先跑一遍
数据库
亲亲小宝宝鸭1 天前
前端性能监控:web-vitals
前端·性能优化·监控
Databend1 天前
在 AWS 中国峰会逛了一天,我在 Databend 展台看到了 Agent 数据基础设施的新思路
数据库·人工智能·agent
ClouGence2 天前
Oracle 数据同步为什么会出现数据不一致?长事务是常被忽略的原因
数据库·后端·oracle
飞将2 天前
从零实现数据库(2)——HashIndex + IndexManager
数据库
Nturmoils3 天前
订单列表慢查询,先看 WHERE、ORDER BY 和 LIMIT
数据库
渣波3 天前
拒绝 SQL 焦虑!手把手带你用 NestJS + Prisma + DTO 写出“防弹”级后端代码
javascript·数据库·后端
TrisighT4 天前
Electron 跑在鸿蒙 PC 上,单窗口和多窗口内存差 800MB?我抓了 5 组数据
性能优化·electron·harmonyos
倔强的石头_4 天前
KingbaseES 新版MySQL 兼容版体验:旧版迁移 + 功能实测
数据库