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 加 索引
相关推荐
摇滚侠6 小时前
《On Java 中文版 基础卷》阅读笔记 实现隐藏 12
java·数据库·笔记
川石课堂软件测试6 小时前
学习软件测试需掌握的一些知识
数据库·python·sql·功能测试·网络协议·mysql·http
2501_937767167 小时前
自建 SQL 数据库定时备份踩坑复盘与落地思路
数据库·sql
silvamux_8 小时前
10款模型写种草笔记:可发布性、返工来源与成本数据缺口复盘
java·数据库·笔记
I Am a robert girl10 小时前
从 GameHorizon 拆解多时间尺度游戏评测:数据管线、离线基准与在线回放的三层设计
数据库·游戏·多时间尺度·数据管线·游戏评测·离线基准·在线回放
躺柒10 小时前
读数据安全架构设计与实践17安全架构案例(下)
数据库·安全·零信任·安全架构·防火墙·无边界
SelectDB10 小时前
实时更新排错现场:导入了却查到旧数据,和 FINAL 越加越慢的两条路
大数据·数据库·数据分析
SelectDB11 小时前
拿 Planet 的账单一行行对下来,我总结出三个可自查的校验点
大数据·数据库·数据分析
SelectDB11 小时前
Apache Doris + Vane:让多模态数据进入业务分析
大数据·数据库·数据分析
pjj1985413 小时前
ClickHouse 与 Doris 数据库技术梳理:看懂分析型数据库
数据库·clickhouse