Redis 缓存面试
标识说明
- 0️⃣ 不重要,可不看,知道即可
- 3️⃣ 一般重要,可看可不看,了解即可
- 5️⃣ 非常重要,一定要看,必须掌握的内容
- 1、Redis 是什么? 5️⃣
- 2、Redis 常见数据结构以及使用场景分析 5️⃣
- 3、说一下 Redis 和 Memcached 的区别和共同点 3️⃣
- 4、为什么要使用 Redis?/为什么要用缓存? 5️⃣
- 5、Redis 单线程模型
- 6、为什么Redis是单线程,没有使用多线程?为什么不使用多线程?
- 7、Redis6.0 之后为何引入了多线程?
- 8、缓存穿透 5️⃣
- 9、缓存雪崩 5️⃣
- 10、缓存预热5️⃣
- 11、如何保证缓存和数据库数据的一致性?
- 12、 Redis是如何判断数据是否过期?
- 13、过期的数据的删除策略
- 14、Redis 内存淘汰机制
- 15、Redis 持久化机制(怎么保证 Redis 挂掉之后再重启数据可以进行恢复) 5️⃣
- 16、Redis 事务
- 参考学习
# 1、Redis 是什么? 5️⃣
参考答案
- Redis采用了RESP(REdis Serialization Protocol)通讯协议,底层通过Socket来实现信息的传递和获取。
- Redis本质上是一个使用 C 语言开发的 Key-Value 类型的内存数据库,由于是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作,是已知性能最快的 Key-Value 数据库,因此被广泛应用于缓存方向。
- Redis 除了做缓存之外,Redis 也经常用来做分布式锁,甚至是消息队列。
- Redis 的出色之处不仅仅是性能,Redis 提供了多种数据类型来支持不同的业务场景。Redis 还支持事务 、持久化、Lua 脚本、多种集群方案。
# 2、Redis 常见数据结构以及使用场景分析 5️⃣
参考答案
# String
基本说明
- 介绍: String 是简单的 key-value 类型数据结构。虽然 Redis 是用 C 语言写的,但是 RediS 并没有使用 C 的字符串表示,而是自己构建了一种 简单动态字符串(Simple dynamic String,SDS)。相比于 C 的原生字符串,Redis 的 SDS 不光可以保存文本数据还可以保存二进制数据,并且获取字符串长度复杂度为 O(1)(C 字符串为 O(N),除此之外,RediS 的 SDS API 是安全的,不会造成缓冲区溢出。
- 常用命令:
set,get,strlen,exists,dect,incr,setex等等。 - 应用场景: 一般常用在需要 计数场景,比如用户的访问次数、热点文章的点赞转发数量等等。
普通字符串的基本操作
127.0.0.1:6379> set key value #设置 key-value 类型的值
OK
127.0.0.1:6379> get key #根据 key 获得对应的 value
"value"
127.0.0.1:6379> exists key #判断某个 key 是否存在
(integer) 1
127.0.0.1:6379> strlen key #返回 key 所储存的字符串值的长度。
(integer) 5
127.0.0.1:6379> del key #删除某个 key 对应的值
(integer) 1
127.0.0.1:6379> get key
(nil)
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
- 批量设置
127.0.0.1:6379> mset key1 value1 key2 value2 #批量设置 key-value 类型的值
OK
127.0.0.1:6379> mget key1 key2 #批量获取多个 key 对应的 value
1) "value1"
2) "value2"
1
2
3
4
5
2
3
4
5
- 计数器
字符串的内容为整数的时候可以使用
127.0.0.1:6379> set number 1
OK
127.0.0.1:6379> incr number #将 key 中储存的数字值增一
(integer) 2
127.0.0.1:6379> get number
"2"
127.0.0.1:6379> decr number #将 key 中储存的数字值减一
(integer) 1
127.0.0.1:6379> get number
"1"
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
- 过期
127.0.0.1:6379> expire key 60 #数据在 60s 后过期
(integer) 1
127.0.0.1:6379> setex key 60 value #数据在 60s 后过期 (setex:[set] + [ex]pire)
OK
127.0.0.1:6379> ttl key # 查看数据还有多久过期
(integer) 56
1
2
3
4
5
6
2
3
4
5
6
# List
- 基本说明
- 介绍: List 即是链表。 链表是一种非常常见的数据结构,特点是易于数据元素的插入和删除并且且可以灵活调整链表长度,但是链表的随机访问困难。许多高级编程语言都内置了链表的实现比如 Java 中的 LinkedList,但是 C 语言并没有实现链表,所以 Redis 实现了自己的链表数据结构。Redis 的 list 的实现为一个 双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。
- 常用命令:
rpush,lpop,lpush,rpop,lrange、llen等。 - 应用场景: 一般常用在需要 发布与订阅场景,或者说消息队列、慢查询。
List的使用
- 通过
rpush/lpop实现队列
127.0.0.1:6379> rpush myList value1 # 向 List 的头部(右边)添加元素
(integer) 1
127.0.0.1:6379> rpush myList value2 value3 # 向 List 的头部(最右边)添加多个元素
(integer) 3
127.0.0.1:6379> lpop myList # 将 List 的尾部(最左边)元素取出
"value1"
127.0.0.1:6379> lrange myList 0 1 # 查看对应下标的 List 列表, 0 为 start,1为 end
1) "value2"
2) "value3"
127.0.0.1:6379> lrange myList 0 -1 # 查看列表中的所有元素,-1表示倒数第一
1) "value2"
2) "value3"
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
- 通过
rpush/lpop实现栈
127.0.0.1:6379> rpush myList2 value1 value2 value3
(integer) 3
127.0.0.1:6379> rpop myList2 # 将 List 的头部(最右边)元素取出
"value3"
1
2
3
4
2
3
4
栈帧(先进后出(FILO))图解:

