文章目录

  • Redis面试题
    • 1.介绍Redis中数据类型
    • 2.redis中的持久化方案
    • 3.redis的优点
    • 4.主从模式
    • 5.主从模式下宕机怎么办
      • slave宕机
      • master宕机
    • 6.缓存问题
      • 缓存穿透
      • 缓存雪崩
      • 缓存击穿
      • 缓存预热
      • 缓存更新
      • 缓存降级
      • 缓存热点key
    • 7.Redis实现消息队列
    • 8.如果有大量的key需要设置同一时间过期,一般需要注意什么?
    • 9.Redis的同步机制了解么?
    • 10.是否使用过Redis集群,集群的原理是什么?

Redis面试题

1.介绍Redis中数据类型

  redis中的五种常用类型分别是string,Hash,List,Set,ZSet。

类型 特点 使用场景
string 简单的key-value类型,value其实不仅是String,也可以是数字 定时持久化,操作日志,常规计数, 微博数, 粉丝数等功能
hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象 存储部分变更数据,如用户信息等
list 有序可重复的列表 twitter的关注列表,粉丝列表,最新消息排行,消息队列
set 无序不可重复的列表 在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis还为集合提供了求交集、并集、差集等操作,可以非常方便的实现如共同关注、共同喜好、二度好友等功能
Sorted set 带有score的Set 排行榜

2.redis中的持久化方案

RDB:快照形式,定期把内存中当前时刻的数据保存到磁盘。Redis默认支持的持久化方案。速度快但是服务器断电的时候会丢失部分数据

AOF:append only file。把所有对redis数据库操作的命令,增删改操作的命令。保存到文件中。数据库恢复时把所有的命令执行一遍即可。两种持久化方案同时开启使用AOF文件来恢复数据库.能保证数据的完整性,但是速度慢。

两者如何选择?

  1. 如果redis仅仅是用来做为缓存服务器的话,我们可以不使用任何的持久化。
  2. 一般情况下我们会将两种持久化的方式都开启。redis优先加载AOF文件来回复数据。RDB的好处是快速。
  3. 在主从节点中,RDB作为我们的备份数据,只在salve(从节点)上启动,同步时间可以设置的长一点,只留(save 900 1)这条规则就可以了。
  4. 开启AOF的情况下,主从同步是时候必然会带来IO的性能影响,此时我们可以调大auto-aof-rewrite-min-size的值,比如5GB。来减少IO的频率
  5. 不开启AOF的情况下,可以节省IO的性能影响,这是主从间通过RDB持久化同步,但如果主从都挂掉,影响较大~

3.redis的优点

  1. 读写速度快. 数据存放在内存中,数据结构类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
  2. 支持丰富的数据类型,string,hash,list,set,sorted
  3. 支持事务,watch
  4. 丰富的特性:可以用于缓存,消息队列,按key设置过期时间,到期后自动删除
  5. 支持数据持久化(将内存数据持久化到磁盘),支持AOF和RDB两种持久化方式,从而进行数据恢复操作,可以有效地防止数据丢失
  6. 支持主从(master-slave)复制来实现数据备份,主机会自动将数据同步到从机

4.主从模式

  主从结构一是可以进行冗余备份,二是可以实现读写分离.
主从复制:
  当我们在redis中开启了持久化(RDB或者AOF)但是这样也不能保证数据一定不会丢失,当Redis服务因为硬盘损坏那么数据也就丢失了,这时我们通过主从模式,一个master设置多个slave,那么在slave上面就会有多个备份数据,提高了我们数据的抗灾能力。

  1. 一个Master可以有多个Slave,不仅主服务器可以有从服务器,从服务器也可以有自己的从服务器
  2. 复制在Master端是非阻塞模式的,这意味着即便是多个Slave执行首次同步时,Master依然可以提供查询服务;
  3. 复制在Slave端也是非阻塞模式的:如果你在redis.conf做了设置,Slave在执行首次同步的时候仍可以使用旧数据集提供查询;你也可以配置为当Master与Slave失去联系时,让Slave返回客户端一个错误提示;
  4. 当Slave要删掉旧的数据集,并重新加载新版数据时,Slave会阻塞连接请求

