用缓存功能解决.NET程序访问数据库的性能问题

摘要

针对.NET项目中数据库查询性能问题,本文提出多维度优化方案:

1)使用SQL Server Profiler等工具分析慢查询;

2)通过添加索引、优化SQL语句提升查询效率;

3)引入缓存机制(Redis/MemoryCache)减轻数据库压力;

4)采用异步操作提高并发能力。

文中给出了C#实现示例,展示如何通过IMemoryCache实现带过期策略的数据缓存,有效提升系统响应速度。

问题描述

在一些.NET项目中,会遇到数据库查询性能问题,影响了整体系统的响应时间。

解决方案和思路

分析查询性能

使用数据库的性能分析工具(如SQL Server Profiler 或MySQL 的EXPLAIN语句)来分析慢查询。

优化查询:

根据分析结果,优化SQL 查询。例如,添加适当的索引,避免使用不必要的子查询,使用

分页等。

缓存:

对于频繁访问且不经常变化的数据,使用缓存(如Redis、MemoryCache)来减少数据库的压力。

异步操作

使用异步数据库操作(如async 和await)来提高系统的并发处理能力。

实现

csharp 复制代码
 var builder = WebApplication.CreateBuilder(args);
 builder.Services.AddMemoryCache();
 
  var mySQLConfig = builder.Configuration.GetSection("MySQL").Get<MySQLConfig>();
  var connectionString = mySQLConfig!.ConnectionString;
  builder.Services.AddDbContext<MySQLContext>(dbContextOptions =>
  {
      dbContextOptions
      .UseMySql(connectionString,
          ServerVersion.AutoDetect(connectionString),
          mysqlOptions =>
          {
              mysqlOptions.EnableStringComparisonTranslations();
              mysqlOptions.EnableRetryOnFailure(
                          maxRetryCount: mySQLConfig.MaxRetryCount,
                          maxRetryDelay: TimeSpan.FromSeconds(mySQLConfig.MaxRetryDelaySeconds),
                          errorNumbersToAdd: null
              );
          })
      .UseLazyLoadingProxies();
  });
        
 var app = builder.Build();
 ServiceLocator.Provider = app.Services;
 
 app.Run();
csharp 复制代码
public static class ServiceLocator
{
    public static IServiceProvider Provider { get; set; } = default!;
}
csharp 复制代码
public static class DbExecuter
{
    public static async Task<TResult> ExecuteAsync<TResult>(Func<MySQLContext, Task<TResult>> action)
    {
        using var scope = ServiceLocator.Provider.CreateScope();
        using var db = scope.ServiceProvider.GetRequiredService<MySQLContext>();
        return await action(db);
    }
}
csharp 复制代码
 private static readonly IMemoryCache cache = ServiceLocator.Provider.GetRequiredService<IMemoryCache>();
 private static TimeSpan DEFAULT_SLIDING_EXPIRATION = TimeSpan.FromHours(3);
 private static TimeSpan DEFAULT_ABSOLUTE_EXPIRATION = TimeSpan.FromHours(24);
 private static TimeSpan NULL_DATA_ABSOLUTE_EXPIRATION = TimeSpan.FromMinutes(5);
 
 public static async Task<List<Product>?> GetProductsByProductTypeAsync(ProductTypeEnum call)
    {
        var cacheKey = KEY_PRODUCTS_BY_PRODUCT_TYPE + call.ToString();
        return await cache.GetOrCreateAsync(cacheKey, async entry =>
        {
            entry.SlidingExpiration = DEFAULT_SLIDING_EXPIRATION;
            var data = await DbExecuter.ExecuteAsync(async db =>
            {
                return await db.Products
                                 .Where(x => x.Type == call)
                                 .OrderBy(x => x.Sort)
                                 .ToListAsync();
            });
            entry.AbsoluteExpirationRelativeToNow =
                data == null ? NULL_DATA_ABSOLUTE_EXPIRATION : DEFAULT_ABSOLUTE_EXPIRATION;
            return data;
        });
    }
相关推荐
Leon-Ning Liu33 分钟前
Oracle 19c RAC ASM 密码文件恢复方案一: asmcmd --nocp credfix 命令修复
数据库·oracle
_果果然34 分钟前
基础SQL语法:SELECT、INSERT、UPDATE、DELETE详解
数据库·sql
时光追逐者36 分钟前
排查 EF 保存数据时提示:Validation failed for one or more entities 的问题
数据库·c#·.net·ef
北慕阳38 分钟前
选择采购单按钮
前端·javascript·数据库
怪侠Kevin38 分钟前
seata事务集成kafka
数据库·分布式·kafka
时光追逐者42 分钟前
在 .NET 中将 EF Core 升级到 9.0.5 MySQL 连接提示 get_LockReleaseBehavior
数据库·mysql·c#·.net·ef core
卜锦元43 分钟前
docker 部署南大通用 GBase 8sV8.8
运维·数据库·docker·容器·部署·运维开发
代码or搬砖44 分钟前
Redis下载以及Redis常用命令
数据库·redis·缓存
棋啊_Rachel1 小时前
面试高频详解:Redis 缓存击穿、雪崩、穿透
redis·缓存·面试