- 通过
lrange查看对应下标范围的列表元素
127.0.0.1:6379> rpush myList value1 value2 value3
(integer) 3
127.0.0.1:6379> lrange myList 0 1 # 查看对应下标的 List 列表,0 为 start,1为 end。通过 lrange 命令,可以基于 List 实现分页查询,性能非常高!
1) "value1"
2) "value2"
127.0.0.1:6379> lrange myList 0 -1 # 查看列表中的所有元素,-1表示倒数第一
1) "value1"
2) "value2"
3) "value3"
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
- 通过
llen查看链表长度
127.0.0.1:6379> rpush myList value1 value2 value3
127.0.0.1:6379> llen myList
(integer) 3
1
2
3
2
3
# Hash
基本说明
- 介绍: Hash 类似于 JDK1.8 前的 HashMap,内部实现也是数组+链表的形式。不过,Redis 的 Hash 做了更多优化。另外,Hash 是一个 String 类型的 field 和 value 的映射表,特别适合用于存储对象,后续操作的时候,你可以直接仅仅修改这个对象中的某个字段的值。
- 常用命令:
hset,hmset,hexists,hget,hgetall,hkeys,hvals等等。 - 应用场景: 一般常用在需要 对象数据的存储。,比如存储用户信息,商品信息等。
Hash 使用
127.0.0.1:6379> hset userInfoKey name "guide" description "dev" age "24"
OK
127.0.0.1:6379> hexists userInfoKey name #查看 key 对应的 value中指定的字段是否存在。
(integer) 1
127.0.0.1:6379> hget userInfoKey name #获取存储在哈希表中指定字段的值。
"guide"
127.0.0.1:6379> hget userInfoKey age
"24"
127.0.0.1:6379> hgetall userInfoKey #获取在哈希表中指定 key 的所有字段和值
1) "name"
2) "guide"
3) "description"
4) "dev"
5) "age"
6) "24"
127.0.0.1:6379> hkeys userInfoKey #获取 key 列表
1) "name"
2) "description"
3) "age"
127.0.0.1:6379> hvals userInfoKey #获取 value 列表
1) "guide"
2) "dev"
3) "24"
127.0.0.1:6379> hset userInfoKey name "GuideGeGe" #修改某个字段对应的值
127.0.0.1:6379> hget userInfoKey name
"GuideGeGe"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# Set
基本说明
- 介绍: Set 类似于 Java 中的 HashSet 。Redis 中的 Set 类型是一种无序集合,集合中的元素没有先后顺序。当你需要存储一个列表数据,又不希望出现重复数据时,Set 是一个很好的选择,并且 Set 提供了判断某个成员是否在一个 Set 集合内的重要接口,而 List 没有提供这个能力。
- 常用命令:
sadd,spop,smembers,sismember,scard,sinterstore,sunion等等。 - 应用场景: 一般常用在 需要存放的数据不能重复以及需要获取多个数据源交集和并集等场景,可以基于 Set 轻易实现交集、并集、差集的操作。比如:你可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis 可以非常方便的实现如共同关注、共同粉丝、共同喜好等功能,这个过程也就是求交集的过程。
Set 使用
127.0.0.1:6379> sadd mySet value1 value2 # 添加元素进去
(integer) 2
127.0.0.1:6379> sadd mySet value1 # 不允许有重复元素
(integer) 0
127.0.0.1:6379> smembers mySet # 查看 set 中所有的元素
1) "value1"
2) "value2"
127.0.0.1:6379> scard mySet # 查看 set 的长度
(integer) 2
127.0.0.1:6379> sismember mySet value1 # 检查某个元素是否存在 set 中,只能接收单个元素
(integer) 1
127.0.0.1:6379> sadd mySet2 value2 value3
(integer) 2
127.0.0.1:6379> sinterstore mySet3 mySet mySet2 # 获取 mySet 和 mySet2 的交集并存放在 mySet3 中
(integer) 1
127.0.0.1:6379> smembers mySet3
1) "value2"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Sorted Set
基本说明
- 介绍: 和 Set 相比,Sorted Set 增加了一个权重参数 Score,使得集合中的元素能够按 Score 进行有序排列,还可以通过 Score 的范围来获取元素的列表。有点像是 Java 中 HashMap 和 TreeSet 的结合体。
- 常用命令:
zadd,zcard,zscore,zrange,zrevrange,zrem等等。 - 应用场景: 一般常用在需要对数据根据某个权重进行排序的场景 ,比如在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息。
Sorted Set 使用
127.0.0.1:6379> zadd myZset 3.0 value1 #添加元素到 sorted set 中 3.0 为权重
(integer) 1
127.0.0.1:6379> zadd myZset 2.0 value2 1.0 value3 #一次添加多个元素
(integer) 2
127.0.0.1:6379> zcard myZset #查看 sorted set 中的元素数量
(integer) 3
127.0.0.1:6379> zscore myZset value1 #查看某个 value 的权重
"3"
127.0.0.1:6379> zrange myZset 0 -1 #顺序输出某个范围区间的元素,0 -1 表示输出所有元素
1) "value3"
2) "value2"
3) "value1"
127.0.0.1:6379> zrange myZset 0 1 #顺序输出某个范围区间的元素,0 为 start 1 为 stop
1) "value3"
2) "value2"
127.0.0.1:6379> zrevrange myZset 0 1 #逆序输出某个范围区间的元素,0 为 start 1 为 stop
1) "value1"
2) "value2"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 3、说一下 Redis 和 Memcached 的区别和共同点 3️⃣
参考答案
- Memcached:Memcached是一个自由开源的,高性能,分布式内存对象缓存系统。
- 相同点:
- 都是基于内存的数据库,一般都用来当做缓存使用。
- 都有过期策略。
- 由于基于内存,两者的性能都非常高。
- 区别:
- Redis 支持更丰富的数据类型(支持更复杂的应用场景)。 Redis 不仅仅支持简单的 k/v 类型的数据,同时还提供 list,set,zset,hash 等数据结构的存储。Memcached 只支持最简单的 k/v 数据类型。
- Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而 Memecache 把数据全部存在内存之中。
- Redis 有容灾机制, 可以把缓存中的数据持久化到磁盘上,而Memecache 没有安全机制。
- Redis 有虚拟内存机制, Redis 在服务器内存使用完之后,可以将不用的数据放到磁盘上。但是,Memcached 在服务器内存使用完之后,就会直接报异常。
- 分布式机制,Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是 Redis 目前是原生支持 cluster 模式的。
- Memcached 是多线程,非阻塞 IO 复用的网络模型;Redis 使用单线程的多路 IO 复用模型。(Redis 6.0 引入了多线程 IO )
- Redis 支持发布订阅模型、Lua 脚本、事务等功能,而 Memcached 不支持。并且,Redis 支持更多的编程语言。
- 过期策略,Memcached过期数据的删除策略只用了惰性删除,而 Redis 同时使用了惰性删除与定期删除。 :::
# 4、为什么要使用 Redis?/为什么要用缓存? 5️⃣
参考答案
一般来说,使用缓存无非就是为了解决高性能、高并发的问题。简单来说使用缓存主要是为了提升用户体验以及应对更多的用户。
- 高性能
- 由下图我们可以看到,如果缓存中存在数据,则可以不经过数据库直接获取数据,由于操作缓存就是直接操作内存,所以速度相当快。不过,要保持数据库和缓存中的数据的一致性,如果数据库中的对应数据改变的之后,需要及时同步到对应的缓存中。

