Redis经典面试题总结分享
文章目录
- 一、常见面试题分享
- 1.Redis优点和缺点
- 2.Redis有哪些数据删除策略?
- 3.Redis中有哪些数据淘汰策略?
- 4.Redis中数据库默认是多少个db即作用?
- 5.缓存穿透、缓存击穿、缓存雪崩解决方案?
- 6.Redis数据持久化有哪些方式?各自有什么优缺点?
- 7.Redis都存在哪些集群方案?
- 8.Redis哈希槽的概念?
- 9.谈谈你对Redis中事务的理解?
- 10.为什么Redis不支持事务回滚?
- 11.Redis事务相关的命令有哪几个?
- 12.Redis是单线的,但是为什么还那么快?
- 13.Redis怎么实现分布式锁思路?
- 14.Redis实现分布式锁如何防止死锁现象?
- 15.Redis实现分布式锁如何合理的控制锁的有效时长?
- 16. 为什么要在多个实例上加锁?
一、常见面试题分享
1.Redis优点和缺点
优点:
- 本质上是一个 Key-Value 类型的内存数据库
- 整个数据库加载在内存当中进行操作,定期通过异步操作把数据库数据 flush 到硬盘上进行保存
- 因为是纯内存操作,Redis 的性能非常出色,每秒可以处理超过 10 万次读写操作,是已知性能最快的Key-Value DB
- 支持保存多种数据结构(string,list,set,hash,sortedset),单个 value 的最大限制是 1GB,
- Redis也可以对存入的 Key-Value 设置 expire 时间
缺点:
- Redis 的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写
2.Redis有哪些数据删除策略?
简介:
Redis中可以对数据设置数据的有效时间,数据的有效时间到了以后,就需要将数据从内存中删除掉。而删除的时候就需要按照指定的规则进行删除,这种删除规则就被称之为数据的
删除策略
。
定时删除
- 概述:在设置某个key 的过期时间同时,我们创建一个定时器,让定时器在该过期时间到来时,立即执行对其进行删除的操作。
优点
:定时删除对内存是最友好的,能够保存内存的key一旦过期就能立即从内存中删除。缺点
:对CPU最不友好,在过期键比较多的时候,删除过期键会占用一部分CPU时间,对服务器的
响应时间和吞吐量造成影响。
惰性删除
- 概述:设置该key过期时间后,我们不去管它,当需要该key时,我们在检查其是否过期,如果过期,我们就删掉它,反之返回该key。
优点
:对CPU友好,我们只会在使用该键时才会进行过期检查,对于很多用不到的key不用浪费时
间进行过期检查缺点
:对内存不友好,如果一个键已经过期,但是一直没有使用,那么该键就会一直存在内存中,
如果数据库中有很多这种使用不到的过期键,这些键便永远不会被删除,内存永远不会释放。
定期删除
- 概述:每隔一段时间,我们就对一些key进行检查,删除里面过期的key(从一定数量的数据库中取
出一定数量的随机键进行检查,并删除其中的过期键)。 优点
:可以通过限制删除操作执行的时长和频率来减少删除操作对 CPU 的影响。另外定期删除,
也能有效释放过期键占用的内存。- 缺点:难以确定删除操作执行的时长和频率。如果执行的太频繁,定期删除策略变得和定时删除策略一样,对CPU不友好。如果执行的太少,那又和惰性删除一样了,过期键占用的内存不会及时得
到释放。
注意
另外最重要的是,在获取某个键时,如果某个键的过期时间已经到了,但是还没执行定期删除,那么就会返回这个键的值,这是业务不能忍受的错误。
Redis的过期删除策略:惰性删除 + 定期删除两种策略进行配合使用定期删除函数的运行频率
在Redis2.6版本中,规定每秒运行10次,大概100ms运行一次。在Redis2.8版本后,可以通过修改配置文件redis.conf 的 hz 选项来调整这个次数。
3.Redis中有哪些数据淘汰策略?
简介:
数据的淘汰策略:当Redis中的内存不够用时,此时在向Redis中添加新的key,那么Redis就会按照某一种规则将内存中的数据删除掉,这种数据的删除规则被称之为内存的淘汰策略。
常见的数据淘汰策略:
noeviction # 不删除任何数据,内存不足直接报错(默认策略)
volatile-lru # 挑选最近最久使用的数据淘汰(举例:key1是在3s之前访问的, key2是在9s之前访问的,删除的就是key2)
volatile-lfu # 挑选最近最少使用数据淘汰 (举例:key1最近5s访问了4次, key2最近5s访问了9次, 删除的就是key1)
volatile-ttl # 挑选将要过期的数据淘汰
volatile-random # 任意选择数据淘汰
allkeys-lru # 挑选最近最少使用的数据淘汰
allkeys-lfu # 挑选最近使用次数最少的数据淘汰
allkeys-random # 任意选择数据淘汰,相当于随机
注意:
- 不带allkeys字样的淘汰策略是随机从Redis中选择指定的数量的key然后按照对应的淘汰策略进行删除,带allkeys是对所有的key按照对应的淘汰策略进行删除。
4.Redis中数据库默认是多少个db即作用?
- Redis默认支持16个数据库,可以通过配置databases来修改这一数字。客户端与Redis建立连接后会自动选择0号数据库,不过可以随时使用select命令更换数据库。
- Redis支持多个数据库,并且每个数据库是隔离的不能共享,并且基于单机才有,如果是集群就没有数据库的概念。
5.缓存穿透、缓存击穿、缓存雪崩解决方案?
缓存穿透:
概述:指查询一个一定不存在的数据,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到 DB 去查询,可能导致 DB 挂掉。
解决方案
:
- 查询返回的数据为空,仍把这个空结果进行缓存,但过期时间会比较短
- 布隆过滤器:将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对DB的查询
缓存击穿:
概述:对于设置了过期时间的key,缓存在某个时间点过期的时候,恰好这时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端 DB 加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把 DB 压垮。
解决方案
:
- 使用互斥锁:当缓存失效时,不立即去load db,先使用如 Redis 的 setnx 去设置一个互斥锁,当操作成功返回时再进行 load db的操作并回设缓存,否则重试get缓存的方法
缓存雪崩:
概述:设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB 瞬时压力过重雪崩。与缓存击穿的区别:雪崩是很多key,击穿是某一个key缓存。
解决方案
:
将缓存失效时间分散开,比如可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件
6.Redis数据持久化有哪些方式?各自有什么优缺点?
在Redis中提供了两种数据持久化的方式:RDB 和 AOF
RDB:
定期更新,定期将Redis中的数据生成的快照同步到磁盘等介质上,磁盘上保存的就是Redis的内存快照
save [seconds] [changes]
save 60 100
save 600 500
优点
:
- 对性能影响最小。如前文所述,Redis在保存RDB快照时会fork出子进程进行,几乎不影响Redis处理客户端请求的效率。
- 每次快照会生成一个完整的数据快照文件,所以可以辅以其他手段保存多个时间点的快照(例如把每天0点的快照备份至其他存储媒介中),作为非常可靠的灾难恢复手段。
- 使用RDB文件进行数据恢复比使用AOF要快很多。
缺点
:
- 快照是定期生成的,所以在Redis crash时或多或少会丢失一部分数据
- 如果数据集非常大且CPU不够强(比如单核CPU),Redis在fork子进程时可能会消耗相对较长的时间,影响Redis对外提供服务的能力
AOF:
采用AOF持久方式时,Redis会把每一个写请求都记录在一个日志文件里。在Redis重启时,会把AOF文件中记录的所有写操作顺序执行一遍,确保数据恢复到最新。
AOF默认是关闭的,如要开启,进行如下配置:
appendonly yes
AOF提供了三种fsync配置:always/everysec/no,通过配置项[appendfsync]指定:
- appendfsync no:不进行fsync,将flush文件的时机交给OS决定,速度最快
- appendfsync always:每写入一条日志就进行一次fsync操作,数据安全性最高,但速度最慢
- appendfsync everysec:折中的做法,交由后台线程每秒fsync一次
优点
:
4. 最安全,在启用appendfsync为always时,任何已写入的数据都不会丢失,使用在启用appendfsync everysec也至多只会丢失1秒的数据
5. AOF文件在发生断电等问题时也不会损坏,即使出现了某条日志只写入了一半的情况,也可以使用redis-check-aof工具轻松修复
6. AOF文件易读,可修改,在进行某些错误的数据清除操作后,只要AOF文件没有rewrite,就可以把AOF文件备份出来,把错误的命令删除,然后恢复数据。
缺点
:
- AOF文件通常比RDB文件更大
- 性能消耗比RDB高
- 数据恢复速度比RDB慢
Redis的数据持久化工作本身就会带来延迟,需要根据数据的安全级别和性能要求制定合理的持久化策略:
- AOF + fsync always的设置虽然能够绝对确保数据安全,但每个操作都会触发一次fsync,会对Redis的性能有比较明显的影响
- AOF + fsync every second是比较好的折中方案,每秒fsync一次
- AOF + fsync never会提供AOF持久化方案下的最优性能
使用RDB持久化通常会提供比使用AOF更高的性能,但需要注意RDB的策略配置
7.Redis都存在哪些集群方案?
主从复制
- 保证高可用性
- 实现故障转移需要手动实现
- 无法实现海量数据存储
哨兵模式 - 保证高可用性
- 可以实现自动化的故障转移
- 无法实现海量数据存储
Redis分片集群 - 保证高可用性
- 可以实现自动化的故障转移
- 可以实现海量数据存储
8.Redis哈希槽的概念?
Redis 集群没有使用一致性 hash,而是引入了哈希槽的概念,Redis 集群有 16384 个哈希槽,每个 key通过 CRC16 校验后对 16384 取模来决定放置哪个槽,集群的每个节点负责一部分 hash 槽。
9.谈谈你对Redis中事务的理解?
事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
Redis中的事务:Redis事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
总结说:Redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。Reids中,单条命令是原子性执行的
,但事务不保证原子性,且没有回滚
10.为什么Redis不支持事务回滚?
多数事务失败是由语法错误或者数据结构类型错误导致的,语法错误说明在命令入队前就进行检测的,而类型错误是在执行时检测的,Redis为提升性能而采用这种简单的事务,这是不同于关系型数据库的,特别要注意区分。Redis之所以保持这样简易的事务,完全是为了保证高并发下的核心问题——性能。
11.Redis事务相关的命令有哪几个?
- MULTI:用来组装一个事务
- EXEC:执行一个事物
- DISCARD:取消一个事务
- WATCH:用来监视一些key,一旦这些key在事务执行之前被改变,则取消事务的执行
- UNWATCH:取消 WATCH 命令对所有key的监视
12.Redis是单线的,但是为什么还那么快?
- 完全基于内存的
- 采用单线程,避免不必要的上下文切换可竞争条件
- 数据简单,数据操作也相对简单
- 使用多路I/O复用模型,非阻塞IO
13.Redis怎么实现分布式锁思路?
Redis实现分布式锁主要利用Redis的setnx命令。setnx是SET if not exists(如果不存在,则 SET)的简写。
127.0.0.1:6379> setnx lock value1 #在键lock不存在的情况下,将键key的值设置为value1
(integer) 1
127.0.0.1:6379> setnx lock value2 #试图覆盖lock的值,返回0表示失败
(integer) 0
127.0.0.1:6379> get lock #获取lock的值,验证没有被覆盖
"value1"
127.0.0.1:6379> del lock #删除lock的值,删除成功
(integer) 1
127.0.0.1:6379> setnx lock value2 #再使用setnx命令设置,返回0表示成功
(integer) 1
127.0.0.1:6379> get lock #获取lock的值,验证设置成功
"value2"
上面这几个命令就是最基本的用来完成分布式锁的命令。
加锁:使用 setnx key value 命令,如果key不存在,设置value(加锁成功)。如果已经存在lock(也就是有客户端持有锁了),则设置失败(加锁失败)。
解锁:使用 del 命令,通过删除键值释放锁。释放锁之后,其他客户端可以通过 setnx 命令进行加锁。
14.Redis实现分布式锁如何防止死锁现象?
产生死锁的原因:如果一个客户端持有锁的期间突然崩溃了,就会导致无法解锁,最后导致出现死锁的现象。
所以要有个超时的机制,在设置key的值时,需要加上有效时间,如果有效时间过期了,就会自动失效,就不会出现死锁。
15.Redis实现分布式锁如何合理的控制锁的有效时长?
有效时间设置过长,假如我的业务操作比有效时间长?我的业务代码还没执行完就自动给我解锁了,不就完蛋了吗。
解决方案
:
- 自己去把握,预估一下业务代码需要执行的时间,然后设置有效期时间比执行时间长
一些,保证不会因为自动解锁影响到客户端业务代码的执行。 - 给锁续期。锁续期实现思路:当加锁成功后,同时开启守护线程,默认有效期是用户所设置的,然后每隔10秒就会给锁续期到用户所设置的有效期,只要持有锁的客户端没有宕机,就能保证一直持有锁,直到业务代码执行完毕由客户端自己解锁,如果宕机了自然就在有效期失效后自动解锁。
16. 为什么要在多个实例上加锁?
本质上是为了【容错】, 部分实例异常宕机,剩余的实例加锁成功,整个锁服务依旧可用。