Java面试题之Redis篇

前言

本系列是有关Java开发的面试题,结合本人实际面试经历以及网上面试经验总结而来。这是第二篇。以下内容均为本人参考别人的文章(https://Javaguide.cn以及搜索到的一些博客),用自己的话总结出来,适合直接背题。如果你简历中写了Redis相关内容,一定要背这篇,面试问的很高频!!!

Java面试题之Redis篇

1.Redis是单线程的,为什么还是那么快

  • 完全基于内存
  • 采用单线程,避免不必要的上下文切换
  • 使用I/O多路复用模型,非阻塞IO

2.能解释一下IO多路复用模型吗

  • IO多路复用是指利用单个线程同时监听多个Socket,并在某个Socket可读、可写时得到通知,避免无效的等待。目前的IO多路复用都是采用epoll模式实现,他会在通知用户进程Socket就绪时,把已经就绪的Socket写入用户空间,不需要挨个遍历Socket来判断是否就绪(poll select模式)。
  • redis 的网络模型就是使用IO多路复用+事件处理器来应对多个Socket请求,比如提供了连接应答处理器、命令回复处理器、命令请求处理器。

3.主从同步原理

  • 从节点请求同步,把自己的replid、offset发给主节点。
  • 主节点判断是否时第一次连接,若是,主节点执行bgSave命令生成RDB文件发给从节点,实现全量同步。之后每次主节点写数据,都把命令传播给从节点
  • 若不是第一次连接,主节点repl-backlog中offset往后的数据都发给从节点,实现增量同步

4.怎么保证Redis集群的高可用

使用哨兵模式,实现主从集群自动故障恢复。

5.哨兵的作用

  • 状态监控:不断检查master和slave是否按期工作
  • 故障恢复:如果master故障,会把slave提升为master
  • 状态通知:把最新redis集群推送给Redis客户端

6.Redis分片集群有什么用

  • 集群中有多个master,每个master保存不同数据
  • 每个master都可以有多个slave节点
  • master之间通过ping检测彼此健康状态
  • 客户端可以访问任意节点,都会被转发到正确节点

7.Redis分片集群中是怎么读取或存取数据

  • Redis引入哈希槽概念,有16384个哈希槽
  • 把16384个插槽分配给不同实例
  • 根据Key的有效部分计算哈希值,用于读写数据

8.Redis的数据过期策略

  • 惰性删除,在key过期后先不管,用到时再去判断是否过期。过期就删除
  • 定期删除,每隔一段时间,就对一些key检查,删除过期key。两种模式 slow fast
    redis采用的是定期删除+惰性删除。定期删除对内存好,惰性删除对cpu友好。Redis会周期性的从设置了过期时间的key中抽查一批,进行删除,这个定期删除会受到执行时间和过期Key的影响。

9.当redis内存不足时,怎么做(内存淘汰策略)

Redis支持8种不同的内存淘汰策略:

  • noeviction: 不淘汰任何key,但是内存满时不允许写入新数据,默认就是这种策略。
  • volatile-ttl: 对设置了TTL的key,比较key的剩余TTL值,TTL越小越先被淘汰
  • allkeys-random:对全体key ,随机进行淘汰。也就是直接从db->dict中随机挑选
  • volatile-random:对设置了TTL的key ,随机进行淘汰。也就是从db->expires中随机挑选。
  • allkeys-lru: 对全体key,基于LRU算法进行淘汰
  • volatile-lru: 对设置了TTL的key,基于LRU算法进行淘汰
  • allkeys-lfu: 对全体key,基于LFU算法进行淘汰
  • volatile-lfu: 对设置了TTL的key,基于LFI算法进行淘汰

10.缓存三兄弟

  • 缓存穿透:请求的Key在缓存和数据库中都密钥,所有请求都打到数据库。
    解决方法:缓存空对象、布隆过滤

  • 缓存雪崩:大量的key同时失效或者是redis服务器宕机。大量的请求都达到数据库
    解决方法:给不同的key添加随机值,使用redis集群

  • 缓存击穿:热点key问题,一个热点key突然失效,无数热点key打到数据库,造成压力。
    解决方法:互斥锁、逻辑过期

11.布隆过滤器原理

  • 需要一个很长的数组,默认每个元素都是0
  • 还需要k个hash函数
  • 判断某个元素是否存在,需要计算k个hash函数的值,得到k个角标,判断每个角标位置是否都是1,存在误判可能。

12.如何保障缓存和数据库的一致性

实际开发中,强一致场景较少,我习惯于先改数据库,再删除缓存,通过对缓存设置一个合理的超时时间,尽量让缓存不一致的时间缩短。保障最终一致性。

13.Redis作为缓存,数据持久化是怎么做的

  • RDB(redis数据备份文件):把内存中的所有数据都记录到磁盘,定期对整个内存做快照,数据完整性不如AOF
  • AOF(追加文件): Redis每处理一个写命令都会记录在AOF,AOF默认是关闭的,需要在redis.conf配置文件中开启
  • 因为AOF是记录命令,所有AOF文件比RDB文件大得多。、

AOF刷盘时机:

awways:同步
everysec:每秒刷盘
no:操作系统控制

14.Redis常用数据类型以及应用场景

五种基本数据类型:

  • String:①缓存session token等常规数据 ②需要技术的常景 ③简易的分布式锁
  • List: ①最新文章、最新动态 ②消息队列。但缺陷很多,不如使用专业的消息队列
  • set: ①共同好友(交集)、好友推荐(差集) ②抽奖系统、随机点名系统
  • sorted set:①排行版
  • hash:①商品信息、购物车信息

三种特殊数据类型:

  • bitmap(位图):用户签到、活跃用户统计
  • HyperLoglog(基数统计):uv统计(uv是独立访客)
  • Geospatial(地理位置):附近的人、附近店铺

15.String 还是 hash存储对象更好呢

  • 对象存储方式:String存储的是序列化后的对象数据,存放的是整个对象,操作简单直接。Hash是对对象的每个字段单独存储,可以获取部分字符按信息。
  • 内存消耗:hash比String更省内存
  • 性能:String操作通常具有O(1)时间复杂度,操作简单高效,整体性能较好。hash会有额外的性能开销

一般情况下,对象结构简单且整体读写是主要操作时,选string。频繁操作对象字段或需要节省内存时,选Hash。

16.使用批量操作减少网络传输(redis性能优化)

redis命令执行简化为一下四步

  • 发送命令
  • 命令排队
  • 命令执行
  • 返回结果

使用批量操作可以减少网络传输次数,有效提高性能

17.什么是bigkey(redis性能优化也可以讲Bigkey)

一个key对应的value所占用的内存比较大,就是大key。有个不精确的参考标准

  • String 类型的value超过1MB
  • 复合类型(List、hash、 set sorted、set)的value包含元素超过5000个

18.大key怎么产生

  • 程序设计不当,比如直接使用String类型存储较大文件内容
  • 业务规模考虑不周,比如使用集合是没考虑到数据量的快速增长
  • 未及时清理数据,比如哈希中冗余了大量无用的键值对

19.大key的危害

  • 网络阻塞:每次获取大key产生的网络较大
  • 工作线程阻塞:操作大key比较耗时,由于redis执行命令是单线程,会阻塞后面命令的执行

20.redis 6.0后为什么引入多线程

redis6.0引入多线程的主要目的是为了提高为了IO读写性能,但执行命令仍然是单线程

21.三种常用的缓存读写策略

  • cache aside pattern(旁路缓存模式):用的比较多,适合读写请求多的场景

① 写数据:先写数据库,再写缓存

②读数据:读缓存,缓存有就直接返回;缓存没有就读数据库返回,在写到缓存中

  • Read/Write Through pattern(读写穿透):比较少见,把缓存是为主要存储

①写数据:先看看缓存有没有,有就直接更新缓存,再由缓存服务更到数据库。如果缓存没有,直接更数据库

②读数据:读缓存,有就返回;没有就从数据库加载到缓存,再返回

  • write Behind Pattern (异步缓存写入):数据库写性能很高,适合经常变化对数据要求不高的场景,如:浏览量、点赞量

与读写穿透模式差不多,只不过读写穿透同步更缓存和数据库;但异步缓存写入只更缓存,不直接更新数据库,而是异步批量方式更新数据库。

22.redis如何实现分布式锁

  • 使用setnx命令,这个命令保证了只有一个线程对某个key设置值,再没有过期或者删除key时,其他线程是不能设置key的
  • 不过在实际项目中都是使用redis一个框架redission实现。在redission中需要手动加锁,存储数据时使用hash结构,大key是业务决定,小key是uuid+线程id,value是当前线程重入次数,
  • 在redission中提供看门狗机制,每隔一段时间就检查当前线程是否持有锁,如果持有就增长持有锁时间,业务执行完成再释放锁

23.Redis 如何解决集群情况下分布式锁的可靠性?

  • 问题描述:由于 Redis 集群数据同步到各个节点时是异步的,如果在 Redis 主节点获取到锁后,在没有同步到其他节点时,Redis 主节点宕机了,此时新的 Redis 主节点依然可以获取锁
  • 使用 Redlock(红锁)解决: 创建锁时,要在超过一半的redis实例上创建,如果能够创建成功,就获得锁。实际项目中不建议使用 Redlock 算法,成本和收益不成正比。

23.redission分布式锁优点

  • 可重入、可以设置锁的超时时间
  • 提供了看门狗机制,为锁续期
  • 内部加锁解锁使用了lua脚本,保证原子性
  • 提供公平锁非公平锁、读锁写锁、阻塞锁(lock)和非阻塞锁(trylock)
  • 支持集群

24.redission延迟队列原理是什么

内置有一个RDelayQueue 延迟队列实现

  • 延迟队列是基于sorted set 实现的,将需要延迟的任务插入到sortedset,执行时间作为分数
  • 当达到执行时间后,会从sortedset移除,加入就绪队列。

25.RedisObject

  • redis五种基本类型最终都会封装成RedisObject结构体。
  • 结构体中由对象类型、对象编码方式、对象引用计数器、指向实际数据的指针等信息,内存占用16字节

26.skipList(跳表)

  • 特点:①元素按升序排序存储 ②节点含有多级指针,指针跨度不同
  • 传统链表只能顺序查找,但跳表含有多级指针,可以跳跃差查找,效率非常高

27.redis 的sorted set底层数据结构是怎样的

使用hashtable+跳表

  • sorted set是有序集合,每个数据都包含element和score两个值,使用hashtable
  • 实现score值排序,同时查询效率要高,使用跳表。

28.redis如何实现多维度排序

  • 关于sortedSet:

    默认升序排列,即通过命令ZRANGE实现;如果要按照降序排列,需要通过命令ZREVRANGE实现;
    当score即得分一样时,按照字典顺序对member进行排序,字典排序用的是二进制,它比较的是字符串的字节数组,所以实际上是比较ASCII码。

  • 如果要实现多维度排序:以我项目为例,整数部分是真实分数,小数部分可以是时间戳。

  • 更高维度:自定义得分权重计算公式,这个公式包含所有影响排序的因子

总结

Redis在面试环节属于高频面试点。欢迎各位进行补充,共同学习共同进步。
今日长缨在手,何时缚住苍龙。希望大家可以关注一下,后续还会接着更新。

### Redis淘汰策略详解 Redis 提供了多种内存淘汰策略,允许开发者根据具体场景选择最合适的方案。以下是详细的介绍: #### 1. noeviction `noeviction` 是一种主动淘汰任何数据的策略。当内存达到上限时,如果尝试写入新数据,则会返回错误给客户端,而会删除已有数据[^2]。 #### 2. volatile-lru (Least Recently Used) 该策略仅针对设置了过期时间的键生效。它会选择最近最少使用的键进行淘汰。通过这种方式可以优先保留更频繁访问的数据[^3]。 #### 3. allkeys-lru 与 `volatile-lru` 类似,但它作用于所有的键(无论是否有设置过期时间)。此策略同样遵循 LRU 原则,即移除那些长时间未被访问过的键[^3]。 #### 4. volatile-random 这种策略随机挑选设置了过期时间的键并将其删除。适用于希望减少特定集合大小但又想依据使用模式的情况[^3]。 #### 5. allkeys-random 类似于 `volatile-random` ,过它是从整个数据库中任意选取一个键来删除,而考虑是否设定了有效期[^3]。 #### 6. volatile-ttl (Time To Live) 对于带有生存期限(TTL)标记的对象来说, 这一方法倾向于先驱逐即将到期的数据项。换句话说就是越接近其有效截止日期的数据越有可能被淘汰掉[^3]。 #### 7. volatile-lfu (Least Frequently Used) 这是另一种形式的选择标准——基于对象在过去一段时间内的使用频率来进行判断;较使用的元素会被清除出去。注意这个选项可能需要额外的支持才能启用,在某些版本里默认可用或者性能开销较大。 #### 配置方式 可以通过修改配置文件中的 maxmemory-policy 参数指定采用哪一类回收算法。例如: ```bash maxmemory-policy allkeys-lru ``` 以上就是在 Java 技术栈下关于 Redis 同类型的缓存清理机制的一些基础知识总结[^3]。 ### 示例代码展示如何查看当前服务器所应用的最大容量以及对应的处理规则 ```python import redis r = redis.StrictRedis(host='localhost', port=6379, decode_responses=True) # 获取最大可用内存限制值 print(r.config_get('maxmemory')) # 查看现在正在实行的是哪种满载应对措施 policy = r.config_get('maxmemory-policy') print(policy['maxmemory-policy']) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值