当前位置: 首页 > news >正文

ABP VNext + EF Core 二级缓存:提升查询性能

ABP VNext + EF Core 二级缓存:提升查询性能 🚀


📚 目录

  • ABP VNext + EF Core 二级缓存:提升查询性能 🚀
    • 引言 🚀
    • 一、环境与依赖 🛠️
    • 二、集成步骤 ⚙️
      • 2.1 安装 NuGet 包
      • 2.2 注册缓存服务与拦截器
      • 2.3 对特定查询启用缓存 🎯
    • 三、缓存依赖与失效 🔄
    • 四、性能对比测试 📈
      • 4.1 测试环境 🖥️
      • 4.2 对比指标 🔥
    • 五、最佳实践与注意事项 ⚠️
    • 六、高级配置 🧩


引言 🚀

TL;DR

  • 集成 EFCoreSecondLevelCacheInterceptor v5.3.1,为 ABP VNext 应用添加跨 DbContext、跨请求的二级缓存,显著降低重复查询开销
  • 几行配置即可启用内存或 Redis 缓存,并支持自动失效与手动失效策略 🔄
  • 支持按实体类型或表名缓存,无需手动管理复杂缓存键 🛡️
  • 实测:平均响应时间由 ~120 ms 降至 ~15 ms,QPS 从 ~500 提升至 ~3 500,数据库访问次数减少至 1 次/秒 📊

关系型数据库在高并发场景下常见瓶颈包括 CPU、IO 与连接数。EF Core 默认仅在单个 DbContext 生命周期内缓存实体,请求结束后即释放。引入二级缓存(跨 DbContext、跨请求)可显著减少重复查询开销,缓解数据库压力。


一、环境与依赖 🛠️

  • 运行平台:.NET 6.0 LTS + ABP VNext 6.x

  • EF Core 版本:6.x

  • EFCoreSecondLevelCacheInterceptor:5.3.1

  • 缓存提供者

    • 内存:EFCoreSecondLevelCacheInterceptor.MemoryCache
    • Redis:EFCoreSecondLevelCacheInterceptor.StackExchange.Redis
  • 其他依赖Volo.Abp.EntityFrameworkCore

  • ABP CLIVolo.Abp.Cli v6.x

  • 前提:项目已集成 EF Core 与 ABP 基础模块,已配置连接字符串与常规 DbContext

  • 注意:如需在 .NET 7/8 下使用,请升级到 ABP 7.x 或 ABP 8.x 🔄


二、集成步骤 ⚙️

项目启动
ConfigureServices
注册缓存服务
配置全局策略
UseMemoryCacheProvider/Redis
CacheAllQueries(...)
Configure
AddInterceptors(SecondLevelCacheInterceptor)
DbContext 工作流程

2.1 安装 NuGet 包

dotnet add package EFCoreSecondLevelCacheInterceptor --version 5.3.1
dotnet add package EFCoreSecondLevelCacheInterceptor.MemoryCache        # 内存缓存
# 或
dotnet add package EFCoreSecondLevelCacheInterceptor.StackExchange.Redis  # Redis 缓存

2.2 注册缓存服务与拦截器

在 ABP 模块(如 MyProjectEntityFrameworkCoreModule)的 ConfigureServices 方法中:

public override void ConfigureServices(ServiceConfigurationContext context)
{// 1. 添加二级缓存服务context.Services.AddEFSecondLevelCache(options =>options.UseMemoryCacheProvider().ConfigureLogging(false)                               // 生产环境关闭日志.UseCacheKeyPrefix("EF_")                              // 统一前缀,便于分区管理.UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1)) // 缓存不可用时回退数据库.CacheAllQueries(CacheExpirationMode.Absolute, TimeSpan.FromMinutes(5)) // 全局缓存所有查询.AllowCachingWithExplicitTransactions(true)            // 显式事务中也可缓存); // 2. 注册 DbContext 并注入拦截器(仅针对 MyDbContext)context.Services.AddAbpDbContext<MyDbContext>(options =>{options.AddDefaultRepositories();});context.Services.Configure<AbpDbContextOptions>(opts =>{opts.Configure<MyDbContext>(config =>{config.DbContextOptions.UseSqlServer(context.Services.GetConfiguration().GetConnectionString("Default")).AddInterceptors(context.Services.GetRequiredService<SecondLevelCacheInterceptor>());});});
}

2.3 对特定查询启用缓存 🎯

// 使用全局策略(5 分钟绝对过期)
var products = await _productRepository.WithDetails().Cacheable().ToListAsync();// 自定义滑动过期 1 分钟
var recentOrders = await _orderRepository.Where(o => o.CreatedDate > since).Cacheable(CacheExpirationMode.Sliding, TimeSpan.FromMinutes(1)).ToListAsync();

三、缓存依赖与失效 🔄

