前言
本系列是有关Java开发的面试题,结合本人实际面试经历以及网上面试经验总结而来。这是第二篇。以下内容均为本人参考别人的文章(https://Javaguide.cn以及搜索到的一些博客),用自己的话总结出来,适合直接背题。如果你简历中写了Redis相关内容,一定要背这篇,面试问的很高频!!!
Java面试题之Redis篇
文章目录
- 前言
- Java面试题之Redis篇
- 1.Redis是单线程的,为什么还是那么快
- 2.能解释一下IO多路复用模型吗
- 3.主从同步原理
- 4.怎么保证Redis集群的高可用
- 5.哨兵的作用
- 6.Redis分片集群有什么用
- 7.Redis分片集群中是怎么读取或存取数据
- 8.Redis的数据过期策略
- 9.当redis内存不足时,怎么做(内存淘汰策略)
- 10.缓存三兄弟
- 11.布隆过滤器原理
- 12.如何保障缓存和数据库的一致性
- 13.Redis作为缓存,数据持久化是怎么做的
- 14.Redis常用数据类型以及应用场景
- 15.String 还是 hash存储对象更好呢
- 16.使用批量操作减少网络传输(redis性能优化)
- 17.什么是bigkey(redis性能优化也可以讲Bigkey)
- 18.大key怎么产生
- 19.大key的危害
- 20.redis 6.0后为什么引入多线程
- 21.三种常用的缓存读写策略
- **22.redis如何实现分布式锁**
- 23.Redis 如何解决集群情况下分布式锁的可靠性?
- 23.redission分布式锁优点
- 24.redission延迟队列原理是什么
- 25.RedisObject
- 26.skipList(跳表)
- 27.redis 的sorted set底层数据结构是怎样的
- **28.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在面试环节属于高频面试点。欢迎各位进行补充,共同学习共同进步。
今日长缨在手,何时缚住苍龙。希望大家可以关注一下,后续还会接着更新。