第一次接触到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 = ; i < ; 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>());
}
 /// <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 = ;
for (int i = index; i < ; i++)
{
lResult += i;
}
List<T> tList = new List<T>();
for (int i = ; i < index % ; 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>() { "", "", "", "" };
CustomCache.Add(name, menu);
}
else
{
menu = CustomCache.Get<List<string>>(name);
}

假如bingle的权限变化了,缓存应该失效。数据更新影响单挑缓存,常规做法是Remove而不是更新,因为缓存只是用来提升效率的,而不是数据保存的,因此不需要更新,只需要删除就好,如果真的下次用上了,到时候再去初始化。

CustomCache类增加删除缓存的方法:
 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>() { "", "", "" };
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( * * );
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
continue;
}
}
});
}

多线程问题:

List<Task> taskList = new List<Task>();
for (int i = ; i < ; i++)
{
int k = i;
taskList.Add(Task.Run(() => CustomCache.Add($"TestKey_{k}", $"TestValue_{k}", )));
}
for (int i = ; i < ; i++)
{
int k = i;
taskList.Add(Task.Run(() => CustomCache.Remove($"TestKey_{k}")));
}
for (int i = ; i < ; 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( * * );
}
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
}
 public class CustomCacheNew
{
//动态初始化多个容器和多个锁
private static int CPUNumer = ;//获取系统的CPU数
private static List<Dictionary<string, object>> DictionaryList = new List<Dictionary<string, object>>();
private static List<object> LockList = new List<object>();
static CustomCacheNew()
{
CPUNumer = ;
for (int i = ; i < CPUNumer; i++)
{
DictionaryList.Add(new Dictionary<string, object>());
LockList.Add(new object());
} Task.Run(() =>
{
while (true)
{
Thread.Sleep( * * );
try
{
for (int i = ; 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;
}
}
});
}

缓存究竟哪里用?满足哪些特点适合用缓存?

  1、访问频繁

  2、耗时耗资源

  3、相对稳定

  4、体积不那么大的

  不是说严格满足,具体的还要看情况,存一次能查三次,就值得缓存(大型想换标准)

  下面应该用缓存

    1、字典数据

    2、省市区
    3、配置文件
    4、网站公告信息
    5、部门权限,菜单权限
    6、热搜
    7、类别列表/产品列表
    8、用户,其实Session也是缓存的一种表现

  股票信息价格/彩票开奖信息,这些不能用缓存,即时性要求很高。图片/视频,这些也不行,太大了。商品评论,这个可以用缓存的,虽然评论汇编,但是这个不重要,我们不一定非要看到最新的,而且第一页一般不变。

  可以测试下CustomCache的性能,十万/百万/千万  插入/获取/删除的性能。

												

缓存cache(擦车)的更多相关文章

  1. POCO库——Foundation组件之缓存Cache

    缓存Cache:内部提供多种缓存Cache机制,并对不同机制的管理缓存策略不同实现: ValidArgs.h :ValidArgs有效键参数类,模板参数实现,_key:键,_isValid:是否有效, ...

  2. [.net 面向对象程序设计进阶] (15) 缓存(Cache)(二) 利用缓存提升程序性能

    [.net 面向对象程序设计进阶] (15) 缓存(Cache)(二) 利用缓存提升程序性能 本节导读: 上节说了缓存是以空间来换取时间的技术,介绍了客户端缓存和两种常用服务器缓布,本节主要介绍一种. ...

  3. [.net 面向对象程序设计进阶] (14) 缓存(Cache) (一) 认识缓存技术

    [.net 面向对象程序设计进阶] (14) 缓存(Cache)(一) 认识缓存技术 本节导读: 缓存(Cache)是一种用空间换时间的技术,在.NET程序设计中合理利用,可以极大的提高程序的运行效率 ...

  4. .Net自带缓存Cache的使用

    对于数据比较大,经常要从数据库拿出来用的,可以考虑使用.Net自带的缓存Cache,简单好用: //向内存中插入一个缓存 System.Web.HttpRuntime.Cache.Insert(&qu ...

  5. 缓存Cache

    转载自  博客futan 这篇文章将全面介绍有关 缓存 ( 互动百科 | 维基百科 )cache以及利用PHP写缓存caching的技术. 什么是缓存Cache? 为什么人们要使用它? 缓存 Cach ...

  6. Java 中常用缓存Cache机制的实现

    所谓缓存,就是将程序或系统经常要调用的对象存在内存中,一遍其使用时可以快速调用,不必再去创建新的重复的实例.这样做可以减少系统开销,提高系统效率. 所谓缓存,就是将程序或系统经常要调用的对象存在内存中 ...

  7. Java 中常用缓存Cache机制的实现《二》

    所谓缓存,就是将程序或系统经常要调用的对象存在内存中,一遍其使用时可以快速调用,不必再去创建新的重复的实例.这样做可以减少系统开销,提高系统效率. AD: Cache 所谓缓存,就是将程序或系统经常要 ...

  8. YII缓存Cache

    缓存Cache 定义:将数据暂时存放在一个存储速度更快的介质上,下次读取数据时就可以从这个介质上来读取数据 介质:内存.文件.数据库(优化好的数据库) Yii缓存的分类:(framework/cach ...

  9. ASP.NET缓存 Cache

    缓存介绍 如果每次进入页面的时候都查询数据库生成页面内容的话,如果访问量非常大,则网站性能会非常差,而如果只有第一次访问的时候才查询数据库生成页面内容,以后都直接输出内容,则能提高系统性能,这样无论多 ...

  10. ASP.NET状缓存Cache的应用-提高数据库读取速度

    原文:ASP.NET状缓存Cache的应用-提高数据库读取速度 一. Cache概述       既然缓存中的数据其实是来自数据库的,那么缓存中的数据如何和数据库进行同步呢?一般来说,缓存中应该存放改 ...

随机推荐

  1. SpringCloud 服务间互相调用 @FeignClient注解

    SpringCloud搭建各种微服务之后,服务间通常存在相互调用的需求,SpringCloud提供了@FeignClient 注解非常优雅的解决了这个问题 首先,保证几个服务都在一个Eureka中注册 ...

  2. python __iter__和__getitem__区别

    __getitem__ 单独实现这个魔法函数,可以让这个类成为一个可迭代的对象,并且可以通过使用下标获取类中元素值下标的元素 class Library(object): def __init__(s ...

  3. JavaScript的概念,引入,基本数据类型

    08.05自我总结 JavaScript 一.概念 JavaScript(下文我们会用简称JS来代替)是脚本编程语言,JS语言开发的文件是以.js为后缀,通过在html文件中引入该js文件来控制htm ...

  4. 一文学会JVM配置参数与工具使用

    经过前面的各种分析,我们知道了关于JVM很多的知识,比如版本信息,类加载,堆,方法区,垃圾回收等,但是总觉得心里不踏实,原因是没看到实际的一些东西. 所以这在本文,咱们就好好来聊一聊关于怎么将这些内容 ...

  5. JS基础语法---String对象

    String---->是一个对象 字符串可以看成是字符组成的数组, 但是js中没有字符类型 字符是一个一个的, 在别的语言中字符用一对单引号括起来 在js中字符串可以使用单引号也可以使用双引号 ...

  6. ArcGIS以数据库作为数据源作为source发布服务步骤详解(以Postgresql为例)及各种发布问题

    创建企业级数据库 Data Management Tools-->Geodatabase Administration-->Create Enterprise Geodatabase 按如 ...

  7. Java 类集初探

    类集 类集:主要功能就是Java数据结构的实现(java.util) 类集就是动态对象数组(链表也是动态数组) Collection 接口* Collection是整个类集之中单值保存的最大 父接口 ...

  8. HTML中特殊符号编码对照表,html特殊符号编码都有哪些?

    HTML中一些无法打出来的符号可以用相应的代码进行代替显示,本文提供了一些HTML特殊符号相应的代码供开发者参考. 特殊符号 命名实体 十进制编码 特殊符号 命名实体 十进制编码 特殊符号 命名实体 ...

  9. jdbc大略

    一.概述JDBC JDBC从物理结构上说就是Java语言访问数据库的一套接口集合. 从本质上来说就是调用者(程序员)和实现者(数据库厂商)之间的协议. JDBC API: 使得开发人员可以使用纯Jav ...

  10. 【1期】mysql必知必会

    再有人问你为什么MySQL用B+树做索引,就把这篇文章发给她