Cacheable
未命中
命中
触发拦截
Query
二级缓存
数据库
返回结果
SaveChanges()/SaveChangesAsync()
自动清理相关缓存
  • 自动失效:拦截所有 SaveChanges()/SaveChangesAsync(),根据受影响表自动清除相关缓存,无需额外配置

  • 批量操作限制:EF Core 的 ExecuteUpdate()ExecuteDelete() 绕过 ChangeTracker,不会触发缓存失效,需手动清理:

    await context.Blogs.Where(b => b.IsObsolete).ExecuteUpdateAsync(s => s.SetProperty(b => b.IsActive, false));
    _cacheServiceProvider.ClearAllCachedEntries();
    
  • 按类型或表名缓存

    services.AddEFSecondLevelCache(options =>
    {options.UseMemoryCacheProvider().CacheQueriesContainingTypes(CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30),typeof(Product), typeof(Order)).CacheQueriesContainingTableNames(CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30),TableNameComparison.ContainsOnly, "Products", "Orders");
    });
    
  • 手动清理示例:在服务中注入并使用 IEFCacheServiceProvider

    public class ProductAppService : ApplicationService
    {private readonly IEFCacheServiceProvider _cacheServiceProvider;public ProductAppService(IEFCacheServiceProvider cacheServiceProvider){_cacheServiceProvider = cacheServiceProvider;}public void RefreshProductCache(){_cacheServiceProvider.ClearAllCachedEntries();          // 清除所有缓存_cacheServiceProvider.ClearCacheByPrefix("EF_Products"); // 按前缀清理}
    }
    

四、性能对比测试 📈

4.1 测试环境 🖥️

  • 机房环境:Windows Server 2019,Intel Xeon Gold 6248(8 核/16 线程),32 GB RAM
  • 数据库:SQL Server 2019
  • 数据量:100 万条订单记录
  • 测试工具:自编脚本 + Stopwatch
// 预热
await WarmUpDbAsync();// 测试 1,000 次请求
var sw = Stopwatch.StartNew();
for (int i = 0; i < 1000; i++)
{await _orderRepository.WithDetails().Cacheable().FirstOrDefaultAsync();
}
sw.Stop();
Console.WriteLine($"Elapsed: {sw.ElapsedMilliseconds} ms");

控制台输出示例

Warm-up completed.
Testing 1000 requests...
Elapsed: 15000 ms

声明:以上测试基于串行脚本,仅对比缓存前后性能变化,实际生产环境下并发吞吐量会更高,读者可使用 BenchmarkDotNet 进行多线程基准测试,并查看脚本和日志以复现。

4.2 对比指标 🔥

指标无缓存模式启用二级缓存
平均响应时间~120 ms~15 ms
QPS~500/sec~3 500/sec
DB 访问次数~10 次/秒~1 次/秒

五、最佳实践与注意事项 ⚠️

  • 读多写少:Cache-Aside 模式仅适合读多写少场景,高写场景慎用
  • 缓存粒度:对超大结果集拆分分页或按关键字段缓存,避免一次性加载过多数据
  • 容量管理:根据业务规模调优 MemoryCache 或 Redis 参数(如内存上限、Eviction 策略),防止 OOM
  • 雪崩/穿透:结合互斥锁、预热与空值缓存策略,保障系统稳定性
  • 事务内缓存:显式事务内查询默认不缓存,启用需调用 .AllowCachingWithExplicitTransactions(true)

六、高级配置 🧩

services.AddEFSecondLevelCache(options =>
{options.UseMemoryCacheProvider()// 跳过包含特定 SQL 的查询缓存.SkipCachingCommands(cmd => cmd.Contains("NEWID()"))// 跳过空结果集的缓存.SkipCachingResults(result =>result.Value == null ||(result.Value is EFTableRows rows && rows.RowsCount == 0))// 避免某些更新命令触发失效.SkipCacheInvalidationCommands(cmd =>cmd.Contains("UPDATE [Posts] SET [Views]"))// 动态覆盖某些查询的缓存策略.OverrideCachePolicy(context =>{if (context.IsCrudCommand) return null; // CRUD 不缓存if (context.CommandTableNames.Contains("posts"))return new EFCachePolicy().ExpirationMode(CacheExpirationMode.NeverRemove);return null;});
});

这些配置取自官方高级示例,可按需组合使用。


http://www.dtcms.com/a/283138.html

相关文章:

  • Mysql系列--1、库的相关操作
  • Mybatis-2快速入门
  • @Binds/@IntoMap/@ClassKey的使用
  • C++ shared_ptr 底层实现分析
  • uniapp+vue3+鸿蒙系统的开发
  • WD5018 同步整流降压转换器核心特性与应用,电压12V降5V,2A电流输出
  • MySQL学习——面试版
  • ssl相关命令生成证书
  • LangChain面试内容整理-知识点21:LangSmith 调试与监控平台
  • 职业发展:把工作“玩”成一场“自我升级”的游戏
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘tkinter’问题
  • webpack相关
  • 基于Matlab的四旋翼无人机动力学PID控制仿真
  • 第五届计算机科学与区块链国际学术会议(CCSB 2025)
  • 大模型训练框架对比
  • CTFMisc之隐写基础学习
  • 重学前端007 --- CSS 排版
  • day22 力扣77.组合 力扣216.组合总和III 力扣17.电话号码的字母组合
  • 异常流程进阶 —— 进出异常时的压栈与出栈
  • LVS集群搭建
  • 【Excel】使用vlookup函数快速找出两列数据的差异项
  • 零基础学Vue3组件化开发
  • 使用Python清理Excel中的空行和单元格内部空行:初学者指南
  • Excel处理控件Aspose.Cells教程:使用 Python 在 Excel 中创建甘特图
  • EP02:【NLP 第二弹】自然语言处理数据
  • Oracle 大页配置use_large_pages 参数解析
  • Antd中使用Table集成 react-resizable实现可伸缩列
  • 高性能上位机界面设计范式:C#与C++/C开发调试无缝衔接
  • AR智能巡检:电力运维的数字化变革
  • Raydium CLMM 协议