【Redis面试常问】Redis常用数据类型;过期键的删除策略;八种淘汰策略;缓存雪崩、缓存击穿、缓存穿透怎么解决;设置过期时间;

Redis系列相关文章如下:
(一)使用docker安装redis、常用五大基本数据类型、Jedis操作Redis、Spring整合Redis
(二)docker安装redis、非docker安装,集群启动
(三)Redis的两种持久化方式:RDB和AOF
(四)Redis主从复制、哨兵(Sentinel)和集群(Cluster)
(五)保障 Redis 与数据库的数据一致性:多方案解析

为什么使用 Redis?

  1. 高性能:Redis的数据存储在内存中,因此读写速度非常快,适用于需要低延迟的应用场景。
  2. 多数据结构支持:Redis支持多种数据结构,如字符串、列表、集合、哈希表、有序集合等,使其能够适应多种应用需求。
  3. 持久化:Redis支持数据持久化,可以将内存中的数据保存到磁盘上,以防止数据丢失。
  4. 分布式:Redis支持分布式部署,可以横向扩展,处理大规模的数据和高并发请求。
  5. 丰富的功能:Redis提供了丰富的功能,如发布订阅、事务、Lua脚本等,支持各种应用场景。

Redis八种数据类型

Redis是一个开源的,内存中的数据结构存储系统,它可以用做数据库,缓存和消息中间件。

支持多种类型的数据结构

  • 字符串 strings
  • 散列 hashes
  • 列表 lists
  • 集合 sets
  • 有序集合 sorted sets
  • bitmaps
  • hyperloglogs
  • 地理空间 geospatial 索引半径查询

支持每秒10w次查询请求
关于前五种常用的数据类型介绍见文章:
【redis】(一)使用docker安装redis、常用五大基本数据类型、Jedis操作Redis、Spring整合Redis

redis过期键的删除策略

常见的删除策略有以下3种:

  • 定时删除

在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除操作。

  • 惰性删除

放任过期键不管,每次从键空间中获取键时,检查该键是否过期,如果过期,就删除该键,如果没有过期,就返回该键。

  • 定期删除

每隔一段时间,程序对数据库进行一次检查,删除里面的过期键,至于要删除哪些数据库的哪些过期键,则由算法决定。

其中定时删除和定期删除为主动删除策略,惰性删除为被动删除策略。

一般选择定期删除与惰性删除结合:Redis 使用定期删除来尽量释放一些内存,然后在读取或写入时,再检查键是否过期,以确保过期的键被及时删除。这种策略综合了定期删除和惰性删除的优点。

Redis八种淘汰策略

  • volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰。
  • volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰。
  • volatile-random:从已设置过期时间的数据集中任意选择数据淘汰。
  • volatile-lfu:从已设置过期时间的数据集挑选使用频率最低的数据淘汰。
  • allkeys-lru:从数据集中挑选最近最少使用的数据淘汰
  • allkeys-lfu:从数据集中挑选使用频率最低的数据淘汰。
  • allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
  • no-enviction(驱逐):禁止驱逐数据,这也是默认策略。意思是当内存不足以容纳新入数据时,新写入操作就会报错,请求可以继续进行,线上任务也不能持续进行,采用no-enviction策略可以保证数据不被丢失。

这八种大体上可以分为4种,lru、lfu、random、ttl。


缓存雪崩,缓存击穿,缓存穿透,怎么解决

缓存穿透

在这里插入图片描述
场景:数据库服务器又崩溃了

1.系统平稳运行过程中

2.应用服务器流量随时间增量较大

3.Redis服务器命中率随时间逐步降低

4.Redis内存平稳,内存无压力

5.Redis服务器CPU占用激增

6.数据库服务器压力激增

7.数据库崩溃

问题排查:

1.Redis中大面积出现未命中

2.出现非正常URL访问

问题分析

  • 获取的数据在数据库中也不存在,数据库查询未得到对应数据
  • Redis获取到null数据未进行持久化,直接返回
  • 下次此类数据到达重复上述过程
  • 出现黑客攻击服务器

现象

key 对应的数据在数据源并不存在,每次针对此 key 的请求从缓存获取不到,请求都会压到数据源,从而可能压垮数据源。

比如用一个不存在的用户 id 获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。

造成:

  1. 应用服务器压力变大。
  2. redis 命中率下降 → 查询数据库 。

