Redis常见面试题(第一季)





我是小小的程序员一枚,北京某厂搬砖,计划以系列的方式写文章,如果觉得文章对您有帮助,请动动小手点播关注~~~

前言

上篇文章介绍了Redis五种基本类型的底层实现(Reids五种数据类型的底层实现),点击括号中的文字可以查看,这篇文章从概念的角度介绍Redis(6.2.6)在使用过程中需要注意的一些问题。PS:文章中可能涉及到Redis配置文件信息,版本不同,参数的名字可能有出入。

1:Redis为什么快?

  • 基于内存操作:Redis中所有数据都存内存,内存操作要比硬盘操作快得多;
  • 特殊的数据结构:Redis中的数据结构都是经过精心设计的,上篇文章中提到了(Reids五种数据类型的底层实现);
  • Redis是单线程,避免了多个线程之间线程切换和锁资源竞争用的开销;
  • I/O多路复用和非阻塞I/O:使用 I/O多路复用功能来监听多个 socket连接客户端,这样就可以使用一个线程连接来处理多个请求,减少线程切换带来的开销,非阻塞I/O避免了 I/O 阻塞操作。

2:Redis持久化机制?

Redis虽然是基于计算机内存操作的数据库,但肯定也支持持久化操作(把数据保存至电脑硬盘),如果没有持久化操作,Redis突然宕机,就会造成Redis中数据丢失问题。在Redis中提供了两种持久化方式。RDB方式默认开启,AOF方式默认不开启(如果RDB和AOF同时开启,将会采用AOF的方式持久化数据。)

  • RDB (Redis DataBase)
  • AOF (Append Of File)

2.1:RDB

在指定的时间间隔内将内存中的数据以快照的方式写入到磁盘,简单的理解就是,每过一段时间,Redis就会把内存中的数据咔嚓(拍照)一下,然后将咔嚓到的数据持久化到本地,咔嚓的时间间隔可以指定。此模式默认开启

2.1.1:RDB执行过程
  • 如果执行的是save命令,Reids会用主进程持久化文件,在此期间,Redis主进程不能接受任何命令请求;
  • 如果是执行的bgsave命令,Redis会单独创建(fork)一个子进程来进行持久化操作。在此期间,Redis主进程可以接受任何命令请求。

无论执行哪个命令进行持久化数据,Redis都会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化文件。其中,先写入到临时文件的目的:如果持久化的过程中出现异常导致数据丢失,还可以用上一次上次持久化文件中的数据。持久化完成后默认(可以指定文件名称)会生成一个dump.rdb文件,Redis断电重启时读取dump.rdb文件中的数据。

  • 简要流程图Redis常见面试题(第一季)
2.1.2 触发快照

下图是Redis配置文件中触发快照条件的截图:Redis常见面试题(第一季)

  • 3600秒内有至少1个key发生变化;
  • 300秒内有至少100个key发生变化;
  • 60秒内有至少10000个key发生变化。

满足以上三个条件之一就会持久化数据,这些都是Redis中的默认值,也可以手动指定。

2.1.3:优点
  • 适合大规模的数据恢复,全量备份、全量复制的场景;
  • 对数据完整性要求不高的场景可以使用;
  • 恢复速度快。
2.1.4:缺点
  • 在备份周期在一定间隔时间做一次备份,所以如果Redis意外down掉的话,就会丢失最后一次快照后的所有修改。
  • 虽然Redis在fork时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能。

2.2:AOF

日志的形式来记录每个写操作,追加的方式来记录数据(即保存增量),将Redis执行过的所有写指令记录下来(读操作不记录),生成一个appendonly.aof文件,每次操作,都会以redis启动之初会读取该文件重新构建数据,也就是说,redis 重启,就根据日志文件中的命令执行一次,以完成数据的恢复工作。默认不开启

2.2.1:AOF执行过程

1:客户端的请求写命令会被append追加到AOF缓冲区内;
2:AOF缓冲区根据AOF持久化策略将操作将数据同步到磁盘的AOF文件中;
3:AOF文件大小超过重写策略或手动重写时,会对AOF文件rewrite重写,压缩AOF文件容量;
4:Redis服务重启时,会重新load加载AOF文件中的写操作达到数据恢复的目的。• 简要流程图Redis常见面试题(第一季)