- 由下图我们可以看到,如果缓存中存在数据,则可以不经过数据库直接获取数据,由于操作缓存就是直接操作内存,所以速度相当快。不过,要保持数据库和缓存中的数据的一致性,如果数据库中的对应数据改变的之后,需要及时同步到对应的缓存中。
- 高并发
- 一般像 MySQL 这类的数据库的 QPS 大概都在 1w 左右(4 核 8g) ,但是使用 Redis 缓存之后很容易达到 10w+,甚至最高能达到 30w+(就单机 redis 的情况,redis 集群的话会更高)。
QPS(Query Per Second):服务器每秒可以执行的查询次数;1
# 5、Redis 单线程模型
参考答案
# 6、为什么Redis是单线程,没有使用多线程?为什么不使用多线程?
参考答案
虽然说Redis 是单线程模型,但是, 实际上,Redis 在 4.0 之后的版本中就已经加入了对多线程的支持。
大体上来说,Redis 6.0 之前主要还是单线程处理。那么,Redis 使用单线程的主要原因官方也有过解释:
- 因为Redis 是基于内存的操作,CPU 不是Redis 的瓶颈,Redis 的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了。
# 7、Redis6.0 之后为何引入了多线程?
参考答案
Redis 为何引入多线程?
- Redis6.0 引入多线程主要是为了提高网络 IO 读写性能,因为Redis 的瓶颈主要受限于内存和网络。
- 虽然,Redis6.0 引入了多线程,但是 Redis 的多线程只是在网络数据的读写这类耗时操作上使用了,执行命令仍然是单线程顺序执行。因此,也不需要担心线程安全问题。
Redis 多线程的开启
- Redis6.0 的多线程默认是禁用的,只使用主线程。如需开启需要修改 redis 配置文件 redis.conf:
io-threads-do-reads yes1- 开启多线程后,还需要设置线程数,否则是不生效的。同样需要修改 redis 配置文件 redis.conf:
io-threads 4 #官网建议4核的机器建议设置为2或3个线程,8核的建议设置为6个线程1
# 8、缓存穿透 5️⃣
参考答案
# 什么是缓存穿透
缓存穿透是指查询一个key值 不存在的数据,由于缓存不命中时需要从数据库查询,这将导致这个不存在的数据每次请求都要到数据库去查询,根本没有经过缓存这一层,造成缓存穿透。

