Redis经典面试题总结

1. 什么是Redis?它主要用来什么的?

Redis,英文全称是Remote Dictionary Server(远程字典服务),是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

与MySQL数据库不同的是,Redis的数据是存在内存中的。它的读写速度非常快,每秒可以处理超过10万次读写操作。因此redis被广泛应用于缓存,另外,Redis也经常用来做分布式锁。除此之外,Redis支持事务、持久化、LUA 脚本、LRU 驱动事件、多种集群方案。

2.Redis的基本数据结构类型

Redis有以下这五种基本类型:

  • String(字符串)
  • Hash(哈希)
  • List(列表)
  • Set(集合)
  • zset(有序集合)

3. Redis为什么这么快?

3.1 基于内存存储实现
Redis基于内存存储实现的数据库,相对于数据存在磁盘的MySQL数据库,省去磁盘I/O的消耗。
3.2 合理的数据编码
譬如:String类型如果存储数字的话,是用int类型的编码
3.3 合理的线程模型

  • I/O 多路复用: IO多路复用其实就是一种同步IO模型,它实现了一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;而没有文件句柄就绪时,就会阻塞应用程序,交出cpu。
  • 单线程模型: Redis是单线程模型的,而单线程避免了CPU不必要的上下文切换和竞争锁的消耗。也正因为是单线程,如果某个命令执行过长(如hgetall命令),会造成阻塞。Redis是面向快速执行场景的数据库。所以要慎用如smembers和lrange、hgetall等命令。

IO多路复用程序(这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程)会监听多个Socket,将Socket产生的事件放入队列中排队,事件分派器每次从队列中取出一个事件,把该事件交给对应的事件处理器进行处理
在这里插入图片描述
Redis客户端对服务端的每次调用都经历了发送命令,执行命令,返回结果三个过程。其中执行命令阶段,由于Redis是单线程来处理命令的,所有每一条到达服务端的命令不会立刻执行,所有的命令都会进入一个队列中,然后逐个被执行。并且多个客户端发送的命令的执行顺序是不确定的。但是可以确定的是不会有两条命令被同时执行,不会产生并发问题,这就是Redis的单线程基本模型。

4. 什么是缓存击穿、缓存穿透、缓存雪崩?

4.1 缓存击穿问题
缓存击穿: 指热点key在某个时间点过期的时候,而恰好在这个时间点对这个Key有大量的并发请求过来,从而大量的请求打到db。缓存击穿和缓存雪崩区别在于击穿针对某一热点key缓存,雪崩则是很多key。

如何避免缓存击穿问题,解决方案就有两种:

  • 使用互斥锁方案。缓存失效时,不是立即去加载db数据,而是先使用某些带成功返回的原子操作命令,如(edis的setnx()方法或者redisTemplate的setIfAbsent()方法)去操作,成功的时候,再去加载db数据库数据和设置缓存。否则就去重试获取缓存。
  • “永不过期”,是指没有设置过期时间,但是热点数据快要过期时,异步线程去更新和设置过期时间。

4.2 缓存穿透问题
缓存穿透:指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,进而给数据库带来压力。

如何避免缓存穿透呢? 一般有三种方法:

  • 如果是非法请求,我们在API入口,对参数进行校验,过滤非法值。
  • 如果查询数据库为空,我们可以给缓存设置个空值,或者默认值。
  • 使用布隆过滤器快速判断数据是否存在。即一个查询请求过来时,先通过布隆过滤器判断值是否存在,存在才继续往下查。

4.2 缓存雪奔问题
缓存雪奔: 指缓存中数据大批量到过期时间,而查询数据量巨大,请求都直接访问数据库,引起数据库压力过大甚至down机。
如何避免缓存雪崩问题:

  • 缓存雪奔一般是由于大量数据同时过期造成的。
  • Redis 故障宕机也可能引起缓存雪奔,这就需要构造Redis高可用集群。

5. Redis 过期策略和内存淘汰策略

