c#编程细节(八)上之缓存

缓存概述

各级缓存,分别就是以下各节点
在这里插入图片描述

浏览器缓存原理

在这里插入图片描述
请求过第一次,就有部分数据缓存在硬盘或内存
在这里插入图片描述
在这里插入图片描述

DNS&&反向代理

其实原理和浏览器缓存也很相似,都是在请求头,响应头里面进行控制,不过这两个缓存都是面向一群用户的
具体细节比较艰深晦涩,主要还是看服务器缓存和浏览器缓存吧
Dns缓存的原理图

服务器缓存

这里才是主要学习的大头
首先自定义写个缓存来深入理解

自定义缓存

缓存:为了快速获取结果,在第一次获取数据后存起来,下次直接用

缓存究竟用在哪里?
/// 1 会重复请求
/// 2 数据相对稳定
/// 3 耗时/耗资源
/// 4 体积不大
/// 配置文件;菜单-权限;省市区;类别数据;
/// 热搜(二八原则);公告;技能/属性;数据字典;
/// 分页(只要数据不是经常变)

public class CustomCache
    {
        /// <summary>
        /// private 保护数据   
        /// static  全局唯一  不释放
        /// Dictionary  保存多项数据
        /// </summary>
        private static Dictionary<string, KeyValuePair<object, DateTime>> CustomCacheDictionary;

        //private static System.Collections.Concurrent.ConcurrentDictionary
        /// <summary>
        /// 主动清理:只要是过期,最多超过10分钟,一定会被清理
        /// </summary>
        static CustomCache()
        {
            CustomCacheDictionary = new Dictionary<string, KeyValuePair<object, DateTime>>();
            Console.WriteLine($"{DateTime.Now.ToString("MM-dd HH:mm:ss fff")}初始化缓存");
            //缓存依托内存,系统重启后,缓存会重启,日志
            Task.Run(() =>
            {
                while (true)
                {
                    LockAction(new Action(() =>
                    {
                        List<string> list = new List<string>();
                        foreach (var key in CustomCacheDictionary.Keys)
                        {
                            var valueTime = CustomCacheDictionary[key];
                            if (valueTime.Value > DateTime.Now)//没有过期
                            {
                                //没过期
                            }
                            else
                            {
                                list.Add(key);
                            }
                        }
                        list.ForEach(key => CustomCacheDictionary.Remove(key));
                    }));
                    Thread.Sleep(1000 * 60 * 10);//10分钟来一遍  CPU影响很小
                }
            });
        }

        /// <summary>
        /// 添加数据  key重复会异常的
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static void Add(string key, object value, int second = 1800)
        {
            LockAction(new Action(() =>
            {
                CustomCacheDictionary.Add(key, new KeyValuePair<object, DateTime>(value, DateTime.Now.AddSeconds(second)));
            }));
        }

        private static readonly object CustomCache_Lock = new object();
        private static void LockAction(Action action)
        {
            lock (CustomCache_Lock)
            {
                action.Invoke();
            }
        }


        /// <summary>
        /// 保存数据,有就覆盖 没有就新增
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static void SaveOrUpdate(string key, object value, int second = 1800)
        {
            LockAction(new Action(() =>
            {
                CustomCacheDictionary[key] = new KeyValuePair<object, DateTime>(value, DateTime.Now.AddSeconds(second));
            }));
        }

        /// <summary>
        /// 获取数据 没有会异常的
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static T Get<T>(string key)
        {
            return (T)CustomCacheDictionary[key].Key;
        }

        /// <summary>
        /// 检查是否存在
        /// 
        /// 清理一下,除非我们去访问这条缓存,才会去清理  被动清理,任何过期的数据,都不可以被查到
        /// 可能有垃圾留在缓存里面
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static bool Exsit(string key)
        {
            if (CustomCacheDictionary.ContainsKey(key))
            {
                var valueTime = CustomCacheDictionary[key];
                if (valueTime.Value > DateTime.Now)//没有过期
                {
                    return true;
                }
                else
                {
                    LockAction(new Action(() =>
                    {
                        CustomCacheDictionary.Remove(key);//清理一下
                    }));
                    return false;
                }
            }
            else
            {
                return false;
            }
        }

        public static void Remove(string key)
        {
            LockAction(new Action(() =>
            {
                CustomCacheDictionary.Remove(key);
            }));
        }

        public static void RemoveAll()
        {
            LockAction(new Action(() =>
            {
                CustomCacheDictionary.Clear();
            }));
        }

        public static void RemoveCondition(Func<string, bool> func)
        {
            LockAction(new Action(() =>
            {
                List<string> list = new List<string>();
                foreach (var key in CustomCacheDictionary.Keys)
                {
                    if (func.Invoke(key))
                    {
                        list.Add(key);
                    }
                }
                list.ForEach(key => CustomCacheDictionary.Remove(key));
            }));
        }





        public static T Find<T>(string key, Func<T> func, int second = 1800)
        {
            T t = default(T);
            if (!Exsit(key))
            {
                t = func.Invoke();
                CustomCache.Add(key, t, second);
            }
            else
            {
                t = Get<T>(key);
            }
            return t;
        }
    }
}

