Redis面试题及详细答案100道(01-15) --- 基础认知篇
《前后端面试题
》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,MySQL,Linux… 。
文章目录
- 一、本文面试题目录
- 1. Redis是什么?主要应用场景有哪些?
- 2. Redis与Memcached的区别是什么?
- 3. Redis是多线程还是单线程?Redis 6.0之后为何引入了多线程?
- 4. Redis为什么是单线程的?单线程为何高效?
- 5. Redis的持久化机制有哪些?RDB和AOF的区别?
- 6. 如何配置Redis的RDB持久化?
- 7. 如何配置Redis的AOF持久化?
- 8. AOF重写(AOF Rewrite)的作用是什么?
- 9. Redis的过期键删除策略有哪些?
- 10. 解释Redis的LRU内存淘汰机制。
- 11. Redis的内存淘汰策略还有哪些?
- 12. Redis支持的最大内存是多少?如何设置?
- 13. Redis的性能如何?读写速度大概是多少?
- 14. Redis为什么要把所有数据放到内存中?
- 15. Redis通讯协议是什么?有什么特点?
- 二、100道面试题目录列表
一、本文面试题目录
1. Redis是什么?主要应用场景有哪些?
Redis(Remote Dictionary Server)是一个开源的、高性能的键值对存储数据库,它支持多种数据结构,如字符串(String)、哈希(Hash)、列表(List)、集合(Set)、有序集合(Sorted Set)等。Redis将数据存储在内存中,同时提供持久化机制,确保数据不会因重启而丢失。
主要应用场景:
- 缓存系统:作为数据库的缓存层,减少数据库访问压力,提高读取速度
- 会话存储:存储用户会话信息,支持分布式系统中的会话共享
- 实时排行榜:利用Sorted Set实现按分数排序的实时排行榜
- 计数器:如文章阅读量、视频播放次数等
- 消息队列:基于List实现简单的消息队列功能
- 地理位置服务:利用Geo类型实现附近的人、商户等功能
- 分布式锁:实现分布式系统中的资源竞争控制
- 限流:基于计数器或滑动窗口实现接口限流
示例:使用Redis作为缓存的伪代码
# 尝试从Redis获取数据
data = redis.get("user:1001")
if data is None:# 缓存未命中,从数据库获取data = db.query("SELECT * FROM users WHERE id=1001")# 存入Redis,设置过期时间redis.setex("user:1001", 3600, json.dumps(data))
# 使用数据
process_data(data)
2. Redis与Memcached的区别是什么?
Redis和Memcached都是内存数据库,但存在以下主要区别:
特性 | Redis | Memcached |
---|---|---|
数据结构 | 支持String、Hash、List、Set、Sorted Set等多种数据结构 | 仅支持简单的键值对(String) |
持久化 | 支持RDB和AOF两种持久化方式 | 不支持持久化 |
集群支持 | 原生支持集群功能 | 不原生支持集群,需依赖第三方 |
内存管理 | 可设置内存上限,采用多种淘汰策略 | 采用预分配内存池机制 |
多线程 | 核心操作单线程,6.0后IO多线程 | 多线程模型 |
网络模型 | 多路I/O复用 | 非阻塞IO |
适用场景 | 复杂数据结构操作、需要持久化、分布式场景 | 简单的键值缓存、高并发读写场景 |
适用场景总结:
- 当需要复杂数据结构、持久化或集群功能时,选择Redis
- 当只需要简单的键值缓存且追求极致性能时,可考虑Memcached
3. Redis是多线程还是单线程?Redis 6.0之后为何引入了多线程?
Redis的核心处理逻辑(命令执行)是单线程的,但在Redis 6.0版本之后,引入了多线程来处理网络IO操作。
Redis 6.0之前,所有操作(包括网络IO和命令执行)都由单线程完成。6.0版本之后,将网络IO操作(如接收客户端连接、读取请求、发送响应)交由专门的IO线程处理,而命令的执行仍然保持单线程。
引入多线程的原因:
- 性能瓶颈:随着网络速度提升,单线程处理网络IO逐渐成为Redis的性能瓶颈
- 充分利用多核CPU:现代服务器通常有多个CPU核心,单线程无法充分利用硬件资源
- 不影响核心设计:保持命令执行的单线程特性,避免了多线程带来的并发问题和锁竞争
多线程IO的工作流程:
- 主线程负责接收新连接,并将连接分配给IO线程
- IO线程负责读取客户端请求和发送响应
- 命令的解析和执行仍由主线程完成
这种设计既提高了IO处理效率,又保持了Redis单线程模型的简单性和安全性。
4. Redis为什么是单线程的?单线程为何高效?
Redis采用单线程模型处理命令执行,主要原因如下:
- 避免并发问题:单线程无需处理线程间同步和锁竞争,简化了设计和实现
- 内存操作:Redis的数据都在内存中,操作速度极快,多线程带来的 overhead 可能超过其带来的收益
- IO多路复用:通过IO多路复用技术处理大量客户端连接,减少了IO等待时间
单线程Redis依然高效的原因:
- 内存操作:所有数据都在内存中,读写操作不需要磁盘IO,速度极快
- 非阻塞IO:使用IO多路复用(epoll/kqueue等)处理多个客户端连接,避免了IO阻塞
- 高效数据结构:Redis的底层数据结构(如跳表、压缩列表等)设计高效,操作复杂度低
- 避免上下文切换:单线程避免了多线程间的上下文切换开销
- 精简的代码:Redis代码简洁高效,没有复杂的线程管理逻辑
虽然单线程,但Redis在普通服务器上也能轻松达到每秒十几万次的读写性能,满足大多数应用场景的需求。
5. Redis的持久化机制有哪些?RDB和AOF的区别?
Redis提供两种主要的持久化机制:
-
RDB(Redis Database):
- 原理:在指定的时间间隔内,将内存中的数据集快照写入磁盘
- 优点:
- 紧凑的单一文件,适合备份和灾难恢复
- 恢复速度比AOF快
- 对Redis性能影响较小,因为fork子进程进行持久化
- 缺点:
- 可能丢失最后一次快照后的所有数据
- fork子进程时,如果数据量大,可能导致短暂阻塞
-
AOF(Append Only File):
- 原理:记录每一个写操作命令到日志文件中,恢复时重新执行这些命令
- 优点:
- 数据安全性更高,支持多种同步策略(always、everysec、no)
- 日志文件是追加操作,不会损坏已有数据
- 支持重写机制,优化日志文件大小
- 缺点:
- 通常AOF文件比RDB文件大
- 恢复速度比RDB慢
- 某些同步策略可能影响性能
RDB和AOF的主要区别:
特性 | RDB | AOF |
---|---|---|
存储内容 | 数据集快照 | 写操作命令 |
文件大小 | 较小 | 较大 |
恢复速度 | 快 | 慢 |
数据安全性 | 可能丢失较多数据 | 可配置,最多丢失1秒数据 |
对性能影响 | 影响小(fork子进程) | 可能有影响(取决于同步策略) |
适用场景 | 备份、灾难恢复 | 对数据安全性要求高的场景 |
实际应用中,可以同时启用RDB和AOF,兼顾数据安全性和性能。
6. 如何配置Redis的RDB持久化?
Redis的RDB持久化可以通过配置文件(redis.conf)进行配置,主要配置项如下:
# 配置RDB文件名
dbfilename dump.rdb# 配置RDB文件存储路径
dir ./# 配置快照触发条件
# 格式:save <seconds> <changes>
# 意思是:在seconds秒内有changes次修改,则触发快照# 900秒(15分钟)内至少1个key被修改
save 900 1# 300秒(5分钟)内至少10个key被修改
save 300 10# 60秒(1分钟)内至少10000个key被修改
save 60 10000# 当RDB持久化出错时,是否停止接受写操作
stop-writes-on-bgsave-error yes# 是否对RDB文件进行压缩
rdbcompression yes# 是否对RDB文件进行校验
rdbchecksum yes
配置说明:
dbfilename
:指定RDB文件的名称,默认是dump.rdbdir
:指定RDB文件的存储路径save
:配置触发RDB快照的条件,可以配置多个条件stop-writes-on-bgsave-error
:当持久化出错时,是否停止接受写操作,建议设为yesrdbcompression
:是否压缩RDB文件,压缩会消耗CPU但节省空间rdbchecksum
:是否对RDB文件进行校验,校验会消耗CPU但提高数据完整性
除了通过配置文件自动触发,还可以通过命令手动触发RDB持久化:
# 同步执行,会阻塞Redis服务器
SAVE# 异步执行,fork子进程进行持久化,不阻塞服务器
BGSAVE
配置完成后,重启Redis服务使配置生效。
7. 如何配置Redis的AOF持久化?
AOF持久化默认是关闭的,需要在配置文件(redis.conf)中手动开启并配置:
# 开启AOF持久化,默认是no
appendonly yes# AOF文件名
appendfilename "appendonly.aof"# AOF文件存储路径,与RDB共用
dir ./# AOF同步策略
# appendfsync always # 每次写操作都同步到磁盘,最安全但性能最差
appendfsync everysec # 每秒同步一次,平衡安全性和性能,默认选项
# appendfsync no # 由操作系统决定何时同步,性能最好但安全性最差# AOF重写期间是否不进行同步
no-appendfsync-on-rewrite no# AOF重写触发条件
auto-aof-rewrite-percentage 100 # 当前AOF文件大小比上次重写后的大小增长了100%
auto-aof-rewrite-min-size 64mb # AOF文件最小重写大小# AOF文件损坏时的处理方式
aof-load-truncated yes # 加载时如果发现AOF文件尾部损坏,是否仍然加载并忽略损坏部分
配置说明:
appendonly
:设置为yes开启AOF持久化appendfilename
:指定AOF文件的名称appendfsync
:配置AOF同步策略,有三种选择:always
:每次写操作都同步到磁盘,最安全但性能影响最大everysec
:每秒同步一次,平衡安全性和性能no
:由操作系统决定何时同步,性能最好但安全性最低
auto-aof-rewrite-percentage
和auto-aof-rewrite-min-size
:共同决定AOF自动重写的触发条件
除了自动重写,还可以通过命令手动触发AOF重写:
# 手动触发AOF重写
BGREWRITEAOF
配置完成后,重启Redis服务使配置生效。如果需要在不重启的情况下开启AOF,可以使用如下命令:
# 动态开启AOF
CONFIG SET appendonly yes
# 保存配置到配置文件
CONFIG REWRITE
8. AOF重写(AOF Rewrite)的作用是什么?
AOF重写是Redis提供的一种优化AOF文件的机制,其主要作用如下:
-
减小AOF文件体积:
- AOF文件记录所有写操作,随着时间推移会变得越来越大
- 重写过程会生成一个新的AOF文件,只包含恢复当前数据集所需的最小命令集
- 例如:多次对同一个key的修改会被合并为一个命令
-
提高恢复速度:
- 较小的AOF文件在Redis重启时加载速度更快
- 优化后的命令序列执行效率更高
-
避免磁盘空间耗尽:
- 防止AOF文件无限制增长导致磁盘空间不足
AOF重写的原理:
- Redis会fork一个子进程负责重写工作,不影响主进程处理命令
- 子进程遍历内存中的数据集,为每个key生成相应的写命令
- 重写期间的新命令会被放入缓冲区,重写完成后会合并到新的AOF文件中
- 新文件生成后,会原子性地替换旧的AOF文件
AOF重写的触发方式:
- 自动触发:当满足配置的
auto-aof-rewrite-percentage
和auto-aof-rewrite-min-size
条件时 - 手动触发:执行
BGREWRITEAOF
命令
示例:
如果一个key经历了以下操作:
SET count 1
INCR count
INCR count
重写后,AOF文件中只会保留:
SET count 3
这大大减少了AOF文件的大小,同时不影响数据恢复的准确性。
No. | 大剑师精品GIS教程推荐 |
---|---|
0 | 地图渲染基础- 【WebGL 教程】 - 【Canvas 教程】 - 【SVG 教程】 |
1 | Openlayers 【入门教程】 - 【源代码+示例 300+】 |
2 | Leaflet 【入门教程】 - 【源代码+图文示例 150+】 |
3 | MapboxGL 【入门教程】 - 【源代码+图文示例150+】 |
4 | Cesium 【入门教程】 - 【源代码+综合教程 200+】 |
5 | threejs 【中文API】 - 【源代码+图文示例200+】 |
6 | Shader 编程 【图文示例 100+】 |
9. Redis的过期键删除策略有哪些?
Redis采用三种策略结合的方式来处理过期键:
-
惰性删除(Lazy Expiration):
- 原理:只有当访问某个key时,才会检查该key是否过期,如果过期则删除
- 优点:不需要额外的CPU资源来监控过期键,对CPU友好
- 缺点:可能会浪费内存空间,过期的key如果长期不被访问,会一直占用内存
-
定期删除(Periodic Expiration):
- 原理:Redis会定期(默认每100ms)执行一次过期键清理工作
- 具体过程:
- 从设置了过期时间的键中随机抽取一部分进行检查
- 删除其中已过期的键
- 如果删除的比例超过一定阈值(25%),则重复执行该过程
- 优点:平衡了内存和CPU资源,定期清理过期键
- 缺点:可能仍有部分过期键未被及时删除
-
内存淘汰机制(Memory Eviction):
- 原理:当Redis内存达到
maxmemory
限制时,会根据配置的淘汰策略删除部分键 - 作用:当内存不足时,确保Redis能继续提供服务
- 常用策略:LRU(最近最少使用)、LFU(最不经常使用)等
- 原理:当Redis内存达到
这三种策略的结合使用,使得Redis能够在CPU占用、内存占用和响应时间之间取得平衡:
- 惰性删除保证了过期键一定会被删除,只是可能会延迟
- 定期删除减少了过期键的堆积,降低了内存浪费
- 内存淘汰机制确保了Redis在内存不足时仍能正常工作
示例:设置键的过期时间
# 设置键expire_key的过期时间为60秒
SET expire_key "value"
EXPIRE expire_key 60# 或者在设置键时直接指定过期时间
SETEX expire_key 60 "value"
10. 解释Redis的LRU内存淘汰机制。
LRU(Least Recently Used,最近最少使用)是Redis中一种常用的内存淘汰策略,当Redis内存达到maxmemory
限制时,会根据此策略淘汰最近最少被访问的键,以释放内存空间。
LRU机制的工作原理:
- 核心思想:如果一个数据最近被访问过,那么它在将来被访问的概率也会更高
- 实现方式:Redis并没有为每个键维护完整的访问时间戳,而是采用一种近似LRU的算法:
- 每个键对象都有一个
lru
字段,记录最后一次被访问的时间戳 - 当需要淘汰键时,Redis会随机采样一部分键,从中选择
lru
值最小(即最近最少使用)的键进行淘汰
- 每个键对象都有一个
配置LRU淘汰策略:
在redis.conf中配置:
# 当内存达到maxmemory时,只淘汰设置了过期时间的键中最近最少使用的键
maxmemory-policy allkeys-lru# 其他相关LRU策略
# volatile-lru:只淘汰设置了过期时间的键中最近最少使用的键
# allkeys-lru:从所有键中淘汰最近最少使用的键
设置最大内存:
# 设置Redis最大使用内存为1GB
maxmemory 1gb
LRU淘汰机制的优缺点:
- 优点:实现相对简单,能有效保留热点数据,适合大多数缓存场景
- 缺点:
- 是一种近似算法,并非严格意义上的LRU
- 可能会淘汰一些偶尔被访问但未来可能被频繁访问的键
示例:LRU策略演示
1. SET a "valueA" # 访问a
2. SET b "valueB" # 访问b
3. GET a # 再次访问a,a的lru时间戳更新
4. SET c "valueC" # 访问c,此时内存达到上限
5. SET d "valueD" # 触发淘汰,最近最少使用的b会被淘汰
在第5步,由于内存已满,Redis会根据LRU策略淘汰最近最少使用的键b,因为a最近被访问过,c刚被访问,而b是最久未被访问的。
11. Redis的内存淘汰策略还有哪些?
除了LRU(最近最少使用),Redis还提供了多种内存淘汰策略,可通过maxmemory-policy
配置项设置:
-
基于过期时间的策略:
volatile-lru
:从设置了过期时间的键中,淘汰最近最少使用的键volatile-lfu
:从设置了过期时间的键中,淘汰最不经常使用的键volatile-ttl
:从设置了过期时间的键中,淘汰剩余生存时间(TTL)最短的键volatile-random
:从设置了过期时间的键中,随机淘汰一个键
-
所有键的策略:
allkeys-lru
:从所有键中,淘汰最近最少使用的键allkeys-lfu
:从所有键中,淘汰最不经常使用的键allkeys-random
:从所有键中,随机淘汰一个键
-
特殊策略:
noeviction
:不淘汰任何键,当内存不足时,拒绝所有写操作并返回错误(默认策略)
各种策略的适用场景:
volatile-lru
:适合需要为不同键设置不同过期时间的场景allkeys-lru
:适合所有键的访问概率分布不均的场景,希望保留热点数据volatile-lfu
/allkeys-lfu
:适合需要根据访问频率而非最近访问时间来保留数据的场景volatile-ttl
:适合希望优先保留生存时间较长的键的场景random
类策略:适合键的访问概率分布均匀的场景noeviction
:适合不允许数据丢失的场景,内存不足时需要人工干预
配置示例:
# 设置内存淘汰策略为allkeys-lfu
maxmemory-policy allkeys-lfu
动态修改策略(无需重启Redis):
CONFIG SET maxmemory-policy volatile-lru
选择合适的淘汰策略需要根据具体的业务场景和数据访问模式来决定,通常建议先进行测试,选择最适合当前应用的策略。
12. Redis支持的最大内存是多少?如何设置?
Redis理论上支持的最大内存取决于操作系统的位数和可用内存:
- 32位操作系统:最大支持约4GB内存
- 64位操作系统:理论上没有内存限制,仅受限于实际可用的物理内存和操作系统的内存管理
在实际应用中,Redis的内存设置需要考虑以下因素:
- 服务器的总内存大小
- 其他进程需要的内存
- 操作系统缓存所需的内存
- Redis持久化(RDB/AOF)过程中可能需要的额外内存
设置Redis最大内存的方法:
- 通过配置文件(redis.conf)设置:
# 设置最大内存为1GB
maxmemory 1gb# 或者使用其他单位
maxmemory 1024mb # 1024兆字节
maxmemory 1073741824 # 1GB(以字节为单位)
- 动态设置(无需重启Redis):
# 登录Redis客户端
redis-cli# 设置最大内存为2GB
CONFIG SET maxmemory 2gb# 查看当前最大内存设置
CONFIG GET maxmemory
当Redis内存达到maxmemory
限制时,会根据配置的maxmemory-policy
淘汰策略来处理新的写操作:
- 如果策略是
noeviction
(默认),则拒绝新的写操作并返回错误 - 其他策略会根据相应规则淘汰部分键,为新数据腾出空间
建议设置:
- 对于专用Redis服务器,可设置为物理内存的70%-80%
- 对于与其他服务共享的服务器,需要根据实际情况调整,确保其他服务有足够的内存
设置最大内存的目的是:
- 防止Redis耗尽服务器所有内存,导致操作系统swap或OOM(Out Of Memory)
- 控制内存使用成本
- 配合内存淘汰策略,实现缓存的自动管理
13. Redis的性能如何?读写速度大概是多少?
Redis以高性能著称,其性能特点如下:
-
读写速度:
- 单节点Redis在普通服务器上,每秒可处理约10万-100万次读写操作
- 具体性能取决于数据大小、操作类型、服务器配置和网络环境
- 简单的get/set操作速度通常在每秒10万次以上
- 复杂操作(如集合交集、排序等)速度会有所下降
-
性能优势来源:
- 内存存储:所有数据都在内存中,避免了磁盘IO的延迟
- 单线程模型:避免了多线程的上下文切换和锁竞争
- 高效数据结构:针对不同场景优化的数据结构(如跳表、压缩列表等)
- IO多路复用:高效处理大量并发连接
- 精简的代码:优化的C语言实现,减少了不必要的开销
-
性能测试:
可以使用Redis自带的redis-benchmark
工具进行性能测试:# 测试100个并发连接,100000个请求的性能 redis-benchmark -c 100 -n 100000
典型的测试结果可能如下:
SET: 120481.93 requests per second GET: 131578.95 requests per second INCR: 129870.13 requests per second
-
影响性能的因素:
- 数据大小:大数据块的操作会比小数据块慢
- 操作复杂度:简单操作(如GET)比复杂操作(如SORT)快
- 网络延迟:远程访问会受网络速度影响
- 持久化配置:AOF的
always
同步策略会显著影响写性能 - 内存使用:当内存接近满时,可能会触发内存淘汰,影响性能
实际应用中,通过合理的架构设计(如集群、读写分离),Redis可以支持更高的并发量,满足大多数高负载场景的需求。
14. Redis为什么要把所有数据放到内存中?
Redis将所有数据存储在内存中的主要原因如下:
-
追求极致性能:
- 内存访问速度远快于磁盘(约10万倍)
- 避免了磁盘IO的延迟,能提供毫秒级甚至微秒级的响应时间
- 适合作为高性能缓存和实时数据处理系统
-
简化设计:
- 内存中的数据可以直接操作,无需复杂的磁盘存储管理
- 可以使用更高效的数据结构(如跳表、哈希表),无需考虑磁盘存储格式
- 简化了并发控制,单线程模型即可支持高并发
-
支持复杂数据结构:
- 内存中的数据可以方便地组织成各种复杂数据结构(List、Set、Sorted Set等)
- 这些数据结构支持丰富的操作,如集合交集、排序等,在磁盘上实现这些操作会非常复杂和低效
-
适用场景特性:
- Redis主要用于缓存、计数器、实时排行榜等场景,这些场景都要求快速响应
- 这些场景通常可以容忍一定的数据丢失,或者有其他数据源可以恢复数据
虽然Redis将数据存储在内存中,但通过以下机制保证了数据的持久性:
- RDB:定期将内存数据快照写入磁盘
- AOF:记录所有写操作,重启时重新执行这些操作恢复数据
这种内存存储+持久化的设计,使得Redis既能提供高性能,又能保证数据不会完全丢失,兼顾了性能和可靠性的需求。
15. Redis通讯协议是什么?有什么特点?
Redis使用的是一种名为RESP(Redis Serialization Protocol,Redis序列化协议)的专门设计的通讯协议。RESP在Redis 1.2版本中引入,并在Redis 2.0成为与客户端通信的标准协议。
RESP的主要特点:
-
简单易读:
- 协议格式简单,人类可直接阅读和理解
- 基于文本,使用换行符分隔不同元素
-
高效解析:
- 客户端和服务器都能快速解析该协议
- 解析逻辑简单,减少了CPU开销
-
支持多种数据类型:
- 支持字符串、错误、整数、数组和批量字符串
- 每种类型有明确的标识:
- 简单字符串:以
+
开头 - 错误:以
-
开头 - 整数:以
:
开头 - 批量字符串:以
$
开头,后跟长度 - 数组:以
*
开头,后跟元素数量
- 简单字符串:以
-
无状态:
- 协议本身是无状态的,每次请求都是独立的
- 便于客户端和服务器的实现
RESP协议示例:
- 客户端发送命令(如
SET mykey "hello"
):
*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$5\r\nhello\r\n
解析:
*3
表示这是一个包含3个元素的数组$3\r\nSET
表示一个长度为3的字符串"SET"$5\r\nmykey
表示一个长度为5的字符串"mykey"$5\r\nhello
表示一个长度为5的字符串"hello"
- 服务器响应(成功设置键):
+OK\r\n
这是一个简单字符串"OK"
- 客户端发送
GET mykey
命令:
*2\r\n$3\r\nGET\r\n$5\r\nmykey\r\n
- 服务器响应:
$5\r\nhello\r\n
这是一个长度为5的批量字符串"hello"
RESP协议的设计平衡了可读性和效率,使得Redis客户端的实现变得简单,同时不会对性能造成太大影响。目前几乎所有的Redis客户端都支持RESP协议。
二、100道面试题目录列表
文章序号 | Redis面试题100道 |
---|---|
1 | Redis面试题及详细答案100道(01-15) |
2 | Redis面试题及详细答案100道(16-32) |
3 | Redis面试题及详细答案100道(33-48) |
4 | Redis面试题及详细答案100道(49-60) |
5 | Redis面试题及详细答案100道(61-70) |
6 | Redis面试题及详细答案100道(71-85) |
7 | Redis面试题及详细答案100道(86-100) |