5.1 Redis的过期策略

  1. 定时过期 每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即对key进行清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
  2. 惰性过期 只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
  3. 定期过期 每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。

Redis中同时使用了惰性过期和定期过期两种过期策略。

5.2 Redis 内存淘汰策略(逐出算法)
Redis使用内存存储数据,在执行每一个命令前,会调用freeMemoryIfNeeded()检测内存是否充足。如果内存不满足新加入数据的最低存储要求,redis要临时删除一些数据为当前指令清理存储空间。
5.2.1 常用的逐出算法:
在这里插入图片描述
5.2.2 影响数据逐出的相关配置

  • maxmemory (最大可使用内存): 占用物理内存的比例,默认值为0,表示不限制。生产环境中根据需求设定,通常设置在50%以上。
  • maxmemory-samples ( 每次选取待删除数据的个数) : 选取数据时并不会全库扫描,导致严重的性能消耗,降低读写性能。因此采用随机获取数据的方式作为待检测删除数据。
  • maxmemory-policy ( 删除策略) : 达到最大内存后的,对被挑选出来的数据进行删除的策略。

6. Redis 的持久化机制有哪些?

Redis提供了RDB(Redis DataBase)和AOF(append only file)两种持久化机制。

6.1 RDB
RDB持久化,是指在指定的时间间隔内,执行指定次数的写操作,将内存中的数据集快照写入磁盘中,它是Redis默认的持久化方式。执行完操作后,在指定目录下会生成一个dump.rdb文件,Redis 重启的时候,通过加载dump.rdb文件来恢复数据。RDB触发机制主要有以下几种:
在这里插入图片描述
RDB 的优点:

  1. RDB是一个紧凑压缩的二进制文件,存储效率较高
  2. 适合大规模的数据恢复场景,如备份,全量复制等,RDB恢复数据的速度要比AOF快很多

RDB缺点:

  1. 没办法做到实时持久化/秒级持久化。
  2. 新老版本存在RDB格式兼容问题

6.2 AOF
AOF持久化,采用日志的形式来记录每个写操作并追加到文件中,重启时再重新执行AOF文件中的命令来恢复数据。它主要解决数据持久化的实时性问题,默认是不开启的。
在这里插入图片描述
在这里插入图片描述

AOF的优点:

  • 数据的一致性和完整性更高

AOF的缺点:

  • AOF记录的内容越多,文件越大,数据恢复变慢。

7.怎么实现Redis的高可用(三种模式)?

Redis 实现高可用有三种部署模式:主从模式,哨兵模式,集群模式。

7.1 主从模式

主从模式中,Redis部署了多台机器,有主节点,负责读写操作,有从节点,只负责读操作。从节点的数据来自主节点,实现原理就是主从复制机制。
主从复制包括全量复制,增量复制两种
7.1.1全量复制
一般当slave第一次启动连接master,或者认为是第一次连接,就采用全量复制,全量复制流程如下:
在这里插入图片描述
redis2.8版本之后,已经使用psync来替代sync,因为sync命令非常消耗系统资源,psync的效率更高。

7.1.2 增量复制
slave与master全量同步之后,master上的数据,如果再次发生更新,就会触发增量复制。
当master节点发生数据增减时,会把用户执行的命令发送到所有的slave节点,让slave节点执行。流程如下:
在这里插入图片描述

7.2 哨兵模式

哨兵(sentinel) 是一个分布式系统,用于对主从结构中的每台服务器进行监控,当出现故障时通过投票机制选择新的master并将所有slave连接到新的master。

7.2.1哨兵的作用(哨兵工作原理):

  • 1.监控
    不断的检查master和slave是否正常运行。
    master存活检测、master与slave运行情况检测
  • 2.通知(提醒)
    当被监控的服务器出现问题时,向其他(哨兵间,客户端)发送通知。
  • 3.自动故障转移
    断开master与slave连接,选取一个slave作为master,将其他slave连接到新的master,并告知客户端新的服 务器地址

