Java面试题总结-Redis篇
来源:segmentfault.com/u/jack3021
Redis篇
1.什么是Redis?
Redis本质是一直Key-Value类型的内存数据库。因为是纯内存操作所以性能非常好,而且还支持多种数据结构。另外Redis可以对存入的Key-Value设置expire时间。缺点是数据库容量受到物理内存的限制,不能做海量数据的高性能读写。
2.Redis支持哪几种数据类型?
数据类型 | 使用场景 |
---|---|
String | 缓存,计数器,session |
Hash | 存储用户信息,【key,field,value】语法:Hset key field value |
List | 最新消息的排行,还可以利用list的push命令,将任务存在list集合中,同时使用pop命令,将任务从集合中取出。模拟消息队列。 |
Set | 无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。 |
Zset | Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但分数(score)却可以重复。 |
3.Redis的过期策略以及内存淘汰机制?
redis采用的是定期删除+惰性删除。
为什么不使用定时删除?
因为定时删除是使用一个定时器去监视key,过期则自动删除。虽然内存及时释放,但是十分消耗cpu资源。在高并发环境下,cpu应该将时间应用在处理请求上,而不是删除key。
定期删除+惰性删除是如何工作的呢?
定期删除:redis每隔100ms检查是否有过期的key,如果有则删除,需要说明的是,redis并不是一下检查所有key,而是随机抽取检查。
因此定期删除可能会导致很多key没有被检查到,这个时候惰性删除就派上用场。
惰性删除:在每一次获取某个key时,去检查key是否过期,如果过期则删除。
如果惰性删除也没有获取到某个key,这个时候采用淘汰机制
在redis.conf中有一行配置
maxmemory-policy volatile-lru
volatile-lru: 从设置过期时间的数据集中挑选最近最少使用的数据淘汰。
volatile-ttl: 从设置过期时间的数据集中挑选将要过期的数据淘汰。
volatile-random: 从设置过期时间的数据集中随机选择数据淘汰。
allkeys-lru: 从数据集中挑选最近最少使用的数据淘汰。
allkeys-random: 从数据集中任意选择数据淘汰。
no-eviction(驱逐): 禁止驱逐数据,新写入操作会报错。
redis默认使用的是no-eviction。
4.为什么Redis的操作是原子性的?
因为redis是单线程的,但是如果在多个命令并发中不是原子性的。
如果想实现原子性可以使用incr命令,或者redis事务,或者使用redis-lua的方式。
5.什么是Redis事务?
Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:
批量操作在发送 EXEC 命令前被放入队列缓存。
收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
一个事务从开始到执行会经历以下三个阶段:
开始事务。
命令入队。
执行事务。
以 MULTI 开始一个事务, 然后将多个命令入队到事务中, 最后由 EXEC 命令触发事务, 一并执行事务中的所有命令
命令 | 含义 |
---|---|
MULTI | 标记一个事务的开始 |
EXEC | 执行事务块中的所有命令 |
DISCARD | 取消事务,放弃执行事务块中的所有命令 |
WATCH | 监视一个或多个key,如果在事务执行前被其他命令改动,事务将被打断 |
UNWATCH | 取消WATCH命令对所有key的监视 |
6.Redis持久化方式有哪些?
RDB 在指定的时间间隔内将数据以快照的形式写入硬盘,恢复时候是直接将文件读取到内存中。
RDB是如果进行备份的?
redis会单独创建一个子进程来进行持久化,先将数据写入一个临时文件,等持久化完毕后再替换上次的文件,整个过程中主进程是不进行IO操作的。
RDB优点:效率非常高。节省磁盘空间。
RDB缺点: RDB都是快照文件,都是默认五分钟甚至更久的时间才会生成一次,这意味着你这次同步到下次同步这中间五分钟的数据都很可能全部丢失掉。AOF则最多丢一秒的数据,数据完整性上高下立判。
AOF 将redis执行中的所有写命令记录下来,只追加文件不改写文件,redis启动之初会读取该文件重新加载数据。
AOF优点:备份机制稳健,丢失概率低。可读的日志文件 可以处理误操作。
AOF缺点:更占用磁盘空间,恢复速度慢,每次读写同步的话性能会有压力。
7. Redis的并发竞争问题如何解决?
主要是发生在并发写竞争。
1.使用乐观锁的方式进行解决;(watch机制配合事务锁)
2.排队的机制进行。将所有需要对同一个key的请求进行入队操作,然后用一个消费者线程从队头依次读出请求,并对相应的key进行操作。
这样对于同一个key的所有请求就都是顺序访问,正常逻辑下则不会有写失败的情况下产生 。从而最大化写逻辑的总体效率。
1.客户端角度,为保证每个客户端间正常有序与Redis进行通信,对连接进行池化,同时对客户端读写Redis操作采用内部锁synchronized。
2.服务器角度,利用setnx实现锁。
8.Redis缓存穿透是什么?
缓存穿透就是指缓存和数据库中都没有的数据,而用户不断发起请求,由于缓存中查不到数据,导致每次都需要去数据库查询,失去了缓存的意义。
比如发起一个id为”-1“的值或者一个特别大的不存在的值,这个时候用户很可能是攻击者,会导致数据库压力过大。
解决方法:
增加接口校验,比如id<=0直接拦截,用户权限校验。
可以从代码中解决:如果缓存中取不到且数据库中也没有,这个时候可以将key-value写为key-null,缓存有效时间设置短点,比如30秒。可以防止用户反复对同一个id暴力攻击。
布隆过滤器(BloomFilter)利用高效的数据结构和算法快速判断出你这个Key是否在数据库中存在,不存在你return就好了,存在你就去查了DB刷新KV再return。
9.Redis缓存击穿是什么?
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。
解决方法:
设置热点信息永不过期。
接口设置限流,熔断,降级。
加互斥锁。参考代码如下:
10.Redis缓存雪崩是什么?
缓存雪崩是指缓存中数据大批量的到达过期时间,而查询数据量巨大,引起数据库压力过大。
解决方案:
缓存数据的过期时间设置随机,防止同一时间大量数据过期。
如果缓存数据库是分布式部署,将热点数据均匀在不同Redis中。
设置热点数据永不过期。
11. Redis为什么这么快?
完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。它的数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);
使用多路I/O复用模型,非阻塞IO;
采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
12.什么是哨兵模式?
哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,可以独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行多个Redis实例。
监控(Monitoring): 哨兵(sentinel) 会不断地检查你的Master和Slave是否运作正常。
提醒(Notification):当被监控的某个 Redis出现问题时, 哨兵(sentinel) 可以通过 API 向管理员或者其他应用程序发送通知。