跳过正文
  1. 面试题库/

06|Redis 场景

·6350 字·13 分钟
目录
Redis面试题库 - 这篇文章属于一个选集。
§ 6: 本文

1. 你有实际使用过 Redis 做什么应用么?
#

分析

有些同学回答没做过,这种肯定不行。只要了解相关应用场景,就可以说有实验过。

一般而言,没经验的同学说 2 个场景会比较好,太多容易被质疑,太少觉得单薄。

如果实际情况应用多于 2 个,按实际说就行。

回答

有在实习中/工作中/实验室中涉及过 Redis 缓存场景、Redis 分布式锁场景。

2. Redis 缓存是如何应用的?
#

分析

一般都是旁路缓存,这里面试官不特别问的话,没必要背一堆缓存模式,只说旁路缓存就行。

回答

我们是用作旁路缓存,在我们订单项目中,是先查询 Redis,没有查 MySQL 并将数据加载到 Redis。

3. Redis 经常作为 MySQL 的缓存来使用,为什么?
#

分析

有几个要点:

  1. MySQL 是磁盘操作,性能不高;
  2. Redis 内存操作,性能非常高;
  3. 缓存本质是高速存储临时存储低速存储的数据。

回答

MySQL 是关系型数据库系统,操作数据需要访问磁盘,性能不高,通常就几千 QPS,而 Redis 基于内存并做了很多优化,性能高达 10W QPS,一些热点数据就可以缓存到 Redis,查询时先查 Redis,不存在才查 MySQL,这种 Redis+MySQL 结合的方式可以有效提高系统 QPS。

4. Redis 和 Memcached 有哪些共同点和不同点?
#

分析

两者都是缓存中常用组件,所以容易拿来比较。

共同点:

  1. 内存存储:两者都使用内存作为主要存储方式,访问速度极快,适用于缓存场景;
  2. 键值存储(Key-Value Store):两者都以键值对(key-value)形式存储数据,并支持 TTL(过期时间);
  3. 高性能:由于存储在内存中,都能在毫秒级响应数据请求,适用于高并发环境;
  4. 分布式扩展:两者都支持分布式部署(但 Redis 需要依赖外部 Cluster 方案,而 Memcached 原生支持分布式);
  5. 应用场景:都用于缓存热点数据、降低数据库压力、提升网站响应速度。

不同点:

对比项RedisMemcached
数据类型支持多种数据结构(String, List, Set, Hash, ZSet)仅支持字符串(String)
持久化支持 RDB 和 AOF 持久化,数据可恢复仅存储在内存,服务器重启后数据丢失
数据淘汰策略多种 LRU/LFU 及定期清理策略LRU 淘汰策略(数据满时删除最久未使用的键)
分布式支持通过 Redis Cluster 实现分布式原生支持分布式(客户端一致性哈希)
事务支持支持 MULTI/EXEC 事务不支持事务
原子操作多个命令支持原子性(如 INCR、LPUSH)仅 INCR/DECR 等有限支持原子操作
Lua 脚本支持 EVAL 执行复杂操作不支持
发布/订阅(Pub/Sub)支持(可用于实时消息推送)不支持
Stream(流式数据)支持(XADD、XREAD)不支持
内存利用率相对较高,占用额外的元数据更紧凑,适合超大规模缓存
并发模型单线程(基于事件驱动,内部优化)多线程(并发性能较好)
应用场景缓存、队列、排行榜、分布式锁、持久化存储主要用于缓存(高速存取数据)

回答

Memcached 适合纯字符串缓存场景,数据结构只支持字符串,超高并发下性能较优,适用于 CDN、页面缓存、数据库查询结果缓存等。

Redis 适合更广泛的场景,支持持久化、多数据结构、事务、分布式锁等,适用于排行榜、消息队列、实时统计等。

理论上来说如果只是简单的高性能缓存,选 Memcached;如果需要更多功能,选 Redis,但是后端实际生产过程中,现在基本都是 Redis,因为场景全面、实践经验完善性能也足够高了。