在这里插入图片描述
注意:
哨兵也是一台redis服务器,只是不提供数据服务,哨兵之间也会相互监控,从而达到高可用。
通常哨兵配置数量为单数。

7.3 cluster集群模式

集群就是使用网络将若干台计算机联通起来,并提供统一的管理方式,使其对外呈现单机的服务效果。

7.3.1 集群作用:

  1. 分散单台服务器的访问压力,实现负载均衡
  2. 分散单台服务器的存储压力,实现可扩展性
  3. 降低单台服务器宕机带来的业务灾难

8. 使用过Redis分布式锁嘛?有哪些注意点呢?

lua脚本如下:

if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end;

一般情况下,已经可以使用这种实现方式。但是存在锁过期释放了,业务还没执行完的问题(实际上,估算个业务处理的时间,一般没啥问题了)。

错误实现:

  1. 命令setnx + expire分开写:
    执行完setnx加锁,正要执行expire设置过期时间时,进程crash掉或者要重启维护了,那这个锁就“长生不老”了,别的线程永远获取不到锁啦,所以分布式锁不能这么实现。
  2. set ex px nx + 校验唯一随机值,再删除:
    判断当前线程加的锁和释放锁是不是一个原子操作。如果调用jedis.del()释放锁的时候,可能这把锁已经不属于当前客户端,会解除他人加的锁。

9. 使用过Redisson嘛?说说它的原理

分布式锁可能存在锁过期释放,业务没执行完的问题。有些小伙伴认为,稍微把锁过期时间设置长一些就可以啦。其实我们设想一下,是否可以给获得锁的线程,开启一个定时守护线程,每隔一段时间检查锁是否还存在,存在则对锁的过期时间延长,防止锁过期提前释放。

当前开源框架Redisson就解决了这个分布式锁问题。我们一起来看下Redisson底层原理是怎样的吧:
在这里插入图片描述
只要线程一加锁成功,就会启动一个watch dog看门狗,它是一个后台线程,会每隔10秒检查一下,如果线程1还持有锁,那么就会不断的延长锁key的生存时间。因此,Redisson就是使用Redisson解决了锁过期释放,业务没执行完问题。

10. MySQL与Redis 如何保证双写一致性

外部链接: redis缓存为什么要延时双删

10.1 缓存为啥是删除,⽽不是更新?
如果是更新,存在分布式事务问题,可能出现修改了缓存,数据库修改失败的情况。只是删除缓存的话,就算数据库修改失败,下次查询会直接取数据库的数据,也不会出现脏数据。
10.2 延时双删是什么?
就是在增删改某实体类的时候,要对该实体类的缓存进行清空,清空的位置在数据库操作方法的前后。

流程:

  • 先删除缓存
  • 再更新数据库
  • 休眠一会(比如1秒),再次删除缓存。

这个休眠时间 = 读业务逻辑数据的耗时 + 几百毫秒。为了确保读请求结束,写请求可以删除读请求可能带来的缓存脏数据。这种方案只有休眠那一会(比如就那1秒),可能有脏数据,一般业务也会接受的。

11. 聊聊Redis 事务机制

Redis通过MULTI、EXEC、WATCH等一组命令集合,来实现事务机制。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。

简言之,Redis事务就是顺序性、一次性、排他性的执行一个队列中的一系列命令。

Redis执行事务的流程如下:
开始事务(MULTI)
命令入队
执行事务(EXEC)、撤销事务(DISCARD )

布隆过滤器

布隆过滤器是一种占用空间很小的数据结构,它由一个很长的二进制向量和一组Hash映射函数组成,它用于检索一个元素是否在一个集合中,空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。

布隆过滤器原理是?假设我们有个集合A,A中有n个元素。利用k个哈希散列函数,将A中的每个元素映射到一个长度为a位的数组B中的不同位置上,这些位置上的二进制数均设置为1。如果待检查的元素,经过这k个哈希散列函数的映射后,发现其k个位置上的二进制数全部为1,这个元素很可能属于集合A,反之,一定不属于集合A。

  • 0
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值