Java非关系型数据库Redis相关面试题

1、Redis常见数据结构

String字符串、Hash、List、Set、Zset

2、zset的底层实现

ziplist/skiplist
参考

3、持久化方案

  • RDB:默认,周期性的保存快照方式
  • AOF:对每条写入命令作为日志记录,以append-only模式写入日志文件。

参考

4、rehash

rehash是指对hash表进行扩容或收缩。为了让哈希表的负载因子(load factor)维持在一个合理的范围之内, 当哈希表保存的键值对数量太多或者太少时, 程序需要对哈希表的大小进行相应的扩展或者收缩。
参考

5、Redis事务的特点

Redis学习二:事务

6、为什么单线程的Redis可达到10万+的QPS?

(一)纯内存操作
(二)单线程操作,避免了频繁的上下文切换
(三)采用了非阻塞I/O多路复用机制

7、Redis的线程模型

经典图
在这里插入图片描述
参考

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

过期策略
定期删除+惰性删除策略
定期删除:指的是redis默认是每隔100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除
惰性删除:在你获取某个key的时候,redis会检查一下 ,这个key如果设置了过期时间那么是否过期了,如果过期了此时就会删除,不会给你返回任何东西。

内存淘汰机制
为什么还需要内存淘汰机制:
如果定期删除没删除key。然后你也没即时去请求key,也就是说惰性删除也没生效。这样,redis的内存会越来越高。那么就应该采用内存淘汰机制。
在这里插入图片描述
参考

9、redis的hash槽

参考1
参考2
Redis集群介绍
一致性哈希与哈希槽

10、redis如何实现延时队列?

使用zset,以时间戳为score,是zadd生产任务,使用zrangebyscore消费任务。

11、缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级等问题

(1)缓存雪崩:某一时刻存在大量的缓存更新或者过期等导致缓存命中失效,从而导致所有请求都去查数据库,导致数据库CPU和内存负载过高,甚至宕机。
在这里插入图片描述
解决方案:

  • 通过设置不同的过期时间,来错开缓存过期,从而避免缓存集中失效,这样子,我们就可以将缓存过期时间均匀地分布在时间轴上,避免缓存同时失效、更新的情况发生。
  • 从应用架构角度,我们可以通过限流、熔断等手段来降低影响,也可以通过多级缓存来避免这种灾难。
    限流方案:
    在这里插入图片描述
    用户发送一个请求,系统 A 收到请求后,先查本地 ehcache 缓存,如果没查到再查 redis。如果 ehcache 和 redis 都没有,再查数据库,
    将数据库中的结果,写入 ehcache 和 redis 中。
    限流组件,可以设置每秒的请求,有多少能通过组件,剩余的未通过的请求,怎么办?走降级!可以返回一些默认的值,或者友情提示,或者
    空白的值。
    好处:
    数据库绝对不会死,限流组件确保了每秒只有多少个请求能通过。
    只要数据库不死,就是说,对用户来说,2/5 的请求都是可以被处理的。
    只要有 2/5 的请求可以被处理,就意味着你的系统没死,对用户来说,可能就是点击几次刷不出来页面,但是多点几次,就可以刷出来一次。

(2)缓存穿透
在高并发场景下,如果某一个key被高并发访问,没有被命中,出于对容错性考虑,会尝试去从后端数据库中获取,从而导致了大量请求达到数据库,而当该key对应的数据本身就是空的情况下,这就导致数据库中并发的去执行了很多不必要的查询操作,从而导致巨大冲击和压力。很容易被黑客利用来攻击。
解决方案:
对所有可能对应数据不为空的key进行统一的存放,并在请求前做拦截,这样避免请求穿透到后端数据库。

布隆过滤器是一个集合数据结构类似于ArrayList,它就一个最主要的方法,判断是否包含(用于确定某个特定元素是否包含在一组元素中)。
其优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率。因此Bloom Filter不适合那些“零错误”的应用
场合,适用于能容忍低错误率的应用场合。
(3)缓存击穿:
缓存击穿,就是说某个 key 非常热点,访问非常频繁,处于集中式高并发访问的情况,当这个 key 在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库,就像是在一道屏障上凿开了一个洞。
解决方案:
可以将热点数据设置为永远不过期;或者基于 redis or zookeeper 实现互斥锁,等待第一个请求构建完缓存之后,再释放锁,进而其它请求才能通过该 key 访问数据。
参考

12、Redis如何解决键冲突

链地址法

13、Redis存在线程安全问题吗?

当然存在。redis只能保证单条命令的原子性,要么失败要么成功,但是无法保证多条命令的原子性,即使是使用事务,也只能保证命令的顺序和隔离性,事务中其中一条失败后依然会继续执行。
解决办法:
(1)redis中也有CAS(check-and-set)机制,使用watch命令做自旋锁
(2)加锁

14、redis集群的一致性hash算法

参考

15、Redis做缓存时如何保证数据一致性

使用Redis做缓存,必然存在的一个问题就是更新数据的时候需要同步更新数据库和Redis,若是其中一个更新失败,一个成功则可能导致数据不一致。
强一致性的解决方案:
(1)为数据库更新操作添加事务控制,更新数据成功后,删除缓存。
实现简单,缺点是删除缓存后,如果有多个查询请求并发过来,都发现缓存中没数据,都会将请求落到数据库上,导致数据库压力瞬间增加。
(2)为数据库更新操作添加事务控制,更新数据成功后,同步更新缓存。
这是对删除方式的改进,但也有缺点,写入前要多一次查询,在部分场景下是没法使用的,比如修改的数据牵扯到多个缓存,自然无法主动写入,只能等缓存失效。

最终一致性解决方案:
(1)MQ异步刷新、定时刷新
采用MQ异步消息机制刷新,如果更新失败要有适当的补偿机制。
所有需要更新的对象存储到一张定时任务表,定时任务扫描任务表异步更新。
这两种更新机制不能保证查询缓存同DB的一致性,但是能够保证最终一致性。
(2)自动失效
合理设置缓存失效时间,需根据业务场景设置每个缓存的失效时间,一致性要求越高,自然失效时间也要越短。

发布了26 篇原创文章 · 获赞 8 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_36142042/article/details/104909966