有关sqlsugar的缓存机制

疑问:在断点调试时在BaseService的更新方法突然跳转到RedisCache的GetAllKey方法。

由于好奇所以查了查,了解到了这个技术。

//BaseService
public int Update(List<T> parm, Expression<Func<T, object>> columns)
{
    return Db.Updateable(parm).WhereColumns(columns).RemoveDataCache().ExecuteCommand();
}
//RedisCache
public IEnumerable<string> GetAllKey<V>()
{
    return RedisServer.Cache.Keys("Cache:SqlSugarDataCache.*");
}

SqlSugar 的缓存机制

SqlSugar 是一个功能强大的 .NET ORM 框架,它提供了一套灵活的缓存机制,旨在提高数据访问的性能。缓存机制可以避免频繁的数据库查询操作,通过缓存来加快查询速度。SqlSugar 支持多种缓存方案,并允许开发者自定义缓存策略。

1. 缓存的类型

SqlSugar 的缓存机制主要包括两种类型:

  • 一级缓存(Session Cache)

    • 作用范围:仅在当前的数据库上下文(DbContext)中有效。
    • 特性:SqlSugar 的一级缓存是基于会话的缓存(类似于传统 ORM 中的 Session Cache),即在同一个上下文对象中,如果多次查询相同的数据,会直接从内存中读取,而不再执行数据库查询。
    • 生命周期:当 DbContext 实例销毁时,一级缓存也会被销毁。
    • 使用场景:适用于短时间内频繁访问同一数据的情况,在同一个上下文实例中多次查询相同的数据。
  • 二级缓存(Global Cache)

    • 作用范围:在整个应用程序中共享。
    • 特性:二级缓存通常是基于分布式缓存(如 Redis 或内存缓存)的持久性缓存。它能够在不同的上下文实例之间共享缓存数据。
    • 生命周期:缓存数据的生命周期可以自定义,如设定缓存过期时间(TTL)。
    • 使用场景:适用于多个上下文实例之间需要共享数据缓存的情况,例如,在高并发读取场景下,使用二级缓存来减轻数据库压力。
2. 缓存的使用方式

在 SqlSugar 中,缓存的使用非常灵活。它通过一个 CacheService 接口来定义缓存的存储和管理方式。开发者可以基于此接口自定义缓存策略,或者使用框架内置的缓存方案。

1. 内置缓存机制

SqlSugar 提供了一个简单的基于内存的内置缓存机制,可以直接启用:

SqlSugarScope db = new SqlSugarScope(new ConnectionConfig()
{
    DbType = DbType.SqlServer,
    ConnectionString = "Server=.;Database=MyDb;Trusted_Connection=True;",
    IsAutoCloseConnection = true,
    ConfigureExternalServices = new ConfigureExternalServices()
    {
        DataInfoCacheService = new SqlSugar.MemoryCache()  // 使用内置内存缓存
    }
});

2. 自定义缓存机制

你可以实现 ICacheService 接口来自定义缓存逻辑,例如使用 Redis 作为缓存存储:

public class RedisCache : ICacheService
{
    // 实现 Add、Get、Remove 等方法,以控制缓存的添加、获取、删除
    public void Add<V>(string key, V value)
    {
        // 实现 Redis 的缓存添加逻辑
    }

    public V Get<V>(string key)
    {
        // 实现 Redis 的缓存获取逻辑
    }

    public void Remove(string key)
    {
        // 实现 Redis 的缓存删除逻辑
    }
}

在数据库上下文配置中使用自定义缓存:

上述问题就是用了这个

SqlSugarScope db = new SqlSugarScope(new ConnectionConfig()
{
    DbType = DbType.SqlServer,
    ConnectionString = "Server=.;Database=MyDb;Trusted_Connection=True;",
    IsAutoCloseConnection = true,
    ConfigureExternalServices = new ConfigureExternalServices()
    {
        DataInfoCacheService = new RedisCache()  // 使用自定义的 Redis 缓存
    }
});
3. 缓存的操作方法

SqlSugar 提供了几个常用的方法用于管理缓存:

RemoveDataCache():用于清除特定的数据缓存,常在更新、删除操作之后调用,以防止过期数据的继续使用。

ClearCacheAll():清除所有的缓存数据,通常在需要刷新所有缓存的场景下使用。

其他:WithCache,WithCacheIdentity,RemoveCache,EnableQueryCache,RemoveAllCache,IsCache 属性....

4. 缓存的优缺点
  • 优点

    • 提高性能:减少重复的数据库查询,提高数据访问速度。
    • 减少数据库负载:通过缓存的使用,显著减少数据库的读取压力,适用于高并发环境。
    • 灵活性高:支持自定义缓存策略,可以根据业务需求灵活配置缓存的存储方式和管理策略。
  • 缺点

    • 数据一致性问题:缓存的数据可能会变得过期,如果不及时清除或更新,可能导致读取到旧数据。
    • 内存占用:缓存的数据会占用一定的内存资源,在缓存的数据量非常大时可能会导致内存溢出问题。
    • 缓存管理复杂性:在使用分布式缓存(如 Redis)时,需要额外的缓存管理和维护工作。
5. 使用缓存的最佳实践
  • 合理设定缓存策略:根据数据的变更频率和访问频率,合理选择数据的缓存时间和策略。
  • 避免过度缓存:并不是所有数据都需要缓存,应根据实际业务需求决定哪些数据需要缓存。
  • 清理过期缓存:确保在数据更新、删除操作后及时清理相关缓存,保持数据的一致性。
  • 监控和优化缓存使用:定期监控缓存的使用情况,分析缓存命中率,优化缓存策略。

疑问原因分析