如何解决

  • 对空值缓存

    如果一个查询返回的数据为空(不管是数据是否不存在),仍然把这个空结果(null)进行缓存,设置空结果的过期时间会很短,最长不超过五分钟。对查询结果为null的数据进行缓存(长期使用,定期清理),设定短时限,例如30-60秒,最高5分钟

  • 设置可访问的名单(白名单):

    使用 bitmaps 类型定义一个可以访问的名单,名单 id 作为 bitmaps 的偏移量,每次访问和 bitmap 里面的 id 进行比较,如果访问 id 不在 bitmaps 里面,进行拦截,则不允许访问。当加载正常数据时放行,加载异常数据时直接拦截(效率偏低)。

  • 采用布隆过滤器

    布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量(位图)和一系列随机映射函数(哈希函数)。

    布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难

    将所有可能存在的数据哈希到一个足够大的 bitmaps 中,一个一定不存在的数据会被这个 bitmaps 拦截掉,从而避免了对底层存储系统的查询压力

  • 进行实时监控

    实时监控redis命中率(业务正常范围时,通常会有一个波动值)与null数据的占比。当发现 Redis命中率开始急速降低,需要排查访问对象和访问的数据,和运维人员配合,可以设置黑名单限制服务。
    非活动时段波动:通常检测3-5倍,超过5倍纳入重点排查对象

    活动时段波动:通常检测10-50倍,超过50倍纳入重点排查对象

    根据倍数不同,启动不同的排查流程。然后使用黑名单进行防控(运营)

总的来说:缓存击穿是指访问了不存在的数据,跳过了合法数据的redis数据缓存阶段,每次访问数据库,导致对数据库服务器造成压力。通常此类数据的出现量是一个较低的值,当出现此类情况以毒攻毒,并及时报警。应对策略应该在临时预案防范方面多做文章。

无论是黑名单还是白名单,都是对整体系统的压力,警报解除后尽快移除。

缓存击穿

在这里插入图片描述
场景:还是数据库服务器崩溃

1.系统平稳运行过程中

2.数据库连接量瞬间激增

3.Redis服务器无大量key过期

4.Redis内存平稳,无波动

5.Redis服务器CPU正常

6.数据库崩溃

问题排查:

1.Redis中某个key过期,该key访问量巨大

2.多个数据请求从服务器直接压到Redis后,均未命中

3.Redis在短时间内发起了大量对数据库中同一数据的访问

总而言之就两点:单个key高热数据,key过期

key 对应的数据存在,但在 redis 中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB 加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端 DB 压垮。

  1. 数据库访问压力瞬间增大。
  2. redis 中没有出现大量 key 过期,redis 正常运行。
  3. (即某个经常访问的 key 过期,突然有大量访问这个数据)

如何解决

  • 预先设置热门数据

    redis 高峰访问之前,把一些热门数据提前存入到 redis 里面,加大这些热门数据 key 的时长。

    以电商为例,每个商家根据店铺等级,指定若干款主打商品,在购物节期间,加大此类信息key的过期时长 注意:购物节不仅仅指当天,以及后续若干天,访问峰值呈现逐渐降低的趋势

  • 实时调整

    现场监控哪些数据热门,实时调整 key 的过期时长。对自然流量激增的数据延长过期时间或设置为永久性key。

  • 使用锁
    分布式锁,防止被击穿,但是要注意也是性能瓶颈,慎重!

  • 后台刷新数据

    启动定时任务,高峰期来临之前,刷新数据有效期,确保不丢失

  • 二级缓存

    设置不同的失效时间,保障不会被同时淘汰就行

总的来说:缓存击穿就是单个高热数据过期的瞬间,数据访问量较大,未命中redis后,发起了大量对同一数据的数据库访问,导致对数 据库服务器造成压力。应对策略应该在业务数据分析与预防方面进行,配合运行监控测试与即时调整策略,毕竟单个key的过 期监控难度较高,配合雪崩处理策略即可。

缓存雪崩

场景:数据库服务器崩溃,一连串的场景会随之儿来

1.系统平稳运行过程中,忽然数据库连接量激增

2.应用服务器无法及时处理请求

3.大量408,500错误页面出现

4.客户反复刷新页面获取数据

5.数据库崩溃

6.应用服务器崩溃

7.重启应用服务器无效

8.Redis服务器崩溃

9.Redis集群崩溃

10.重启数据库后再次被瞬间流量放倒

问题排查

1.在一个较短的时间内,缓存中较多的key集中过期

