决战紫禁之巅之Redis面试

Redis单线程为什么那么快?

redis的速度非常快,速度快的原因主要有以下几点:

  1. 完全基于内存操作
  2. C语言实现,优化过的数据结构,基于几种基础的数据结构,redis做了大量的优化,性能极高
  3. 使用单线程,无上下文的切换成本
  4. 基于非阻塞的IO多路复用机制

请说说常用的Redis基本类型

类型底层实现场景
String (字符串)SDS动态字符串,小于多少的时候是采用压缩存储的1.验证码时间判断 2.分布式锁,3.去重等
List(链表结构,双向链表)LinkedLIst双端队列,zip压缩链表1.数据的延迟加载效果,2.消息队列
Hash(参考HashMap,键值对)zip压缩链表,哈希表1.购物车,2.用户信息等
Set(元素集合不能重复)哈希表,字典1、共同好友 2、利用唯一性,统计访问网站的所有独立ip 3、好友推荐时,根据tag求交集,大于某个阈值就可以推荐
Sorted Set(有序集合)zip压缩链表,跳跃表[skiplist]1、排行榜 ,2.延迟消息队列

Redis不是单线程?Redis6.0为什么采用了多线程?

redis认为性能瓶颈在于网络IO而非CPU,使用多线程能提升IO读写的效率,从而整体提高redis的性能,处理客户端请求依然是单线程。

MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据

这个考察的就是热点key问题,

热点key问题就是某一时间段,很多请求去请求某一个key,造成该流量高峰,导致Redis雪崩造成宕机
怎么解决呢

  1. 提前把热key打散到不同的服务器,降低压力
  2. 加入二级缓存,提前加载热key数据到内存中,如果redis宕机,走内存查询,二级缓存很多如Ehcache,Caffeine 都可以去看看

经典三大问题:缓存击穿、缓存穿透、缓存雪崩?

缓存击穿

缓存击穿的概念就是单个key并发访问过高,过期时导致所有请求直接打到db上,这个和热key的问题比较类似,只是说的点在于过期导致请求全部打到DB上而已。
解决方案:

  1. 加锁更新,当来了一个请求,发现缓存中没有,我们就对这个请求的这个key加锁,同时去数据库查询数据,写入缓存,再返回给用户,这样后面的请求就可以从缓存中拿到数据了。

缓存穿透

缓存穿透是指查询不存在缓存中的数据,每次请求都会打到DB,就像缓存不存在一样。
解决方案:
1,缓存一个过期时间短的空值,这样当数据不存在的时候还是走的是Redis没有全部请求打到数据中
2,针对这个问题,Redis提供了布隆过滤器。布隆过滤器的原理是在你存入数据的时候,会通过散列函数(多次不同的Hash方法)将它映射为一个位数组中的K个点,同时把他们置为1。这样当用户再次来查询A,而A在布隆过滤器值为0,直接返回,就不会产生击穿请求打到DB了。显然,使用布隆过滤器之后会有一个问题就是误判,因为它本身是一个数组,可能会有多个值落到同一个位置,那么理论上来说只要我们的数组长度够长,误判的概率就会越低,布隆过滤器也可以自定义参数,减少误判率增加布隆过滤器的长度,浪费了内存,这个看实际情况怎么衡量了

缓存雪崩

当某一时刻发生大规模的缓存失效的情况,比如你的缓存服务宕机了,会有大量的请求进来直接打到DB上,这样可能导致整个系统的崩溃,称为雪崩。雪崩和击穿、热key的问题不太一样的是,雪崩是指大规模的缓存都过期失效了。

解决方案:

  1. 既然是同一时间过期导致的,我们就对不同key设置不同的过期时间,避免同时过期
  2. 二级缓存,雪崩也是大量key失效导致请求都去请求数据库了,二级缓存预先加载,我们加二级缓存当Redis中没有就走本地缓存查询

Redis的过期策略有哪些?

redis主要有2种过期删除策略

惰性删除

