.net core redis 缓存穿透 缓存击穿 缓存雪崩
缓存 击穿 就是 缓存过期了 就需要查询数据库 高并发 数据库有可能挂了
缓存 击穿 解决方法 缓存预热 上线前 把数据倒到 redis里 过期时间 不用设置过期 就 永不过期 就行
缓存 穿透 就是 查询的时候 缓存没查到 数据库也没查到 ,类似的情况 比如 查询用户 get/user/id 我瞎写一个 可能就是 缓存中没有 数据库中也没有 这个id数据
就是走了一圈 即 消耗了redis的资源 又消耗了 数据库资源 浪费资源 而且高并发 还能把 数据库搞挂了 可恶
缓存 穿透的解决方法 直接点用 布隆过滤 期 先查询过滤期有没有该 数据key 如果有才查询缓存 如果没有就返回null
缓存 雪崩 就是 同一时间 redis 的数据过期了 失效了 或者 redis崩了 造成查询不到了 只能查询数据库了 也会把 数据库搞挂了 太可恶了
缓存雪崩 就搞搞个 集群就解决了,过期时间搞个随机数别写成一样的 俗称打散总是搞的高大上的名称 但是 解释起来就那么回事
下面来 一一解决这些问题
先 nuget 下在布隆过滤器
BloomFilter.NetCore
由于 我用的 是CSRedisCore 所以下载这个 github 还适配其他的 redis 的链接库
BloomFilter.CSRedis.NetCore
源码地址 使用方式 人家写的 挺详细的
vla/BloomFilter.NetCore: Library Bloom filters in C# with optional Redis-backing (github.com)
不墨迹了 说了那么多
直接上代码
//添加 布隆过滤器 services.AddBloomFilter(setupAction => { setupAction.UseCSRedis(new FilterCSRedisOptions { Name = "Redis1", RedisKey = "CSRedis1", Client = services.BuildServiceProvider().GetRequiredService<CSRedisClient>(),//这里就演示一下后期需要封装 用的是CSRedisCore 给客户端赋值 ConnectionStrings = new[] { "192.168.0.192" }.ToList() }); });
测试一下
private readonly IService<Order> _service; private readonly CSRedisClient _cSRedisClient; private readonly ICapPublisher _capPublisher; private readonly IBloomFilter _bloomFilter; public OrdersController(IService<Order> service, CSRedisClient cSRedisClient, ICapPublisher capPublisher, IBloomFilter bloomFilter ) { _service = service; _cSRedisClient = cSRedisClient; _capPublisher = capPublisher; _bloomFilter = bloomFilter; }
[HttpPost] public async Task<IActionResult> CreateAsync([FromBody] Order order) { string id = order.Id.ToString(); string key = $"{RedisKeyPrefix.OrderPrefix}:{id}"; //存到redis 现在 1个0.5秒 _cSRedisClient.HSet(key, id, order); //为了避免缓存不被击穿 不设置过期时间 //await _cSRedisClient.ExpireAsync(key, TimeSpan.FromSeconds(12)); //为了避免缓存不被穿透 添加布隆过滤器 await _bloomFilter.AddAsync(key); await _capPublisher.PublishAsync(CapKeyPrefix.CapOrderCreate, order); //直接添加 数据库 1个请求 需要20秒 //var orderEntity = await _service.CreateAsync(order); //await _service.SaveChangeAsync(); //return Ok(orderEntity); return Ok(order); } [HttpGet("{Id}")] public async Task<IActionResult> GetAsync([FromRoute] int Id) { string id = Id.ToString(); string key = $"{RedisKeyPrefix.OrderPrefix}:{id}"; Order order = null; //布隆过滤器 如果存在就查询 如果不存在就返回null 布隆过滤器 的 数据应该是预热的时候添加进去 bool isExits = await _bloomFilter.ContainsAsync(key); if (isExits) { //单条查询 20请求并发 最大值 45-138毫秒 //Order order = await _service.GetAsync(Id); //redis 单条查询 20请求并发 最大值 42毫秒 order = await _cSRedisClient.HGetAsync<Order>(key, id); if (order == null) { //如果 缓存中没有数据,数据过期 将查询数据库 这就造成了 缓存击穿了 //解决方式 先数据预热缓存的数据不设置过期时间 保证查询的数据 一定存在 //解决了缓存击穿了 如果我随意写个Id 这个时候 就会出现 缓存中没有 数据库中也没有 如果 //高并发 或者恶意攻击 那么 就会 一直查询数据库 会拖垮数据库 //此时用 布隆过滤器解决 order = await _service.GetAsync(Id); } } //var orderEntity = await _service.UpdateAsync(order); //await _service.SaveChangeAsync(); //return Ok(orderEntity); return Ok(order); }
以上就是测试
这个就是刚创建的布隆过滤器