5. Redis 做旁路缓存,如果 MySQL 更新了,此时何去何从?
#

分析

经典的缓存一致性问题,从八股上来就是:

Cache Aside

  • 原理:先从缓存中读取数据,如果没有就再去数据库里面读数据,然后把数据放回缓存中,如果缓存中可以找到数据就直接返回数据;更新数据的时候先把数据持久化到数据库,然后再让缓存失效;
  • 问题:假如有两个操作一个更新一个查询,第一个操作先更新数据库,还没来得及删除缓存,查询操作可能拿到的就是旧的数据;更新操作马上让缓存失效了,所以后续的查询可以保证数据的一致性;还有的问题就是有一个是读操作没有命中缓存,然后就到数据库中取数据,此时来了一个写操作,写完数据库后,让缓存失效,然后,之前的那个读操作再把老的数据放进去,也会造成脏数据;
  • 可行性:出现上述问题的概率其实非常低,需要同时达成读缓存时缓存失效并且有并发写的操作。数据库读写要比缓存慢得多,所以读操作在写操作之前进入数据库,并且在写操作之后更新,概率比较低。

Read/Write Through

  • 原理:Read/Write Through 原理是把更新数据库(Repository)的操作由缓存代理,应用认为后端是一个单一的存储,而存储自己维护自己的缓存;
  • Read Through:就是在查询操作中更新缓存,也就是说,当缓存失效的时候,Cache Aside 策略是由调用方负责把数据加载入缓存,而 Read Through 则用缓存服务自己来加载,从而对调用方是透明的;
  • Write Through:当有数据更新的时候,如果没有命中缓存,直接更新数据库,然后返回。如果命中了缓存,则更新缓存,然后再由缓存自己更新数据库(这是一个同步操作)。

Write Behind

  • 原理:在更新数据的时候,只更新缓存,不更新数据库,而缓存会异步地批量更新数据库。这个设计的好处就是让数据的 I/O 操作非常快,带来的问题是,数据不是强一致性的,而且可能会丢。

但是这里需要结合实际经验回答,不能回答得太八股,要展现自己的思考和能力。

回答

我在项目中使用过期时间来兜底,并且在更新 DB 后删除缓存来提升一致性的方式。另外,在做方案设计时候我还考虑过订阅 binlog 的方式,但这种方式额外引入了消息队列和消费服务,成本太高而收益不足,所以还是选择了前者。

我也调研过业界一些团队,包括腾讯云 xx 团队,字节跳动飞书团队,在大部分场景也是相同的选择,甚至一部分连删除都不用,本身对缓存一定程度不一致的容忍还是有的。

6. 如何保证删除缓存操作一定能成功?
#

分析

缓存删除是一次 Redis 操作,可能因为网络波动等原因执行失败,提高删除成功率有 2 种思路,一个是失败就扔入重试队列,但是扔入队列这一步也可能失败,所以只能说是提高,第二种就是绕过这个问题,缓存删除一般是为了数据同步,可以使用订阅 MySQL BinLog 的方式来同步数据。

回答

可以引入消息队列,删除缓存的操作由消费者来做,删除失败的话重新去消息队列拉取相应的操作,超过一定次数没有删除成功就像业务层报错。

如果是 MySQL、Redis 缓存同步场景,为了保证成功率,可以用一个消费服务订阅 MySQL binlog 日志,拿到具体要操作的数据,然后再向 Redis 执行缓存同步操作。

7. 业务缓存一致性要求高怎么办?
#

分析

既然用了缓存,就始终存在不一致性的时间,只能说尽可能减少这个时间,不可能完全一致,如果需要完全一致就不应该用缓存。

回答

