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 加 索引
相关推荐
Yeyu14 小时前
Binder 阻塞检测:跨进程通信的性能陷阱与监控方案
android·性能优化
AC赳赳老秦15 小时前
OpenClaw任务复盘自动化:统计每日完成工作、遗留问题,优化工作节奏
java·大数据·linux·运维·服务器·数据库·openclaw
星夜夏空9915 小时前
FreeRTOS学习(12)——任务通知
学习·性能优化
AOwhisky15 小时前
学习自测(MySQL系列第一期、第二期)
linux·运维·数据库·学习·mysql·云计算
我叫张小白。15 小时前
Redis BitMap实现用户签到功能
数据库·redis·缓存·fastapi
大数据魔法师15 小时前
MongoDB(九) - MongoDB分片集安装与配置
数据库·mongodb
念何架构之路15 小时前
存储层技术MySQL
数据库·mysql
cfm_291415 小时前
Redis高并发多级缓存介绍 + JDHotkey热点探测了解
数据库·redis·缓存
yun呐15 小时前
mysql数据库误删恢复
数据库·mysql·adb
IvorySQL16 小时前
PostgreSQL 技术日报 (6月3日)|复制日志补丁更新,PG 黑客坊开启
数据库·人工智能·postgresql