读写分离
  主从架构中,可以考虑关闭主服务器的数据持久化功能,只让从服务器进行持久化,这样可以提高主服务器的处理性能。从服务器通常被设置为只读模式,这样可以避免从服务器的数据被误修改

5.主从模式下宕机怎么办

slave宕机

  相对简单,slave启动后会自动同步数据,增量同步。

master宕机

手动恢复

  1. 在从数据库中执行SLAVEOFNO ONE命令,断开主从关系并且将从库提升为主库继续服务;
  2. 将主库重新启动后,执行SLAVEOF命令,将其设置为其他库的从库,这时数据就能更新回来

哨兵功能自动恢复
  通过sentinel模式启动redis后,自动监控master/slave的运行状态, 已经被集成在redis2.4+的版本中如果Master异常,则会进行Master-Slave切换,将其中一个Slave作为Master,将之前的Master作为Slave
基本原理是:心跳机制+投票裁决

6.缓存问题

缓存穿透

  缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。

解决办法

  1. 对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃。还有最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

  2. 也可以采用一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。

缓存雪崩

  如果缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,造成了缓存雪崩。
解决办法

  1. 在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
  2. 可以通过缓存reload机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存
  3. 不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀. 比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件
  4. 做二级缓存,或者双缓存策略。A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期。

缓存击穿

  缓存被“击穿”的问题,这个和缓存雪崩的区别在于这里针对某一key缓存,前者则是很多key。

缓存预热

  缓存预热就是系统上线后,提前将相关的缓存数据直接加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!

缓存预热解决方案

  1. 直接写个缓存刷新页面,上线时手工操作下;
  2. 数据量不大,可以在项目启动的时候自动进行加载;
  3. 定时刷新缓存;

缓存更新

  我们知道通过expire来设置key 的过期时间,那么对过期的数据怎么处理呢?除了缓存服务器自带的缓存失效策略之外(Redis默认的有6中策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:

  1. 定时去清理过期的缓存;
  2. 当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。

  两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!具体用哪种方案,大家可以根据自己的应用场景来权衡。

缓存降级

  当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
  降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。
  在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:

  1. 一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
  2. 警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
  3. 错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
  4. 严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。

缓存热点key

  使用缓存 + 过期时间的策略既可以加速数据读写,又保证数据的定期更新,这种模式基本能够满足绝大部分需求。但是有两个问题如果同时出现,可能就会对应用造成致命的危害:

  1. 当前 key 是一个热点 key( 可能对应应用的热卖商品、热点新闻、热点评论等),并发量非常大。

  2. 重建缓存不能在短时间完成,可能是一个复杂计算,例如复杂的 SQL、多次 IO、多个依赖等。

在缓存失效的瞬间,有大量线程来重建缓存,造成后端负载加大,甚至可能会让应用崩溃。
热点 key 失效后大量线程重建缓存
要解决这个问题也不是很复杂,但是不能为了解决这个问题给系统带来更多的麻烦,所以需要制定如下目标:
减少重建缓存的次数
数据尽可能一致
较少的潜在危险

1)互斥锁 (mutex key)

此方法只允许一个线程重建缓存,其他线程等待重建缓存的线程执行完,重新从缓存获取数据即可,

下面代码使用 Redis 的 setnx 命令实现上述功能,伪代码:

String get(String key) {//从redis中获取key String value = redis.get(key); //如果value为空则开始重构缓存 if (value == null) { //只允许一个线程重构缓存,使用nx,并设置过期时间ex String mutexKey = "mutex:key" + key; if (redis.set(mutexKey, "1", "ex 180", "nx")) { //从数据源获取数据 value = db.get(key); //回写redis并设置过期时间 redis.set(key, value, timeout); //删除mutexKey redis.del(mutexKey); } else {//其他线程睡眠50秒再重试 Thread.sleep(50); get(key); } } return value;
}