2.此周期内请求访问过期的数据,redis未命中,redis向数据库获取数据

3.数据库同时接收到大量的请求无法及时处理

4.Redis大量请求被积压,开始出现超时现象

5.数据库流量激增,数据库崩溃

6.重启后仍然面对缓存中无数据可用

7.Redis服务器资源被严重占用,Redis服务器崩溃

8.Redis集群呈现崩塌,集群瓦解

9.应用服务器无法及时得到数据响应请求,来自客户端的请求数量越来越多,应用服务器崩溃

10.应用服务器,redis,数据库全部重启,效果不理想

总而言之就两点:短时间范围内,大量key集中过期

key 对应的数据存在,但在 redis 中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB 加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端 DB 压垮。

缓存雪崩与缓存击穿的区别在于这里针对很多 key 缓存,前者则是某一个 key

  1. 数据库压力变大。
  2. 即极少的时间段,查询大量 key 的集中过期情况。

如何解决

  • 构建多级缓存架构

    nginx 缓存 + redis 缓存 + 其他缓存(ehcache等)

  • 使用锁或队列:

    用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。不适用高并发情况。

  • 设置过期标志更新缓存:

    记录缓存数据是否过期(设置提前量),如果过期会触发通知另外的线程在后台去更新实际 key 的缓存。

  • 将缓存失效时间分散开:

    比如我们可以在原有的失效时间基础上增加一个随机值,比如 1~5 分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

  • 检测Mysql严重耗时业务进行优化

    对数据库的瓶颈排查:例如超时查询、耗时较高事务等

  • 灾难预警机制

    监控redis服务器性能指标

    CPU占用、CPU使用率

    内存容量

    查询平均响应时间

    线程数

  • 限流、降级

    短时间范围内牺牲一些客户体验,限制一部分请求访问,降低应用服务器压力,待业务低速运转后再逐步放开访问

落地实践:

  1. LRU与LFU切换

  2. 数据有效期策略调整

    根据业务数据有效期进行分类错峰,A类90分钟,B类80分钟,C类70分钟;

    过期时间使用固定时间+随机值的形式,稀释集中到期的key的数量

  3. 超热数据使用永久key

  4. 定期维护(自动+人工)

    对即将过期数据做访问量分析,确认是否延时,配合访问量统计,做热点数据的延时

  5. 加锁:慎用!

总的来说:缓存雪崩就是瞬间过期数据量太大,导致对数据库服务器造成压力。如能够有效避免过期时间集中,可以有效解决雪崩现象的 出现(约40%),配合其他策略一起使用,并监控服务器的运行数据,根据运行记录做快速调整。


Java中结合redis设置过期时间

在Java中,可以使用Jedis库来操作Redis,并且设置过期时间。Jedis是一个流行的Java客户端,用于与Redis数据库交互。

首先,确保已经将Jedis添加到项目依赖中。Maven项目在pom.xml文件中添加以下依赖:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.7.0</version> <!-- 请检查最新版本 -->
</dependency>

然后,你可以使用以下代码来设置Redis中的键的过期时间:

import redis.clients.jedis.Jedis;

public class RedisExample {
    public static void main(String[] args) {
        // 连接到Redis服务器
        Jedis jedis = new Jedis("localhost");
        System.out.println("连接成功");

        // 设置键的值
        jedis.set("myKey", "myValue");

        // 设置键的过期时间,单位为秒
        int expirationTimeInSeconds = 60; // 60秒后过期
        jedis.expire("myKey", expirationTimeInSeconds);

        // 打印键的剩余生存时间
        long ttl = jedis.ttl("myKey");
        System.out.println("剩余生存时间(秒): " + ttl);

        // 关闭连接
        jedis.close();
    }
}

在上面的示例中,我们使用jedis.expire()方法来设置键的过期时间。过期时间以秒为单位。此外,我们还使用jedis.ttl()方法来获取键的剩余生存时间。

请注意,你需要将localhost替换为你实际运行的Redis服务器的地址。如果设置了密码,还需要在连接前使用jedis.auth("password")进行验证。

Redis系列相关文章如下:
(一)使用docker安装redis、常用五大基本数据类型、Jedis操作Redis、Spring整合Redis
(二)docker安装redis、非docker安装,集群启动
(三)Redis的两种持久化方式:RDB和AOF
(四)Redis主从复制、哨兵(Sentinel)和集群(Cluster)
(五)保障 Redis 与数据库的数据一致性:多方案解析

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值