注意:AOF的具体持久化数据的方式三种:

  • 始终同步,每次Redis的写入都会立刻记入日志;性能差但数据完整性比好;
  • 每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失;
  • redis不主动进行同步,把同步时机交给操作系统。
2.2.3:优点
  • 备份机制更加稳健,丢失数据概率更低;
  • 可读的日志文本,通过操作AOF更加稳健,可以处理误操作。
2.2.4:缺点
  • 仅记录数据,还要记录操作,aof文件的大小要大于rdb格式的文件;
  • 恢复备份速度要慢;
  • 每次读写都同步的话,容易造成系统卡顿。

3:Redis缓存穿透?

使用: 一般情况下,为了减小数据库的压力,会把数据库的中的一些数据放到MySQL中,这样请求会  先从缓存中查询数据,如果缓存中没有,再去数据库中查,然后更新缓存,在返回结果。

Redis常见面试题(第一季)

缓存穿透现象: 某种原因服务器请求增大,而且这些数据Redis中没有,大量的请求直接查数据库,给数据库造成很大的压力,甚至造成数据库不可用。或者去查询一个缓存中和数据库中不存在的值。

解决:

  • 对空值进行缓存: 如果查询的数据Redis和数据库都为不存在,可以把这个为空的结果放到缓存中,并设置较为短暂的过期时间;
  • 设置访问名单: 如果是恶意攻击,指定可访问的请求;
  • 布隆过滤器(BloomFilter): 请求过来时,先从布隆过滤器中查询,如果过滤器中有,放行,如果没有直接返回。

4:Redis缓存击穿?

缓存击穿现象: Redis中的某一个key过期,这时,有大量的请求过来,都需要使用这个key,Redis中不存在,大量的请求到数据库中查询。对数据库造成很大的压力。

解决:

  • 预先设置热门数据: 在redis高峰访问之前,把一些热门数据提前存入到redis里面,加长这些热门数据key的时长;
  • 使用互斥锁: 在缓存失效时,不是立即去数据库读取数据,而是,先使用缓存工具的某些带成功操作返回值的操作(Redis的SETNX)去set一个mutex key,当操作返回成功时,再读取数据库并把值写到缓存;否则,就重新获取缓存。

5:Redis缓存雪崩?

缓存雪崩现象: 大量的key在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从数据库加载数据并保存到缓存中,这个时候大并发的请求可能会瞬间把数据库压垮。

注意:缓存击穿是一个key过期,大量的请求访问这一个key;缓存雪崩是大量的key过期,大量的请求访问这一批key

解决:

  • 设置过期标志更新缓存: 专门设置一个值来记录缓存数据是否过期,如果过期就通知另外的线程在后台去更新这个key的缓存。
  • 缓存失效时间加上随机值:比如原有的过期时间固定为60分钟,在这个固定时间上随机加上1-5分钟的时间,这样尽量减少缓存在同一时间内过期。

6:Redis过期、淘汰策略?

6.1:Redis过期策略

Redis是基于内存操作的,Redis中的数据会加载到内存中,常见的电脑的运行内存有16G,32G等。试想如果电脑内存满了怎么办呢?Redis为了解决这个问题设计了三大策略:

  • 定期删除: 采用时间换空间的思想,Redis会为每个key设置过期时间,到期后就会对这个过期key进行删除,其优点是数据过期就进行清理,节省内存;缺点是对CPU不友好,CPU需要时刻都要处理过期的数据;
  • 惰性删除: 采用空间换时间的思想,当Redis中的key到达过期时间,不做处理。等下次访问该数据时,返回不存在,并删除。优点:对CPU友好,CPU不需要时刻处理过期的数据。缺点:对内存不友好,有些key已经过期,一直没有请求访问,那么这个key永远不会被删除;
  • 定时删除: 该方案是以上两种方案的折中办法:定时删除策略每隔一段时间执行一次删除过期键操作,时间可以手动指定。但是该策略每次执行都会选取一部分key,假如有的key一直没有被选中,这条数据将无法被删除。