从 Redis 获取数据,如果值不为空,则直接返回值。
如果 set(nx 和 ex) 结果为 true,说明此时没有其他线程重建缓存,那么当前线程执行缓存构建逻辑。
如果 setnx(nx 和 ex) 结果为 false,说明此时已经有其他线程正在执行构建缓存的工作,那么当前线程将休息指定时间 (例如这里是 50 毫秒,取决于构建缓存的速度 ) 后,重新执行函数,直到获取到数据。

2)永远不过期

永远不过期”包含两层意思:

从缓存层面来看,确实没有设置过期时间,所以不会出现热点 key 过期后产生的问题,也就是“物理”不过期。

从功能层面来看,为每个 value 设置一个逻辑过期时间,当发现超过逻辑过期时间后,会使用单独的线程去构建缓存。

从实战看,此方法有效杜绝了热点 key 产生的问题,但唯一不足的就是重构缓存期间,会出现数据不一致的情况,这取决于应用方是否容忍这种不一致。下面代码使用 Redis 进行模拟:

String get(final String key) { V v = redis.get(key); String value = v.getValue(); //逻辑过期时间 final Long logicTimeout = v.getLogicTimeout(); //如果逻辑时间小于当前时间,开始重建缓存 if (logicTimeout <= System.currentTimeMillis()) { final String mutexKey = "mutex:key" + key; if (redis.set(mutexKey, "1", "ex 180", "nx")) { //重建缓存 threadPool.execute(new Runnable() { @Override public void run() { String dbValue = db.get(key); redis.set(key, (dbValue, newLogicTimeout));redis.del(mutexKey); } }); } }return value;
}

作为一个并发量较大的应用,在使用缓存时有三个目标:
第一,加快用户访问速度,提高用户体验。
第二,降低后端负载,减少潜在的风险,保证系统平稳。
第三,保证数据“尽可能”及时更新。
下面将按照这三个维度对上述两种解决方案进行分析。

互斥锁 (mutex key):这种方案思路比较简单,但是存在一定的隐患,如果构建缓存过程出现问题或者时间较长,可能会存在死锁和线程池阻塞的风险,但是这种方法能够较好的降低后端存储负载并在一致性上做的比较好。

” 永远不过期 “:这种方案由于没有设置真正的过期时间,实际上已经不存在热点 key 产生的一系列危害,但是会存在数据不一致的情况,同时代码复杂度会增大。

7.Redis实现消息队列

普通队列:一般使用list结构作为队列,rpush生产消息,lpop消费消息,blpop阻塞消费。
消费多次:生产一次消费多次的情况使用发布/订阅模式
延时队列:使用sortedset,拿时间戳作为score,消息内容作为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理

8.如果有大量的key需要设置同一时间过期,一般需要注意什么?

如果大量的key过期时间设置的过于集中,到过期的那个时间点,redis可能会出现短暂的卡顿现象。一般需要在时间上加一个随机值,使得过期时间分散一些。

9.Redis的同步机制了解么?

主从同步。第一次同步时,主节点做一次bgsave,并同时将后续修改操作记录到内存buffer,待完成后将rdb文件全量同步到复制节点,复制节点接受完成后将rdb镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。

10.是否使用过Redis集群,集群的原理是什么?

Redis Sentinal着眼于高可用,在master宕机时会自动将slave提升为master,继续提供服务。
Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。

