只读查询的“零分配”之路:EF Core + Dapper + MemoryPack 的组合优化

🚀 只读查询的“零分配”之路:EF Core + Dapper + MemoryPack 的组合优化

目标:在只读接口的热路径上,把关键路径上的临时对象与复制降至最低,降低 GC 压力与 p95/p99 尾延迟,提升吞吐。
主线(“三轨并行”)

  1. EF Core:编译查询 + AsNoTracking() + 显式投影(中低 QPS 的默认路径);
  2. Dapper:手写 SQL + 非缓冲流式(buffered:false)+ 扁平 DTO(热点/大结果集);
  3. 序列化MemoryPack + HttpResponse.BodyWriterIBufferWriter<byte>直写;浏览器/通用生态 → 回退 System.Text.Json(配 Source Generator)。

ℹ️ 术语澄清:“零分配”是工程目标,即尽量将关键路径上的中间对象/复制去除或显著降低;受字符串、网络缓冲、运行库内部对象等影响,端到端“绝对零分配”不可达。



🏗️ 架构鸟瞰(三轨并行总览)

序列化轨
Dapper 轨
EF Core 轨
EF Core 轨
Dapper 轨
DTO 流
Row 流
PipeWriter.FlushAsync
MemoryPack / JSON 写出
IBufferWriter
Dapper
buffered:false
🗄️ PostgreSQL
EF Core 编译查询
AsNoTracking + 投影
👤 Client
ASP.NET Core Minimal API

1) 🧭 适用与边界

  • 适用:只读 API、列表页、导出、报表快照;一致性级别以“读已提交/快照读”为主。
  • 暂不讨论:复杂对象图(建议显式投影 DTO,避免 Include 拉整图)、强事务读写混合。

2) ⚙️ 三轨并行:总体设计

  • EF Core 轨(可维护)
    AsNoTracking() + 投影 DTO + 编译查询;用 TagWith("hotpath:...") 标注,便于日志/执行计划定位。编译查询把 LINQ 预编译为委托,适合高重复度查询(是否采用以基准评估为准)。

  • Dapper 轨(性能优先)
    稳定 SQL + 扁平 DTO,必要时 buffered:false 非缓冲流式,明显降低大结果集峰值内存(连接在枚举全过程必须保持打开)。

  • 序列化/输出轨
    MemoryPackIBufferWriter<byte>/PipeWriter 直写Accept 不支持时回退 System.Text.Json(建议启用 Source Generation 以减少反射、兼容 AOT/Trim)。对 BodyWriter调用 FlushAsync 才会把缓冲推入响应体。


3) 🧩 EF Core:编译查询 + 禁跟踪 + 显式投影

准则

  1. 编译查询EF.CompileQuery/CompileAsyncQuery 将 LINQ 表达式编译为委托,绕过查询缓存查找,在高重复度场景更优(先做基准)。
  2. 只读禁跟踪AsNoTracking() 是只读查询的常规选择;AsNoTrackingWithIdentityResolution 会在无跟踪下做身份解析(去重相同主键实例),仅在确需语义时使用。
  3. 查询标签TagWith("hotpath:xxx") 写入 SQL 注释,帮助把 LINQ 与生成 SQL/日志对应。

示例

// EF Core 8/9
using Microsoft.EntityFrameworkCore;

public sealed record OrderDto(int Id, string No, decimal Amount, DateTime CreatedAt);

public static class Queries
{
   
   
    // 编译查询:只读 + 投影 + 限制条数
    public static readonly Func<AppDbContext, int, IAsyncEnumerable<OrderDto>>
    GetRecentOrders = EF.CompileAsyncQuery((AppDbContext db, int take) =>
        db.Orders
          .TagWith("hotpath:list-orders")   // 起始处标注,便于日志/执行计划定位
          .AsNoTracking()
          .OrderByDescending(x => x.CreatedAt)
          .Select(x => new OrderDto(x.Id, x.No, x.Amount, x.CreatedAt))
          .Take(take));
}

4) 🧵 Dapper:扁平 DTO + 非缓冲流式(热点/大结果集)

  • 列顺序与 DTO 对齐,降低映射开销;
  • 非缓冲buffered:false 使结果延迟枚举,结合“边读边写”显著降低峰值内存;务必保证连接在枚举全过程保持打开
  • AOT/裁剪友好:可评估 Dapper.AOT(构建期生成/拦截器),减少运行时反射/发射。

示例(流式 JSON:取消令牌 + 分段 Flush + STJ SourceGen + 统一限量 + 无 RegisterForDispose)

using System.Data;
using System.Text.Json
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kookoos

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值