存在问题: 在Redis中,使用的是惰性删除 + 定时删除,这时是不是会想:如果某些key没有被使用,而且没有被定时删除选中,那么他会一直存在Redis中,随着这种key的增多,Redis所占内存的内存越来越大,直到有一天会把内存占满,造成OOM,为了解决这个问题Redis中还有内存淘汰策略

当内存不足时会执行以下指定的内存淘汰策略:

  • volatile-lru:对设置过期时间的key采用LRU(最近最少使用)算法进行淘汰;
  • allkeys-lru:对所有的key采用LRU(最近最少使用)算法进行淘汰;
  • volatile-lfu:对设置过期时间的key采用LFU(最不经常使用)算法进行淘汰;
  • allkeys-lfu:对任何的key采用LFU(最不经常使用)算法进行淘汰;
  • volatile-random:从设置过期时间的key中随机淘汰数据;
  • allkeys-random:从所有的key中随机淘汰数据;
  • volatile-ttl:淘汰距离过期时间最近的key;
  • noeviction:不删除任何key。

7:Redis高可用?

在生产环境中,Redis不会部署一台,如果只要有一台的话,这台机器出现一旦问题,会造成服务不可用,所以生产环境中一般都会多机器部署,高可用模式一般分为一下三种:

  • 主从模式
  • 哨兵模式
  • 集群模式

7.1:主从模式

主从模式是多个Redis部署,即一主多从模式,主节点(master)负责写数据,从节点(slave)负责读数据,这样就做到了读写分离。主节点新写入的数据也会同步到从节点中。

Redis常见面试题(第一季)
7.1.1:主从复制过程

1:从服务器第一次启动完成后,会进行全量复制,向主服务器发送一个SYNC命令。

2:主服务器收到SYNC命令后,会执行BGSAVE命令,把数据持久化,生成一个RDB文件,并在此期间,生成一个缓冲区来记录新写入主服务器数据的命令。

3:等BGSAVE命令执行完成后,主服务会将生成的RDB文件发送至从服务器,从服务器读取这个RDB文件,完成数据同步(全量复制)。

4:如果主服务器又有新的数据写入,主服务器会将自己缓冲区的写命令发送至从服务器,从服务器执行这些命令,完成写入操作(增量复制)。

具体步骤如下图:

Redis常见面试题(第一季)

注意:

  • 一:上图中的步骤7的是全量复制,步骤9是增量复制,但是如果是新加的从服务器,就必须的完成全量复制,在Reids旧版本中(2.8以前),没有新加从节点,原有的从节点断线重连了,它断线重连后依旧会执行SYNC命令,这样无论主节点还是从节点来说,都是有性能的开销,导致效率低下。所以从2.8版本开始,使用PSYNC替代了SYNC命令。如果从服务器断线重连,在条件允许的情况下,从服务器不会全量复制,而进行类似增量复制的操作。
  • 二:如果主节点挂掉,从节点是不会上位成主机,如果非要从节点上位成主节点,需要人工操作。

7.1.2:优缺点

优点

实现数据的读写分离,部分从节点出问题,服务依然可用,从节点可以扩展主库节点的读能力,有效应对大并发量的读操作。

缺点

故障恢复复杂,当主库节点出现故障时,需要手动将一个从节点晋升为主节点,此时Redis的IP地址发生变化,需要开发者修改代码中连接Redis的配置文件,整个过程需要人为干预,比较繁琐。

7.2:哨兵模式(Sentinel)

哨兵 (Sentinel)模式是Redis高可用的解决方案之一,由一个或多个Sentinel监听Redis的主从服务,该模式可以理解为主从模式的加强版,此模式能够监控主节点是否有故障,如果主节点出现故障,从节点会自动投票选出新的主节点。注意:有故障的主节点重新启动,这时它就会变成从节点。

Redis常见面试题(第一季)
7.2.1:哨兵作用
  • 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
  • 当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器修改配置文件,让它们切换主机。
7.2.2:选举规则

sentinel检测到主服务下线后,将会在从节点中挑选一个服务,将其转成主节点:

