Redis之缓存穿透击穿雪崩问题与缓存删除淘汰策略
Posted 丨Jack_Chen丨
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis之缓存穿透击穿雪崩问题与缓存删除淘汰策略相关的知识,希望对你有一定的参考价值。
一、缓存问题与解决
缓存穿透
缓存穿透是指查询缓存和DB中都不存在的数据
。
缓存穿透示例:
public Station findProjectStation(Long stationId)
//从缓存中查询
Station station = (Station)redisTemplate.boundHashOps("project_station").get(stationId);
if(station==null)
//缓存中没有,从数据库查询
Station st = stationMapper.selectByPrimaryKey(stationId);
if(st!=null)
station = st;
redisTemplate.boundHashOps("project_station").put(stationId,station);
return station;
解决方案:
1.接口层增加校验,如鉴权校验
2.id做校验,id<=0的直接拦截;
3.从缓存取不到的数据,在数据库中也没有取到,就将key-value对写为key-空对象。以此防止攻击者反复用同一个id暴力攻击。
4. 使用缓存预热,缓存预热就是将数据提前加入到缓存中,当数据发生变更,再将最新的数据更新到缓存。
5. 利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试
6. 利用布隆过滤器,内部维护一系列合法有效的key。迅速判断出,请求所携带的Key是否合法有效。如果不合法,则直接返回
解决缓存穿透示例:
public Station findProjectStation(Long stationId)
//从缓存中查询
Station station = (Station)redisTemplate.boundHashOps("project_station").get(stationId);
if(station==null)
//缓存中没有,从数据库查询
Station st = stationMapper.selectByPrimaryKey(stationId);
if(st!=null)
station = st;
redisTemplate.boundHashOps("project_station").put(stationId,station);
else
redisTemplate.boundHashOps("project_station").put(stationId,new Station());
redisTemplate.boundHashOps("project_station").expire(60, TimeUnit.SECONDS);
return station;
缓存击穿
缓存击穿是指缓存中没有但数据库中有的数据
。
可能产生缓存击穿示例:
public List<Station> findStationList()
//从缓存中查询
List<Station> stationList = (List<Station>)redisTemplate.boundValueOps("station_list").get();;
if(stationList==null)
//缓存中没有,从数据库查询
Example example=new Example(Station.class);
Example.Criteria criteria = example.createCriteria();
criteria.andEqualTo("type","1");
List<Station> findStationList = stationMapper.selectByExample(example);
if(findStationList!=null)
stationList = findStationList;
redisTemplate.boundValueOps("station_list").set(stationList);
return stationList;
解决方案:
1.设置热点数据永远不过期。
2.缓存预热
3.数据不设置过期时间,在缓存的对象上添加一个属性标识过期时间,每次获取到数据时,校验对象中的过期时间属性,如果数据即将过期,则异步发起一个线程主动更新缓存中的数据,当然也可能拿到过期的值,看具体需求。
缓存雪崩
缓存雪崩是指缓存数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至宕机
。
解决方案:
1.缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。如果Redis是集群部署,将热点数据均匀分布在不同的Redis库中也能避免全部失效的问题
2.设置热点数据永远不过期。
3.使用缓存预热
4.使用互斥锁,但是该方案将导致吞吐量明显下降
三者区别
缓存穿透:通常请求携带有参数,不断发起请求。
缓存击穿:通常是某个时刻并发大量请求,并发查询同一条数据。
缓存雪崩:缓存不同的数据大批量到过期时间,很多数据都查不到从而查数据库。
缓存预热
实现缓存预热方法:
@Component
public class RedisInit implements InitializingBean
@Autowired
private IStationService stationService;
/**
* 缓存预热
*
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception
stationService.saveStationListToRedis();
# 二、缓存淘汰机制
>Redis可以对存储在Redis中的缓存数据设置过期时间,但是并非key过期时间到了就一定会被Redis给删除。
Redis可以设置最大缓存即最大内存,当内存已使用率到达,则开始清理缓存
maxmemory <bytes> :设置最大内存 maxmemory 500mb
## 删除策略
>设置了expire的key缓存过期了,但是服务器的内存还是会被占用,这是因为redis是基于删除策略进行删除
Redis删除策略主要有三种:
1.定期删除
>Redis默认是每隔 100ms 就随机抽取一些设置了过期时间的 Key,检查其是否过期,如果过期就删除。
2.惰性删除
>定期删除由于是随机抽取可能会导致很多过期 Key 到了过期时间并没有被删除。在从缓存获取数据的时候,redis会检查这个key是否过期了,如果过期就删除这个key。也就是说在查询的时候将过期key从缓存中清除。
3.主动删除
>当内存满了,依据配置的淘汰策略进行删除
## 内存淘汰机制
>仅使用定期删除 + 惰性删除机制存在一个严重的隐患:如果定期删除留下了很多已经过期的key,并且长时间都没有使用过这些过期key,会导致过期key无法被惰性删除,从而导致过期key一直堆积在内存里,最终造成Redis内存块被消耗殆尽。
Redis内存淘汰机制应运而生。Redis内存淘汰机制提供了八种不同的内存淘汰策略,4.0前有6种。
volatile-lru : 从已设置过期时间的缓存中,淘汰最近最少使用的数据,推荐使用策略
volatile-ttl: 在设置了过期时间的缓存中,挑选将要过期的数据,直接淘汰
volatile-random: 从已设置过期时间的缓存中,随机淘汰数据
allkeys-lru: 所有数据,淘汰最近最少使用的,即清除最少用的旧缓存,然后保存新的缓存,推荐使用
allkeys-random: 在所有的缓存中随机删除,不推荐
noeviction: 旧缓存永不过期,新缓存设置不了,不淘汰,内存满了返回错误,默认策略
volatile-lfu: 从已设置过期时间的缓存中,淘汰使用频率最低的数据
allkeys-lfu: 所有数据,淘汰使用频率最低的数据
以上是关于Redis之缓存穿透击穿雪崩问题与缓存删除淘汰策略的主要内容,如果未能解决你的问题,请参考以下文章