惰性删除指的是当我们查询key的时候才对key进行检测,如果已经达到过期时间,则删除。这个跟我们前面讲的Redis渐进式Hash是否相似查到了就移动到新的字典中,但是Redis的渐进式hash会有定时任务定时移动旧字典的key到新字典中,而惰性删除没有定时删除,所以惰性删除当没有访问到的key就会一直占用内存,浪费内存
在这里插入图片描述

定期删除

我们上面才说了Redis不会定期删除,啪啪啪的打脸了,
Redis的定期删除指的是redis每隔一段时间对数据库做一次检查,删除里面的过期key。由于不可能对所有key去做轮询来删除,所以redis会每次随机取一些key去做检查和删除。

Redis的拒绝策略

当上面俩种策略都没有删除redis的key的时候,这些key也没有走查询的话,就会导致这些key一直保存在redis里面无法被删除,这时候就会走到redis的内存淘汰机制。【特意列在一起了,好观察】

  1. volatile-lru:从已设置过期时间的key中,移出最近最少使用的key进行淘汰
  2. volatile-ttl:从已设置过期时间的key中,移出将要过期的key
  3. volatile-random:从已设置过期时间的key中随机选择key淘汰
  4. allkeys-lru:从key中选择最近最少使用的进行淘汰
  5. allkeys-random:从key中随机选择key进行淘汰
  6. noeviction:当内存达到阈值的时候,新写入操作报错

Redis4.0新增加了俩种拒绝策略(LFU算法淘汰)

  • volatile-lfu:从所有配置了过期时间的键中驱逐使用频率最少的键
  • allkeys-lfu:从所有键中驱逐使用频率最少的键

持久化方式有哪些?有什么区别?

redis持久化方案分为RDB和AOF两种。

RDB

RDB持久化可以手动执行也可以根据配置定期执行,它的作用是将某个时间点上的数据库状态保存到RDB文件中,RDB文件是一个压缩的二进制文件,通过它可以还原某个时刻数据库的状态。由于RDB文件是保存在硬盘上的,所以即使redis崩溃或者退出,只要RDB文件存在,就可以用它来恢复还原数据库的状态。
可以通过SAVE或者BGSAVE来生成RDB文件。
SAVE命令会阻塞redis进程,直到RDB文件生成完毕,在进程阻塞期间,redis不能处理任何命令请求,阻塞的持久化方式策略就不适合,
BGSAVE则是会fork出一个子进程,然后由子进程去负责生成RDB文件,父进程还可以继续处理命令请求,不会阻塞进程。

#Redis的RDB配置的持久化策略
save 900 1 #在900秒(15分钟)之后,如果至少有1个key发生变化,则dump内存快照。

save 300 10  #在300秒(5分钟)之后,如果至少有10个key发生变化,则dump内存快照。
 
save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,则dump内存快照。

AOF

AOF是通过保存redis服务器所执行的写命令来记录数据库状态的。
AOF通过追加、写入、同步三个步骤来实现持久化机制。

  1. 选择了Aof的持久化策略,服务器执行完写命令之后,
  2. 在服务器每结束一个事件循环之前,将会调用flushAppendOnlyFile函数决定是否要将aof_buf的内容保存到AOF文件中,可以通过配置appendfsync来决定。
#Aof默认关闭的 
appendonly yes #开启Aof
appendfsync always     #每次有数据修改发生时都会写入AOF文件。
appendfsync everysec  #每秒钟同步一次,该策略为AOF的缺省策略。
appendfsync no          #从不同步。高效但是数据不会被持久化。

如果不设置,默认选项将会是everysec,因为always来说虽然最安全(只会丢失一次事件循环的写命令),但是性能较差,而everysec模式只不过会可能丢失1秒钟的数据,而no模式的效率和everysec相仿,但是会丢失上次同步AOF文件之后的所有写命令数据。

Redis的高可用方案你能说说?

Redis的高可用方案有俩种 Redis的哨兵方案跟集群方案,

主从架构

