通常我们为了保证缓存中的数据与数据库中的数据一致性,会给 Redis 里的数据设置过期时间,当缓存数据过期后,用户访问的数据如果不在缓存里,业务系统需要重新生成缓存,因此就会访问数据库,并将数据更新到 Redis 里,这样后续请求都可以直接命中缓存。
缓存雪崩
如果同一时间大量的缓存数据同时过期(失效)或redis服务宕机,于是全部请求都直接访问数据库,从而导致数据库的压力骤增,严重的会造成数据库宕机。
发生缓存雪崩有两个原因
- 大量的缓存数据同时过期(失效)
- redis服务宕机
大量的缓存数据同时过期(失效)的解决方案
1、随机设置过期时间 (TTL)
2、互斥锁
当业务线程在处理用户请求时,如果发现访问的数据不在 Redis 里,就加个互斥锁,保证同一时间内只有一个请求来构建缓存(从数据库读取数据,再将数据更新到 Redis 里),当缓存构建完成后,再释放锁。未能获取互斥锁的请求,要么等待锁释放后重新读取缓存,要么就返回空值或者默认值。
实现互斥锁的时候,最好设置超时时间,不然第一个请求拿到了锁,然后这个请求发生了某种意外而一直阻塞,一直不释放锁,这时其他请求也一直拿不到锁,整个系统就会出现无响应的现象。
3、双 key 策略
我们对缓存数据可以使用两个 key,一个是主 key,会设置过期时间,一个是备 key,不会设置过期,它们只是 key 不一样,但是 value 值是一样的,相当于给缓存数据做了个副本。
当业务线程访问不到「主 key 」的缓存数据时,就直接返回「备 key 」的缓存数据,然后在更新缓存的时候,同时更新「主 key 」和「备 key 」的数据。
4、多级缓存
jvm本地缓存,nginx缓存等
Redis 故障宕机引发的缓存雪崩的解决方案
服务熔断或请求限流机制 (sentinel);
构建 Redis 缓存高可用集群 (主从集群);
缓存击穿
缓存击穿也称(热点key问题),一个被高并发访问且缓存业务重建困难的key突然过期(失效)了,于是全部请求都直接访问数据库,从而导致数据库的压力骤增甚至宕机。
如:tb秒杀活动,wb热榜等
解决方案
1、互斥锁
互斥锁保证同一时间只有一个业务线程更新缓存,未能获取互斥锁的请求,要么等待锁释放后重新读取缓存,要么就返回空值或者默认值。最后给锁也添加过期时间
redis命令
SETNX KEY_NAME VALUE
使用setIfAbsent,boot2.1版本以上才支持
stringRedisTemplate.opsForValue().setIfAbsent(key, "1",10, TimeUnit.SECONDS);
使用lua脚本
private Boolean tryGetLock1(String key){/** redisUtil.setIfAbsent 新加的带有超时的setIfAbsent 脚本*///KEYS[1] 用来表示在redis 中用作键值的参数占位,// 主要用來传递在redis 中用作keys值的参数// ARGV[1] 用来表示在redis 中用作参数的占位,// 主要用来传递在redis中用做 value值的参数。String newSetIfAbsentScriptStr = " if 1 == redis.call('setnx',KEYS[1],ARGV[1]) then" +" return 1;" +" else" +" return 0;" +" end;";//创建 redis脚本对象RedisScript<Boolean> newSetIfAbsentScript = new DefaultRedisScript<>(newSetIfAbsentScriptStr,Boolean.class);List<String> keys = new ArrayList<>();keys.add(key);// keyObject[] values = {"1"};// value// 执行脚本Boolean res = stringRedisTemplate.execute(newSetIfAbsentScript, keys, values);System.out.println("result:"+res);return res;}
2、逻辑过期
逻辑过期是指不给热点数据设置过期时间,由后台异步更新缓存,或者在热点数据准备要过期前,提前通知后台线程更新缓存以及重新设置过期时间
互斥锁和逻辑过期对比
缓存穿透
缓存穿透是指客户端请求的数据在缓存和数据库中都不存在,于是全部请求都直接访问数据库,从而导致数据库的压力骤增甚至宕机。
存在原因:恶意攻击,故意大量访问某些读取不存在数据的业务
解决方案
1、请求校验
2、缓存空对象
3、布隆过滤器
布隆过滤器校验的结果特点是:
- 若过滤器判断某个元素存在,那么这个元素不一定存在
- 若过滤器判断某个元素不存在,那么这个元素一定不存在
布隆过滤器讲解:https://blog.csdn.net/cssweb_sh/article/details/124284785