缓存
定义: 缓存顾名思义就是将常用的东西存储起来,为了下一次的获取更快速。在互联网上,一个常用数据存储在硬盘中查询可能消耗很多时间,但是存储在内存中就能节约很多时间,因为内存的响应速度是硬盘的百倍速度,所以提升是巨大的,将常用的数据存储在内存中的手段就是缓存。
缓存在web中的表现位置:
数据不止可以存储再内存中快速响应,亦可以在服务器,用户的本地等等。在用户本地的速度指定是最快的,毕竟一次网络请求消耗的时间是很大的。
由上图可知,缓存在不同的位置都发挥着巨大的作用,但是不同的位置的缓存发挥的用处不同。但是缓存的目的是一致的,都是为了提升用户的使用体验,当使用缓存的时候,可以加快服务器的响应效率与页面的渲染时间,这样可以减少在用户量大的时候服务器的压力,也可让用户减少等待时间。
其中客户端缓存一般是缓存前端的一些样式文件或者图片、js文件等等。前端是暴露在用户的面前得,是不安全的,所以一些重要的数据是不能缓存在前端的,以免非法用户的篡改。
CDN的全称是Content Delivery Network,即内容分发网络。CDN是构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN的关键技术主要有内容存储和分发技术。这个是需要还钱购买的,买完配置就好了。
反向代理缓存通常是应对集群并发,负载均衡的情况,为了服务器的压力减少,对客户端更快速的响应而使用的缓存技术。
我们要学习的就是常见的数据缓存——本地缓存。这种通常是应对数据频繁查询的情况,将数据放在服务器内存中,而使用的技术,当数据量达到一定程度,就可以使用分布式缓存来应对,开多台服务器做集群,都用来缓存,这样就可以应对数据量大的问题了。
本地缓存
一般的缓存都是设置一个本地的静态数组来存储缓存数据的,在fw框架中内置了缓存数组集,框架自带的缓存就是一个静态的keyvaluepaire的集合,相当于字典,这里用框架的memorycache来实现接口中定义的功能。
首先线引入框架的缓存组件:
设计缓存接口
系统给与的功能过于繁杂,可能有些功能并用不到,也可能有某些功能会用错,所以自己定义缓存接口,去实现一些常用功能是必要的,接口是规约,可扩展的。这里设计了ICache接口:
namespace Tools.CacheTools
{
// 缓存的功能
internal interface ICache
{
int Count { get; }
object this[string key] { get; set; }
T Get<T>(string key);
void Add(string key, object data, int cacheTime = 30);
bool Contains(string key);
void Remove(string key);
void RemoveAll();
}
}
接口中定义了缓存的常用功能。那么来实现这个接口吧:(用缓存组件来实现接口中的功能)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Caching;
using System.Text.RegularExpressions;
namespace Tools.CacheTools
{
/// <summary>
/// 封装了框架的cache,需引用: using System.Runtime.Caching;
/// </summary>
class MemoryCacheOp : ICache
{
/// <summary>
/// 构造
/// </summary>
public MemoryCacheOp() { }
protected ObjectCache Cache
{
get
{
return MemoryCache.Default;
}
}
/// <summary>
/// 索引
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public object this[string key]
{
get { return Cache.Get(key); }
set { Add(key, value); }
}
/// <summary>
/// 获取缓存中数据的总数
/// </summary>
public int Count { get { return (int)(Cache.GetCount()); } }
/// <summary>
/// 添加时效缓存
/// </summary>
/// <param name="key">数据key</param>
/// <param name="data">数据value</param>
/// <param name="cacheTime">数据时效(分钟);默认30分钟</param>
public void Add(string key, object data, int cacheTime = 30)
{
if (data == null)
{
return;
}
var policy = new CacheItemPolicy();
policy.AbsoluteExpiration = DateTime.Now + TimeSpan.FromMinutes(cacheTime);
Cache.Add(new CacheItem(key, data), policy);
}
/// <summary>
/// 比较缓存中是否有key值
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public bool Contains(string key)
{
return Cache.Contains(key);
}
/// <summary>
/// 获取缓存数据
/// </summary>
/// <typeparam name="T">泛型数据</typeparam>
/// <param name="key">key</param>
/// <returns>value</returns>
public T Get<T>(string key)
{
if (Cache.Contains(key))
{
return (T)Cache[key];
}
else
{
return default(T);
}
}
/// <summary>
/// 获取由key返回类型为object类型的数据
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public object Get(string key)
{
return Cache[key];
}
/// <summary>
/// 移除对应key的value
/// </summary>
/// <param name="key">key</param>
public void Remove(string key)
{
if (Contains(key))
{
Cache.Remove(key);
}
else
{
throw new Exception("无此数据");
}
}
/// <summary>
/// 清除全部缓存
/// </summary>
public void RemoveAll()
{
KeyValuePair<string, object>[] keyValuePairs = Cache.ToArray();
foreach (var item in keyValuePairs)
{
Cache.Remove(item.Key);
}
}
/// <summary>
/// 正则删除
/// </summary>
/// <param name="pattern"></param>
public void RemoveByPattern(string pattern)
{
var regex = new Regex(pattern, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase);
var keysToRemove = new List<String>();
foreach (var item in Cache)
{
if (regex.IsMatch(item.Key))
{
keysToRemove.Add(item.Key);
}
}
foreach (string key in keysToRemove)
{
Remove(key);
}
}
}
}
这样一个可以使用的本地缓存组件就可以使用了。我们也可以不用系统的,而是自己定义一个静态的本地数组来实现缓存的功能:这里用MyCacheOp实现了ICache接口。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Tools.CacheTools
{
/// <summary>
/// 自制缓存工具
/// </summary>
class MyCacheOp : ICache
{
/// <summary>
/// 缓存的本质就是一个静态的key-value数组,缓存的静态数组
/// </summary>
private static Dictionary<string, KeyValuePair<DateTime,object>> _MemoryCacheDictionary = new Dictionary<string, KeyValuePair<DateTime, object>>();
/// <summary>
/// 被动过期:长时间运行
/// </summary>
static MyCacheOp()
{
Task.Run(() =>
{
while (true)
{
if (_MemoryCacheDictionary.Count > 0)
{
List<string> list = new List<string>();
foreach (var item in _MemoryCacheDictionary)
{
KeyValuePair<DateTime, object> keyValuePair = item.Value;
if (DateTime.Now > keyValuePair.Key)
{
list.Add(item.Key);
}
}
foreach (var item in list)
{
_MemoryCacheDictionary.Remove(item);
}
}
Thread.Sleep(1000);
}
});
}
public object this[string key] {
get { return _MemoryCacheDictionary[key]; }
set { Add(key, value); }
}
/// <summary>
/// 获取缓存总数s
/// </summary>
public int Count { get { return _MemoryCacheDictionary.Count(); } }
/// <summary>
/// 添加键值对如缓存,并设置缓存保留时长
/// </summary>
/// <param name="key"></param>
/// <param name="data"></param>
/// <param name="cacheTime"></param>
public void Add(string key, object data, int cacheTime = 30)
{
if (data==null)
{
return;
}
_MemoryCacheDictionary.Add(key, new KeyValuePair<DateTime, object>(DateTime.Now.AddMinutes(cacheTime),data));
}
/// <summary>
/// 做缓存中对比是否有对应key
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public bool Contains(string key)
{
return _MemoryCacheDictionary.ContainsKey(key);
}
/// <summary>
/// 获取key对应的value
/// </summary>
/// <typeparam name="T">泛型</typeparam>
/// <param name="key">key</param>
/// <returns>value</returns>
public T Get<T>(string key)
{
if (_MemoryCacheDictionary.ContainsKey(key))
{
KeyValuePair<DateTime, object> keyValue = _MemoryCacheDictionary[key];
if (DateTime.Now > keyValue.Key)
{
_MemoryCacheDictionary.Remove(key);
return default(T);
}
else
{
return (T)keyValue.Value;
}
}
else
{
return default(T);
}
}
public object Get(string key)
{
return _MemoryCacheDictionary[key];
}
/// <summary>
/// 清楚key对应的value
/// </summary>
/// <param name="key"></param>
public void Remove(string key)
{
if (!Contains(key))
{
return;
}
_MemoryCacheDictionary.Remove(key);
}
/// <summary>
/// 将所有的键值清除
/// </summary>
public void RemoveAll()
{
_MemoryCacheDictionary.Clear();
}
}
}
这里有一些线程安全的问题,但是由于是字典,所以当是多线程的时候可以换成线程安全的字典。
缓存的问题
缓存是什么?
缓存在不同的地方作用不同,但是目的都是为了加快服务器的响应速度,提升用户使用产品体验。
缓存的作用?
为了加快服务器的响应速度
缓存在语言中的体现?
语言中缓存可以是本地缓存,也可以是远程服务缓存(redis)。
哪里用?什么时机用?
缓存穿透?
以上是本地缓存字典,小项目缓存的东西少,本地的cache就足够了,当缓存的东西超出了本地能承受的范围,就可以用分布式缓存redis。