1、基础面试题
1.1、简单介绍redis
- redis本质上是一个key-value的nosql数据库。整个数据库都加载在内存当中进行操作,定期通过异步操作吧数据库数据flush到硬盘上进行保存。
- redis性能非常出色,每秒可以处理超过10万次读写操作。支持多种数据结构。
- 单个value的最大限制为1GB。
- 可以实现很多功能:list可以做fifo双向链表;可以实现一个轻量级高性能的消息队列;粗略的做个排序等等。
- 缺点在于数据库容量受物理内存的限制,不能用作海量数据的高性能读写,适合在较小数据量的高性能操作和运算场景。
1.2、redis支持的数据类型
Redis-01 redis基础
1.3、redis主要消耗什么物理资源
内存
1.4、redis全称是什么
remote dictionary server
1.5、redis数据淘汰策略
noeviction:返回错误:当内存限制打到并且客户端尝试执行会让更多内存被使用的命令;
allkeys-lru:回收最少使用的键;
volatile-lru(推荐):回收设置了过期时间的最少使用的键;
allkeys-random:随机回收所有键;
volatile-random:随机回收设置了过期时间的键;
volatile-ttl:淘汰剩余有效时间最少的键。
1.6、一个字符串类型的value最大存储量是多少
512M
1.7、为什么redis需要把所有数据放到内存里
redis为了达到最快的读写速度,将数据都存在内存中,并通过异步的方式将数据写入磁盘。
如果不将数据放入内存中,磁盘I/O会严重影响radis性能。
1.8、redis适合哪些场景?
会话缓存,全页缓存,队列,排行榜/计数器,发布/订阅
1.9、redis设置密码和验证密码?
设置密码:config set requirepass 123456
授权密码:auth 123456
1.10、redis哈希槽的概念?
redis集群没有使用一致性的hash,而是引用了hash槽的概念。redis集群有16384个hash槽,每个key通过CRC16校验后对16384取模来决定放置到哪个槽,集群的每个节点负责一部分槽。
2、中级面试题
2.1、redis集群方案应该怎么做?有哪些方案?
2.1.1、redis cluster方案
- 亟待解决的问题
高可用问题,如何保证redis的持续高可用性;
容量问题,单实例的redis内存无法无线扩充,内存体量大之后性能下降;
并发性能问题,单实例并发有上限。 - 方案优势
去中心化,集群最大可以增加1000个节点,性能随节点增加而线性扩展;
管理方便,后续可自行增加或摘除节点,移动分槽;
易上手。 - 简介
特点在于他的分布式算法不是一致性的hash,而是hash槽(slot)的概念。一共分为16384个slot,设置多个节点(node),每个节点处理一部分slot的值。推荐node采用主从模式(master-slave),即每个master配置多个slave。当master失效时,他所负责的slots也停止工作,有一部分key是找不到的,这是Redis Cluster会从slave中选取一个上升为master,继续提供服务。
2.2、redis集群方案什么情况下会导致整个集群不可用?
有A、B、C三个节点的集群,在没有复制模型的情况下,如果节点B失败了,则其负责的slots也不可用。
2.3、MySql里有2000w的数据,redis中只有20w,如何保证redis中的数据都是热点数据?
redis内存数据集大小上升到一定限度的时候,就会执行数据淘汰策略。
2.4、redis集群会有写操作丢失么?为什么?
redis并不能保证数据的强一致性,这意味着在实际中集群在特定的条件下可能会丢失写操作。
常见的丢失场景
- 程序bug或人为误操作。
- 因客户端缓冲区内存使用过大,导致大量键被LRU淘汰。
- 主库故障后自动重启,可能导致数据丢失。
- 网络分区的问题,可能导致短时间的写入数据丢失。
- 主从复制数据不一致,发生故障切换后,出现数据丢失。
- 大量过期键,同时被淘汰清理。
2.5、redis集群是怎么复制的
异步复制的。
2.6、redis最大节点数?
16384
2.7、什么是redis管道?
- redis使用的是cs模型和请求/响应协议的TCP服务器,一个redis客户端可以通过一个socket发起多个请求命令,每个请求命令发出后client通常会阻塞斌给等待redis服务器处理,redis处理完请求命令后会通过响应报文返回给client,因此执行多条命令时需要等待上一条执行完毕。
- 受网络延迟的影响,不能体现redis的处理能力。
- 管道可以一次性发送多条命令并在完成后一次性将结果返回,pipeline通过减少客户端与redis的通信次数来实现降低往返往返延时时间,pipeline的原理是队列。
- pipeline默认同步数据个数为53个。
- 用pipeline方式打包发送命令,redis必须在处理完所有命令前先缓存起所有命令的处理结果。打包的命令越多,缓存消耗的内存也就越多。
- 使用场景:批量将数据写入redis。
Pipeline pipe = jedis.pipelined(); // 先创建一个pipeline的链接对象
long start_pipe = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
pipe.set(String.valueOf(i), String.valueOf(i));
}
pipe.sync(); // 获取所有的response
long end_pipe = System.currentTimeMillis();
logger.info("the pipe total time is:" + (end_pipe - start_pipe));
2.8、如何理解redis事务?
- 是一个单独的隔离操作,所有命令都会被序列化、按顺序执行。
- 在执行的过程中不会被新来的请求打断。
- 是一个原子操作,要么全部执行,要么全部都不执行。
2.9、redis key的过期时间和永久有效如何设置?
- EXPIRE key 30000
- PERSIST key 移除过期时间
2.10、redis的内存优化
- redisObject对象:所有的值都存储在redisObject结构体中。
分为type字段:记录对象是哪种类型;
encoding字段:是redis内部的编码类型,代表当前对象内部采用哪种数据结构实现。
lru字段:记录对象最后一次被访问的时间。
refcount字段:记录对象被引用的次数,用于回收内存,当其为0时,可以安全回收当前对象空间。
*ptr字段:与对象的数据内容相关,如果是整数直接存储数据,否则表示指向数据的指针。 - 缩减键值对象:
value长度控制:常见需求是吧业务对象序列化成二进制数组放入redis。首先去掉不必要的属性避免存储无效数据;其次在序列化工具上,选择更高效的序列化工具来降低字节数组的大小。 - 共享对象池
object refcount key
使用共享对象池之后,相同数据内存使用降低30%以上。 - 字符串优化
预分配机制:字符串中尽量少追加数据,会造成内存碎片上升。若直接插入则节省预分配空间。
尽量少的运用append、setrange,改为直接使用set修改字符串。降低预分配带来的内存浪费和内存碎片化。 - 编码优化
通过object encoding key来获取编码类型。
redis对每种类型至少采用两种编码方式来实现,可以根据自己的需要修改编码方法。 - 控制key的数量
2.11、redis回收进程如何工作?
一个客户端运行了新的命令,添加了新数据。
redis检查内存使用情况,如果大于maxmemory的限制,则根据制定好的策略进行回收。
执行新命令。
通过不断打到内存边界不断进行内存的回收。
如果一个命令导致大量内存被使用,不用多久内存限制就会被这个内存使用量超越。
2.12、如何做到redis大量数据插入?
使用管道pipeline。
2.13、redis分区?
参考redis集群的内容。
2.14、redis分区有什么缺点?
设计多个key的操作通常不会被支持。如不能对两个集合求交集,因为可能会存到不同的redis实例中。
数据处理会非常复杂,如为了备份必须从不同的redis实例和主机同时收集RDB/AOF文件。
1、基础面试题
1.1、简单介绍redis
- redis本质上是一个key-value的nosql数据库。整个数据库都加载在内存当中进行操作,定期通过异步操作吧数据库数据flush到硬盘上进行保存。
- redis性能非常出色,每秒可以处理超过10万次读写操作。支持多种数据结构。
- 单个value的最大限制为1GB。
- 可以实现很多功能:list可以做fifo双向链表;可以实现一个轻量级高性能的消息队列;粗略的做个排序等等。
- 缺点在于数据库容量受物理内存的限制,不能用作海量数据的高性能读写,适合在较小数据量的高性能操作和运算场景。
1.2、redis支持的数据类型
Redis-01 redis基础
1.3、redis主要消耗什么物理资源
内存
1.4、redis全称是什么
remote dictionary server
1.5、redis数据淘汰策略
noeviction:返回错误:当内存限制打到并且客户端尝试执行会让更多内存被使用的命令;
allkeys-lru:回收最少使用的键;
volatile-lru(推荐):回收设置了过期时间的最少使用的键;
allkeys-random:随机回收所有键;
volatile-random:随机回收设置了过期时间的键;
volatile-ttl:淘汰剩余有效时间最少的键。
1.6、一个字符串类型的value最大存储量是多少
512M
1.7、为什么redis需要把所有数据放到内存里
redis为了达到最快的读写速度,将数据都存在内存中,并通过异步的方式将数据写入磁盘。
如果不将数据放入内存中,磁盘I/O会严重影响radis性能。
1.8、redis适合哪些场景?
会话缓存,全页缓存,队列,排行榜/计数器,发布/订阅
1.9、redis设置密码和验证密码?
设置密码:config set requirepass 123456
授权密码:auth 123456
1.10、redis哈希槽的概念?
redis集群没有使用一致性的hash,而是引用了hash槽的概念。redis集群有16384个hash槽,每个key通过CRC16校验后对16384取模来决定放置到哪个槽,集群的每个节点负责一部分槽。
1.11、redis与其他key-value存储有什么不同?
- redis有更为复杂的数据结构并且提供对他们的原子性操作。
- redis运行在内存中但是可以持久化到磁盘。
1.12、redis的内存占用情况如何?
- 键值对方面,redis的内存开销要小一点。
1.13、降低redis内存使用情况的方法?
1.14、查看redis使用情况及状态的命令是?
info cpu/memory…
1.15、redis的内存用完了会发生什么?
当达到上限,redis的写命令会返回错误信息。设置淘汰机制。
1.16、一个redis实例最多能存放多少keys?
理论上redis 可以处理多达2^32的keys。
1.17、修改配置不重启redis会实时生效么?
针对运行实例,很多配置可以通过CONFIG SET命令进行修改,而不需执行任何形式的重启。
从AOF切换到RDB的方式也不需要重启redis。
修改某些CONFIG SET命令不支持的参数配置的时候需要重启。
2、中级面试题
2.1、redis集群方案应该怎么做?有哪些方案?
2.1.1、redis cluster方案
- 亟待解决的问题
高可用问题,如何保证redis的持续高可用性;
容量问题,单实例的redis内存无法无线扩充,内存体量大之后性能下降;
并发性能问题,单实例并发有上限。 - 方案优势
去中心化,集群最大可以增加1000个节点,性能随节点增加而线性扩展;
管理方便,后续可自行增加或摘除节点,移动分槽;
易上手。 - 简介
特点在于他的分布式算法不是一致性的hash,而是hash槽(slot)的概念。一共分为16384个slot,设置多个节点(node),每个节点处理一部分slot的值。推荐node采用主从模式(master-slave),即每个master配置多个slave。当master失效时,他所负责的slots也停止工作,有一部分key是找不到的,这是Redis Cluster会从slave中选取一个上升为master,继续提供服务。
2.2、redis集群方案什么情况下会导致整个集群不可用?
有A、B、C三个节点的集群,在没有复制模型的情况下,如果节点B失败了,则其负责的slots也不可用。
2.3、MySql里有2000w的数据,redis中只有20w,如何保证redis中的数据都是热点数据?
redis内存数据集大小上升到一定限度的时候,就会执行数据淘汰策略。
2.4、redis集群会有写操作丢失么?为什么?
redis并不能保证数据的强一致性,这意味着在实际中集群在特定的条件下可能会丢失写操作。
常见的丢失场景
- 程序bug或人为误操作。
- 因客户端缓冲区内存使用过大,导致大量键被LRU淘汰。
- 主库故障后自动重启,可能导致数据丢失。
- 网络分区的问题,可能导致短时间的写入数据丢失。
- 主从复制数据不一致,发生故障切换后,出现数据丢失。
- 大量过期键,同时被淘汰清理。
2.5、redis集群是怎么复制的
异步复制的。
2.6、redis最大节点数?
16384
2.7、什么是redis管道?
- redis使用的是cs模型和请求/响应协议的TCP服务器,一个redis客户端可以通过一个socket发起多个请求命令,每个请求命令发出后client通常会阻塞斌给等待redis服务器处理,redis处理完请求命令后会通过响应报文返回给client,因此执行多条命令时需要等待上一条执行完毕。
- 受网络延迟的影响,不能体现redis的处理能力。
- 管道可以一次性发送多条命令并在完成后一次性将结果返回,pipeline通过减少客户端与redis的通信次数来实现降低往返往返延时时间,pipeline的原理是队列。
- pipeline默认同步数据个数为53个。
- 用pipeline方式打包发送命令,redis必须在处理完所有命令前先缓存起所有命令的处理结果。打包的命令越多,缓存消耗的内存也就越多。
- 使用场景:批量将数据写入redis。
Pipeline pipe = jedis.pipelined(); // 先创建一个pipeline的链接对象
long start_pipe = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
pipe.set(String.valueOf(i), String.valueOf(i));
}
pipe.sync(); // 获取所有的response
long end_pipe = System.currentTimeMillis();
logger.info("the pipe total time is:" + (end_pipe - start_pipe));
2.8、如何理解redis事务?
- 是一个单独的隔离操作,所有命令都会被序列化、按顺序执行。
- 在执行的过程中不会被新来的请求打断。
- 是一个原子操作,要么全部执行,要么全部都不执行。
2.9、redis key的过期时间和永久有效如何设置?
- EXPIRE key 30000
- PERSIST key 移除过期时间
2.10、redis的内存优化
- redisObject对象:所有的值都存储在redisObject结构体中。
分为type字段:记录对象是哪种类型;
encoding字段:是redis内部的编码类型,代表当前对象内部采用哪种数据结构实现。
lru字段:记录对象最后一次被访问的时间。
refcount字段:记录对象被引用的次数,用于回收内存,当其为0时,可以安全回收当前对象空间。
*ptr字段:与对象的数据内容相关,如果是整数直接存储数据,否则表示指向数据的指针。 - 缩减键值对象:
value长度控制:常见需求是吧业务对象序列化成二进制数组放入redis。首先去掉不必要的属性避免存储无效数据;其次在序列化工具上,选择更高效的序列化工具来降低字节数组的大小。 - 共享对象池
object refcount key
使用共享对象池之后,相同数据内存使用降低30%以上。 - 字符串优化
预分配机制:字符串中尽量少追加数据,会造成内存碎片上升。若直接插入则节省预分配空间。
尽量少的运用append、setrange,改为直接使用set修改字符串。降低预分配带来的内存浪费和内存碎片化。 - 编码优化
通过object encoding key来获取编码类型。
redis对每种类型至少采用两种编码方式来实现,可以根据自己的需要修改编码方法。 - 控制key的数量
2.11、redis回收进程如何工作?
一个客户端运行了新的命令,添加了新数据。
redis检查内存使用情况,如果大于maxmemory的限制,则根据制定好的策略进行回收。
执行新命令。
通过不断打到内存边界不断进行内存的回收。
如果一个命令导致大量内存被使用,不用多久内存限制就会被这个内存使用量超越。
2.12、如何做到redis大量数据插入?
使用管道pipeline。
2.13、redis分区?
参考redis集群的内容。
2.14、redis分区有什么缺点?
设计多个key的操作通常不会被支持。如不能对两个集合求交集,因为可能会存到不同的redis实例中。
数据处理会非常复杂,如为了备份必须从不同的redis实例和主机同时收集RDB/AOF文件。
尽量不在master机器上进行备份。
2.15、redis持久化数据和缓存怎么做扩容?
- 如果redis被当做缓存使用,使用一致性hash实现动态扩容缩容。
- 如果redis被当作一个持久化存储使用,必须使用固定的keys-to-nodes映射关系,节点数量一旦确定不能变化。若redis节点需要动态变化,则使用可以在运行时进行数据再平衡的一套系统,而当前只有redis集群可以做到这样。
2.16、分布式redis是前期做还是后期规模上来了再做好?
最好是一开始就多设置几个redis实例,例如32或64个。
这样做之后当数据不断增长,需要更多redis服务器时,仅需要将redis实例从一台服务器迁移至另一台服务器即可不用考虑重新分区。
2.17、redis是单线程的,如何提高多核cpu的利用率?
可以再同一个服务器部署多个redis实例,并把它们当做不同的服务器来使用。
2.18、redis常见性能问题和解决方案?
- master最好不要做任何持久化工作,RDB内存快照和AOF日志文件。
- 如果数据比较重要,某个slave开启AOF备份数据,策略设置为每秒同步一次。
- master和slave最好在同一个局域网中,为了主从复制的速度和连接的稳定性。
- 尽量避免在压力很大的主库上增加从库。
- 主从复制尽量使用单项链表结构,更加稳定。
2.19、redis提供了哪些持久化方式?
RDB持久化方式能够在指定的时间间隔对数据进行快照;
AOF持久化方式用来记录每次对服务器的写操作;
同时开启两个持久化方式,当redis重启时会优先载入AOF文件来恢复原始数据,因为通常情况下AOF会保存的完整一些。
2.20、如何选择合适的持久化方案?
如果想要保证书安全性,一般要使用两种持久化功能。
如果可以承受数分钟内的数据丢失,可以用RDB方式。
2.21、介绍一下Redis分布式锁?
- 线程锁:主要用来给方法和代码块加锁。线程锁只适用于单个JVM的情况,因为线程锁的是实现根本上是靠线程之间共享内存实现的。
- 分布式锁:当多个进程不在同一个系统中,用其控制多个进程对资源的访问。
- 实现原理:redis是单进程单线程模式,采用队列方式将并行访问变成串行访问,且多客户端对redis的链接不存在竞争关系,所以setnx命令可以方便的实现分布式锁。
- redis分布式锁主要用于分布式情况下多进程并发的问题,即多JVM之间的线程锁。
- 常用命令setnx key value:当且仅当key不存在时,set一个key为value的字符串,返回1;若key存在,则返回0。
empire key timeout:为key设置一个过期时间,单位为second,超过过期时间则自动释放,避免死锁。
delete key:删除key - 步骤:获取锁的时候,使用setnx加锁,并使用empire命令设置一个超时时间,超时则自动释放锁,锁的value值为一个
3、应用面试题
3.1、redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头,如何将它们全部找出来?
- 使用keys指令将它们找出来
如:keys hell*
keys h?llo等 - 如果这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题?
- redis是单线程的,keys执行会导致线程阻塞一点时间,线上的服务会停顿,指导指令执行完毕,服务才能恢复。这个时候可以使用scan指令,此指令可以无阻塞的提取出指定模式的key列表,不会阻塞服务器。
3.2、redis是单线程,为什么会快?
- 绝大部分操作是纯粹的内存操作,速度非常快;
- 采用单线程,避免了不必要的上下文切换和竞争关系;
- 利用非阻塞IO-IO多路复用
3.3、redis缓存穿透
- 原因:多次查询redis缓存中和数据库中没有的数据,请求数量太大,数据库承受不住。
- 解决办法:可以同步
3.4、redis缓存击穿
- 原因:查询一个必然不存在的数据。导致每次请求都需要从存储层查询,这样缓存就失去了意义,大流量下数据库会挂掉。缓存被击穿。
- 解决方法:
①、使用互斥锁
再根据key获取value值为空时,先锁上,再从数据库加载,加载完毕,释放锁。若其他线程发现获取锁失败,则休眠50ms。
②、布隆过滤器
将已存在的缓存放到布隆过滤器中,当访问不存在的缓存时迅速返回避免缓存和DB挂掉。
3.5、redis雪崩
- 原因:每个key都设置了失效时间,如果大量key同时过期的时候,这些key又被大量请求,因为现在redis中已经没有这些数据了,所以大量的请求会用想数据库,导致数据库处理不过来,导致雪崩。
- 解决方法:
在设置失效时间的时候,给它加一个随机的秒数,来让这些大量的数据请求错开对数据库的访问;
或者,在key的访问频率频繁的时候,可以让它每查一次就加点有效时间。
3.6、懂redis事务么?
生产上使用的是redis cluster集群架构,不同的key是可能会分配到不同的redis节点上的,这种情况下redis事务机制是不生效的。
redis事务不支持回滚。
3.7、redis多数据库机制?
redis cluster集群架构下只有一个数据库空间,没有多数据库机制。
3.8、redis集群机制中的不足?
如果一个key的value是hash类型的,而且非常大,是不支持映射到不同节点的。只能映射到集群的一个节点上。
批量操作比较麻烦。
3.9、redis的批量操作?
在集群模式下,不同的key会映射到不同的slot上,因此,用mset和mget是不行的。
3.10、在redis集群模式下,如何进行批量操作?
- 若数量比较少,可以进行串行get操作。