第一次打仗到Cache的时间,是在WebForm中,第一次打仗,我就再也没能忘记,cache(擦车,的拼音)
客户端欣赏器缓存https://blog.csdn.net/y874961524/article/details/61419716
CDN缓存原理https://www.cnblogs.com/shijingxiang/articles/5179032.html
阿里云CDN开启设置https://jingyan.baidu.com/article/948f5924f1d642d80ff5f980.html
有句话叫做,系统性能优化的第一步,就是使用缓存,所以,缓存真的很重要
缓存:
实际上是一种结果&目的,就是获取数据点时间,第一次获取之后找个地方存起来,反面直接用,这样一来可以提拔反面每次获取数据的效率。读取设置文件的时间把信息放在静态字段,这个就是缓存。缓存是无处不在的。
我们来哀求一个网站,打开开辟职员工具
客户端缓存的利益:
1、紧缩网络路径,加速相应速度
2、减少哀求,低落服务器压力
欣赏器缓存毕竟是怎么做到的?
打开一个网页,欣赏器-----哀求---服务器---处理哀求会发相应------欣赏器展示
Http协议,数据传输的格式(协议,就像是两人交换,都用什么语言)
信息是否缓存,一定是服务器控制的。ResponseHeader--Cache---Control来指定下缓存计谋,欣赏器看到了这个,就去存储一下。
第一次哀求服务器:
再一次哀求服务器
DNS是互联网的第一跳,DNS缓存就是CDN,内容分发网络,CDN就是加速缓存的
没有效CDN的哀求:
使用了CDN缓存
反向署理:
1、隔离网络,保护服务器(节省公共IP)
2、网络加速,反向署理双网卡
3、负载平衡
4、缓存(跟CDN,也是辨认一下header,压缩到一个物理路径/内存)
为什么叫反向署理?因为他就是一个署理,一样平常的署理,是客户端和服务器之间,有一个署理,去做处理的。但是这个署理是安装在服务器端的。
几种缓存套路相同,但是位置不同,影响的范围也不同。
客户端缓存:只影响当前用户
CDN缓存:针对一批用户
反向署理缓存:针对全部用户。
客户端缓存,存在内存或者硬盘,下次直接用。Cookie,存在内存或者硬盘,欣赏器每次哀求服务器都会带上的信息。
什么时间用缓存?
1、重复哀求,100人访问首页,每个人其实做的都一样,不就是重复
2、耗时好资源
3、结果没变的
下面有一个第三方数据存储和获取的地方:
- /// <summary>
- /// 第三方数据存储和获取的地方
- /// </summary>
- public class CustomCache
- {
- /// <summary>
- /// private:私有一下数据容器,安全
- /// static:不被GC
- /// 字典:读写效率高
- /// </summary>
- private static Dictionary<string, object> CustomCacheDictionary = new Dictionary<string, object>();
- public static void Add(string key, object oVaule)
- {
- CustomCacheDictionary.Add(key, oVaule);
- }
- /// <summary>
- /// 要求在Get前做Exists检测
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="key"></param>
- /// <returns></returns>
- public static T Get<T>(string key)
- {
- return (T)CustomCacheDictionary[key];
- }
- public static bool Exists(string key)
- {
- return CustomCacheDictionary.ContainsKey(key);
- }
- public static T GetT<T>(string key, Func<T> func)
- {
- T t = default(T);
- if (!CustomCache.Exists(key))
- {
- t = func.Invoke();
- CustomCache.Add(key, t);
- }
- else
- {
- t = CustomCache.Get<T>(key);
- }
- return t;
- }
- }
复制代码
存取数据的唯一标识:1 唯一的 2 能重现
- for (int i = 0; i < 5; i++)
- {
- Console.WriteLine($"获取{nameof(DBHelper)} {i}次 {DateTime.Now.ToString("yyyyMMdd HHmmss.fff")}");
- //List<Program> programList = DBHelper.Query<Program>(123);
- List<Program> programList = null;
- string key = $"{nameof(DBHelper)}_Query_{123}";
- //存取数据的唯一标识:1 唯一的 2 能重现
- //if (!CustomCache.Exists(key))
- //{
- // programList = DBHelper.Query<Program>(123);
- // CustomCache.Add(key, programList);
- //}
- //else
- //{
- // programList = CustomCache.Get<List<Program>>(key);
- //}
- programList = CustomCache.GetT<List<Program>>(key, () => DBHelper.Query<Program>(123));
- }
复制代码
- /// <summary>
- /// 数据库查询
- /// </summary>
- public class DBHelper
- {
- /// <summary>
- /// 1 耗时耗资源
- /// 2 参数固定时,结果稳定
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="index"></param>
- /// <returns></returns>
- public static List<T> Query<T>(int index)
- {
- Console.WriteLine("This is {0} Query", typeof(DBHelper));
- long lResult = 0;
- for (int i = index; i < 1000000000; i++)
- {
- lResult += i;
- }
- List<T> tList = new List<T>();
- for (int i = 0; i < index % 3; i++)
- {
- tList.Add(default(T));
- }
- return tList;
- }
- }
复制代码
缓存优化性能,焦点就是结果重用,下次哀求照旧上一次的结果。如果数据库中有变化,岂不是用了一个错误的数据?是的,缓存是难免的,缓存难免会有脏数据,固然了,我们也会分门别类的去只管减少脏数据。
用户--脚色--菜单,用户权限查的多+比力耗资源+相对稳固,非常得当缓存,缓存方式应该是用户id为key,菜单列表作为value。
- string name = "bingle";
- List<string> menu = new List<string>();
- if (!CustomCache.Exists(name))
- {
- menu = new List<string>() { "123", "125553", "143", "123456" };
- CustomCache.Add(name, menu);
- }
- else
- {
- menu = CustomCache.Get<List<string>>(name);
- }
复制代码
如果bingle的权限变化了,缓存应该失效。数据更新影响单挑缓存,常规做法是Remove而不是更新,因为缓存只是用来提拔效率的,而不是数据生存的,因此不必要更新,只必要删除就好,如果真的下次用上了,到时间再去初始化。 - CustomCache类增长删除缓存的方法:<br />
复制代码- public static void Remove(string key)
- {
- CustomCacheDictionary.Remove(key);
- }
复制代码
- string name = "bingle";
- CustomCache.Remove(name);
- List<string> menu = new List<string>();
- if (!CustomCache.Exists(name))
- {
- menu = new List<string>() { "123", "125553", "143" };
- CustomCache.Add(name, menu);
- }
- else
- {
- menu = CustomCache.Get<List<string>>(name);
- }
复制代码
删除了某个菜单,影响了一大批用户。根据菜单--昭觉寺---找用户---每一个拼装key然后去Remove(最准确)。但是这种方式不可,为了缓存增长数据库的任务,最大的问题是数据量的问题,缓存是二八原则,只有20%的热门用户才缓存,这样做的本钱太高。
可以选择加上一个RemoveAll的方法
- public static void RemoveAll()
- {
- CustomCacheDictionary.Clear();
- }
复制代码
或者,菜单删除了,能不能只影响一部门的缓存数据呢?
1、添加缓存时,key带上规则,比如权限包罗_menu_
2、整理时,就只删除key含_menu_的
- /// <summary>
- /// 按条件删除
- /// </summary>
- /// <param name="func"></param>
- public static void RemoveCondition(Func<string, bool> func)
- {
- List<string> keyList = new List<string>();
- lock (CustomCache_Lock)
- foreach (var key in CustomCacheDictionary.Keys)
- {
- if (func.Invoke(key))
- {
- keyList.Add(key);
- }
- }
- keyList.ForEach(s => Remove(s));
- }
复制代码
第三方修改了数据,缓存并不知道,这个就没办法了
a 可以调用接口整理缓存,b系统修改数据,调用c西永通知下缓存更新,b就只能容忍了,容忍脏数据,但是可以加上时间限制,减少影响时间。
时间,逾期计谋:
永世有效----现在就是
绝对逾期:
有个时间点,高出就逾期了
滑动逾期:
多久之后逾期,如果期间更新/查询/查抄存在,就再次延长多久。
- /// <summary>
- /// 自动整理
- /// </summary>
- static CustomCache()
- {
- Task.Run(() =>
- {
- while (true)
- {
- try
- {
- List<string> keyList = new List<string>();
- lock (CustomCache_Lock)
- {
- foreach (var key in CustomCacheDictionary.Keys)
- {
- DataModel model = (DataModel)CustomCacheDictionary[key];
- if (model.ObsloteType != ObsloteType.Never && model.DeadLine < DateTime.Now)
- {
- keyList.Add(key);
- }
- }
- keyList.ForEach(s => Remove(s));
- }
- Thread.Sleep(1000 * 60 * 10);
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- continue;
- }
- }
- });
- }
复制代码
多线程问题:
- List<Task> taskList = new List<Task>();
- for (int i = 0; i < 110000; i++)
- {
- int k = i;
- taskList.Add(Task.Run(() => CustomCache.Add($"TestKey_{k}", $"TestValue_{k}", 10)));
- }
- for (int i = 0; i < 100; i++)
- {
- int k = i;
- taskList.Add(Task.Run(() => CustomCache.Remove($"TestKey_{k}")));
- }
- for (int i = 0; i < 100; i++)
- {
- int k = i;
- taskList.Add(Task.Run(() => CustomCache.Exists($"TestKey_{k}")));
- }
- //Thread.Sleep(10*1000);
- Task.WaitAll(taskList.ToArray());
复制代码
多线程操作非现场安全的容器,会造成冲突
1、线程安全容器ConcurrentDictionary
2、用lock---Add/Remove/遍历,可以解决问题,但是性能呢?
怎么低落影响,提拔性能呢?多个数据容器,多个锁,容器之间可以并发
为相识决多线程问题,CustomCache 类最终修改成如下:
- public class CustomCache
- {
- //ConcurrentDictionary
- private static readonly object CustomCache_Lock = new object();
- /// <summary>
- /// 自动整理
- /// </summary>
- static CustomCache()
- {
- Task.Run(() =>
- {
- while (true)
- {
- try
- {
- List<string> keyList = new List<string>();
- lock (CustomCache_Lock)
- {
- foreach (var key in CustomCacheDictionary.Keys)
- {
- DataModel model = (DataModel)CustomCacheDictionary[key];
- if (model.ObsloteType != ObsloteType.Never && model.DeadLine < DateTime.Now)
- {
- keyList.Add(key);
- }
- }
- keyList.ForEach(s => Remove(s));
- }
- Thread.Sleep(1000 * 60 * 10);
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- continue;
- }
- }
- });
- }
- /// <summary>
- /// private:私有一下数据容器,安全
- /// static:不被GC
- /// 字典:读写效率高
- /// </summary>
- //private static Dictionary<string, object> CustomCacheDictionary = new Dictionary<string, object>();
- private static Dictionary<string, object> CustomCacheDictionary = new Dictionary<string, object>();
- public static void Add(string key, object oVaule)
- {
- lock (CustomCache_Lock)
- CustomCacheDictionary.Add(key, new DataModel()
- {
- Value = oVaule,
- ObsloteType = ObsloteType.Never,
- });
- }
- /// <summary>
- /// 绝对逾期
- /// </summary>
- /// <param name="key"></param>
- /// <param name="oVaule"></param>
- /// <param name="timeOutSecond"></param>
- public static void Add(string key, object oVaule, int timeOutSecond)
- {
- lock (CustomCache_Lock)
- CustomCacheDictionary.Add(key, new DataModel()
- {
- Value = oVaule,
- ObsloteType = ObsloteType.Absolutely,
- DeadLine = DateTime.Now.AddSeconds(timeOutSecond)
- });
- }
- /// <summary>
- /// 相对逾期
- /// </summary>
- /// <param name="key"></param>
- /// <param name="oVaule"></param>
- /// <param name="duration"></param>
- public static void Add(string key, object oVaule, TimeSpan duration)
- {
- lock (CustomCache_Lock)
- CustomCacheDictionary.Add(key, new DataModel()
- {
- Value = oVaule,
- ObsloteType = ObsloteType.Relative,
- DeadLine = DateTime.Now.Add(duration),
- Duration = duration
- });
- }
- /// <summary>
- /// 要求在Get前做Exists检测
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="key"></param>
- /// <returns></returns>
- public static T Get<T>(string key)
- {
- return (T)(((DataModel)CustomCacheDictionary[key]).Value);
- }
- /// <summary>
- /// 被动整理,哀求了数据,才能整理
- /// </summary>
- /// <param name="key"></param>
- /// <returns></returns>
- public static bool Exists(string key)
- {
- if (CustomCacheDictionary.ContainsKey(key))
- {
- DataModel model = (DataModel)CustomCacheDictionary[key];
- if (model.ObsloteType == ObsloteType.Never)
- {
- return true;
- }
- else if (model.DeadLine < DateTime.Now)//现在已经高出你的末了时间
- {
- lock (CustomCache_Lock)
- CustomCacheDictionary.Remove(key);
- return false;
- }
- else
- {
- if (model.ObsloteType == ObsloteType.Relative)//没有逾期&是滑动 所以要更新
- {
- model.DeadLine = DateTime.Now.Add(model.Duration);
- }
- return true;
- }
- }
- else
- {
- return false;
- }
- }
- /// <summary>
- /// 删除key
- /// </summary>
- /// <param name="key"></param>
- public static void Remove(string key)
- {
- lock (CustomCache_Lock)
- CustomCacheDictionary.Remove(key);
- }
- public static void RemoveAll()
- {
- lock (CustomCache_Lock)
- CustomCacheDictionary.Clear();
- }
- /// <summary>
- /// 按条件删除
- /// </summary>
- /// <param name="func"></param>
- public static void RemoveCondition(Func<string, bool> func)
- {
- List<string> keyList = new List<string>();
- lock (CustomCache_Lock)
- foreach (var key in CustomCacheDictionary.Keys)
- {
- if (func.Invoke(key))
- {
- keyList.Add(key);
- }
- }
- keyList.ForEach(s => Remove(s));
- }
- public static T GetT<T>(string key, Func<T> func)
- {
- T t = default(T);
- if (!CustomCache.Exists(key))
- {
- t = func.Invoke();
- CustomCache.Add(key, t);
- }
- else
- {
- t = CustomCache.Get<T>(key);
- }
- return t;
- }
- }
- /// <summary>
- /// 缓存的信息
- /// </summary>
- internal class DataModel
- {
- public object Value { get; set; }
- public ObsloteType ObsloteType { get; set; }
- public DateTime DeadLine { get; set; }
- public TimeSpan Duration { get; set; }
- //数据整理后出发事件
- public event Action DataClearEvent;
- }
- public enum ObsloteType
- {
- Never,
- Absolutely,
- Relative
- }
复制代码
View Code
- public class CustomCacheNew
- {
- //动态初始化多个容器和多个锁
- private static int CPUNumer = 0;//获取系统的CPU数
- private static List<Dictionary<string, object>> DictionaryList = new List<Dictionary<string, object>>();
- private static List<object> LockList = new List<object>();
- static CustomCacheNew()
- {
- CPUNumer = 4;
- for (int i = 0; i < CPUNumer; i++)
- {
- DictionaryList.Add(new Dictionary<string, object>());
- LockList.Add(new object());
- }
- Task.Run(() =>
- {
- while (true)
- {
- Thread.Sleep(1000 * 60 * 10);
- try
- {
- for (int i = 0; i < CPUNumer; i++)
- {
- List<string> keyList = new List<string>();
- lock (LockList[i])//减少锁的影响范围
- {
- foreach (var key in DictionaryList[i].Keys)
- {
- DataModel model = (DataModel)DictionaryList[i][key];
- if (model.ObsloteType != ObsloteType.Never && model.DeadLine < DateTime.Now)
- {
- keyList.Add(key);
- }
- }
- keyList.ForEach(s => DictionaryList[i].Remove(s));
- }
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- continue;
- }
- }
- });
- }
复制代码
View Code
缓存毕竟那里用?满足哪些特点得当用缓存?
1、访问频仍
2、耗时耗资源
3、相对稳固
4、体积不那么大的
不是说严酷满足,详细的还要看情况,存一次能查三次,就值得缓存(大型想换标准)
下面应该用缓存
1、字典数据
2、省市区 3、设置文件 4、网站公告信息 5、部门权限,菜单权限 6、热搜 7、种别列表/产物列表 8、用户,其实Session也是缓存的一种表现
股票信息代价/彩票开奖信息,这些不能用缓存,即时性要求很高。图片/视频,这些也不可,太大了。商品评论,这个可以用缓存的,虽然评论汇编,但是这个不重要,我们不一定非要看到最新的,而且第一页一样平常稳定。
可以测试下CustomCache的性能,十万/百万/万万 插入/获取/删除的性能。
来源:https://www.cnblogs.com/taotaozhuanyong/p/11565162.html |