1、数据类型
2、过期策略
Redis会把设置了过期时间的key放入一个独立的字典中,在key过期时并不会立即删除它。
Redis会通过如下两种策略来删除过期的key。
- 惰性删除:客户端在访问某一个key的时候,Redis会检查该key是否过期,若过期则删除;
- 存在的问题是:有些key已经过期了,但是由于再也没有被访问,导致它一直都不会被删除,因而占用内存;
- 定期扫描:Redis默认每秒执行10次过期扫描,扫描策略如下:
- 从过期字典中随机选择20个key;
- 删除20个key中已过期的key;
- 如果过期的key比例超过25%,则重复步骤1;
3、内存淘汰策略
Redis占用内存超出最大限制(maxmemory)时,可采用内存淘汰策略让Redis淘汰一些数据,以腾出空间继续提供读写服务。
- noeviction:对当 Redis 的内存不足时,只要有新的键值对需要插入,Redis 就会返回错误信息并停止插入操作;是 Redis 的默认内存淘汰策略,也是最简单的策略。
- volatile-ttl:在设置了过期时间的key中,选择剩余寿命最短的key将其淘汰;
- volatile-lru:在设置了过期时间的key中,选择最近最少使用的key将其淘汰;
- volatile-random:在设置了过期时间的key中,随机选择一些key将其淘汰;
- allkeys-lru:在所有的key中,选择最近最少使用的key将其淘汰;
- allkeys-random:在所有的key中,随机选择一些key将其淘汰;
其实还有volatile-lfu、allkeys-lfu,所谓LFU算法,就是先考虑键值对访问的次数,优先淘汰访问次数最少的键值对,对于访问次数相同的键值对,再选择最近最少使用的键值对进⾏淘汰,也就是LRU算法。
LRU算法的实现:
维护一个链表,用于顺序存储被访问过的key;在访问数据的时候,每次将最新访问的key移动到表头,即最近被访问的key在表头,最少访问的key在表尾;
Redis对LRU算法做了改进:
给每个key维护一个时间戳,淘汰时随机采样5个key,从中淘汰掉最旧的key;如果还是超出内存限制,则继续随机采样淘汰。优点就是比原始的LRU算法节约内存,并可以取得非常近似得效果;
4、缓存穿透
- 场景
- 查询根本不存在的数据,使大量请求直接达存储层,导致负载过大,甚至宕机
- 解决方案
- 缓存空对象
- 存储层未命中后,仍然将空值存入缓存层,再次访问该数据时,缓存层就会直接返回空值
- 布隆过滤器
- 将所有存在的key提前存入布隆过滤器,在访问缓存层之前,先通过布隆过滤器拦截,若请求的是不存在的key,则直接返回空值
- 缓存空对象
5、缓存击穿
- 场景
- 一份热点数据,它的访问量非常大,在其缓存失效瞬间大量请求直达存储层,导致服务崩溃
- 解决方案
- 加互斥锁
- 对数据的访问加互斥锁,当一个线程访问该数据时,其它线程只能等待;
- 该线程访问结束后,缓存中的数据被重建,届时其它线程就可以直接从缓存取值了;
- 永不过期
- 对热点数据不设置过期时间,就不会出现缓存击穿的问题了;
- 为每个value设置逻辑过期时间,是对value,当发现该值逻辑过期时,使用单独的线程重建缓存;
- 加互斥锁
6、缓存雪崩
- 场景
- 由于某些原因,缓存层不能提供服务,导致所有的请求直达存储层;或者是大批热点数据瞬间同时过期,导大量请求瞬间直达存储层;造成存储层宕机。
- 解决方案
- 避免同时过期
- 为key设置过期时间的时候,附加一个随机数避免大量的key同时过期;
- 构建高可用的Redis缓存
- 部署多个Redis实例,个别实例宕机依然可以保持服务的整体可用性;
- 构建多级缓存
- 增加本地缓存,在存储层前面多加一级屏障,降低请求直达存储层的几率;
- 启用限流和降级措施
- 对存储层增加限流措施,当请求超出限制时对其提供降级服务;
- 避免同时过期