面试准备:Redis常见面试题汇总

文章目录

  • 1.介绍一下Redis
  • 2.Redis为什么这么快?
  • 3.Redis支持哪些数据类型?
  • 4.Redis与Memcached的区别?
  • 5.Redis跳表是什么?
  • 6.什么是冷热数据分离?什么是VM机制?
  • 7.Redis过期键的删除策略有哪些?
  • 8.说说Redis的同步机制?
  • 9.Pipeline有什么好处?
  • 10.Redis如何实现分布式锁?
  • 11.Redis如何实现幂等性?
  • 12. 接触过哪些Redis客户端?
  • 13.什么是Redis哈希槽?
  • 14.Redis 集群会有写操作丢失吗?
  • 15.如何保证Redis中存的都是热点数据?
  • 16.简要介绍缓存穿透、击穿、雪崩?
  • 17.什么是热点key?怎么解决热点key引起的问题?
  • 18.什么是大Key(Big Key)?怎么解决大key引起的问题?
  • 19.无底洞问题?
  • 20.缓存与数据库一致性问题?
  • 21.Redis持久化机制?
  • 22. Redis内存淘汰机制有哪些?
  • 23. 描述下Redis事务?

1.介绍一下Redis

Redis 是一款使用 C 语言编写的高性能 key-value 数据库

特点:

  • 性能极高,能到 100000 次/s 读写速度
  • 支持数据的持久化,对数据的更新采用Copy-on-write技术,可以异步地保存到磁盘上
  • 丰富的数据类型,String(字符串)、List(列表)、Hash(字典)、Set(集合)、Sorted Set(有序集合)
  • 原子性:Redis 的所有操作都是原子性的,多个操作通过 MULTI 和 EXEC 指令支持事务
  • 丰富的特性:key 过期、publish/subscribe、notify
  • 支持数据的备份,快速的主从复制
  • 节点集群,很容易将数据分布到多个Redis实例中
  • 单线程:避免了不必要的上下文切换和竞争条件

用途:
高速缓存。
分布式锁。
单点登录共享session。
计数器。

2.Redis为什么这么快?

1、基本基于内存的,绝大部分请求都是在内存中操作。另外,Redis为了保证内存足够,实现冷热数据的分离,并直接自己构建了VM 机制,相比于一般的系统调用函数,由于通信的应用协议不一样,Redis更高效。

2、数据结构简单高效,如 Hash数据结构、跳表等。Hash数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);跳表基于有序的链表,包含多个指向后继结点的指针,实现快速查找。

3、采用单线程,使用多路I/O复用模型,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;

3.Redis支持哪些数据类型?

Redis 支持五种数据类型

string:字符串
hash:哈希
list:列表
set:集合
sorted set:有序集合(也叫zset)

我们常说的String,List,Hash,Set,Sorted Set只是对外的编码,实际上每种数据结构都有自己底层的内部编码实现,而且是多种实现,这样Redis可以在合适的场景选择更合适的内部编码:

  • Redis 内部使用一个 redisObject 对象来表示所有的 key 和 value。type 表示一个 value 对象外部具体是何种数据类型,encoding 是不同数据类型在 Redis 内部的存储方式。
  • 比如:type=string 表示 value 存储的是一个普通字符串,那么 encoding 可以是 raw 或者 int。

redisobject最主要的信息:

//redisobject源码
typedef struct redisObject{
     //类型
     unsigned type:4;
     //编码
     unsigned encoding:4;
     //指向底层数据结构的指针
     void *ptr;
     //引用计数
     int refcount;
     //记录最后一次被程序访问的时间
     unsigned lru:22;
}robj

因为c语言不具备自动内存回收功能,当将redisObject对象作为数据库的键或值而不是作为参数存储时其生命周期是非常长的,为了解决这个问题,Redis自己构建了一个内存回收机制,通过redisobject结构中的refcount实现.

4.Redis与Memcached的区别?

现在公司一般都是用 Redis 来实现缓存,而且 Redis 自身也越来越强大了!了解 Redis 和 Memcached 的区别和共同点,有助于我们在做相应的技术选型的时候,能够做到有理有据!

共同点 :

都是基于内存的缓存。
都有过期策略。
两者的性能都非常高。

区别 :

Redis 支持更丰富的数据类型(支持更复杂的应用场景)。Redis 不仅仅支持简单的 k/v 类型的数据,同时还提供 list,set,zset,hash 等数据结构的存储。Memcached 只支持最简单的 k/v 数据类型。
Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而 Memecache 把数据全部存在内存之中。
Redis 有灾难恢复机制。 因为可以把缓存中的数据持久化到磁盘上。
Redis 在服务器内存使用完之后,可以将不用的数据放到磁盘上。但是,Memcached 在服务器内存使用完之后,就会直接报异常。
Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是 Redis 目前是原生支持 cluster 模式的.
Memcached 是多线程,非阻塞 IO 复用的网络模型;Redis 使用单线程的多路 IO 复用模型。 (Redis 6.0 引入了多线程 IO )
Redis 支持发布订阅模型、Lua脚本、事务等功能,而Memcached不支持。并且,Redis支持更多的编程语言。