判断条件:

  • 选择优先级靠前的:在Redis的redis.conf文件中,有一个默认的:slave-priority=100值,值越小优先级越高。
  • 如果优先级一样,sentinel会,选择偏移最量大的slaver:偏移量是指获得原Master节点数据最全的。
  • 如果优先级和偏移量都一样,然后选择runid最小的:每个Redis启动后都会生成一个40位的runid。
  • sentinel向被选中的从服务器发送SLAVEOF命令,发送完成之后,再以一定的频率发送INFO命令,并观察命令回复中的角色信息,检测到被选中的从服务器角色已经变成Master时,Sentinel才知道被选中的slaver升级成了Master
  • 当已下线的Master重新上线时,sentinel会发送SLAVEOF命令,让其变为从节点。

注意: 如果sentinel是多节点部署,每个哨兵都会经过以上的选举步骤,有N个以上sentinel同意才能完成准备切换(N是自定义的)。

  • 主观下线:假设主服务器宕机,哨兵A先检测到这个结果,系统并不会马上进行故障切换,此时,哨兵A主观的认为主服务器不可用。
  • 客观下线:当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时(N),那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行故障切换操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机。

Master认定为客观下线时,sentinel才进行选举。

7.2.3:优缺点

优点

  • 部署简单;
  • 能够解决 Redis 主从模式下的高可用主从切换问题;
  • 开发者只是配置对外暴露的sentinel地址,如果Redis发生主从切换,使用方无需修改Redis的IP地址。

缺点

1:消耗资源,Sentinel本身就是特殊配置的Redis服务;2:哨兵模式下每台 Redis 服务器都存储相同的数据,很浪费内存空间;3:哨兵模式始终只有一个Redis主机来接收和处理写请求,写操作还是受单机瓶颈影响,没有实现真正的分布式架构。

7.3:集群模式(Cluster)

哨兵模式已经实现了高可用,但是在数据量大的情况下,也会出现问题,比如:Redis的容量不够,哨兵模式下无法扩容,并发写操作也无法实现,而且哨兵模式下只能有一个Reids主机来接收写请求,等等。。。所以,在Redis3.0中,加入了Cluster模式,该模式将数据按照一定的规则将数据分配到了多台机器上(数据分片),不但解决了单机Redis容量有限问题,还解实现了并发写。

7.3.1:插槽(slot)

有Redis集群,必须说到插槽,因为Redis是通过插槽算法完成数据分配的。在一个Redis集群中会有16384个插槽

集群中每个节包含一部分插槽,举个例子:假如该集群中有A、B、C3个节点:

其中:

  • 节点 A 负责处理 0 号至 5460 号插槽;
  • 节点 B 负责处理 5461 号至 10922 号插槽;
  • 节点 C 负责处理 10923 号至 16383 号插槽。

当有集群有写入操作时,会对写入的key通过CRC16算法产生一个16bit的值,然后用这个值对16384进行取模运算,最后得出这个数据写入到集群的节点:

Redis常见面试题(第一季)

8:Redis的事务?

Redis中的事务主要作用是串联多个命令防止别的命令插队,通过MULTI、EXEC、WATCH等命令来实现事务功能。

简单的说具有三大特性:

  • 单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断;
  • 无隔离级别:队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行;
  • 不保证原子性:事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚。

大致流程如下:

  • 开始事务(执行MULTI)
  • 命令入队(操作数据)
  • 事务执行(EXEC)或  撤销事务(DISCARD)
Redis常见面试题(第一季)

常见的操作命令:

命令 描述
MULTI 执行该命令标志事务开始(此时操作数据,会将操作放在一个队列中)
EXEC 顺序执行队列中的命令
DISCARD 放弃执行队列中的命令
WATCH 该命令是一个乐观锁监视命令,监视Redis中数据的key是否至少有一个被修改。
UNWATCH 解除对WATCH命令对所有key的监视

因为能力有限,先对简单的问题进行解答,Redis有关的问题不只这些,接下来依旧会写Redis相关问题。





你懂的越多,不懂的越多,我是小小的程序员,咱们下期见~~~

                              码字不易,最后请大家动动小手,点波关注                          



原文始发于微信公众号(Java的编程之美):Redis常见面试题(第一季)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/37778.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!