# 怎么解决缓存穿透
参数校验
- 首先,做好页面传入接口参数格式的校验。比如,手机号码位数、是否只包含数字,邮箱格式等;
- 其次,做好接口内部参数的校验,接口内部可以再次对参数格式、请求来源、请求频率等做校验。
缓存空对象(无效key)
- 如果缓存和数据库都查不到某个 key 的数据,那么就写一个到 Redis 中去并设置过期时间,具体命令如下:
SET key value EX 10086。这种方式可以解决请求的key 变化不频繁的情况,如果黑客恶意攻击,每次构建不同的请求key,会导致Redis 中缓存大量无效的key,会浪费大量的内存资源。 - 很明显,这种方案并不能从根本上解决此问题。如果非要用这种方式来解决穿透问题的话,尽量将无效的key 的过期时间设置短一点比如 1 分钟。
- 如果缓存和数据库都查不到某个 key 的数据,那么就写一个到 Redis 中去并设置过期时间,具体命令如下:
布隆过滤器
- 布隆过滤器:布隆过滤器(BloomFilter)是一个很长的二进制向量和一系列随机映射函数。布隆过滤器非常神奇的数据结构,可以用于检索一个元素是否在一个集合(海量数据)中。
- 具体实现:所有可能存在的请求的值都存放在布隆过滤器中,当用户请求过来,先判断用户发来的请求的值是否存在于布隆过滤器中。不存在的话,直接返回请求参数错误信息给客户端,存在的话才会走下面的流程。

