
欢迎来到预见猿份,本站项目均为站长原创,学习中有问题可直接提交给站长老苗解决(微信:mrt_0607)。
苗润土老师,20余年一线项目经验,2014年加入黑马,星辰wms、云岚到家、学成在线项目作者,历任高级讲师、教学主管及课程研究员。 b站老苗
Redis 常见面试题
Redis现在做为最流行的缓存数据库,使用的场景也是越来越多,重要性也是不言而喻
首先我们要先对Redis有个总体认识
Redis命令速查:https://yjoffer.com/tools/redis-cheatsheet.html
Redis系统学习: 基础知识:https://yjoffer.com/project_preposition/redis/redis.html
高级知识:https://yjoffer.com/springcloud/chapter9/chapter9.html
为什么用
Redis本身在执行的时候是单线程,所以没有并发安全问题
Redis数据存储在内存中,速度非常快
不可避免地缺陷
由于数据在内存中,所以会有数据丢失的风险
不是关系型数据库,所以只能存一些弱关系数据
1.原理相关(难度★★★★)
Redis的数据结构以及对应的使用场景
| 结构 | 底层 | 场景 |
|---|---|---|
| String | SDS简单动态字符串 | 缓存、计数器(incr by)、分布式锁(setnx)、bitmap(布隆过滤器、签到) |
| List | 双向链表+压缩列表 | 消息队列、栈 |
| Hash | 哈希表 | 对象存储,列表对象 |
| Set | 数组+哈希表 | 去重、社交关系(粉丝、关注、收藏) |
| Zset | 跳表+压缩列表 | 排行榜 |
缓存三剑客
比较简单,所以这里简单描述一下,具体原理同学们自行查阅
缓存雪崩
现象:是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。 解决:给不同的Key的TTL添加随机值。对Redis做哨兵集群
缓存击穿
现象:给某一个key设置了过期时间,当key过期的时候,恰好这时间点对这个key有大量的并发请求过来,这些并发的请求可能会瞬间把DB压垮 解决:互斥锁(强一致、性能差)或者逻辑过期(高可用、性能好)
缓存穿透
现象:查询一个不存在的数据,mysql查询不到数据也不会直接写入缓存,就会导致每次请求都查数据库 解决:缓存空数据,查询返回的数据为空,仍把这个空结果进行缓存或者布隆过滤器
Redis和Mysql中的数据怎么保证一致性
首先这个问题没有完美的解决方案,只能说结合你们具体的业务选择一个最优的方案
如果要强一致性,那就必然牺牲性能,但是问题是我们用缓存就是为了它的高性能,如果要求强一致,那最好别用缓存。
所以理论上来说你用缓存就必然能接受一部分的不一致,我们只要做到最终一致即可
首先介绍双写一致性 当修改了数据库的数据也要同时更新缓存的数据,缓存和数据库的数据要保持一致 想做到双写一致有大概四种做法
- 先写Redis,再写DB
- 先写DB,再写Redis
- 先删Redis,再写DB
- 先写DB,再删Redis
这四种做法正常情况下都是ok的,但是如果是高并发的场景,每个都会有问题,具体问题我这里只举一个例子,剩下的同学们自行推演即可 例如做法1,如果写完redis之后,写数据库失败,就会造成两边数据不一致,则会出现问题 这里面相对来说出现问题概率最低的就是先写DB,再删Redis。 首先删缓存肯定比写缓存来的要更简单一些 其次如果写数据库成功后,再删redis失败的概率较低,并且A线程删了缓存,B线程查缓存之间再有并发导致不一致的问题概率比先删Redis再操作DB来的要低得多 这里我们介绍其他几种方案 延迟双删 思路:先删缓存,然后更新数据库,然后暂停一段时间,到了之后再次删除一次缓存。这样在删缓存和更新数据库的这段时间内如果有新的查询请求,也会因为再次删除而更新到数据库 但是,如果在延迟期间,又有新的写入操作,就会导致数据还是不一致,但是我们日常的业务大多是高并发读,偶尔写,所以这种能覆盖较大的场景,并且性能较好。 延迟双删只是能带来一部分一致性的提升,并不能保证强一致。所以相对来说比较鸡肋 如果要追求强一致性,那只能加锁,等数据库处理完毕在释放锁,保证强一致,但是性能会受到较大影响 其他还可以使用消息队列的方式和使用canal来做到最终一致性。这里不再详细介绍
Redis的持久化机制
具体分为RDB和AOF RDB是快照,AOF存储的是命令记录 所以相对来说RDB文件更小,恢复速度更快。而AOF文件更大,恢复速度更慢。 但是RDB的执行一次比较耗时,而AOF是记录每条增删改的命令,所以更快一些 下面介绍一下RDB和AOF的配置方式 将两个配置都打开,就是RDB和AOF一起使用