Redis面试题汇总相关推荐

  1. Redis 面试题汇总

    1.Redis 使用场景有哪些? 答:Redis 使用场景如下: 记录帖子点赞数.点击数.评论数 缓存近期热帖 缓存文章详情信息 记录用户会话信息 2.Redis 有哪些功能? 答:Redis 功能如 ...

  2. redis专题:redis面试题汇总

    文章目录 1. redis是单线程的吗?为什么这么快? 2. Redis 单线程如何处理那么多的并发客户端连接? 3. redis管道的作用 4. redis中Lua脚本的作用 5 单台redis可以 ...

  3. Redis高频面试题汇总(2021最新版)

        本文已收录于专栏 ⭐️<Redis面试题汇总--2021最新版本>⭐️ 上千人点赞收藏,全套Redis高频面试题,大厂必备技能! 面试官心理分析 从面试官的角度分析,出这道题的目的 ...

  4. Java面试题汇总及答案2021最新(序列化含答案)

    Java面试题汇总及答案2021最新(序列化含答案) 为大家整理了2021最新的Java面试题及答案下载,这套Java面试题总汇已经汇总了Java基础面试到高级Java面试题,几乎涵盖了作为一个Jav ...

  5. access汇总含义_2020最新大厂内部 PHP 高级工程师面试题汇总(二)

    51.数据库中的存放了用户 ID, 扣费很多行,redis 中存放的是用户的钱包,现在要写一个脚本,将数据库中的扣费记录同步到 redis 中,每 5 分钟执行一次.请问要考虑哪些问题? 思路:生产者 ...

  6. 19年BAT常问面试题汇总:JVM+微服务+多线程+锁+高并发性能

    1.Redis 面试题 1.什么是 Redis?. 2.Redis 的数据类型? 3.使用 Redis 有哪些好处? 4.Redis 相比 Memcached 有哪些优势? 5.Memcache 与 ...

  7. 百度校招历年经典面试题汇总:Java开发岗

    这个系列计划收集几百份朋友和读者的面经,作者合集方便查看,各位有面经屯着可以联系我哦 这个系列离结束差的还特别多,会更新涵盖所有一线大厂的所有岗位,也可以关注一下. 百度校园招聘历年经典面试题汇总:C ...

  8. Dubbo 面试题汇总(附答案)

    作者 | Dean Wang 来源 | deanwang1943.github.io dubbo是什么 dubbo是一个分布式框架,远程服务调用的分布式框架,其核心部分包含: 集群容错:提供基于接口方 ...

  9. 年终盘点 | 2019年Java面试题汇总篇(附答案)

    作者 | 老王 来源 | Java中文社群「微信公众号」 在这岁月更替辞旧迎新的时刻,老王盘点了一下自己 2019 年发布的所有文章,意外的发现关于「Java面试」的主题文章,竟然发布了 52 篇,几 ...

最新文章

  1. 生产者、消费者模式实现
  2. LeetCode刷题感想
  3. 在应用程序中宿主MEF
  4. jquery调用asp.net 页面后台方法
  5. 【线性查询之间存在相关性误差】差分隐私系统学习记录(七)
  6. PHPSession-完全PHP5之session篇
  7. php session实现原理
  8. js动态加载css文件和js文件的方法
  9. Oracle→简介、用户、数据字典、表空间及其文件
  10. 谷歌了java集成开发_Spring整合Kaptcha谷歌验证码工具的开发步骤
  11. 支付宝 支付宝扫描生成的二维码提示:码已失效,请刷新二维码后重试
  12. git拉取代码报错:Permission denied (publickey) 解决方案
  13. 【水果识别】基于matlab GUI橙子数量识别【含Matlab源码 1821期】
  14. 【优化算法】蛾群优化算法(MSA)【含Matlab源码 1451期】
  15. STC学习:八位数码管动态扫描
  16. 简单易用的国产GIS工具软件
  17. 自适应滤波器之块自适应滤波器
  18. java初学者看什么书比较好
  19. 计算机英语口语900句,日常英语口语900句
  20. 使用Arduino开发ESP32(18):使用Preferences保存数据

热门文章

  1. 男孩取名:英姿勃勃的敬姓男孩名,好听又暖心
  2. cFosSpeed 安装及使用教程
  3. Python中的全局变量
  4. MySQL 日期/时间 操作函数(2.1)
  5. JavaScript实现元素定点删除
  6. w3cschool php 正则表达式,正则表达式说明
  7. 【原创】再来二频,发现改变的东西太多了。
  8. 【翻译】关于程序员的59条搞笑但却真实无比的编程语录
  9. echarts + vue 实现项目进度不同颜色柱形甘特图
  10. 计算机小高考成绩,2018“小高考”成绩新鲜出炉!据分析难度又大了