- 注意:布隆过滤器是有一定的误判识别率和删除困难。
- 误判识别,简单来说就是,布隆过滤器说某个元素存在,小概率会误判。布隆过滤器说某个元素不在,那么这个元素一定不在。
- 为什么会出现误判的情况?要从布隆过滤器的原理来说!
- 我们先来看下,当一个元素加入布隆过滤器中的时候,会进行哪些操作:
- 使用布隆过滤器中的哈希函数对元素值进行计算,得到哈希值(有几个哈希函数得到几个哈希值);
- 根据得到的哈希值,在位数组中把对应下标的值置为 1。
- 我们再来看下,当我们需要判断一个元素是否存在于布隆过滤器的时候,会进行哪些操作:
- 使用布隆过滤器中的哈希函数对元素值进行计算,对给定元素再次进行相同的哈希计算;
- 得到值之后判断每位数组中的每个元素是否都为 1,如果值都为 1,那么说明这个值在布隆过滤器中,如果存在一个值不为 1,说明该元素不在布隆过滤器中。
- 值得注意的是,一定会出现这样一种情况:
不同的字符串可能哈希出来的位置相同。
:::
- 我们先来看下,当一个元素加入布隆过滤器中的时候,会进行哪些操作:
# 9、缓存雪崩 5️⃣
参考答案
# 什么是缓存穿透
缓存雪崩指缓存由于某些原因(比如:缓存过期、宕机、cache 服务挂了或者不响应)整体 crash 掉了,导致大量请求到达后端数据库,对数据库造成巨大压力,从而导致数据库崩溃,整个系统崩溃,发生灾难。
# 怎么解决缓存雪崩
- 加锁/队列(限流):降低直接访问数据库压力,避免数据库 down 机。
- 缓存标记:给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓存。
- 错开缓存过期时间:设置缓存时间错开,可以在设置过期时间的时候,加一个一定范围内的随机值来错开,避免同一时间给数据库造成巨大压力。
- 分布式数据库:缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
- 主从缓存:避免主缓存 down 机,从缓存能及时提供能力。
# 10、缓存预热5️⃣
参考答案
# 11、如何保证缓存和数据库数据的一致性?
参考答案
# 12、 Redis是如何判断数据是否过期?
参考答案
# 13、过期的数据的删除策略
参考答案
# 14、Redis 内存淘汰机制
参考答案
# 15、Redis 持久化机制(怎么保证 Redis 挂掉之后再重启数据可以进行恢复) 5️⃣
参考答案
# 16、Redis 事务
参考答案
# 参考学习
最近更新: 2025/03/05, 12:34:17