调用方式:

                    //for (int i = 0; i < 5; i++)
                    //{
                    //    Console.WriteLine($"获取{nameof(DBHelper)} {i}次 {DateTime.Now.ToString("yyyyMMdd HHmmss.fff")}");
                    //    List<Program> programList = null;
                    //    string key = $"{nameof(DBHelper)}_Query_{nameof(Program)}_{123}";//1 能精确描述这次操作  2 能够重现
                    //    //if (!CustomCache.Exsit(key))
                    //    //{
                    //    //    programList = DBHelper.Query<Program>(123);
                    //    //    //CustomCache.SaveOrUpdate(key, programList);
                    //    //    CustomCache.Add(key, programList);
                    //    //}
                    //    //else
                    //    //{
                    //    //    programList = CustomCache.Get<List<Program>>(key);
                    //    //}
                    //    programList = CustomCache.Find<List<Program>>(key, () => DBHelper.Query<Program>(123));
                    //    Console.WriteLine($"{i}次获取的数据为{programList.GetHashCode()}");
                    //}

现在缓存的效果已经有了但是这里的数据一直是用之前的,如果结果有变怎么办呢?
就比如一个用户权限----用户-角色-菜单 查询比较麻烦,也很频繁,相对稳定 所以为每个用户缓存一个
//1 假如修改了Eleven用户的角色,缓存数据的其实失效了,
只对Eleven用户的权限数据失效,只有一条数据失效了 就直接Remove
是Remove不是Update:缓存只是一个临时存储,不是数据源,也许更新了还用不上的,所以直接删除重新添加

//2 如果删除一个菜单,会影响一大批的用户
菜单–角色–用户,然后拼装出全部的key,然后遍历删除? 不对! 成本太高,缓存应该是提示性能,而不是负担
全部删除 还会误伤别的缓存,造成缓存穿透
解决这个问题可以在key的名字上做文章
比如
string key = “Eleven_Privage_Menu”;//只要缓存跟菜单有关的数据,都加上_Menu_
CustomCache.RemoveCondition(s => s.Contains(“Menu”));

//3 private static Dictionary<string, KeyValuePair<object, DateTime>> CustomCacheDictionary;
这里为啥是KeyValuePair<object, DateTime>而不是直接一个object,这就是我们的第三种场景了
//定时作业更新了数据–远程接口更新了数据,更多的时候是我们不知道的情况下,数据变了
给系统提供一个接口,更新数据也来通知下系统(能解决少量场景)
缓存,是沿用上一次的结果,根本就不会去数据源的-----肯定有误差
可以做一下过期,加一个时间有效性, 数据延迟来换取性能,降低压力,需要抉择
过期的就要清除,所以又分成主动清理(在构造方法中新建一个线程每隔一段时间进行)
被动清理(每一次请求的时候才检测)

//4 多线程并发了,插入数据/删除/查询,会不会冲突? 肯定会的!
首先弄清这个字典数据结构,其实里面是数组,然后多线程并发导致的种种问题
加锁,基本全部环节都需要lock的 可以测试一下,100线程-插入数据/获取数据/清除数据
操作并不是很耗时间,锁倒是很耗时间

还有一种系统内置加锁的字典private static System.Collections.Concurrent.ConcurrentDictionary
线程安全的字典ConcurrentDictionary 多线程操作时候不冲突,内部实现了锁的
/// 如果要线程安全,对一块儿内存的操作,必须是单线程的,

以上就是自定义缓存的写法,微软有memoryCache可以直接用的

Memorycache原理其实也差不多,但是比较老了,而且只能存key-value
不如redis

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值