Redis面试问题

Redis面试问题

一、Redis简介

  Redis是一个key-vakue存储系统,支持五种存储结构:String,Hash,List,Set,Sorted Set。与memcached一样为了保证效率,将数据储存在内存中。区别的是Redis会周期的把更新的数据写入磁盘或者把修改操作写入追加的文件中,并在此基础上实现了主从同步。

二、Redis详细介绍

2.1 Redis每种数据类型的使用场景

  • String:最常规的set/get 操作,value可以说string也可以是数字。一般做一些复杂的计数功能的缓存。
  • hash:这里value放的是结构化的对象,比较方便的就是操作其中的某个字段。
  • list:使用List的数据结构,可以做简单的消息队列功能。另外还可以利用lrang命令,做基于redis的分页功能。
  • set:因为set堆放的是一些不重复值的集合。所以可以做全局去重的功能。可以利用交集,并集,差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能。
  • sorted set:sorted set 多了一个权重参数score,集合中的元素能够按score进行排序。可以做排行榜应用,取Top N操作。Sorted set 可以用来做延时任务,最后一个应用就是可以做范围查找。

2.2 Redis的优缺点

  使用Redis主要考虑两个角度(优点):性能和并发。
(1)我们在碰到需要执行耗时特别久,且结果不频繁变动的SQL,就特别适合价格运行结果放入缓存,这样,后面的请求就去缓存中读取,使得请求能够迅速响应。
(2)在大并发的情况下,所有请求直接访问数据库,数据库会出现连接异常。这个时候,就需要使用redis做一个缓冲操作,让请求先访问到Redis,而不是直接访问数据库。
使用Redis也有一些缺点
(1)缓存和数据库双写写一致性问题;
(2)缓存雪崩问题;
(3)缓存击穿问题;
(4)缓存的并发竞争问题

2.3 Redis的速度为什么这么快?

(1)纯内存操作
(2)单线程操作,避免了频繁的上下文切换
(3)采用了非阻塞IO多路复用机制

2.4 redis的过期策略以及内存淘汰机制

  采用定期删除和惰性删除的策略。
  定期删除,Redis默认没个100ms检查是否有过期的key需要删除,有过去则删除,需要说明的是,Redis不是每隔100ms将所有的key检查一次,而是随机抽查进行检查(如果每隔100ms将全部key积蓄检查,Redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。
  于是,惰性删除就派上用场了。大概我们获取一个key的时候,Redis会检查一下,这个key有没有过期,过期的话就删除了。
  采用定期删除+惰性删除也会有问题。如果定期删除没有删除key,然后也没有即时请求key 也就是惰性杀出没生效。这样,redis的内存会越来越高。那么就应该采用内存淘汰机制
  在redis.conf中有一行配置
  #maxmemory-policy volatile-lru

该配置就是配置内存淘汰策略的。

  • noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。不推荐。
  • allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。推荐使用。
  • allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。不推荐。
  • volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。这种情况一般是把redis既当缓存,又做持久化存储的时候才用。不推荐
  • volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。依然不推荐
  • volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。不推荐

2.5 Redis和数据库双写一致性问题

  分析:一致性问题是分布式常见问题,还可以再分为最终一致性和强一致性。数据库和缓存双写,就必然会存在不一致的问题。答这个问题,先明白一个前提。就是如果对数据有强一致性要求,不能放缓存。我们所做的一切,只能保证最终一致性。另外,我们所做的方案其实从根本上来说,只能说降低不一致发生的概率,无法完全避免。因此,有强一致性要求的数据,不能放缓存。
   回答:首先,采取正确更新策略,先更新数据库,再删缓存。其次,因为可能存在删除缓存失败的问题,提供一个补偿措施即可,例如利用消息队列。

2.6 如何应对缓存穿透和缓存雪崩问题

  缓存穿透,即黑客故意去请求缓存中不存在的数据,导致所有的请求都怼到数据库上,从而数据库连接异常。
解决方案:

  • 利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试
  • 采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。
  • 提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器,内部维护一系列合法有效的key。迅速判断出,请求所携带的Key是否合法有效。如果不合法,则直接返回。

  缓存雪崩,即缓存同一时间大面积的失效,这个时候又来了一波请求,结果请求都怼到数据库上,从而导致数据库连接异常。
   解决方案:

  • 给缓存的失效时间,加上一个随机值,避免集体失效。
  • 使用互斥锁,但是该方案吞吐量明显下降了。
  • 双缓存。我们有两个缓存,缓存A和缓存B。缓存A的失效时间为20分钟,缓存B不设失效时间。自己做缓存预热操。

2.7 如何解决redis的并发竞争key问题

  分析:这个问题大致就是,同时有多个子系统去set一个key。这个时候要注意什么呢?大家思考过么。回答:如下所示

  • 如果对这个key操作,不要求顺序
    这种情况下,准备一个分布式锁,大家去抢锁,抢到锁就做set操作即可,比较简单。
  • 如果对这个key操作,要求顺序
    假设有一个key1,系统A需要将key1设置为valueA,系统B需要将key1设置为valueB,系统C需要将key1设置为valueC.
    期望按照key1的value值按照 valueA–>valueB–>valueC的顺序变化。这种时候我们在数据写入数据库的时候,需要保存一个时间戳。假设时间戳如下
    系统A key 1 {valueA 3:00}
    系统B key 1 {valueB 3:05}
    系统C key 1 {valueC 3:10}
    那么,假设这会系统B先抢到锁,将key1设置为{valueB 3:05}。接下来系统A抢到锁,发现自己的valueA的时间戳早于缓存中的时间戳,那就不做set操作了。以此类推。

2.8 Redis的持久化

  Redis 提供了多种不同级别的持久化方式:

  • RDB 持久化可以在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot)。
  • AOF (Append-only file)持久化记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。 AOF 文件中的命令全部以 Redis 协议的格式来保存,新命令会被追加到文件的末尾。 Redis 还可以在后台对 AOF 文件进行重写(rewrite),使得 AOF 文件的体积不会超出保存数据集状态所需的实际大小。
  • Redis 还可以同时使用 AOF 持久化和 RDB 持久化。 在这种情况下, 当 Redis 重启时, 它会优先使用 AOF 文件来还原数据集, 因为 AOF 文件保存的数据集通常比 RDB 文件所保存的数据集更完整。

你甚至可以关闭持久化功能,让数据只在服务器运行时存在。

RDB
工作原理:每隔一定时间给内存照一个快照,将内存中的数据写入文件(rdb文件)
配置参数:redis.conf文件

RDB示例测试:可以使用redis-benchmark进行压力测试
./bin/redis-benchmark -n 100000 表示执行100000个操作
RDB的缺点:
在两次快照之间,如果发生断电,数据会丢失
举例:在生成rdb后,插入新值。突然断电,数据可能会丢失
AOF:通过日志的方式
工作原理:记录操作的命令
配置参数:

什么是AOF的重写:rewrite
将内存中的key逆向生成命令,如同一个key,反复操作了100次,aof文件会记录100次操作,这样会导致AOF文件过大
例如:
set age 0
incr age
incr age
… 100次
最后 age的值是100
经过重写后,直接执行: set age 100
可以通过观察aof日志文件的大小

3、Redis持久化注意的问题

  • RDB恢复的速度快
  • 如果RDB和AOF都有,默认使用AOF进行恢复

你可能感兴趣的:(java)