延迟双删是提高一致性的方案,先删除缓存,然后更新数据库,等待一段时间再删除缓存。保证第一个操作再睡眠之后,第二个操作完成更新缓存操作。但是具体睡眠多久其实是个玄学,很难评估出来,这个方案也只是尽可能保证一致性而已,依然也会出现缓存不一致的现象。

8. 如何避免缓存失效?
#

分析

缓存正常来说都是有过期时间的,过期时间到了,这时候缓存就会被删除,对于 MySQL(或其它数据源)而言也就是缓存失效了。

回答

首先业务发现缓存失效,是会去读 MySQL 数据重新加载进去的,但是为了尽可能避免缓存失效,我们可以由后台线程频繁地检测缓存是否有效,检测到即将失效了马上从数据库读取数据,并更新到缓存。

另外,在业务刚上线的时候,最好提前把数据缓存起来,而不是等待用户访问才来触发缓存构建,这就是所谓的缓存预热。

9. Redis 做秒杀场景可以吗?讲讲思路
#

分析

Redis 在秒杀场景主要的应用方式是两个。

一个是把 Redis 当消息队列用,用于削峰,一个是用 Redis 记录库存,进行库存的加减。

回答

Redis 可以用来记录库存,利用 Redis 的高性能进行库存的扣减,一个 Redis 处理 6W 的请求问题不大,100W/s 流量就 20 台 Redis 来支撑,当然,每个节点都要做主从容灾。

另一个方式就是把 Redis 作为轻量级消息队列,来接受请求,但是不如 kafka 这种可靠。

10. Redis 管道有什么用?
#

分析

Redis 管道(Pipeline)是一种优化客户端与 Redis 服务器之间通信的机制,主要用于减少网络往返时间(RTT, Round-Trip Time),从而提升性能。

管道的工作原理:管道技术本质上是客户端提供的功能,而非 Redis 服务器端的功能。

  1. 客户端
    • 客户端将多个命令缓存到本地,而不是立即发送到服务器;
    • 当缓存达到一定数量或显式调用 EXEC 时,客户端一次性将所有命令发送到服务器。
  2. 服务器
    • 服务器按照接收到的顺序依次执行所有命令;
    • 执行完成后,服务器将所有结果一次性返回给客户端。
  3. 结果返回
    • 客户端接收到所有命令的执行结果,并按顺序处理。

管道的使用场景:

  • 批量写入:需要一次性写入大量数据时(如初始化缓存、批量插入数据),使用管道可以显著提高性能。示例:批量设置多个键值对;
  • 批量读取:需要一次性读取多个键的值时,使用管道可以减少网络延迟。示例:批量获取多个用户的信息;
  • 高并发场景:在高并发场景下,使用管道可以减少客户端与服务器之间的通信次数,降低系统负载;
  • 事务优化:在事务(MULTI/EXEC)中,使用管道可以避免多次网络往返。

回答

管道技术是客户端提供的一种批处理技术,用于一次处理多个 Redis 命令,从而提高整个交互的性能。

Pipeline 的本质,是将请求在客户端打包,然后一次发送给服务端,服务端处理完成之后,会将结果存起来,等 Pipeline 中所有命令都完成了,再一起回包,这样可以节约很多网络交互的时间。

但使用管道技术也要注意避免发送的命令过大,或管道内的数据太多而导致的网络阻塞。

11. 什么是热 key?
#

分析

讲清楚热 key 的定义和常见场景。

回答

热 Key,也称为热点 Key 或热门 Key,就是在 Redis 中,访问频率极高、请求量特别大的某些特定 Key。由于这些 Key 的访问量远远高于其他 Key,就可能会导致以下问题:

  1. 资源集中消耗:大量请求集中在少数几个 Key 上,可能导致某个节点负载过高,影响系统的整体性能;
  2. 单点瓶颈:在分布式缓存系统中,如果热 Key 集中在某一个节点上,可能会导致该节点成为性能瓶颈,甚至崩溃;
  3. 网络带宽压力:频繁访问热 Key 会占用较多的网络带宽,可能引发网络拥塞。

