using Common;
using Microsoft.Extensions.Caching.Memory;
using MyBll;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MyCache
{
class Program
{
static void Main(string[] args)
{
//缓存用在哪里?满足哪些特点适合用缓存?
//访问频繁+耗时耗资源+相对稳定+体积不太大
//也看业务需要,一般大型项目存一次缓存三个请求都用上了,就可以用
//省市区/配置文件/网站公告/部门/权限/热搜/产品列表/用户信息
//现在一般使用微软自带的缓存类,MemoryCache 这个时线程安全的
//.net framework 引用using System.Runtime.Caching;
//.net core 引用using Microsoft.Extensions.Caching.Memory;
//core的这个类的方法好像没有Add(不知道怎么用) 使用方式和framework版本的不一样
#region 缓存的应用
1.缓存是优化系统的第一步
浏览器(客户端缓存)--DNS解析(CDN)--反向代理(反向代理缓存)--服务器--(本地缓存[内存]、分布式缓存)--数据库
1 重复请求,比如100个人访问首页,每个请求的返回其实都一样
2 耗时耗资源
3 结果没变
此时应该缓存下来提升性能
//for (int i = 0; i < 5; i++)
//{
// Console.WriteLine($"获取{nameof(DbHelper)} {i}次 {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
// //存取数据的唯一标识
// //1 唯一性; 2 能重现,取的时候还要用呢
// string key = $"{nameof(DbHelper)}_Query_{123}";
// List<Program> programList = CustomCache.GetValue<List<Program>>(key, () => DbHelper.Query<Program>(123));
// //if (!CustomCache.Exists(key))
// //{
// // programList = DbHelper.Query<Program>(123);
// // CustomCache.Add(key, programList);
// //}
// //else
// //{
// // programList = CustomCache.GetT<List<Program>>(key);
// //}
//}
//for (int i = 0; i < 5; i++)
//{
// Console.WriteLine($"获取{nameof(FileHelper)} {i}次 {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
// string key = $"{nameof(FileHelper)}_Query_{123}";
// List<Program> programList = CustomCache.GetValue<List<Program>>(key, () => FileHelper.Query<Program>(123));
//}
//for (int i = 0; i < 5; i++)
//{
// Console.WriteLine($"获取{nameof(RemoteHelper)} {i}次 {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
// string key = $"{nameof(RemoteHelper)}_Query_{123}";
// List<Program> programList = CustomCache.GetValue<List<Program>>(key, () => RemoteHelper.Query<Program>(123));
//}
#endregion
#region 缓存清理
{
//缓存核心优化就是重用结果,下次请求用这次的结果
//数据如果有变化还是用的上次的数据,用的就是脏数据;缓存难免有脏数据
//需要根据业务分类缓存,尽量减少脏数据
{
1 个人的权限变化,缓存应该失效
数据更新应该只影响单挑缓存,常规做法是Remove,而不是更新
因为缓存是用来提升性能的,而不是数据保存的,因此不需要更新,只需要删除就好
如果下次用上了,到时候再初始化
//string name = "XiaoWang";
//CustomCache.Remove(name);
//List<string> menu = null;
//if (!CustomCache.Exists(name))
//{
// menu = new List<string>() { "123", "124", "125" };
// CustomCache.Add(name, menu);
//}
//else
//{
// menu = CustomCache.GetT<List<string>>(name);
//}
}
{
2 废弃某个菜单,影响一批用户的缓存
全部删除影响太大
//CustomCache.RemoveAll();
找出每个用户单独删除成本太大
那么就删除所有用户的菜单权限,别影响其他的缓存?
a) 添加缓存时有规则的命名key,在key上带上菜单标志,比如menu
b) 清理时清理key中名字有menu的,这样所有用户的菜单缓存就清理了,其他业务的缓存不受影响
//CustomCache.RemoveCondition(t => t.Contains("menu"));
}
{
3.第三方修改数据,缓存并不知道;但是有时候是没办法的,比如从数据库修改
a)提供个清理缓存的接口给第三方
b)给缓存加上过期时间,定期清理,减少脏数据的影响范围
//for (int i = 0; i < 10_000; i++)
//{
// int k = i;
// Task.Run(() => CustomCacheTiming.Add($"TestKey_{k}", $"TestKey_{k}", 10));
//}
//for (int i = 0; i < 10_000; i++)
//{
// int k = i;
// Task.Run(() => CustomCacheTiming.Remove($"TestKey_{k}"));
//}
//for (int i = 0; i < 10_000; i++)
//{
// int k = i;
// Task.Run(() => CustomCacheTiming.Exists($"TestKey_{k}"));
//}
}
{
for (int i = 0; i < 10_000; i++)
{
int k = i;
Task.Run(() => CustomCacheTimingSectioning.Add($"TestKey_{k}", $"TestKey_{k}", 10));
}
for (int i = 0; i < 10_000; i++)
{
int k = i;
Task.Run(() => CustomCacheTimingSectioning.Remove($"TestKey_{k}"));
}
for (int i = 0; i < 10_000; i++)
{
int k = i;
Task.Run(() => CustomCacheTimingSectioning.Exists($"TestKey_{k}"));
}
}
}
#endregion
}
}
}
using System;
using System.Collections.Generic;
namespace Common
{
/// <summary>
/// 第三方数据存储和获取
/// </summary>
public class CustomCache
{
/// <summary>
/// private:避免被外部调用,数据容器私有,保证安全
/// static:不被GC
/// 字典:读写效率高
/// </summary>
private static Dictionary<string, object> Dic = new Dictionary<string, object>();
public static void Add(string key, object oValue)
{
Dic.Add(key, oValue);
}
public static bool Exists(string key)
{
return Dic.ContainsKey(key);
}
public static T GetT<T>(string key)
{
if (!Dic.ContainsKey(key))
throw new Exception($"没有查找的值{key}");
return (T)Dic[key];
}
public static void Remove(string key)
{
Dic.Remove(key);
}
public static void RemoveAll()
{
Dic.Clear();
}
public static void RemoveCondition(Func<string,bool> func)
{
List<string> keyList = new List<string>();
foreach (var item in Dic)
{
if (func.Invoke(item.Key))
keyList.Add(item.Key);
}
keyList.ForEach(t => Dic.Remove(t));
}
public static T GetValue<T>(string key, Func<T> func)
{
T programList = default(T);
if (!Exists(key))
{
programList = (T)func.Invoke();
Add(key, programList);
}
else
{
programList = GetT<T>(key);
}
return programList;
}
}
}
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Common
{
/// <summary>
/// 第三方数据存储和获取
///
/// 过期策略:
/// 永久有效
/// 绝对过期--设置个时间点,超过就过期
/// 滑动过期--多久之后过期,如果期间再次查询/更新/检查存在,有效期再次延长
///
/// 主动清理+被动清理,保证过期数据不会被查询;过期数据也不会滞留太久
/// 多线程操作非线程安全的容器,会冲突
/// 1.线程安全容器 ConcurrentDictionary 需要引用using System.Collections.Concurrent;
/// 2.lock Add/Remove/遍历
///
/// 锁能解决问题,但是性能怎么办?
/// 分片,多个容器,多个锁,容器之间可以并发
/// </summary>
public class CustomCacheTiming
{
private static readonly object Lock = new object();
//主动清理
static CustomCacheTiming()
{
Task.Run(() =>
{
while (true)
{
try
{
//Thread.Sleep(1000 * 60 * 10);
List<string> keyList = new List<string>();
lock (Lock)
{
foreach (var item in Dic)
{
if (item.Value.ObsoleteType != ObsoleteType.Never && item.Value.DeadLine < DateTime.Now)//过期清理
{
keyList.Add(item.Key);
}
}
}
keyList.ForEach(s => Remove(s));
}
catch (Exception ex)
{
Console.WriteLine("异常");
continue;
}
}
});
}
/// <summary>
/// private:避免被外部调用,数据容器私有,保证安全
/// static:不被GC
/// 字典:读写效率高
/// </summary>
private static Dictionary<string, CacheModel> Dic = new Dictionary<string, CacheModel>();
/// <summary>
/// 永不过期
/// </summary>
/// <param name="key"></param>
/// <param name="oValue"></param>
public static void Add(string key, object oValue)
{
CacheModel cacheModel = new CacheModel()
{
Value = oValue,
ObsoleteType = ObsoleteType.Never
};
lock (Lock)
{
Dic.Add(key, cacheModel);
}
}
/// <summary>
/// 绝对过期
/// </summary>
/// <param name="key"></param>
/// <param name="oValue"></param>
public static void Add(string key, object oValue, int outTimeSecond)
{
CacheModel cacheModel = new CacheModel()
{
Value = oValue,
ObsoleteType = ObsoleteType.Absolutely,
DeadLine = DateTime.Now.AddSeconds(outTimeSecond)
};
lock (Lock)
{
Dic.Add(key, cacheModel);
}
}
/// <summary>
/// 滑动过期
/// </summary>
/// <param name="key"></param>
/// <param name="oValue"></param>
public static void Add(string key, object oValue, TimeSpan duration)
{
CacheModel cacheModel = new CacheModel()
{
Value = oValue,
ObsoleteType = ObsoleteType.Absolutely,
DeadLine = DateTime.Now.Add(duration),
Duration = duration
};
lock (Lock)
{
Dic.Add(key, cacheModel);
}
}
/// <summary>
/// Get前必须执行此方法
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static bool Exists(string key)
{
if (Dic.ContainsKey(key))
{
CacheModel cacheModel = Dic[key];
switch (cacheModel.ObsoleteType)
{
case ObsoleteType.Never:
return true;
case ObsoleteType.Absolutely:
if (cacheModel.DeadLine < DateTime.Now)
{
Dic.Remove(key);
return false;
}
else
{
return true;
}
case ObsoleteType.Relative:
if (cacheModel.DeadLine < DateTime.Now)
{
Dic.Remove(key);
return false;
}
else
{
cacheModel.DeadLine.Add(cacheModel.Duration);
return true;
}
}
return true;
}
else
{
return false;
}
}
public static T GetT<T>(string key)
{
if (!Exists(key))
throw new Exception($"没有查找的值{key}");
return (T)Dic[key].Value;
}
public static void Remove(string key)
{
lock (Lock)
{
Dic.Remove(key);
}
}
public static void RemoveAll()
{
lock (Lock)
{
Dic.Clear();
}
}
public static void RemoveCondition(Func<string, bool> func)
{
List<string> keyList = new List<string>();
lock (Lock)
{
foreach (var item in Dic)
{
if (func.Invoke(item.Key))
keyList.Add(item.Key);
}
}
keyList.ForEach(t => Remove(t));
}
public static T GetValue<T>(string key, Func<T> func)
{
T programList = default(T);
if (!Exists(key))
{
programList = (T)func.Invoke();
Add(key, programList);
}
else
{
programList = GetT<T>(key);
}
return programList;
}
}
}
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Common
{
/// <summary>
/// 第三方数据存储和获取
///
/// 过期策略:
/// 永久有效
/// 绝对过期--设置个时间点,超过就过期
/// 滑动过期--多久之后过期,如果期间再次查询/更新/检查存在,有效期再次延长
///
/// 主动清理+被动清理,保证过期数据不会被查询;过期数据也不会滞留太久
/// 多线程操作非线程安全的容器,会冲突
/// 1.线程安全容器 ConcurrentDictionary 需要引用using System.Collections.Concurrent;
/// 2.lock Add/Remove/遍历
///
/// 锁能解决问题,但是性能怎么办?
/// 分片,多个容器,多个锁,容器之间可以并发
/// </summary>
public class CustomCacheTimingSectioning
{
private static int CpuNumber = 0;//获取CPU数量
private static readonly List<object> LockList = new List<object>();//多个锁
private static List<Dictionary<string, CacheModel>> DicList = new List<Dictionary<string, CacheModel>>();//多个容器
//主动清理
static CustomCacheTimingSectioning()
{
//初始化;多个容器和对应的锁
CpuNumber = 3;
for (int i = 0; i < CpuNumber; i++)
{
DicList.Add(new Dictionary<string, CacheModel>());
LockList.Add(new object());
}
Task.Run(() =>
{
while (true)
{
for (int i = 0; i < CpuNumber; i++)
{
try
{
//Thread.Sleep(1000 * 60 * 10);
List<string> keyList = new List<string>();
lock (LockList[i])
{
foreach (var item in DicList[i])
{
if (item.Value.ObsoleteType != ObsoleteType.Never && item.Value.DeadLine < DateTime.Now)//过期清理
{
keyList.Add(item.Key);
}
}
}
keyList.ForEach(s => Remove(s));
}
catch (Exception ex)
{
Console.WriteLine("异常");
continue;
}
}
}
});
}
/// <summary>
/// 永不过期
/// 添加时得按照算法 计算相应key进入哪个容器,用哪个锁
/// </summary>
/// <param name="key"></param>
/// <param name="oValue"></param>
public static void Add(string key, object oValue)
{
CacheModel cacheModel = new CacheModel()
{
Value = oValue,
ObsoleteType = ObsoleteType.Never
};
int dicIndex = GetDicIndex(key);
lock (LockList[dicIndex])
{
DicList[dicIndex].Add(key, cacheModel);
}
}
/// <summary>
/// 绝对过期
/// </summary>
/// <param name="key"></param>
/// <param name="oValue"></param>
public static void Add(string key, object oValue, int outTimeSecond)
{
CacheModel cacheModel = new CacheModel()
{
Value = oValue,
ObsoleteType = ObsoleteType.Absolutely,
DeadLine = DateTime.Now.AddSeconds(outTimeSecond)
};
int dicIndex = GetDicIndex(key);
lock (LockList[dicIndex])
{
DicList[dicIndex].Add(key, cacheModel);
}
}
/// <summary>
/// 滑动过期
/// </summary>
/// <param name="key"></param>
/// <param name="oValue"></param>
public static void Add(string key, object oValue, TimeSpan duration)
{
CacheModel cacheModel = new CacheModel()
{
Value = oValue,
ObsoleteType = ObsoleteType.Absolutely,
DeadLine = DateTime.Now.Add(duration),
Duration = duration
};
int dicIndex = GetDicIndex(key);
lock (LockList[dicIndex])
{
DicList[dicIndex].Add(key, cacheModel);
}
}
/// <summary>
/// Get前必须执行此方法
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static bool Exists(string key)
{
int dicIndex = GetDicIndex(key);
if (DicList[dicIndex].ContainsKey(key))
{
CacheModel cacheModel = DicList[dicIndex][key];
switch (cacheModel.ObsoleteType)
{
case ObsoleteType.Never:
return true;
case ObsoleteType.Absolutely:
if (cacheModel.DeadLine < DateTime.Now)
{
DicList[dicIndex].Remove(key);
return false;
}
else
{
return true;
}
case ObsoleteType.Relative:
if (cacheModel.DeadLine < DateTime.Now)
{
DicList[dicIndex].Remove(key);
return false;
}
else
{
cacheModel.DeadLine.Add(cacheModel.Duration);
return true;
}
}
return true;
}
else
{
return false;
}
}
public static T GetT<T>(string key)
{
int dicIndex = GetDicIndex(key);
if (!Exists(key))
throw new Exception($"没有查找的值{key}");
return (T)DicList[dicIndex][key].Value;
}
public static void Remove(string key)
{
int dicIndex = GetDicIndex(key);
lock (LockList[dicIndex])
{
DicList[dicIndex].Remove(key);
}
}
public static void RemoveAll()
{
for (int i = 0; i < CpuNumber; i++)
{
lock (LockList[i])
{
DicList[i].Clear();
}
}
}
public static void RemoveCondition(Func<string, bool> func)
{
List<string> keyList = new List<string>();
for (int i = 0; i < CpuNumber; i++)
{
lock (LockList[i])
{
foreach (var item in DicList[i])
{
if (func.Invoke(item.Key))
keyList.Add(item.Key);
}
}
}
keyList.ForEach(t => Remove(t));
}
public static T GetValue<T>(string key, Func<T> func)
{
T programList = default(T);
if (!Exists(key))
{
programList = (T)func.Invoke();
Add(key, programList);
}
else
{
programList = GetT<T>(key);
}
return programList;
}
private static int GetDicIndex(string key)
{
int hashcode = key.GetHashCode() % 10;
if (hashcode < 4)
return 0;
else if (hashcode < 8)
return 1;
else
return 2;
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Common
{
public class CacheModel
{
public object Value { set; get; }
public ObsoleteType ObsoleteType { set; get; }
public DateTime DeadLine { set; get; }
public TimeSpan Duration { set; get; }
}
public enum ObsoleteType
{
Never,
Absolutely,
Relative
}
}
using System;
using System.Collections.Generic;
namespace MyBll
{
public class DbHelper
{
public static List<T> Query<T>(int index)
{
Console.WriteLine($"This is {typeof(DbHelper)} Query");
long lResult = 0;
for (int i = index; i < 100_000_000; i++)
{
lResult += i;
}
List<T> rstList = new List<T>();
for (int i = 0; i < index % 3; i++)
{
rstList.Add(default(T));
}
return rstList;
}
}
}
using System;
using System.Collections.Generic;
namespace MyBll
{
public class FileHelper
{
public static List<T> Query<T>(int index)
{
Console.WriteLine($"This is {typeof(FileHelper)} Query");
long lResult = 0;
for (int i = index; i < 100_000_000; i++)
{
lResult += i;
}
List<T> rstList = new List<T>();
for (int i = 0; i < index % 3; i++)
{
rstList.Add(default(T));
}
return rstList;
}
}
}
using System;
using System.Collections.Generic;
namespace MyBll
{
/// <summary>
/// 模拟远程调用接口
/// </summary>
public class RemoteHelper
{
public static List<T> Query<T>(int index)
{
Console.WriteLine($"This is {typeof(RemoteHelper)} Query");
long lResult = 0;
for (int i = index; i < 100_000_000; i++)
{
lResult += i;
}
List<T> rstList = new List<T>();
for (int i = 0; i < index % 3; i++)
{
rstList.Add(default(T));
}
return rstList;
}
}
}