Redis基础面试整理
2023-02-06 16:07:31 24 举报
AI智能生成
Redis基础面试整理
作者其他创作
大纲/内容
Redis 为什么这么快?
基于内存操作
数据类型丰富且结构简单
v6.0 之前使用单线程
v6.0 之后使用了多线程
采用多路I/O 复用模型(非阻塞IO)
底层模型(实现方式和通信应用协议不一样),自己构建了VM机制
Redis 有哪些数据类型(对应的应用场景)?
String
简单的k-v缓存
List
列表,粉丝列表,评论列表
Set
交集,并集,差集操作
Zset
去重排序
Hash
结构化的数据,对象
Redis 的应用场景?
计数器
缓存
消息队列
分布式锁
Redis 持久化?
机制
AOF
数据安全性高(可以配置没进行一次命令操作就记录到aof 文件中一次)
append 模式写文件,即使中途服务器宕机,可以通过 redis-check-aof 工具解决数据一致性问题。
AOF 机制的 rewrite 模式
AOF 文件比 RDB 文件大,且恢复速度慢。
数据集大的时候,比 rdb 启动效率低。
RDB
默认持久化机制
只有一个文件dump.rdb
性能最大化(fork 子进程来完成写操作,让主进程继续处理命令,IO 最大化,保证了Redis 的高性能)
数据安全性低(间隔一段时间才进行持久化,持久化期间发生故障,会导致数据丢失)
区别
AOF 文件比RDB 更新频率高,优先使用AOF 还原数据。
AOF 比RDB 更安全也更大
RDB 性能比AOF 好
如果两个都配了优先加载AOF
如何选择?
想确保数据安全,应该同时使用两种持久化功能
可以接受部分数据丢失,就可以只使用RDB 持久化
如果只希望数据在服务器运行时存在,可以不使用任何持久化
Redis 过期键的删除策略?
定时过期
设定过期时间,过期立即删除(对内存友好,占用CPU 资源)
惰性过期
访问key,在判断是否过期,过期则删除(节省CPU 资源,但是对内存不友好)
定期过期
隔一定时间,扫描一定数量的数据库的expires字典中一定数量的key,清除过期的(上面两者的折中方案)
Redis 内存淘汰策略?
全局的键空间选择性移除
noeviction:内存不足,写入报错
allkeys-lru:内存不足,移除最近最少使用的key(最常用)
allkeys-lfu:内存不足,移除最近最不频繁使用的键值对
allkeys-random:内存不足,随机移除一个key
设置过期时间的键空间选择性移除
volatile-lru:内存不足,在设置了过期时间的键空间中,移除最近最少使用的key
volatile-lfu:在设置了过期时间的键空间中,移除最近最不频繁使用的键值对
volatile-random:内存不足,在设置了过期时间的键空间中,随机移除一个key
volatile-ttl:内存不足,在设置了过期时间的键空间中,最早过期的key优先移除
Redis 线程模型?
基于Reactor 模式开发了网络事件处理器(文件事件处理器)
组成结构
多个套接字
IO多路复用程序
文件事件分派器
事件处理器
文件事件分派器队列的消费是单线程的
文件事件处理器 使用 I/O 多路复用程序 来同时监听 多个套接字,并且根据套接字目前执行的任务来为套接字关联不同的 事件处理器
被监听的 套接字 准备好执行 连接答应(accept)、读取(read)、写入(write)、关闭(close)等操作时,与操作相对应的 文件事件 就会产生,这个时候 文件事件处理器 就会调用套接字之前关联好的 事件处理器 来处理这些事件
Redis 事务?
基础概念
是一次性、顺序性、排他性的执行一个队列中的一系列命令。(通过MULTI、EXEC、DISCARD和WATCH 四个原语实现的)
MULTI
开启一个事务
EXEC
提交事务
DISCARD
放弃一个事务
WATCH
测试一个或者多个键的值在事务执行期间是否发生变化,如果发生变化,当前事务放弃执行
事务阶段
事务开始MULTI
命令入队
事务执行EXEC
事务管理(ACID)
支持隔离性
支持一致性
在AOF 持久化模式下,并且appendfsync 选项的值是always 时,也具有持久性
不保证原子性
基于Lua 脚本,Redis 可以保证脚本内命令的一次性,按顺序执行,但同时也不提供事务运行错误的回滚。
Redis 缓存异常?
穿透?
概念
指一个一定不存在的数据,由于缓存未命中这条数据,就会去查询数据库,数据库也没有这条数据,返回 null 。如果每次都查询都走数据库,缓存就失去了意义,就像“穿透”了缓存一样(利用不存在的数据攻击,导致数据库压力增大,最终导致系统崩溃)
解决方案
对 null 进行缓存
使用布隆过滤器(bitmap)做过滤,迅速判断数据是否存在,避免一直从数据库中查询
前端进行请求校验,过滤恶意请求
雪崩?
概念
缓存多条数据时,采用了相同的过期时间,当缓存同时过期时,大量的请求进来了,查询不到缓存,直接去查询数据库,导致数据库压力增大,最终导致“雪崩”(系统崩溃)
解决方案
在原有的过期时间基础上添加一个随机值,降级过期时间重复率,避免大面积失效
Redis 高可用,主从 + 哨兵, Redis cluster 避免全盘崩溃
限流
热点key 识别 + 本地缓存
singleflight 降低缓存并发重复读
击穿?
概念
某个 key 设置了过期时间,但在正好失效的时候,有大量请求进来了,导致请求都到数据库查询了。就像“把一面墙击穿了一个洞”
解决方案
如果缓存的数据是 基本不会发生变化(更新)的,那么可以尝试将该数据设置为永不过期
如果缓存的数据是 更新不频繁,且 缓存刷新 的整个流程 耗时较少 的情况下,可以采用基于Redis ,Zk 等分布式中间件的分布式互斥锁,或者本地互斥锁以保证仅少量的请求能请求数据库并 重新构建缓存,其余线程则可以在锁释放后能访问到新缓存
如果缓存的数据是 更新频繁 或者在 缓存刷新 的整个流程 耗时较长 的情况下,可以利用 定时线程(任务) 在 缓存过期前 主动的重新构建缓存或者延长过期时间,以确保所有的请求能一直访问到对应的缓存
单系统Redis 如何实现并发控制?
单命令操作
就是一个命令中带有多个数据操作,比如:读取数据、数据增减、写回数据三个操作。如 INCR/DECR 命令可以对数据进行增值 / 减值操作。
但是当我们需要对读取的数据做更多判断,或者是我们对数据的修改不是简单的增减时,单命令操作就不适用了。
Lua 脚本
Redis 的 Lua 脚本可以包含多个操作,这些操作都会以原子性的方式执行,绕开了单命令操作的限制。
但是如果把很多操作都放在 Lua 脚本中原子执行,会导致 Redis 执行脚本的时间增加,同样也会降低 Redis 的并发性能。所以,在编写 Lua 脚本时,避免把不需要做并发控制的操作写入脚本中。
Redis 内存容量增加后带来的问题?
内存快照RDB 生产和恢复效率低
主从节点全量同步时长增加,缓冲区易溢出
Redis 慢查询?
缓存和数据库一致性问题?
只读缓存的方案
有数据新增时,直接写入数据库
有数据改动时
先删除缓存,在更新数据库
无并发请求
缓存删除成功,但是数据库更新失败,导致从数据库读取旧数据
重试数据库更新
有并发请求
缓存删除成功后,尚未更新数据库,有并发读请求,并发请求从数据库读取到旧数据,并且更新到缓存,导致后面的请求都读取到旧数据
延迟双删
先删除缓存值再更新数据库,有可能导致请求因缓存缺失而访问数据库,给数据库带来压力;如果业务应用中读取数据库和写缓存的时间不好估算,那么,延迟双删中的等待时间就不好设置。
先更新数据库,在删除缓存
无并发请求
数据库更新成功,但是缓存删除失败,请求从缓存中读取到旧数据
重试缓存删除
有并发请求
数据库更新成功后,尚未删除缓存,有并发读请求,并发请求从缓存中读取到旧数据
等待缓存删除完成期间可能会存在不一致数据的短暂存在
收藏
0 条评论
下一页
为你推荐
查看更多