当前位置:   article > 正文

Redis常见面试题总结

redis常见面试题


Redis

0、Redis是什么?

Redis(Remote Dictionary Server)是一个使用 C 语言编写的,高性能非关系型的键值对数据库。与传统数据库不同的是,Redis 的数据是存在内存中的,所以读写速度非常快,被广泛应用于缓存方向。Redis可以将数据写入磁盘中,保证了数据的安全不丢失,而且Redis的操作是原子性的。

1、redis为啥那么快

  • 基于内存:Redis是使用内存存储,没有磁盘IO上的开销。数据存在内存中,读写速度快。

  • 单线程实现( Redis 6.0以前):Redis使用单个线程处理请求,避免了多个线程之间线程切换和锁资源争用的开销。

  • IO****多路复用模型:Redis 采用 IO 多路复用技术。Redis 使用单线程来轮询描述符,将数据库的操作都转换成了事件,不在网络I/O上浪费过多的时间。

  • 高效的数据结构:Redis 每种数据类型底层都做了优化,目的就是为了追求更快的速度。

2、基本数据类型:

数据类型HashStringListSetZset内部编码数量2种3种编码3种编码2种3种编码2种3种编码2种3种编码内部编码hashtable、ziplistraw、int、embstrlinkedlist、ziplisthashtable、intsetskiplist、ziplist

3、reids的内存删除策

  1. 定期清理:随机选择dicEntry出来判断是否过期,过期就清理掉,没有过期则返回重新随机选择。清理超过25%的数量,则循环前面的步骤<=20次。

随机的测试20个带有timeout(过期时间)信息的key;

  1. 删除其中已经过期的key;

  2. .如果超过25%的key被删除,则重复执行步骤1

  3. 惰性删除:被动删除策yu,访问时候判断是否过期。目的主要删除策与节省CPU资源

  4. LRU:最近最久未使用,从设置过期时间的数据集中选择最近最久未使用的数据进行释放

  5. LFU:对LRU的优化的算法

4、缓存穿透

key对应的数据在数据源中并不存在,每次针对此KEY的请求从缓存中获取不到,请求就会到数据源中,从而可能压垮数据源

  • 解决方案:

1、使用缓存空数据:会占用更过的key,还有可能带来短期的数据不一致。

