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 加 索引
相关推荐
Dxy12393102161 小时前
Python请求方式介绍:JSON、表单及其他常见数据传输格式
数据库·python·json
环流_3 小时前
Redis中string类型的应用场景
数据库·redis·缓存
倔强的石头_3 小时前
拒绝被复杂报表拖垮!HTAP场景下“标量子查询消除”硬核调优指南
数据库
环流_3 小时前
redis中list类型
数据库·redis·list
jiayong234 小时前
Tool Permission 与 Sandbox 的安全流水线:Agent 工具系统的工程边界
java·数据库·安全·agent
三无推导4 小时前
OpenHuman 开源项目详解:个人 AI 助手架构与核心技术拆解
人工智能·性能优化·架构·开源·ai助手
weixin_444012934 小时前
如何在MongoDB中实现按时间跨度的分片路由_时间序列范围分片与冷热节点架构
jvm·数据库·python
六月雨滴4 小时前
块(Block)管理
数据库·oracle·dba
东风破1374 小时前
DM存储过程及系统 表,系统视图,以及常规的运维SQL语句
运维·数据库·dm达梦数据库