id|名称|地址 :---:|:---:|:---: 1|Java面试题整理--2021|https://www.kuangstudy.com/bbs/1435775483164753922 2|Spring常见面试问题整理---2021|https://www.kuangstudy.com/bbs/1436221935506710530 3|Mysql常见面试题整理--2021|https://www.kuangstudy.com/bbs/1443509349690408962 4|SpringMVC常见面试题整理--2021|https://www.kuangstudy.com/bbs/1448083936524660737 5|Mybatis常见面试题整理--2021|https://www.kuangstudy.com/bbs/1447483689515671554 6|Springboot常见面试题整理--2021|https://www.kuangstudy.com/bbs/1448817680281792514 7|Springcloud常见面试题整理--2021|https://www.kuangstudy.com/bbs/1449898142534328322
1.什么是Redis?Redis有什么优点?======被问+1
-
Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI?C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。是一个NoSQL数据库,也被称为结构化数据库
-
优点:
-
速度快:因为数据存在内存中,类似于 HashMap ,HashMap 的优势就是查找和操作的时间复杂度都是O (1);纯内存操作,Redis 的性能非常出色,每秒可以处理超过 10 万次读写操作,是已知性能最快的 Key-Value 数据库.
-
支持丰富数据类型:支持 String ,List,Set,Sorted Set,Hash,当然还有几个Geospatial,Hyperloglog,Bitmap
-
丰富的特性:
-
订阅发布 Pub / Sub 功能
-
Key 过期策略
-
事务,计数,等等
-
-
持久化存储:Redis 提供 RDB 和 AOF 两种数据的持久化存储方案,解决内存数据库最担心的万一 Redis 挂掉,数据会消失掉
2.Redis为什么这么快?======+1
-
采用了多路复用io阻塞机制
-
数据结构简单,操作节省时间
-
运行在内存中,所以速度快
-
采用单线程,避免了不必要的上下文切换和竞争条件,安全;也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗
3.Redis有哪些数据类型?======+1
-
String
-
List
-
Set
-
Hash
-
Zset
-
Geospatial
-
Hyperloglog
-
Bitmap
4.Zset数据类型使用场景?======+1
- 常用于排行榜,视频点赞排行,关注排行
5.String类型的Value最大可以容纳数据长度是多少?======+1
- Redis中字符串类型的Value最多可以容纳的数据长度是512M
6.Redis 的持久化机制是什么?各自的优缺点?======+1
-
Redis持久化机制有RDB和AOF
-
RDB持久化机制:
-
在指定的时间间隔将内存中的数据集快照写入磁盘,他恢复时是将快照文件直接读到内存里,Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好了的文件,整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能,如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高校,RDB的缺点是最后一次持久化后的数据可能丢失,我们默认的就是RDB,一般情况下不需要修改这个配置
-
触发机制:
-
Save规则满足的情况下,会自动触发rdb文件
-
执行命令flushall命令,也会自动触发rdb文件
-
退出redis也会产生rdb文件
-
备注就自动生成一个dump.rdb文件
-
-
如何修复rdb文件:
- 只需要将rdb文件放在我们redis启动目录就可以,redis启动的时候就会自动检查dump.rdb恢复其中的数据(config get dir 命令可以查看redis的启动目录)
-
优点:
- 适合大规模的数据恢复(如果你对数据完整性要求不高)
-
缺点:
-
需要一定的时间间隔进程操作,如果redis意外宕机了,这个最后一次修改数据就没有了
-
Fork进程的时候,会占用一定的内容空间
-
- AOF持久化机制:
-
以日志的形式来记录每一个写的操作,将Redis执行过程中的所有指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之处会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写的指令从前到后执行一次以完成数据的恢复工作
-
比如aof文件被破坏了我们可以使用redis-check-aof --fix appendonly.aof命令去修复它
-
优点:
-
每一次修改都同步,这样会使文件完整性更好
-
默认每秒同步一次,可能会丢失一秒的数据
-
如果选择不同步的话,效率最高
-
-
缺点:
-
相对于数据文件来说,aof远远大于rdb,修复的速度也比rdb慢
-
Aof的运行效率也比rdb慢,所以redis默认是rdb
-
PS:注意点:如果aof文件大于配置的64m的时候,就会fork一个新的进程来将我们的文件进行重写
6.如何选择合适的持久化方式?
-
一般来说, 如果想达到足以媲美PostgreSQL的数据安全性, 你应该同时使用两种持久化功能
-
如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失,那么你可以只使用RDB持久化
7.Redis持久化数据和缓存怎么做扩容?
-
如果Redis被当做缓存使用,使用一致性哈希实现动态扩容缩容
-
如果Redis被当做一个持久化存储使用,必须使用固定的keys-to-nodes映射关系,节点的数量一旦确定不能变化。否则的话(即Redis节点需要动态变化的情况),必须使用可以在运行时进行数据再平衡的一套系统,而当前只有Redis集群可以做到这样
8.Redis事务支持隔离性吗?
- Redis 是单进程程序,并且它保证在执行事务时,不会对事务进行中断,事务可以运行直到执行完所有事务队列中的命令为止。因此,Redis 的事务是总是带有隔离性的
9.Redis事务保证原子性吗,支持回滚吗?
- Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行
10.Redis的缓存穿透,缓存击穿,缓存雪崩你有了解吗?======+2
- 缓存穿透
-
概念:缓存穿透的概念很简单,用户想要查询一个数据,发现redis内存中没有,也就是缓存没有命中,于是向持久层数据库查询,发现没有,于是本次查询失败,当用户很多的时候,缓存没有命中,于是都去请求了持久层的数据库,这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透
-
解决方案:
-
布隆过滤器:布隆过滤器是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力
-
缓存空对象:存储层不命中时,即使返回的空对象也将其缓存下来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据源。但是这种方法会存在两个问题:1.如果空值能被缓存下来,这就意味着缓存需要更多的空间存储更对的键,因为这当中会有很多的空值的键。2.即使对空值进行了过期处理,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响
-
- 缓存击穿
-
概念:设置了过期时间的key,这个key是一个热点数据,承载着高并发情况下,当这个key在失效的瞬间,持续的大并发就击穿缓存了,直接请求数据库,大量的请求有可能把数据库打死
-
解决方案:
-
设置key永远不过期,或者快过期时,通过另一个异步线程重新设置key
-
加互斥锁
-
- 缓存雪崩
-
概念:由于原有缓存失效,新缓存未到期间(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃
-
解决办法:
-
尽量避免给大量的数据设置相同的过期时间,如果业务层上有要求某些数据同时失效,可以在设置过期时间时,给这个过期时间加上个较小的随机数,例如随机个1-3分钟。这样就不会有大量的数据同时过期失效,同时也保证了这些数据在相近的时间失效,仍然满足业务需求
-
可以通过服务降级去应对缓存雪崩。针对不同的数据有不同的访问方式:针对非核心数据,暂停从缓存中获取这些数据,而是直接返回预定义、空值或是错误信息;针对核心数据,仍然走缓存,缓存找不到,继续通过数据库读取
-
如果是因为实例发生宕机了,就需要依靠Redis缓存高可用集群了,通过发现节点宕机,将该节点切换为其它节点
-
11.如何保证redis与数据库数据的一致性?
-
首先理解一下这个问题,我们从两个情况去分析考虑这个问题?
-
首先第一种情况:如果我们先删缓存,再写数据库,在高并发的情况下,当第一个线程删除了缓存,还没来得及写数据库,这时第二个线程来读取数据,这时因为缓存删除了数据,所以会去读数据库,然而这时因为还没写入,所以读取到的就是旧数据。读完之后,会把这个数据存入缓存中(这时第一个线程已经将新的修改的值写入缓存了),这样缓存中的值就会被覆盖成修改前的数据(即旧数据)。
-
解决方法:对于第一种情况,通常要求写的操作不会太频繁。
-
先操作缓存,但是不删除缓存,将缓存修改为一个特殊值(即和业务无关的值:比如-1000等等这样的),客户端读取缓存时,发现是特殊值,就休眠一小会(再休眠的时候,再进行删除缓存,写数据库),再去查一次Redis。这个解决办法可能存在的问题:特殊值可能存在义务侵入。休眠时间可能会出现多次,对性能有一定影响。
-
延时双删:先删除缓存,然后再写数据库,休眠一会,再删除一次缓存。这个解决办法可能存在的问题:如果写操作频繁,还是会存在数据脏数据的可能。
-
- 先写数据库,再删缓存:如果数据库写完以后,缓存删除失败,数据就会不一致。
-
解决办法:
-
给缓存设置一个过期时间。问题:过期时间内,缓存数据不会更新
-
将热点数据缓存设置永不过期,但是在value里面设置一个逻辑上的过期时间,另外起一个后台线程,扫面这些key,对于逻辑上过期的缓存,进行删除。
-
12.Redis如何设计一个分布式锁?如何对锁性能进行优化?
- 首先理解几个Redis的常用命令:
-
SETNX key value:当key存在时,就将key设置成value,并返回1,如果key存在,返回0。
-
EXPIRE key 10: 设置当前key的过期时间
-
DEL key: 删除key
-
GETSET key value: 如果不存在这个key,则返回空,然后会将这个key的值设置为value,如果存在这个key,会将原先key的值返回,然后set新的值,即覆盖。
- 最简单的分布式锁:使用SETNX和DEL两个命令即可。存在的可能问题:如果获取锁的进程失败,那么它就永远不会解锁。那这个锁就会被锁死。
-
优化:给锁设置一个过期时长(这样还是会有可能造成锁死的问题)
-
优化:将锁的内容设置成过期时间,SETNX获取锁失败的时候,拿这个时间跟当前时间比对,如果时过期时间的锁,就先删除锁,再重新上锁。(这样可能存在的问题,在高并发的情况下,会存在多个进程同时拿到锁的情况)
-
还有后续更