如果一个查询返回的数据为空(不管是数 据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。

2、使用布隆过滤器bloom filter:是一种预防的方案,占用空间少、误差可控。

将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力

布隆过滤器:

它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中

5、缓存击穿

对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题,这个和缓存雪崩的区别在于这里针对某一key缓存,前者则是很多key。

缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。

解决方案

缓存失效时的雪崩效应对底层系统的冲击非常可怕。大多数系统设计者考虑用加锁或者队列的方式保证缓存的单线 程(进程)写,从而避免失效时大量的并发请求落到底层存储系统上。这里分享一个简单方案就时讲缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

6、缓存雪崩

当cache缓存服务器异常的时候,这时又有大量的请求涌入,而cache异常,直接涌向后端组件造成级联故障,还有造成数据源崩溃

解决方案

1.使用互斥锁(mutex key)

业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。

SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果。在redis2.6.1之前版本未实现setnx的过期时间

2."提前"使用互斥锁(mutex key):

在value内部设置1个超时值(timeout1), timeout1比实际的memcache timeout(timeout2)小。当从cache读取到timeout1发现它已经过期时候,马上延长timeout1并重新设置到cache。然后再从数据库加载数据并设置到cache中。

3.“永远不过期”:

这里的“永远不过期”包含两层意思:

(1) 从redis上看,确实没有设置过期时间,这就保证了,不会出现热点key过期问题,也就是“物理”不过期。

(2) 从功能上看,如果不过期,那不就成静态的了吗?所以我们把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建,也就是“逻辑”过期

从实战看,这种方法对于性能非常友好,唯一不足的就是构建缓存时候,其余线程(非构建缓存的线程)可能访问的是老数据,但是对于一般的互联网功能来说这个还是可以忍受。

总结

穿透:缓存不存在,数据库不存在,高并发,少量key

击穿:缓存不存在,数据库存在,高并发,少量key

雪崩:缓存不存在,数据库存在,高并发,大量key

7、热key重建

高并发多线程的情况下,热Key重键是使用redis比较典型的一个问题:

解决方案:

  • 加锁重键(互斥锁):
  • 热键不过期:在缓存中创建一个时间戳,先判断时间戳是否过期,如果没有过期返回原数据,过期了则访问数据源

8、Redis的持久化原理

将Redis内存中的数据异步地保存在磁盘上就是Redis的持久化

1、RDB(快照)

RDB是 Redis 默认的持久化方案。RDB持久化时会将内存中的数据写入到磁盘中,在指定目录下生成一个

dump.rdb文件。Redis 重启会加载

dump.rdb文件恢复数据。

有三种触发机制

(手动)同步: save

redis服务器收到客户端发送的SAVE命令,会创建快照。在创建结束之前,不会响应客户端的请求。

(手动)异步: bgsave

当接收到客户端的BGSAVE命令时,redis会调用fork创建一个子进程,然后由子进程负责将快照写入磁盘中,而父进程继续处理客户端请求

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JUJ8iAj8-1650014652077)(https://pizximfzuc.feishu.cn/space/api/box/stream/download/asynccode/?code=NWViZDAwYmQwNGE5MTRjMWI3MjZjYWJkNTc4MzE5YjlfNTVQTjhaU1dhaDBIV0pqT1RRd2pkcklpVXh4RVpTcGhfVG9rZW46Ym94Y25ORTNFMzFZN1R6SmN4V3NEcmF1UlRwXzE2NTAwMTQ2MDQ6MTY1MDAxODIwNF9WNA)]

  • 执行BGSAVE命令

  • Redis 父进程判断当前是否存在正在执行的子进程,如果存在,BGSAVE命令直接返回。

  • 父进程执行fork操作创建子进程,fork操作过程中父进程会阻塞。

  • 父进程fork完成后,父进程继续接收并处理客户端的请求,而子进程开始将内存中的数据写进硬盘的临时文件

  • 当子进程写完所有数据后会用该临时文件替换旧的 RDB 文件

Redis启动时会读取RDB快照文件,将数据从硬盘载入内存。通过 RDB 方式的持久化,一旦Redis异常退出,就会丢失最近一次持久化以后更改的数据。

**Fork操作:**开启一个子进程,使用linux的copy-on-write机制用于同步操作

(自动) 配置文件

如果用户在redis.conf中设置了save配置选项,redis会在save选项条件满足之后自动触发一次BGSAVE命令(当设置多个save配置选项的时候,任意满足一个都会触发一次BGSAVE)

触发的机制:
  • 全量复制

  • debug reload

  • shutdown

优点

  1. Redis 加载 RDB 恢复数据远远快于 AOF 的方式
  2. 使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 Redis 的高性能

缺点

  1. RDB方式数据无法做到实时持久化。因为

BGSAVE每次运行都要执行

fork操作创建子进程,属于重量级操作,频繁执行成本比较高。

  1. RDB 文件使用特定二进制格式保存,Redis 版本升级过程中有多个格式的 RDB 版本,存在老版本 Redis 无法兼容新版 RDB 格式的问题

2、AOF (只追加日志文件)

将所有客户端执行的写命令记录打牌日志文件中,AOF持久化会将被执行的写命令写到AOF的文件末尾,以此来记录数据发生的变化。因此只要redis从头到尾的执行一次AOF文件,就可以恢复AOF中记录的数据集。

  • 开启AOF持久化

    • a.修改 appendonly yes 开启持久化
  • b.修改 appendfilename “appendonly.aof” 指定生成文件名称

  • 日志追加频率

appendfsync always //每次写入aof文件都会执行同步,最安全最慢,不建议配置
appendfsync everysec  //既保证性能也保证安全,建议配置
appendfsync no //由操作系统决定何时进行同步操作
  • 1
  • 2
  • 3

接下来看一下 AOF 持久化执行流程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yYFMwukY-1650014652078)(https://pizximfzuc.feishu.cn/space/api/box/stream/download/asynccode/?code=OGFhY2JhODZjMTI1ZjEzMDQzYWZlYWM2NmI3ODJjNTBfY0FLeWk3ektsNTBiMkhpNlM4d21HOXVHSXl0Z2xmSUVfVG9rZW46Ym94Y25lNkxMbm9Mdlp2ZXplUkZFb0tmWmc4XzE2NTAwMTQ2MDQ6MTY1MDAxODIwNF9WNA)]

  1. 所有的写入命令会追加到 AOP 缓冲区中。
  2. AOF 缓冲区根据对应的策略向硬盘同步。
  3. 随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩文件体积的目的。AOF文件重写是把Redis进程内的数据转化为写命令同步到新AOF文件的过程。
  4. 当 Redis 服务器重启时,可以加载 AOF 文件进行数据恢复。

优缺点:

  • 优点

  1. Redis 加载 RDB 恢复数据远远快于 AOF 的方式

  2. 使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 Redis 的高性能

  • 缺点

  1. RDB方式数据无法做到实时持久化。因为BGSAVE每次运行都要执行fork操作创建子进程,属于重量级操作,频繁执行成本比较高。

  2. RDB 文件使用特定二进制格式保存,Redis 版本升级过程中有多个格式的 RDB 版本,存在老版本 Redis 无法兼容新版 RDB 格式的问题

AOF重写
  • AOF策yu可以将每条命令都写入到AOF中,但是随着时间的推移命令越来越多oaof,AOF的体积也会你越来越大。在我们aof来恢复的时候也会很慢。对硬盘也占用很多。

bgrewriteaof: bgrewriteaof命令是AOF的异步的持久化重写的命令

9、主从复制

Redis的复制功能是支持多个数据库之间的数据同步。主数据库可以进行读写操作,当主数据库的数据发生变化时会自动将数据同步到从数据库。从数据库一般是只读的,它会接收主数据库同步过来的数据。一个主数据库可以有多个从数据库,而一个从数据库只能有一个主数据库。

主从复制原理?

主从复制的原理?

  1. 当启动一个从节点时,它会发送一个 PSYNC 命令给主节点;

  2. 如果是从节点初次连接到主节点,那么会触发一次全量复制。此时主节点会启动一个后台线程,开始生成一份 RDB 快照文件;

  3. 同时还会将从客户端 client 新收到的所有写命令缓存在内存中。RDB 文件生成完毕后, 主节点会将RDB文件发送给从节点,从节点会先将RDB文件写入本地磁盘,然后再从本地磁盘加载到内存中

  4. 接着主节点会将内存中缓存的写命令发送到从节点,从节点同步这些数据;

  5. 如果从节点跟主节点之间网络出现故障,连接断开了,会自动重连,连接之后主节点仅会将部分缺失的数据同步给从节点。

  • 其他:

主从复制架构仅仅用来解决数据的冗余备份,从节点仅仅用来同步数据串的命令流来保持对 slave 的更新,以便于将自身数据集的改变复制给 slave ,包括客户端的写入、key 的过期或被逐出等等。

当 master 和 slave 之间的连接断开之后,因为网络问题、或者是主从意识到连接超时, slave 重新连接上 master 并会尝试进行部分重同步:这意味着它会尝试只获取在断开连接期间内丢失的命令流。

当无法进行部分重同步时, slave 会请求进行全量重同步。这会涉及到一个更复杂的过程,例如 master 需要创建所有数据的快照,将之发送给 slave ,之后在数据集更改时持续发送命令流到 slave 。

10、哨兵Sentinel机制

主从复制存在不能自动故障转移、达不到高可用的问题。哨兵模式解决了这些问题。通过哨兵机制可以自动切换主从节点。

客户端连接Redis的时候,先连接哨兵,哨兵会告诉客户端Redis主节点的地址,然后客户端连接上Redis并进行后续的操作。当主节点宕机的时候,哨兵监测到主节点宕机,会重新推选出某个表现良好的从节点成为新的主节点,然后通过发布订阅模式通知其他的从服务器,让它们切换主机。

解决高可用性方案:

由一个或者多个Sentinel实例组成系统可以监视任意多个主服务器,以及这些住服务器属下的所有从服务器,并且被监视的住服务器下线状态时,自动将属下的某个从服务器升级为住服务器。简单的说就是哨兵就是带有自动故障转移功能的主从架构。

  • 无法解决:

1、单节点并发压力问题

2、单节点内存和磁盘物理上限问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qzXar1MB-1650014652079)(https://pizximfzuc.feishu.cn/space/api/box/stream/download/asynccode/?code=MTkyMmY0NzRjNjExMTM1M2U5NmZmOGFkM2RlMjc1N2FfTjZwcWpwaXNjeUtoSDNMUlQ1R1pEWmNZRTQ4NmRMU1lfVG9rZW46Ym94Y25zNTdrclZYeTdqbzl3WFdiU1Y3MHpCXzE2NTAwMTQ2MDQ6MTY1MDAxODIwNF9WNA)]

工作原理

  • 每个Sentinel以每秒钟一次的频率向它所知道的MasterSlave以及其他 Sentinel实例发送一个 PING命令。

  • 如果一个实例距离最后一次有效回复 PING 命令的时间超过指定值, 则这个实例会被 Sentine 标记为主观下线。

  • 如果一个Master被标记为主观下线,则正在监视这个Master的所有 Sentinel要以每秒一次的频率确认Master是否真正进入主观下线状态。

  • 当有足够数量的 Sentinel(大于等于配置文件指定值)在指定的时间范围内确认Master的确进入了主观下线状态, 则Master会被标记为客观下线 。若没有足够数量的 Sentinel同意 Master 已经下线, Master 的客观下线状态就会被解除。若 Master重新向 SentinelPING 命令返回有效回复, Master 的主观下线状态就会被移除。

  • 哨兵节点会选举出哨兵 leader,负责故障转移的工作。

  • 哨兵 leader 会推选出某个表现良好的从节点成为新的主节点,然后通知其他从节点更新主节点信息。

10、说一下 Redis 和Memcached 的区别和共同点

  1. Redis 只使用单核,而 Memcached 可以使用多核。

  2. MemCached 数据结构单一,仅用来缓存数据,而 Redis 支持多种数据类型

  3. MemCached 不支持数据持久化,重启后数据会消失。Redis 支持数据持久化

  4. Redis 提供主从同步机制和 cluster 集群部署能力,能够提供高可用服务。Memcached 没有提供原生的集群模式,需要依靠客户端实现往集群中分片写入数据。

  5. Redis 的速度比 Memcached 快很多。

  6. Redis 使用单线程的多路 IO 复用模型,Memcached使用多线程的非阻塞 IO 模型。

11、缓存数据的处理流程是怎样的?

简单来说就是:

  1. 如果用户请求的数据在缓存中就直接返回。
  2. 缓存中不存在的话就看数据库中是否存在。
  3. 数据库中存在的话就更新缓存中的数据。
  4. 数据库中不存在的话就返回空数据。

12、为什么要用 Redis为什么要用缓存?

简单,来说使用缓存主要是为了提升用户体验以及应对更多的用户。

下面我们主要从“高性能”和“高并发”这两点来看待这个问题。

高性能

对照上面

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/493348
推荐阅读
相关标签