前言
本文主要总结一下
之前学习的笔记知识点
Redis框架从入门到学精(全)
以及面试八股文中常见的题目
也可参考一下他人的面试总结题
Redis 常见面试题(2020最新版)
1. 了解一下你认识的Redis
高性能的 key-value
- Redis 支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的候可以再次加载进行使用
- key-value数据多种,不仅是string
- Redis 有着更为复杂的数据结构并且提供对他们的原子性操作
- 丰富的特性:可用于缓存,消息,按 key 设置过期时间,过期后将会自 动删除
总体来说,它的功能还是多种多样的,比如缓存、数据持久化、支持事务、支持消息队列等
2. Redis的适用场景
- 缓存:减少压力,增加性能
- 排行榜:通过Sortset数据格式实现
- 计数器:原子性的自增操作(点赞访问量等)
- 集合关系:交并补集合的关系,共同兴趣点等
- 消息队列:自身的发布订阅模式
- session共享:通过保存服务器文件,集群服务中,哪台服务器登录都可以获取信息
3. Redis的线程机制
单线程模式(在redis6.0之后引入了多线程io,只是用来处理网络数据的读写和协议的解析,但执行命令还是单线程)
redis 利用队列技术将并发访问变为串行访问
,消除了传统数据库串行控制的开销
4. Redis单线程,如何提高多核cpu利用率
部署多个Redis,当作不同服务器使用。也可考虑分片
5. Redis持久化机制
两种持久化机制 RDB 和 AOF 机制
RDB:在`指定的时间间隔内将内存中的数据集快照写入磁盘
写入一个临时文件,持久化结束后,用这个临时文件替换上持久化的文件,达到数据恢复`
优点:
- 节省磁盘空间
- 可以安全保存到磁盘
- 性能最大化,用单独子进程进程持久化
缺点:
数据安全性低。RDB 是间隔一段时间
进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失
AOF:
以日志的形式
来记录每个写操作(增量保存),将Redis执行过的所有写指令记录下来(读操作不记录), 只许追加文件但不可以改写文件
优点:
- 数据安全,每一次命令操作就可以追加到后面,即使宕机,也可以通过redis-check-aof来修复
缺点:
文件过大,恢复速度慢
数据集大的时候,比RDB要慢
6. Redis过期键的删除策略
(1) 定时删除:在设置键的过期时间的同时,创建一个定时器 timer). 让定时器在键的过期时间来临时,立即执行对键的删除操作。
(2) 惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,就返回该键。
(3) 定期删除:每隔一段时间程序就对数据库进行一次检查,删除里面的过期键。至于要删除多少过期键,以及要检查多少个数据库,则由算法决定。
7. Redis的同步机制
从机主动发送
- Slave启动成功连接到master后,从机slave会发送一个sync命令
- Master接到命令启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令, 在后台进程执行完毕之后,master将传送整个数据文件到slave,以完成一次完全同步
全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。(刚开始从机连接主机,主机一次给)
增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步 (主机修改了数据会给予从机修改的数据同步,叫做增量复制)
断开之后重新连接,只要是重新连接master,一次完全同步(全量复制)将被自动执行,rdb的数据就会给从机。
主机负责写,从机负责读
8. 了解过集群吗
Redis 集群实现了对Redis的水平扩容,即启动N个redis节点,将整个数据库分布存储在这N个节点中,每个节点存储总数据的1/N。
Redis 集群通过分区(partition)来提供一定程度的可用性(availability): 即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求
具体的原理:
- 哨兵机制
Sentinal
着眼于高可用,在 master 宕机时会自动将 slave 提升 为 master,继续提供服务 - 集群分片,
Cluster
着眼于扩展性,在单个 redis 内存不足时,使用Cluster 进行分片存储
9. 什么情况会导致整个集群无法使用
- cluster-require-full-coverage
为yes ,那么 ,整个集群都挂掉
为no ,那么,该插槽数据全都不能使用,也无法存储
10. 设置Redis的密码
通过启动界面后
在内部输入该命令set requirepass 123456
,启动之后通过auth 123456
也可以在配置文件直接修改,变成永久密码
11. Redis中slots的卡槽有了解吗
一个 Redis 集群包含 16384 个插槽(hash slot)也就是使用的是哈希槽
集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽
12. 测试Redis的连通性
通过ping命令
13. 理解Redis的事务
具体的命令有这么几个MULTI、EXEC、DISCARD、WATCH
事务开始 MULTI
命令入队
事务执行 EXEC
具体Redis的事务特性有:
- 单独的隔离操作(不会被打断)
- 没有隔离级别
- 不保证原子性
事务中的所有命令都会序列化、按顺序地执行
14. Redis事务保证原子性吗
Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。
15. 降低Redis的内存使用情况
- 通过使用比较小key-value,可以更加好的紧凑在一起
也就是设计键值的时候,越短越好
- 好好利用 Hash,list,sorte,set,set 等集合类型数据。内部会针对不同类型存在编码的概念
- 控制key的数量
16. 大量的 key 设置同一时间过期,需要注意什么
redis可能会出现短暂的卡顿现象。一般需要在时间上加一个随机值,使得过期时分散一些
17. 了解过 Redis 分布式锁吗
Redis 的并发竞争Key
先拿 setnx 来争抢锁,抢到之后,再用 expire 给锁加一个过期时间防止锁忘 记了释放
但是如果setnx之后系统维护了,就不能给锁加时间
正确的应该是同时把 setnx 和 expire 合成一条指令来用的
补充基于zookeeper:
基于zookeeper临时有序节点可以实现的分布式锁。大致思想为:每个客户端对某个方法加锁时,在
zookeeper上的 与该方法对应的指定节点的目录下,生成一个唯一的瞬时有序节点。 判断是否获取锁的
方式很简单,只需要判断有 序节点中序号最小的一个。 当释放锁的时候,只需将这个瞬时节点删除即
可。同时,其可以避免服务宕机导致的锁 无法释放,而产生的死锁问题。完成业务流程后,删除对应的
子节点释放锁
18. 为什么要用Redis这种新的数据库
主要是因为 Redis 具备高性能和高并发两种特性。
- 高性能:假如用户第一次访问数据库中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该用户访问的数据存在缓存中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可!
- 高并发:直接操作缓存能够承受的请求是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。
19. 单线程的Redis为什么这么快
主要是有三个原因:
1、Redis的全部操作都是纯内存的操作;
2、Redis采用单线程,有效避免了频繁上下文切换
;
3,采用了非阻塞I/O多路复用机制
20. MySQL有1000万数据,Redis作为中间缓存,取其中的10万,如何保证Redis中的数据都是热点数据?
可以使用Redis的数据淘汰策略,Redis 内存数据集大小上升到一定大小的时候,就会施行这种策略。具
体说来,主要有 6种内存淘汰策略:
- voltile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
- volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
- volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
- allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
- allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
- no-enviction(驱逐):禁止驱逐数据
21. AOF重写了解吗?
AOF重写可以产生一个新的AOF文件,这个新的AOF文件和原有的AOF文件所保存的数据库状态一样,
但体积更小。
在执行 BGREWRITEAOF 命令时,Redis 服务器会维护一个 AOF 重写缓冲区,该缓冲区会在子进程创
建新AOF文件期间,记录服务器执行的所有写命令。当子进程完成创建新AOF文件的工作之后,服务器
会将重写缓冲区中的所有内容 追加到新AOF文件的末尾,使得新旧两个AOF文件所保存的数据库状态
一致。最后,服务器用新的AOF文件替换旧的 AOF文件,以此来完成AOF文件重写操作。
补充:(实时更新面经的常问题目:)
22. redis的数据结构以及应用场景
数据类型 | 可以存储的值 | 操作 | 应用场景 |
---|---|---|---|
string | 字符串、整数或者浮点 | 对整个字符串或者字符串的其中一部分执行操作,对整数和浮点数执行自增或者自减操作 | 做简单的键值对缓存,计数器,共享session以及限速 |
list | 列表 | 从两端压入或者弹出元素,对单个或者多个元素进行修剪,只保留一个范围内的元素 | 存储一些列表型的数据结构,类似粉丝列表、文章的评论列表之类的 |
set | 无序集合 | 添加、获取、移除单个元素,检查一个元素是否存在于集合中。计算交集、并集、差集。从集合里面随机获取元素 | 交集、并集、差集的操作,比如交集,可以把两个人的粉丝列表整一,或者是用户的喜好标签等 |
hash | 包含键值对的无序散列 | 添加、获取、移除单个键值对。获取所有键值对。检查某个键是否存在 | 结构化的数据,比如一个对象 |
zset | 有序集合 | 添加、获取、删除元素。根据分值范围或者成员来获取元素。计算一个键的排名。 | 去重但可以排序,如获取排名前几名的用户,排行榜等 |
23. 缓存穿透
key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到
,请求都会压到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库
通俗的来说:访问的数据缓存找不到,一直转而发送到数据库
解决方案:
一个一定不存在缓存及查询不到的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义
(1)对空值缓存:如果一个查询返回的数据为空(不管是数据是否不存在),我们仍然把这个空结果(null)进行缓存,设置空结果的过期时间会很短,最长不超过五分钟
(2)设置可访问的名单(白名单):
使用bitmaps类型定义一个可以访问的名单,名单id作为bitmaps的偏移量,每次访问和bitmap里面的id进行比较,如果访问id不在bitmaps里面,进行拦截,不允许访问。
(3)采用布隆过滤器:(布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量(位图)和一系列随机映射函数(哈希函数)。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。)
将所有可能存在的数据哈希到一个足够大的bitmaps中,一个一定不存在的数据会被 这个bitmaps拦截掉,从而避免了对底层存储系统的查询压力。
(4)进行实时监控:当发现Redis的命中率开始急速降低,需要排查访问对象和访问的数据,和运维人员配合,可以设置黑名单限制服务
总结如下:
设置空值缓存,而且设置超时时间
通过bitmap的位运算进行存储,数据量比较小
- 实时监控,将其禁止访问
24. 缓存击穿
key对应的数据存在,但在redis中过期,此时若有大量并发请求过来
,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮
也就是一个key过期,一直访问数据库
解决方案:
key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题。
(1)预先设置热门数据:在redis高峰访问之前,把一些热门数据提前存入到redis里面,加大这些热门数据key的时长
(2)实时调整:现场监控哪些数据热门,实时调整key的过期时长
(3)使用锁:先判断值是否为空再让他进来与否
总结如下:
- 设置热门的key,加大时长过期
- 实时监控调整
25. 缓存雪崩
key对应的数据存在,但在redis中过期
,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
缓存雪崩与缓存击穿的区别在于这里针对很多key缓存,前者则是某一个key正常访问
解决方案:
(1)构建多级缓存架构:nginx缓存 + redis缓存 +其他缓存(ehcache等)
(2)使用锁或队列:
用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。不适用高并发情况
(3)设置过期标志更新缓存:
记录缓存数据是否过期(设置提前量),如果过期会触发通知另外的线程在后台去更新实际key的缓存。
(4)将缓存失效时间分散开:
比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
总结如下:
- 设置多个级别的缓存架构,时间来得及缓冲
- 使用锁的机制
- 设置一个过期时间标志来通知
- 将过期时间分散,比如5分钟、5.01分钟等
26. 缓存预热
系统上线前提前将相关数据加载到缓存系统中
如果不进行预热,刚开始缓存数据为空,对于高并发数据,对数据库有压力
- 数据量小,启动系统先加载缓存
- 数据量一般,设置定时脚本缓存的刷新
- 数据量大,将热数据提前加载到缓存
27. 缓存降级
缓存失效或者服务器宕机,不访问数据库,直接访问服务内存数据
通过参考日志提前设置预案:
- 一般:服务因为抖动或者上线超时,自动降级
- 警告:服务在一段时间内波动,可自动降级或者人工降低发送警告
- 错误:可用率低90%,访问量的猛然增加,可根据情况自动降级
- 严重错误:某些特殊原因,需紧急人工降级