java - redis 缓存穿透

java - redis 缓存穿透 一、缓存穿透定义查询一个数据库里面根本不存在的数据Redis 查不到 → 去查 MySQLMySQL也查不到。缓存永远不会生效每一次请求都会直接打到数据库。举例子商铺 id 数据库最大只有 10但是有人疯狂请求id-1、id99999。Rediscache:shop:-1不存在 → 返回null代码去查 MySQL数据库没有这条记录返回null不会往Redis存任何东西下一次再来请求-1流程又重复一遍又访问数据库如果恶意发起成千上万次这种不存在 id 的请求数据库压力直接被打崩这就是缓存穿透。区分记忆缓存击穿数据是存在的只是缓存刚好过期缓存穿透这条数据压根就不存在于数据库解决方法1缓存空值小项目首选核心思路数据库查不到数据时向Redis存一个空字符串并且设置一个短过期时间2分钟。下次再来相同请求Redis拿到直接返回失败不再访问数据库。你项目里面对应的核心代码OverridepublicResultqueryById(Longid){// 1. 从 redis 查询商铺缓存StringkeyCACHE_SHOP_KEYid;StringshopJsonstringRedisTemplate.opsForValue().get(key);// 2. 判断是否存在有真实的商铺JSON数据if(StrUtil.isNotBlank(shopJson)){// 3. 存在直接返回ShopshopJSONUtil.toBean(shopJson,Shop.class);returnResult.ok(shop);}// 判断命中的是否是空值【解决缓存穿透】// shopJson 不是 null但却是 空字符串命中空缓存if(shopJson!null){// 说明 Redis 存了空字符串代表缓存不存在returnResult.fail(商铺不存在);}// 4. 缓存未命中查询数据库// 只有 shopJson null 的时候才能跑到这里ShopshopgetById(id);// 5. 数据库没查到if(shopnull){// 写入空值过期时间 2 分钟stringRedisTemplate.opsForValue().set(key,,CACHE_NULL_TTL,TimeUnit.SECONDS);returnResult.fail(商铺不存在);}// 6. 查询到数据写入 RedisstringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL,TimeUnit.MINUTES);// 返回结果returnResult.ok(shop);}# 什么时候会进到数据库查询shopJsonnull1.第一次访问这个idRedis里面还没有创建任何key2.之前存的空字符串 过期了Redis自动把这个key删掉了3.Redis缓存被手动清空。✅优点简单好实现❌缺点Redis会存很多没用的空key占用一点点内存方案2布隆过滤器海量大数据场景思路提前把数据库所有合法id全部放进布隆过滤器。请求进来先经过布隆过滤器校验。如果过滤器判定id不存在 → 直接拦截Redis、数据库都不去访问如果过滤器判定id存在 → 才放行查询缓存、数据库✅优点完全拦截无效请求不会产生大量空key❌缺点实现复杂布隆过滤器有极小误判概率新增数据要维护过滤器小型项目一般不用方案3接口参数校验第一道防线在最开始就拦截非法参数不往下走缓存逻辑比如商铺id必须大于0if(id0){returnResult.fail(id非法);}三、快速区分三大缓存问题问题本质解决方案缓存穿透查询不存在的数据每次请求直达数据库缓存空值、布隆过滤器、参数校验缓存击穿热点key缓存过期大量并发同时打到数据库互斥锁、逻辑过期缓存雪崩大批量缓存key同时过期数据库压力瞬间暴涨过期时间加上随机偏移值