主从模式是最简单的实现高可用的方案,核心就是主从同步。主从同步的原理如下:

  1. 从服务器向主服务器发送SYNC命令。
  2. 收到SYNC命令的主服务器执行BGSAVE命令,在后台生成一个RDB文件,并使用一个缓冲区记录从现在开始执行的所有写命令。
  3. 当主服务器的BGSAVE命令执行完毕,主服务器将生成的RDB文件发送给从服务器,从服务器接收并加载这个RDB文件,将自己的数据库状态更新至主服务器执行BGSAVE命令时的数据库状态。
  4. 主服务器将记录在缓冲区里面的所有写命令发送给从服务器,从服务器执行这些写命令,将自己的数据库状态更新至主服务器当前所处的状态。

看过之前的文章都看到我上面写的是Redis2.8之前的主从同步操作,Redis2.8之后新引入了psync 命令
psync命令

PSYNC命令具有完全同步和部分重同步功能。
完全同步跟sync是一样的,
psync命令在断线后的重复制情况:当从服务器断开重新连接后,主服务器将断开期间的写命令发给从服务器,从服务器执行断开期间的写命令完成同步,将数据库更新到和主服务器一致。【这个阶段增量同步的队列是可用配置大小的,不知道请回看之前的文章】

Redis的哨兵

主从方案的缺点还是很明显的,假设master宕机,那么就不能写入数据,那么slave也就失去了作用,整个架构就不可用了,除非你手动切换,主要原因就是因为没有自动故障转移机制。而哨兵(sentinel)的功能比单纯的主从架构全面的多了,它具备自动故障转移、集群监控、消息通知等功能。

哨兵流程请参考原文章

redis集群的原理吗?

Redis集群还没有说过,这里我们就从为什么需要Redis集群讨论:
我们的Redis哨兵可实现高可用,而Redis称能到达10万的QPS,当请求多的时候这个QPS也抗不住了,那怎么保持又能高可用又能支持高并发呢?Redis集群就出来了
Redis集群是Redis提供的分布式数据存储方案,集群通过数据分片sharding来进行数据的共享,同时提供复制和故障转移的功能。

图中是不同请求落到Master的不同的主节点上,主节点之间的箭头表示节点间的心跳检测,
在这里插入图片描述

图中的请求都落到不同的Redis上面了,数据是怎么得到的?
Redis有一个slot插槽的概念【Redis Cluster 将所有数据划分为 16384 个 slots(槽位),每个节点负责其中一部分槽位。槽位的信息存储于每个节点中】,
讲到这里还是没有说清楚是怎么获取到其他节点的数据?只说了他把数据存储在哪?获取呢?

当客户端向一个节点发出获取指令,当该节点发现指令的 key 所在的槽位并不归自己管理,这时它会向客户端发送一个特殊的跳转指令携带目标操作的节点地址,告诉客户端去连这个节点去获取数据。客户端收到指令后除了跳转到正确的节点上去操作,还会同步更新纠正本地的槽位映射表缓存,后续所有 key 将使用新的槽位映射表

当对集群增加或者移出节点,对于slot的重新分配也是非常方便的,redis提供了工具帮助实现slot的迁移,整个过程是完全在线的,不需要停止服务。

Redis集群的故障转移

如果节点A向节点B发送ping消息,节点B没有在规定的时间内响应pong,那么节点A会标记节点B为pfail疑似下线状态,同时把B的状态通过消息的形式发送给其他节点,如果超过半数以上的节点都标记B为pfail状态,B就会被标记为fail下线状态,此时将会发生故障转移,优先从复制数据较多的从节点选择一个成为主节点,并且接管下线节点的slot,整个过程和哨兵非常类似,都是基于Raft协议做选举。

知道Redis事务机制吗?

参考以前的文章

进阶面试题:

  1. Redis的字符串小于多少的时候会采用压缩存储呢?
  2. Redis的哨兵集群是怎么通信的【Redis哨兵的时候讲了】
  3. Redis集群为什么至少需要三个master节点,并且推荐节点数为奇数呢【跟哨兵原理差不多】?
  4. 跳跃表数据结构也常考

【还有进阶的面试题欢迎补充,这几个面试题下次说】

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值