原因分析

  1. 缓存机制的工作原理: SqlSugar 的缓存机制依赖于缓存键(Key)的管理。缓存键通常包含特定的前缀(例如 "Cache

    .*")来标识缓存项。在执行数据更新或删除操作后,RemoveDataCache 方法会被调用,以确保缓存中存储的数据与数据库的实际数据保持一致。
  2. 查找和清除缓存: RemoveDataCache 方法需要清除与某个表或查询相关的缓存项。为了找到这些缓存项,SqlSugar 需要查找符合特定规则的所有缓存键。例如,通过 RedisServer.Cache.Keys("Cache:SqlSugarDataCache.*") 查找所有以 "Cache

    ." 为前缀的缓存键。GetAllKey<V>() 方法就是用来获取这些符合条件的缓存键的列表。
  3. 调用的具体过程:RemoveDataCache 方法执行时:

    • 它会调用 ICacheService 接口中定义的 GetAllKey<V>() 方法。
    • 该方法会返回一个符合特定前缀(如 "Cache.*")的所有缓存键的集合。
    • 随后,RemoveDataCache 会根据这些键执行缓存删除操作,从而清除与这些键相关的缓存数据。
为什么会跳转到 GetAllKey<V>()

GetAllKey<V>() 的目的是获取所有符合特定规则的缓存键,以便 RemoveDataCache 方法能够正确地定位并清除所有需要删除的缓存项。这种设计确保了在数据变化(如插入、更新、删除)时,能够及时清除相关的缓存数据,防止客户端读取到过期数据。

实现符合 ICacheService 接口(使用 Redis 实现缓存查找和清除)的自定义缓存服务的其他方法

内存缓存(Memory Cache)

文件系统缓存

分布式缓存(如 Memcached 或其他 NoSQL 缓存)

数据库缓存

项目实现 ICacheService 接口的整个过程

1.在Progaram.cs中配置数据库:

 services.AddScoped<ISqlSugarClient>(x =>
 {
     return new SqlSugarClient(new ConnectionConfig()
     {
         ConnectionString = AppSettings.Configuration["DbConnection:ConnectionString"],
         DbType = (DbType)Convert.ToInt32(AppSettings.Configuration["DbConnection:DbType"]),
         IsAutoCloseConnection = true,
         InitKeyType = InitKeyType.Attribute,
         ConfigureExternalServices = new ConfigureExternalServices()
         {
             //配置RedisCache类
             DataInfoCacheService = new RedisCache()
         },
         MoreSettings = new ConnMoreSettings()
         {
             //自动清除缓存
             IsAutoRemoveDataCache = true
         }
     });
 });
2.实现上面new出来的RedisCache配置类
namespace Meiam.System.Core
{
    //配合sqlsugar在操作数据库时顺便操作redis中缓存的数据,保证两个库数据的统一
    public class RedisCache : ICacheService
    {
        public void Add<V>(string key, V value)
        {
            RedisServer.Cache.Set(key, value);
        }

        public void Add<V>(string key, V value, int cacheDurationInSeconds)
        {
            RedisServer.Cache.Set(key, value, cacheDurationInSeconds);
        }

        public bool ContainsKey<V>(string key)
        {
            return RedisServer.Cache.Exists(key);
        }

        public V Get<V>(string key)
        {
            return RedisServer.Cache.Get<V>(key);
        }



        public IEnumerable<string> GetAllKey<V>()
        {
            return RedisServer.Cache.Keys("Cache:SqlSugarDataCache.*");
        }

        public V GetOrCreate<V>(string cacheKey, Func<V> create, int cacheDurationInSeconds = int.MaxValue)
        {
            if (ContainsKey<V>(cacheKey))
            {
                return Get<V>(cacheKey);
            }
            else
            {
                var result = create();
                Add(cacheKey, result, cacheDurationInSeconds);
                return result;
            }
        }

        public void Remove<V>(string key)
        {
            RedisServer.Cache.Del(key.Remove(0, 6));
        }
    }
}
3.在需要的地方使用

如BaseService


...

        public List<T> GetWhere(Expression<Func<T, bool>> where, 
        bool useCache = false, 
        int cacheSecond = 3600)
        {
            var query = Db.Queryable<T>().Where(where).WithCacheIF(useCache, 
            cacheSecond);
            return query.ToList();
        }


        #region 修改操作


        public int Update(T parm)
        {
            return Db.Updateable(parm).RemoveDataCache().ExecuteCommand();
        }

         public int Update(T parm, Expression<Func<T, object>> columns)
        {
            return Db.Updateable(parm).WhereColumns(columns).
            RemoveDataCache().ExecuteCommand();
        }

...

        public int Update(List<T> parm, Expression<Func<T, object>> columns)
        {
            return  Db.Updateable(parm).WhereColumns(columns).
            RemoveDataCache().ExecuteCommand();
        }


...
        #endregion

        #region 删除操作
        public int Delete(object id)
        {
            return Db.Deleteable<T>(id).RemoveDataCache().ExecuteCommand();
        }


...

什么样的数据会使用到sqlsugar和 Redis 实现缓存查找和清除?

通常会涉及到频繁查询、不常变化或者允许短时间内数据一致性稍微延迟的数据。这种类型的数据适合缓存以提升查询性能和减少数据库的负载。

1. 适合缓存的数据类型

基础数据,热门数据,字典表数据,统计数据,分页查询结果,配置数据....

2. 使用缓存的场景

  • 读取操作频繁,写入操作较少:例如,商品列表、文章列表等,特别是在有热门内容的情况。
  • 读取性能要求高:例如,用户体验要求较高的应用场景,比如社交媒体、电子商务网站的首页加载。
  • 数据不会快速过期或变化:例如,固定的配置数据、国家和城市等相对稳定的数据。

  • 11
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值