IMemoryCache组件实现缓存键值滑动刷新及超时失效的回调通知事件
超时缓存滑动刷新超时通知事件缓存失效事件局部替代Redis
博客随笔
8
0 积分
手头一个项目里有个功能模块,任务不轻,得接收海量的并发数据请求,还得把这些数据一股脑地更新到数据库里,或者新增记录进去。可问题来了,要是碰上一堆重复的数据库请求,数据库哪受得了,压力 “蹭蹭” 就上去了。 咋解决呢?我们用上了 Redis 的缓存超时通知模块。这玩意儿作用可大了,就是专门对付那些频繁来的数据请求,给数据库前面加个 “缓冲垫”。一旦收到重复请求,它就不着急通知数据库干活,先拖着,把后面来的重复请求都挡一挡。一直等到最后一个重复请求超时了,它才把这一箩筐数据一次性稳稳地保存到数据库里。这么干,那些没用的数据库 I/O 操作就少多了,数据库的压力自然就小了,整个项目运作起来也顺畅多了。
目前项目里用 Redis 缓存超时通知模块都好几年了,除了部署麻烦点一直也没啥大毛病。不过,就为了实现这么一个小功能,每次还得专门单独给它装个 Redis,总觉得有点大材小用。本着精益求精的精神,就动了脑筋,琢磨着能不能干脆把这个小功能直接集成到程序里头。
说干就干,一通研究下来,嘿,还真让咱给鼓捣出了差不多的效果。在本地测试的时候,看起来还挺像那么回事儿,运行得挺顺溜。但话说回来,在实际的高并发环境里,这玩意儿性能咋样、稳不稳定,目前还没真正测过。
代码如下:
public class MemoryCacheExtension
{
/// <summary>
/// 线程并发锁 线程安全考虑
/// </summary>
readonly static SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1);
/// <summary>
/// 读取内存缓存注册服务
/// </summary>
readonly IMemoryCache _memoryCache = Ioc.Default.GetService<IMemoryCache>();
/// <summary>
/// 动态缓存更新/添加
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="timeOffset"></param>
/// <returns></returns>
public async Task SlidingItemAsync(object key, object value, TimeSpan timeOffset)
{
await _semaphoreSlim.WaitAsync();
try
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
cancellationTokenSource.CancelAfter(timeOffset);
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(timeOffset)
.RegisterPostEvictionCallback(new PostEvictionDelegate(delegate (object _key, object _value, EvictionReason reason, object state)
{
if (OnExpirationEvent is not null)
{
if (reason == EvictionReason.TokenExpired)
{
OnExpirationEvent(_key, _value);
}
}
}))
.AddExpirationToken(new CancellationChangeToken(cancellationTokenSource.Token));
// 添加或更新缓存项
_memoryCache.Set(key, value, cacheEntryOptions);
}
finally
{
_semaphoreSlim.Release();
}
}
/// <summary>
/// 超时失效事件
/// </summary>
public event Action<object, object> OnExpirationEvent;
}