5.Redis跳表是什么?

跳跃表基于有序单链表,在链表的基础上,每个结点不只包含一个指针,还可能包含多个指向后继结点的指针,这样就可以跳过一些不必要的结点,从而加快查找、删除等操作。
在这里插入图片描述

跳表有一个层级(level)的概念,层级越多访问越快。
比如上图有三个层级,从下到上依次是level1到level3。可以参考链接:跳表查看跳表是如何插入删除数据的。

跳跃表在 Redis 中使用不是特别广泛,只用在了两个地方。一是实现有序集合键,二是集群节点中用作内部数据结构。

6.什么是冷热数据分离?什么是VM机制?

Redis使用到了VM(virtual memory,虚拟内存), 通过VM功能可以实现冷热数据分离。使热数据仍在内存中,冷数据保存到磁盘。这样就可以避免因为内存不足而造成访问速度下降的问题。

冷数据即是那些不经常访问、但又无法删除的信息。

7.Redis过期键的删除策略有哪些?

惰性删除:不主动删除过期键,从键空间中获取键时,都检查取得的键是否过期,过期则删除;没过期则返回
定期删除:每隔一段时间对字典进行一次检查,删除里面的过期键。删除多少过期键、检查多少个数据库,由算法决定。

redis 会将每个设置了过期时间的 key 放入到一个独立的字典中,以后会定期遍历这个字典来删除到期的 key。

参考:Java架构直通车——Redis缓存过期处理与内存淘汰机制

8.说说Redis的同步机制?

2.8 版以前:
Redis 通过同步(sync)和指令传播(command propagate)两个操作完成同步:

  1. 同步(sync):将从节点的数据库状态更新至与主节点的数据库状态一致。
    从节点向主节点发送 SYNC 指令,主节点执行 BGSAVE 指令,在后台生成一个 RDB 文件,并使用一个缓冲区记录从现在开始执行的所有写指令。执行完成后,会将生成的 RDB 文件发送给从节点,从节点接受并加载RDB文件,加载完成后,通知主节点将记录在缓冲区里面的所有写指令发送给从节点,将数据库状态更新至主节点当前数据库状态。
  2. 指令传播(command propagate):主节点数据被修改,会主动向从节点发送执行的写指令,从节点执行之后,两个节点数据状态又保持一致。

为了解决主从节点断线复制低效的问题(SYNC过程中生成、传输、载入 RDB 文件耗费大量 CPU、内存、磁盘 IO 资源),2.8 版开始新增 PSYNC 指令。

2.8版本后:
PSYNC 具有两种模式:

  1. 全量重同步(full resynchronization)
    与上面的过程基本一致
  2. 增量重同步(partial resynchronization)
    借助复制偏移量、复制积压缓冲区、服务器运行 ID ,完成主从节点断开连接后,从节点重连主节点后,条件允许,主节点将连接断开期间执行的写指令发送给从节点,从节点接收并执行写指令,将数据库更新至主节点当前状态

参考:Java架构直通车——Redis主从数据同步机制。

9.Pipeline有什么好处?

Pipeline指的是管道技术,指的是客户端允许将多个请求依次发给服务器,过程中而不需要等待请求的回复,在最后再一并读取结果即可。

多个指令之间没有依赖关系,可以使用 pipeline 一次性执行多个指令,减少 IO,缩减时间,提高吞吐量。
未使用pipeline执行N条命令:
面试准备:Redis常见面试题汇总_第1张图片
使用了pipeline执行N条命令:
面试准备:Redis常见面试题汇总_第2张图片

10.Redis如何实现分布式锁?

参考:Java架构直通车——基于Redis的Set NX实现分布式锁

11.Redis如何实现幂等性?

为需要保证幂等性的每一次请求创建一个唯一标识token, 先获取token, 并将此token存入redis, 请求接口时, 将此token放到header或者作为请求参数请求接口, 后端接口判断redis中是否存在此token:

如果存在, 正常处理业务逻辑, 并从redis中删除此token, 那么, 如果是重复请求, 由于token已被删除, 则不能通过校验, 返回请勿重复操作提示。

12. 接触过哪些Redis客户端?

Redisson
优点:

  • 实现了分布式特性和可扩展的 Java 数据结构,适合分布式开发
  • API 线程安全
  • 基于 Netty 框架的事件驱动的通信,可异步调用

缺点:

  • API 更抽象,学习使用成本高

Jedis
优点:

  • 提供了比较全面的 Redis 操作特性的 API
  • API 基本与 Redis 的指令一一对应,使用简单易理解