常见场景:

  • 电商促销活动:例如秒杀商品的库存信息可能成为热 Key;
  • 社交平台:例如某个爆款帖子的点赞数或评论数;
  • 游戏领域:例如排行榜 Top1 玩家的分数。

12. 热 key 问题如何发现和解决?
#

分析

热 key 的发现需结合 Redis 本身的命令操作和业务统计完成,解决则需通过分散压力和多级缓存策略。

回答

发现热 key 一般有以下几种方案:

  1. 使用 redis-cli 的 hotkeys 参数可以统计出热 Key 信息;
  2. 通过 Redis 的 MONITOR 命令找出热 Key;
  3. 通过业务层定位热 Key,在业务层增加相应的代码,埋点统计高频访问的 key。

而对于热 key 的解决核心是降低单点压力:

  1. 分散请求
    • 负载均衡:将热 key 加上前缀或者后缀,把热 key 的数量从 1 个变成实例个数,利用分片特性将这 n 个 key 分散在不同节点上,这样就可以在访问的时候,采用客户端负载均衡的方式,随机选择一个 key 进行访问,将访问压力分散到不同的实例中。这个方案有个明显的缺点,就是缓存的维护成本大:假如有 n 为 100,则更新或者删除 key 的时候也需要操作 100 个 key;
    • 读写分离:通过主从复制的方式,增加 slave 节点来实现读请求的负载均衡;
    • 集群扩容:增加 Redis 实例,利用分片机制分摊压力。
  2. 多级缓存
    • 将热 key 缓存到本地,构成多级缓存存储结构。

13. 什么是大 key?
#

分析

讲清楚大 key 的定义和常见场景。

回答

大 Key 是指在 Redis 中,存储了大量数据的 Key。具体来说,大 Key 通常包含非常大的 Value 值,例如一个巨大的字符串、列表、哈希表或集合等。这些 Key 占用的内存资源较多,可能会对系统的性能和稳定性造成影响。

而且大 Key 也会导致这些问题:

  1. 内存占用过高:大 Key 会占用大量的内存空间,可能导致缓存系统内存不足,进而引发性能问题或 OOM(Out of Memory);
  2. 删除或操作耗时:当删除大 Key 或对其进行操作(如遍历、更新)时,可能会阻塞主线程,导致 Redis 响应变慢甚至无响应;
  3. 网络传输压力:如果需要将大 Key 的数据从缓存中读取到应用层,可能会占用较多的网络带宽,增加延迟。

大 Key 的常见场景:

  • 粉丝列表:ZSet 存储千万级用户 ID;
  • 用户行为记录:将某个用户的长时间行为记录存储在一个 Key 中;
  • 缓存大对象:比如直接用 String 来存储图片/视频元数据。

14. 大 key 问题怎么排查和解决?
#

分析

考察面试者是否有使用过一些工具来排查大 Key 问题,以及是否知道场景的解决手段。

回答

排查大 key 问题常见的手段有:

  1. 使用 redis-cli 的参数
    • bigkeys:统计大 Key 信息,集合或列表类型返回元素个数;
    • memkeys:统计大 Key 信息,返回所有数据类型所占内存大小。
  2. 第三方工具
    • 通过开源项目 redis-rdb-tools 分析 RDB 快照文件进行灵活查询,按照业务需求分析定制化地找出大 key;
    • RedisInsight(官方工具):提供图形化大 Key 分析。
  3. 慢查询日志分析
    • 大 Key 可能触发慢查询。

解决方法:

  1. 数据拆分
    • 将大 key 拆分为多个小 key,如将一个大 List 拆分为多个小 List;
    • 使用 Hash 结构存储数据,将相关字段分开存储。
  2. 定期清理
    • 设置过期时间,自动删除不再需要的数据;
    • 定期运行脚本清理历史数据。
  3. 优化存储策略
    • 使用压缩算法减少数据大小;
    • 将大文件或数据存储在文件系统或对象存储中,Redis 仅存储引用或元数据,比如图片存储到 OSS(对象存储服务)里面,Redis 里面只存对应的访问 URL。

