zoukankan      html  css  js  c++  java
  • 浅谈缓存管理

    缓存这个东西可大可小,小到一个静态的字段,大到将整个数据库Cache起来。项目开发过程中缓存的应用到处可见,在这篇博文中笔者就来谈谈自己的项目中关于缓存实现。

          最常见的缓存功能,如C#语言中的Dictionary对象,应该至少包含以下几个功能:

    1. Init():缓存的初始化;
      如:Dictionary<int, object> dic = new Dictioinary<int, object>();
    2. Add():增加缓存;
      如:dic.Add(1, new object());
    3. Set():设置缓存 ;
      这里的Set()和Add()是有一点区别的,Add()的时候发现已存在的缓存,则直接返回;而调用Set()的时候会检查,如果不存在执行Add();如果存在,则替换(Remove first,then add)。如:dic[1] = new object();
    4. Reload();重新加载缓存,在某些情况想功能相当于Init;
      如:dic=new Dictionary<int, object>();
    5. Remove():移除缓存;
      如:dic.Remove(1),(注:执行Remove方法的时候,类似于Set,当key不存在的时候,不作任何处理);
    6. Clear():清空缓存。
      如:dic.clear();
          如果你用过Dictionary对象的话,相信上面提到的方法对你都不会感到陌生,但是,仅仅一个Dictionary就能够满足我们的需要吗?这里笔者来说说Dictionary的缺点:
    1. 一个Dictionary对象的实例是不够用的;
      并非所有的缓存的key都是int类型,所以你会在需要的地方不断的定义Dictionary对象,管理起来到底有多难,你懂的。
    2. Dictionary的索引问题;
      大家都知道,在进行Dictionary[key]操作的时候,首先要判断是否存在。如果存在,则返回【或许你可以通过Dictionary.TryGetValue(...)方法来避开这一步检查】;如果不存在,则要通过一些方式来获取该对象,然后追加到缓存中去;
    3. Dictionary的遍历问题;
      或许你会讲,Dictionary是支持foreach遍历的,好吧,这个我也承认;但是,你有没有在有的时候,需要采用for的方式来遍历Dictioanry对象实例呢?那个时候我想你肯定也纠结了一番。
    4. Dictionary的查找问题;
      查找?不是可以通过key来索引吗,还需要什么查找?好吧,这个我也承认;但是有些时候,我不仅仅只是需要通过键名key来查找啊,可能还需要通过dic.Find<TValue>(Predicate match), dic.FindAll<TValue>(Predicate match)等方式来查找,这个Dictionary有吗?答案是:没有。
    5. Dictionary的线程安全问题;
      这点不多说,因为Dictionary不是线程安全的。
    6. Dictionary的与实际数据的同步问题;
      一般来说,既然用到缓存,那么这些数据基本上是读操作远远大于写操作的,所以不可能为了那么一点点的写操作,来额外加多一个定时器进行数据同步,如果是这样的话,如果有100类数据需要进行缓存,那不是伴随着100个定时。,难以想想,这是多么大的一个累赘啊。
          请别上火,因为写了这么多还在说理论;在写我自己的缓存实现的时候,我首先要谈谈我这个缓存管理的应用场景,因为任何一项技术都是因为特定需求而产生的,脱离了实际需求的技术都是空谈,说白了就是无用功。

          在我的项目中,有很多的基础对象,如果数据字典,枚举,模块,权限等数据库对象,这些信息是在项目开发完成后基本上是不会再发生太大的改变的,而且这些数据的访问平率是非常高的,如果每次访问都需要一次数据库连接的话,那对数据库的压力可想而知了。下面是ConcurrentDictionary.cs的代码:

    复制代码
      1 public class ConcurrentDictionary<TKey, TData>
      2 {
      3     #region 私有字段
      4 
      5     /// <summary>
      6     /// 单一源数据(GetOneDataByOneKey)获取器
      7     /// </summary>
      8     private Func<TKey, TData> _SourceDataGetter;
      9     /// <summary>
     10     /// 所有源数据获取器
     11     /// </summary>
     12     private Func<List<TData>> _SourceAllDataGetter;
     13     /// <summary>
     14     /// 缓存存放字典对象
     15     /// </summary>
     16     private Dictionary<TKey, TData> _Dic;
     17     /// <summary>
     18     /// 缓存存放列表对象
     19     /// </summary>
     20     private List<TData> _List;
     21     /// <summary>
     22     /// 缓存锁
     23     /// </summary>
     24     private object _Lock;
     25 
     26     #endregion
     27 
     28     #region 公共属性
     29 
     30     /// <summary>
     31     /// 换成对象个数
     32     /// </summary>
     33     public int Count
     34     {
     35         get
     36         {
     37             return this._Dic.Count;
     38         }
     39     }
     40 
     41     /// <summary>
     42     /// 列表数据
     43     /// </summary>
     44     public List<TData> List
     45     {
     46         get
     47         {
     48             if (this._List.Count < this._Dic.Count)
     49             {
     50                 this._List.Clear();
     51                 foreach (KeyValuePair<TKey, TData> kvp in this._Dic)
     52                 {
     53                     this._List.Add(kvp.Value);
     54                 }
     55             }
     56             return this._List;
     57         }
     58     }
     59 
     60     #endregion
     61 
     62     #region 构造函数
     63 
     64     /// <summary>
     65     /// 默认构造
     66     /// </summary>
     67     public ConcurrentDictionary()
     68     {
     69         this._Dic = new Dictionary<TKey, TData>();
     70         this._List = new List<TData>();
     71         this._Lock = new object();
     72     }
     73 
     74     /// <summary>
     75     /// 设置源数据获取器
     76     /// </summary>
     77     /// <param name="sourceDataGetter">单一源数据(GetOneDataByOneKey)获取器</param>
     78     public ConcurrentDictionary(Func<TKey, TData> sourceDataGetter)
     79     {
     80         if (sourceDataGetter == nullthrow new ArgumentNullException("sourceDataGetter");
     81         this._SourceDataGetter = sourceDataGetter;
     82         this._Dic = new Dictionary<TKey, TData>();
     83         this._List = new List<TData>();
     84         this._Lock = new object();
     85     }
     86 
     87     /// <summary>
     88     /// 设置源数据获取器
     89     /// </summary>
     90     /// <param name="sourceAllDataGetter">所有源数据获取器</param>
     91     public ConcurrentDictionary(Func<List<TData>> sourceAllDataGetter)
     92     {
     93         if (sourceAllDataGetter == nullthrow new ArgumentNullException("sourceAllDataGetter");
     94         this._SourceAllDataGetter = sourceAllDataGetter;
     95         this._Dic = new Dictionary<TKey, TData>();
     96         this._List = sourceAllDataGetter();
     97         this._Lock = new object();
     98     }
     99 
    100     #endregion
    101 
    102     #region 公共方法
    103 
    104     /// <summary>
    105     /// 获取缓存数据
    106     /// <para> 如果缓存中不存在,则通过SourceGetter获取</para>
    107     /// <para> 如果通过SourceGetter获取到null对象,则不添加到缓存</para>
    108     /// </summary>
    109     /// <param name="key">键名</param>
    110     /// <param name="value">键值</param>
    111     /// <returns>返回是否获取成功</returns>
    112     public bool Get(TKey key, out  TData value)
    113     {
    114         if (_Dic.TryGetValue(key, out value)) return true;
    115         else
    116         {
    117             lock (_Lock)
    118             {
    119                 if (_Dic.TryGetValue(key, out value)) return true;
    120                 if (_SourceDataGetter == nullreturn false;
    121                 TData tempData = _SourceDataGetter(key);
    122                 if (tempData != null)
    123                 {
    124                     _Dic.Add(key, tempData);
    125                     _List.Add(tempData);
    126                     value = tempData;
    127                     return true;
    128                 }
    129                 return false;
    130             }
    131         }
    132     }
    133 
    134     /// <summary>
    135     /// 设置缓存数据
    136     /// </summary>
    137     /// <param name="key">键名</param>
    138     /// <param name="value">键值</param>
    139     /// <returns>返回是否设置成功,如果键值已存在,则返回false</returns>
    140     public bool Add(TKey key, TData value)
    141     {
    142         if (_Dic.ContainsKey(key)) return false;
    143         else
    144         {
    145             lock (_Lock)
    146             {
    147                 if (_Dic.ContainsKey(key)) return false;
    148                 else
    149                 {
    150                     _Dic.Add(key, value);
    151                     if (!this._List.Contains(value)) this._List.Add(value);
    152                     return true;
    153                 }
    154             }
    155         }
    156     }
    157 
    158     /// <summary>
    159     /// 设置缓存数据
    160     /// </summary>
    161     /// <param name="key">键名</param>
    162     /// <param name="value">键值</param>
    163     /// <returns>返回是否设置成功,如果键值已存在,则覆盖</returns>
    164     public bool Set(TKey key, TData value)
    165     {
    166         if (_Dic.ContainsKey(key))
    167         {
    168             lock (this._Lock)
    169             {
    170                 //移除老数据
    171                 TData oldData = _Dic[key];
    172                 _List.Remove(oldData);
    173                 //增加新数据
    174                 _Dic[key] = value;
    175                 _List.Add(value);
    176                 return true;
    177             }
    178         }
    179         else
    180         {
    181             lock (_Lock)
    182             {
    183                 if (_Dic.ContainsKey(key))
    184                 {
    185                     //移除老数据
    186                     TData oldData = _Dic[key];
    187                     _List.Remove(oldData);
    188                     //增加新数据
    189                     _Dic[key] = value;
    190                     _List.Add(value);
    191                     return true;
    192                 }
    193                 else
    194                 {
    195                     _Dic.Add(key, value);
    196                     if (!this._List.Contains(value)) this._List.Add(value);
    197                     return true;
    198                 }
    199             }
    200         }
    201     }
    202 
    203     /// <summary>
    204     /// 通过SourceDataGetter重新加载指定key的值
    205     /// </summary>
    206     /// <param name="key">键值</param>
    207     /// <returns></returns>
    208     public bool Reload(TKey key)
    209     {
    210         if (_SourceDataGetter == nullreturn false;
    211         TData tempData = _SourceDataGetter(key);
    212         return this.Set(key, tempData);
    213     }
    214 
    215     /// <summary>
    216     /// 通过SourceAllDataGetter重新加载所有缓存对象
    217     /// </summary>
    218     /// <returns></returns>
    219     public bool ReloadAll()
    220     {
    221         if (_SourceAllDataGetter == nullreturn false;
    222         lock (this._Lock)
    223         {
    224             this._List = _SourceAllDataGetter();
    225         }
    226         return true;
    227     }
    228 
    229     /// <summary>
    230     /// 移除键/值
    231     /// </summary>
    232     /// <param name="key">键名</param>
    233     /// <returns>返回是否移除成功,如果不存在,则返回false</returns>
    234     public bool Remove(TKey key)
    235     {
    236         TData tempData;
    237         if (this._Dic.TryGetValue(key, out tempData))
    238         {
    239             lock (_Lock)
    240             {
    241                 _Dic.Remove(key);
    242                 _List.Remove(tempData);
    243                 return true;
    244             }
    245         }
    246         return false;
    247     }
    248 
    249     /// <summary>
    250     /// 清空缓存
    251     /// </summary>
    252     public void Clear()
    253     {
    254         lock (_Lock)
    255         {
    256             _Dic.Clear();
    257             _List.Clear();
    258         }
    259     }
    260 
    261     #endregion
    复制代码

    262 } 

          代码中的注释写的相对比较详细了,所以怎么用这里就不作任何多余的解释了,如果只是上面这个类的话,还不足以满足上面列出的Dictionary的缺点的第一点,所以笔者另外写了一个缓存管理类,如下:

    复制代码
      1 public static class CacheManager
      2 {
      3     #region 私有字段
      4 
      5     /// <summary>
      6     /// 缓存器
      7     /// </summary>
      8     static Dictionary<intobject> _Dic;
      9 
     10     #endregion
     11 
     12     #region 私有方法
     13 
     14     static CacheManager()
     15     {
     16         _Dic = new Dictionary<intobject>();
     17     }
     18     /// <summary>
     19     /// 获取下一个缓存键名
     20     /// </summary>
     21     /// <returns></returns>
     22     private static int _GetNextKey()
     23     {
     24         int key = Common.Random.Next(11000000);
     25         while (_Dic.ContainsKey(key))
     26         {
     27             key = new Random().Next(11000000);
     28         }
     29         return key;
     30     }
     31     /// <summary>
     32     /// 注册字典
     33     /// </summary>
     34     /// <typeparam name="TKey">缓存键名类型</typeparam>
     35     /// <typeparam name="TData">缓存键值类型</typeparam>
     36     /// <param name="dic">缓存</param>
     37     /// <returns>缓存类别键名</returns>
     38     private static int _Register<TKey, TData>(ConcurrentDictionary<TKey, TData> dic)
     39     {
     40         int key = _GetNextKey();
     41         _Dic.Add(key, dic);
     42         return key;
     43     }
     44 
     45     #endregion
     46 
     47     #region 公共方法
     48 
     49     /// <summary>
     50     /// 注册缓存,并返回缓存键值
     51     /// </summary>
     52     /// <typeparam name="TKey">缓存键名类型</typeparam>
     53     /// <typeparam name="TData">缓存键值类型</typeparam>
     54     /// <returns>缓存类别键名</returns>
     55     public static int Register<TKey, TData>()
     56     {
     57         return _Register(new ConcurrentDictionary<TKey, TData>());
     58     }
     59 
     60     /// <summary>
     61     /// 注册缓存,并返回缓存键值
     62     /// </summary>
     63     /// <typeparam name="TKey">缓存键名类型</typeparam>
     64     /// <typeparam name="TData">缓存键值类型</typeparam>
     65     /// <param name="sourceDataGetter">单一源数据(GetOneDataByOneKey)获取器</param>
     66     /// <returns>缓存类别键名</returns>
     67     public static int Register<TKey, TData>(Func<TKey, TData> sourceDataGetter)
     68     {
     69         return _Register(new ConcurrentDictionary<TKey, TData>(sourceDataGetter));
     70     }
     71 
     72     /// <summary>
     73     /// 注册缓存,并返回缓存键值
     74     /// </summary>
     75     /// <typeparam name="TKey">缓存键名类型</typeparam>
     76     /// <typeparam name="TData">缓存键值类型</typeparam>
     77     /// <param name="sourceAllDataGetter">所有源数据获取器</param>
     78     /// <returns>缓存类别键名</returns>
     79     public static int Register<TKey, TData>(Func<List<TData>> sourceAllDataGetter)
     80     {
     81         return _Register(new ConcurrentDictionary<TKey, TData>(sourceAllDataGetter));
     82     }
     83 
     84     /// <summary>
     85     /// 获取缓存数据
     86     /// <para> 如果缓存中不存在,则通过SourceGetter获取</para>
     87     /// <para> 如果通过SourceGetter获取到null对象,则不添加到缓存</para>
     88     /// </summary>
     89     /// <typeparam name="TKey">缓存键名类型</typeparam>
     90     /// <typeparam name="TData">缓存键值类型</typeparam>
     91     /// <param name="cacheTypeKey">缓存类别键名</param>
     92     /// <param name="key">键名</param>
     93     /// <param name="value">键值</param>
     94     /// <returns>返回是否获取成功</returns>
     95     public static bool Get<TKey, TData>(int cacheTypeKey, TKey key, out  TData value)
     96     {
     97         object obj;
     98         if (_Dic.TryGetValue(cacheTypeKey, out obj))
     99         {
    100             ConcurrentDictionary<TKey, TData> dic = obj as ConcurrentDictionary<TKey, TData>;
    101             return dic.Get(key, out value);
    102         }
    103         value = default(TData);
    104         return false;
    105     }
    106 
    107     /// <summary>
    108     /// 设置缓存数据
    109     /// </summary>
    110     /// <typeparam name="TKey">缓存键名类型</typeparam>
    111     /// <typeparam name="TData">缓存键值类型</typeparam>
    112     /// <param name="cacheTypeKey">缓存类别键名</param>
    113     /// <param name="key">键名</param>
    114     /// <param name="value">键值</param>
    115     /// <returns>返回是否设置成功,如果键值已存在,则返回false</returns>
    116     public static bool Add<TKey, TData>(int cacheTypeKey, TKey key, TData value)
    117     {
    118         object obj;
    119         if (_Dic.TryGetValue(cacheTypeKey, out obj))
    120         {
    121             ConcurrentDictionary<TKey, TData> dic = obj as ConcurrentDictionary<TKey, TData>;
    122             return dic.Add(key, value);
    123         }
    124         return false;
    125     }
    126 
    127     /// <summary>
    128     /// 设置缓存数据
    129     /// </summary>
    130     /// <typeparam name="TKey">缓存键名类型</typeparam>
    131     /// <typeparam name="TData">缓存键值类型</typeparam>
    132     /// <param name="cacheTypeKey">缓存类别键名</param>
    133     /// <param name="key">键名</param>
    134     /// <param name="value">键值</param>
    135     /// <returns>返回是否设置成功,如果键值已存在,则覆盖</returns>
    136     public static bool Set<TKey, TData>(int cacheTypeKey, TKey key, TData value)
    137     {
    138         object obj;
    139         if (_Dic.TryGetValue(cacheTypeKey, out obj))
    140         {
    141             ConcurrentDictionary<TKey, TData> dic = obj as ConcurrentDictionary<TKey, TData>;
    142             return dic.Set(key, value);
    143         }
    144         return false;
    145     }
    146 
    147     /// <summary>
    148     /// 通过SourceDataGetter重新加载指定key的值
    149     /// </summary>
    150     /// <typeparam name="TKey">缓存键名类型</typeparam>
    151     /// <typeparam name="TData">缓存键值类型</typeparam>
    152     /// <param name="cacheTypeKey">缓存类别键名</param>
    153     /// <param name="key">键值</param>
    154     /// <returns></returns>
    155     public static bool Reload<TKey, TData>(int cacheTypeKey, TKey key)
    156     {
    157         object obj;
    158         if (_Dic.TryGetValue(cacheTypeKey, out obj))
    159         {
    160             ConcurrentDictionary<TKey, TData> dic = obj as ConcurrentDictionary<TKey, TData>;
    161             return dic.Reload(key);
    162         }
    163         return false;
    164     }
    165 
    166     /// <summary>
    167     /// 通过SourceAllDataGetter重新加载指定类型缓存的所有缓存对象
    168     /// </summary>
    169     /// <typeparam name="TKey">缓存键名类型</typeparam>
    170     /// <typeparam name="TData">缓存键值类型</typeparam>
    171     /// <param name="cacheTypeKey">缓存类别键名</param>
    172     /// <returns></returns>
    173     public static bool Reload<TKey, TData>(int cacheTypeKey)
    174     {
    175         object obj;
    176         if (_Dic.TryGetValue(cacheTypeKey, out obj))
    177         {
    178             ConcurrentDictionary<TKey, TData> dic = obj as ConcurrentDictionary<TKey, TData>;
    179             return dic.ReloadAll();
    180         }
    181         return false;
    182     }
    183 
    184     /// <summary>
    185     /// 移除键/值
    186     /// </summary>
    187     /// <typeparam name="TKey">缓存键名类型</typeparam>
    188     /// <typeparam name="TData">缓存键值类型</typeparam>
    189     /// <param name="cacheTypeKey">缓存类别键名</param>
    190     /// <param name="key">键名</param>
    191     /// <returns>返回是否移除成功,如果不存在,则返回false</returns>
    192     public static bool Remove<TKey, TData>(int cacheTypeKey, TKey key)
    193     {
    194         object obj;
    195         if (_Dic.TryGetValue(cacheTypeKey, out obj))
    196         {
    197             ConcurrentDictionary<TKey, TData> dic = obj as ConcurrentDictionary<TKey, TData>;
    198             return dic.Remove(key);
    199         }
    200         return false;
    201     }
    202 
    203     /// <summary>
    204     /// 清空缓存
    205     /// </summary>
    206     /// <typeparam name="TKey">缓存键名类型</typeparam>
    207     /// <typeparam name="TData">缓存键值类型</typeparam>
    208     /// <param name="cacheTypeKey">缓存类别键名</param>
    209     public static void Clear<TKey, TData>(int cacheTypeKey)
    210     {
    211         object obj;
    212         if (_Dic.TryGetValue(cacheTypeKey, out obj))
    213         {
    214             ConcurrentDictionary<TKey, TData> dic = obj as ConcurrentDictionary<TKey, TData>;
    215             dic.Clear();
    216         }
    217     }
    218 
    219     /// <summary>
    220     /// 清空所有缓存
    221     /// </summary>
    222     public static void ClearAll()
    223     {
    224         _Dic.Clear();
    225     }
    226 
    227     #endregion
    复制代码

    228 } 

           以上两个类 就是我的缓存管理的全部实现了,谢谢!

    ASP.NET开发技术交流群: 67511751

    另:本人想找一些志同道合的人,可以是跟我一起交流技术的,或者是给予鼓励和支持的,非诚勿扰,谢谢!

    QQ:1054930154 

     
  • 相关阅读:
    Springboot框架添加防止XSS攻击功能
    mybatis传入参数为0被误认为是空字符串的解决方法
    js 遇到 Permission denied to access property ***
    SpringBoot 实现App第三方微信登录
    RedisTemplate和StringRedisTemplate的使用导致数据不一致
    mybatis传入值为null时提示无效的列类型
    Oracle批量插入sql和Mysql大不一样
    SSM+Oracle自动生成uuid作为主键
    mysql5.7创建用户 分配权限
    R语言统计分析-方差分析
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2556058.html
Copyright © 2011-2022 走看看