缺点:

  • 同步阻塞 IO
  • 不支持异步
  • 线程不安全

13.什么是Redis哈希槽?

Redis 集群没有使用一致性 hash,而是引入了哈希槽的概念。哈希槽是 Redis 集群管理数据的基本单位,集群伸缩就是槽和数据在节点之间的移动。通过对每个key做hash运算,判读其对应的哈希槽,从而找到在redis集群架构里对应存储该key的节点

Redis 集群有 16384 个哈希槽,每个 key 通过 CRC16 算法计算的结果,对 16384 取模后放到对应的编号在 0-16383 之间的哈希槽,集群的每个节点负责一部分哈希槽。

参考:Java架构直通车——Redis 主从/哨兵/集群 架构详解

14.Redis 集群会有写操作丢失吗?

以下情况可能导致写操作丢失:

  • 最大内存不足,会使用内存淘汰机制(比如LRU):Redis 自动清理部分 key 以节省空间。
  • 发生网络分区的时候,脑裂产生写操作丢失。
  • 主从复制数据不一致,发生故障切换后,出现数据丢失。
  • 主库故障后自动重启,可能导致数据丢失。

15.如何保证Redis中存的都是热点数据?

Redis存储在内存中的数据升到配置大小时,就进行数据淘汰。使用 allkeys-LRU (最近最少使用)策略,从数据集(server.db[i].dict)中挑选最近最少使用的数据优先淘汰,即可满足保存热点数据

16.简要介绍缓存穿透、击穿、雪崩?

参考:Java架构直通车——Redis缓存穿透/击穿/雪崩

17.什么是热点key?怎么解决热点key引起的问题?

缓存中的某些Key(可能对应用与某个促销商品)对应的value存储在集群中一台机器,使得所有流量涌向同一机器,成为系统的瓶颈,该问题的挑战在于它无法通过增加机器容量来解决。(当key失效的时候,大量的线程构建缓存,导致负载增加)

解决方案:

  1. 客户端热点key缓存:将热点key对应value并缓存在客户端本地,并且设置一个失效时间。对于每次读请求,将首先检查key是否存在于本地缓存中,如果存在则直接返回,如果不存在再去访问分布式缓存的机器。
  2. 将热点key分散为多个子key,然后存储到缓存集群的不同机器上,这些子key对应的value都和热点key是一样的。当通过热点key去查询数据时,通过某种hash算法随机选择一个子key,然后再去访问缓存机器,将热点分散到了多个子key上。
  3. 为每个 value 设置一个逻辑过期时间,当发现超过逻辑过期时间后,会使用单独的线程去构建缓存。缓存层面没有设置过期时间(这种方案可以解决大量线程等待的问题,但它可能导致数据不一致的问题)
  4. 针对热点key失效,大量线程构建缓存(使用分布式锁,只让一个线程构建缓存,可以解决这个问题)。

18.什么是大Key(Big Key)?怎么解决大key引起的问题?

Redis使用过程中经常会有各种大key的情况, 比如单个简单的key存储的value很大。
由于redis是单线程运行的,如果一次操作的value很大会对整个redis的响应时间造成负面影响,导致IO网络拥塞。

解决:

将整存整取的大对象,分拆为多个小对象。可以尝试将对象分拆成几个key-value使用multiGet获取值,这样分拆的意义在于分拆单次操作的压力,将操作压力平摊到多个redis实例中,降低对单个redis的IO影响;

19.无底洞问题?

暂时不做了解。

20.缓存与数据库一致性问题?

参考:缓存架构
参考:分布式DB与Cache一致性

21.Redis持久化机制?

参考:Java架构直通车——Redis持久化和宕机恢复机制

22. Redis内存淘汰机制有哪些?

Redis 提供 8 种数据淘汰策略:

volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
volatile-lfu:从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰

allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
allkeys-lfu:当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key

no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧!

23. 描述下Redis事务?

Redis的事务不像Mysql的事务功能强大。

Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。

Redis中,单条命令是原子性执行的但事务不保证原子性,且没有回滚事务中任意命令执行失败,其余的命令仍会被执行

实际正常开发中很少遇到使用Redis事务的场景,因为Lua脚本同样可以帮我们实现Redis事务相关功能,并且功能要强大很多。

Redis事务相关命令:

  • watch key1 key2 … : 监视一或多个key,如果在事务执行之前,被监视的key被其他命令改动,则事务被打断 ( 类似乐观锁 )
  • multi : 标记一个事务块的开始( queued )
  • exec : 执行所有事务块的命令 ( 一旦执行exec后,之前加的监控锁都会被取消掉 )
  • discard : 取消事务,放弃事务块中的所有命令
  • unwatch : 取消watch对所有key的监控

你可能感兴趣的:(面试准备)