15. Redis 如何处理大 key?
#

分析

一般而言 String 类型的值大于 10 KB;Hash、List、Set、ZSet 类型的元素的个数超过 5000 个。

影响:

  • 客户端超时阻塞:由于 Redis 执行命令是单线程处理,然后在操作大 key 时会比较耗时。客户端认为很久没有响应;
  • 引发网络阻塞:每次获取大 key 产生的网络流量较大;
  • 阻塞工作线程:如果使用 del 删除大 key 时,会阻塞工作线程,这样就没法处理后续的命令,一般要用 unlink 去异步删除;
  • 内存分布不均:集群模型在 slot 分片均匀情况下,会出现数据和查询倾斜情况,部分有大 key 的 Redis 节点占用内存多,QPS 也会比较小。

处理:

  • 当 value 是 string 时,比较难拆分,则使用序列化、压缩算法将 key 的大小控制在合理范围内,但是序列化和反序列化都会带来更多时间上的消耗;
  • 当 value 是 string,压缩之后仍然是大 key,则需要进行拆分,一个大 key 分为不同的部分,记录每个部分的 key,使用 multiget 等操作实现事务读取;
  • 也可以针对 string 大 key 的场景,把 string 的 value 分拆成几个 key-value,存储在一个 hash 中,每个 field 代表一个具体的属性,使用 hget、hmget 来获取部分的 value,使用 hset、hmset 来更新部分属性;
  • 当 value 是 list/set 等集合类型时,根据预估的数据规模来进行分片,不同的元素计算后分到不同的片。

16. Redis 支持事务回滚吗?
#

分析

Redis 不具备完整的 ACID 特性,执行的命令都没有回滚之说,无论是事务还是 LUA 脚本中的命令,都是一样的。

回答

不支持,Redis 不具备完整的 ACID 特性,执行的命令都没有回滚之说,Redis 提供的 DISCARD 命令只能用来主动放弃事务执行,把暂存的命令队列清空,起不到回滚的效果。至于 LUA 脚本也是一样,比如 LUA 里面有 2 个写操作,执行了第一个如果 Redis 挂掉,那么第二个不会执行,第一个也不回撤回。

17. Redis 如何实现延迟队列?
#

分析

首先要讲清楚什么是延迟队列:

延迟队列是指把当前要做的事情,往后推迟一段时间再做:

  • 在淘宝、京东等购物平台上下单,超过一定时间未付款,订单会自动取消;
  • 打车的时候,在规定时间没有车主接单,平台会取消你的单并提醒你暂时没有车主接单;
  • 点外卖的时候,如果商家在 10 分钟还没接单,就会自动取消订单。

知道延迟队列是什么之后,不难发现 Redis 中 ZSet 最适合模拟这个功能。

回答

使用 ZSet,ZSet 有一个 Score 属性可以用来存储延迟执行的时间。使用 zadd score1 value1 命令,再利用 zrangebyscore 查询符合条件的所有待处理的任务,通过循环执行队列任务。

18. Redis 可以做消息队列吗?什么时候能用 Redis 做消息队列?
#

分析

在某些场景,其实我们并不是一定需要有多可靠、多完善的消息队列,比如用消息队列发短信,我们肯定也经常遇到过,短信没收到的场景吧?没收到重试就行了。

所以,轻量级消息队列也有了市场需要,Redis 就很适合来做一个不那么完善的消息队列。

回答

Redis 可以作为轻量级消息队列。如果是本身业务轻量级,且团队没有已经接入完备的消息队列,这个时候没有必要引入一个重量消息队列,使用 Redis 即可满足要求,没有不能用的组件,只有不合适的场景。

Redis面试题库 - 这篇文章属于一个选集。
§ 6: 本文