Redis的数据删除策略
惰性删除:设置该key过期时间后,我们不去管它,当需要该key时,我们在检查其是否过期,如果过期,我们就删掉它,反之返回该key 定期删除:每隔一段时间,我们就对一些key进行检查,删除里面过期的key(从一定数量的数据库中取出一定数量的随机key进行检查,并删除其中的过期key)。 定期清理有两种模式: SLOW模式是定时任务,执行频率默认为10hz,每次不超过25ms,以通过修改配置文件redis.conf 的hz 选项来调整这个次数 FAST模式执行频率不固定,但两次间隔不低于2ms,每次耗时不超过1ms Redis的过期删除策略:惰性删除 + 定期删除两种策略进行配合使用
Redis的内存淘汰机制
数据的淘汰策略:当Redis中的内存不够用时,此时在向Redis中添加新的key,那么Redis就会按照某一种规则将内存中的数据删除掉,这种数据的删除规则被称之为内存的淘汰策略。 Redis支持8种不同策略来选择要删除的key:
noeviction: 不淘汰任何key,但是内存满时不允许写入新数据,默认就是这种策略。volatile-ttl: 对设置了TTL的key,比较key的剩余TTL值,TTL越小越先被淘汰allkeys-random:对全体key ,随机进行淘汰。volatile-random:对设置了TTL的key ,随机进行淘汰。allkeys-lru: 对全体key,基于LRU算法进行淘汰volatile-lru: 对设置了TTL的key,基于LRU算法进行淘汰allkeys-lfu: 对全体key,基于LFU算法进行淘汰volatile-lfu: 对设置了TTL的key,基于LFU算法进行淘汰LRU(Least Recently Used)最近最少使用。用当前时间减去最后一次访问时间,这个值越大则淘汰优先级越高。LFU(Least Frequently Used)最少频率使用。会统计每个key的访问频率,值越小淘汰优先级越高。
追问:那你们采取什么策略呢?
结合你们项目选择合适的策略
- 优先使用
allkeys-lru策略。充分利用LRU算法的优势,把最近最常访问的数据留在缓存中。如果业务有明显的冷热数据区分,建议使用。 - 如果业务中数据访问频率差别不大,没有明显冷热数据区分,建议使用
allkeys-random,随机选择淘汰。 - 如果业务中有置顶的需求,可以使用
volatile-lru策略,同时置顶数据不设置过期时间,这些数据就一直不被删除,会淘汰其他设置过期时间的数据。 - 如果业务中有短时高频访问的数据,可以使用
allkeys-lfu或volatile-lfu策略。
Redis的分布式锁如何实现
Redis实现分布式锁主要利用Redis的setnx命令。setnx是SET if not exists**(如果不存在,则 SET)的简写。 举例,set lock value nx ex 10能设置成功,则获取锁成功 del lock就是释放锁 但是这种简单的分布式锁有很多问题,比如不可重入,超时误删等一系列问题,所以我们会采用redisson提供的分布式锁,具体原理大家自行搜索,面试官会追问,这里我只做简单介绍**
- redisson底层基于redis的setnx命令进行改进,使用lua脚本保证命令的原子性
- 利用hash结构,记录线程标示和重入次数
- 利用watchDog看门狗延续锁的时间
- 控制了锁重试等待
- 使用RedLock红锁解决主从数据一致性问题,但是性能很差
- 如果业务需要强一致性,建议使用zookeeper实现的分布式锁
Redis是单线程为什么还这么快
Redis是纯内存操作,执行速度非常快 采用单线程,避免不必要的上下文切换可竞争条件,多线程还要考虑线程安全问题 使用I/O多路复用模型,非阻塞IO
什么是I/O多路复用模型
是指利用单个线程来同时监听多个Socket ,并在某个Socket可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。目前的I/O多路复用都是采用的epoll模式实现,它会在通知用户进程Socket就绪的同时,把已就绪的Socket写入用户空间,不需要挨个遍历Socket来判断是否就绪,提升了性能。
2.运维部署(难度★★★)
Redis的集群有哪些方案?
- 主从复制
- 哨兵集群
- 分片集群
介绍一下怎么实现主从复制
主从复制原理大家自行搜索
单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。 一般都是一主多从,主节点负责写数据,从节点负责读数据 追问:能说一下,主从同步数据的流程全量同步:
- 从节点请求主节点同步数据(
replication id、offset) - 主节点判断是否是第一次请求,是第一次就与从节点同步版本信息(
replication id和offset) - 主节点执行bgsave,生成rdb文件后,发送给从节点去执行
- 在rdb生成执行期间,主节点会以命令的方式记录到缓冲区(一个日志文件)
- 把生成之后的命令日志文件发送给从节点进行同步 增量同步:
- 从节点请求主节点同步数据,主节点判断不是第一次请求,不是第一次就获取从节点的offset值
- 主节点从命令日志中获取offset值之后的数据,发送给从节点进行数据同步
如何保证你们Redis的高可用
哨兵原理大家自行搜索
首先可以搭建主从集群,再加上Redis的哨兵模式 哨兵模式可以实现主从集群的自动故障恢复、其中包含对主从服务的监控、自动故障恢复等。 如果Master故障,Sentinel会将一个slave提升为master,当故障示例恢复后,也会以新的master为主。
你们的Redis使用的是单点还是集群,那种集群?
我们使用的是主从(1主1从)加哨兵。一般单节点不超过10G内存,如果内存不足可以给不同服务拆分不同的Redis主从节点,尽量不采用分片。因为集群维护起来很麻烦,并且也没法使用lua脚本和事务 追问:那你们集群脑裂了怎么解决呢脑裂是由于master和slave处于不同的网络分区,使得哨兵没有监控到master的心跳,然后通过选举选出slave变为master,这样就存在两个master,就像大脑分裂一样。会导致客户端还在向老的master发数据,新的master还无法同步。 如果想解决可以在redis中进行设置 第一设置最少slave节点个数,比如至少要有一个salve节点才允许同步 第二可以设置主从数据复制和同步的延迟时间,答不到要求就拒绝,也可以避免数据丢失
Redis中的分片集群了解过吗
分片集群解决的是海量数据的存储问题,集群中会有多个master,每个master保存的数据是不同的,这样就能增大整体集群的并发能力,同时每个master之间通过ping检测彼此健康状态,就类似哨兵了。并且Redis的分片集群引入了哈希槽的概念,有16384个槽,每个master节点都绑定一定范围的槽,利用CRC16校验后并对节点数进行取模操作,来决定放哪个槽
3.场景题(难度★★★★★)
现在数据库1000万数据,Redis只能存20万,怎么办?
优先存储热点数据 怎么确定热点数据? 使用allkeys-lru淘汰策略,留下来的就都是经常访问的热点数据
Redis里的大Key如何处理?
什么是大Key
通常我们会将含有较大数据或含有大量成员、列表数的Key称之为大Key 举例说明:(但是具体的数值大小需要结合实际业务和场景)
- 一个
STRING类型的Key,它的值为5MB(数据过大) - 一个
LIST类型的Key,它的列表数量为20000个(列表数量过多) - 一个
ZSET类型的Key,它的成员数量为10000个(成员数量过多) - 一个
HASH格式的Key,它的成员数量虽然只有1000个但这些成员的value总大小为100MB(成员体积过大)
大Key带来的问题
- 查询速度变慢
Redis内存不断变大导致OOM
如何寻找大Key
通过Redis官方客户端redis-cli的bigkeys参数发现大Key
大Key的处理方式
- 对大Key进行拆分,比如Hash成员过多,根据实际业务再继续拆分成多个Key。或者拆分到不同的分片集群
String中数据量过大,采用合适的压缩方式或其他数据结构
如何删除大Key
直接del删除的话,可能会导致主线程阻塞,一般采用unlink命令异步删除
Redis中的热点Key问题
什么是热点Key问题 热点 key 问题是指在 Redis 中某些特定的 key 被频繁访问,可能会导致 Redis 服务器负载过高,影响系统的性能和稳定性
- 找到热点Key
- 在应用程序中添加代码来统计对不同 key 的访问频率。可以使用一个哈希表来记录每个 key 的访问次数,并定期(如每分钟)更新和分析这些统计信息。
- 可以使用一些开源的 APM(Application Performance Monitoring,应用性能监控)工具,这些工具可以监控应用程序对 Redis 的访问,并识别出热点 key。
- 处理热点 key 的方法
- 限流降级,最简单朴素的办法
- 多级缓存,也就是对热点key再加一层本地缓存,缓解redis的压力
- 主从集群,使用多个从节点来分散redis单节点的压力
- 优化数据结构和算法,例如,可以使用更高效的数据结构(如哈希表、跳表等)来存储数据,或者优化查询算法,减少查询时间。
Redis如果内存满了会出现什么
主要看数据淘汰策略是什么?如果是默认的配置(noeviction),会直接报错
你们Redis挂了怎么办
结合实际情况,先确定自己公司的Redis是自己搭建还是买的云服务厂商现成的,参考下面问题 如果是自己搭建,那就集合持久化机制(RDB\AOF)和哨兵机制来综合回答,意思就是有做防单点故障的策略 如果使用云服务厂商提供的,那就不用太担心
你们Redis用了多少台
两种答案 1.我们直接使用的阿里云提供的Redis(主从版、单机版、集群版),所以不担心它的高可用问题 2.基于项目实际情况选择使用哨兵还是主从,具体参考 微服务课程中讲的Redis面试题

