2024年最新Redis常见面试题及解答_redis 7,面试突击版

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

dict \*dict;
//跳表:用来存储集合元素及元素直接的跳跃关系
zskiplist \*zsl;

} zset;


### 9.4 hashtable(字典)


`Redis`中的`hashtable`跟`Java`中的`HashMap`类似,都是通过"数组+链表"的实现方式解决部分的哈希冲突。


`字典的定义`



typedef struct dict {
//类型特定函数,可以根据所存数据类型的不同,指向不同的数据操作实现
dicType **type;
/私有数据,配合type使用
void *privdate;
//hash表
dictht ht[2];
//rehash索引,当不进行rehash时值伟-1
int trehashidx;

}dict;


**hash表:**



typedef struct dictht {
//哈希表数组
dicEntry **table;
//哈希表大小
unsigned long size;
//已有节点数量
unsigned long used;

}dictht;


**hash表节点:**



typedef struct dicEntry {
//键
void *key
//值
union{
void *val;
}v;

//下一个节点指向
struct dicEntry \*next;

}dicEntry;


**`Redis`的`Hash`的字典的结构图:**  
 ![Redis的Hash的字典的结构图](https://img-blog.csdnimg.cn/eed55ab143d24847944738a811396303.png#pic_center)


## 10 Redis的AOF和RDB


### 10.1 Redis缓存宕机如何处理


1. **监控和警报系统**:设置监控和警报系统,以便在`Redis`出现故障时及时收到通知。这可以帮助快速采取行动,减少停机时间。
2. **备份和恢复**:定期备份`Redis`数据是一种好的实践。如果发生宕机,可以使用备份数据来恢复`Redis`的状态。确保备份是定期进行的,并测试备份的可恢复性。
3. **高可用架构**:考虑使用`Redis`的高可用架构,如主从复制或`Redis`集群。主从复制允许在主`Redis`实例宕机时使用从实例继续提供服务。`Redis`集群则可以将数据分布在多个节点上,提高可用性和性能
4. **持久化策略**:`Redis`提供了两种主要的持久化方式,分别是`RDB`快照和`AOF`日志。通过配置适当的持久化策略,可以在宕机发生时最大程度地减少数据丢失。
5. **故障转移**:如果使用主从复制,当主节点宕机时,可以手动或自动地将从节点提升为新的主节点。这将允许继续提供服务,同时可以修复原始主节点。
6. **负载均衡**:如果使用`Redis`集群,可以通过负载均衡将请求分发到不同的`Redis`节点上。这有助于减轻单个节点宕机的影响。
7. **监控和分析**:使用监控工具来实时监测`Redis`的性能和健康状况。这将能够预测问题并采取措施,以防止宕机。
8. **容错处理**:在应用程序代码中实现适当的容错处理。如果`Redis`缓存不可用,应用程序应该能够从备用数据源获取数据或以某种方式继续运行,而不会导致严重故障。


### 10.2 RDB机制


`RDB(Redis Database Backup)`是`Redis`默认的持久化方式,它是在指定的时间间隔内把数据以快照的形式保存在磁盘上,实际是写入到二进制文件中,默认的文件名为`dump.rdb`。


* 可以在配置文件中设置触发`RDB`快照的条件,例如每隔一段时间或在达到一定的写入操作次数后。
* 当触发条件满足时,`Redis`会`fork`一个子进程。这个子进程负责生成`RDB`文件,这个`RDB`文件是一个二进制文件,包含了当前内存中的数据快照。
* 在子进程生成`RDB`文件的过程中,`Redis`主进程仍然继续处理命令请求。
* 一旦`RDB`文件生成完毕,`Redis`会用新的`RDB`文件覆盖旧的`RDB`文件,以实现数据持久化。
* 在`Redis`重启时,它会尝试加载最新的`RDB`文件,将数据恢复到内存中。



> 
> 在安装了`Redis`之后,所有的配置都是在`redis.conf`文件中,里面保存了`RDB`和`AOF`两种持久化机制的各种配置。
> 
> 
> 


`RDB`机制的触发时机有两个:手动触发、自动触发。


#### 10.2.1 手动触发


手动触发是通过向`Redis`发送特定的命令来手动触发`RDB`快照生成,比如使用`save`或`bgsave`命令。


但是使用`save`命令会阻塞当前`Redis`服务器,因为它会在生成`RDB`文件时暂停处理其他命令请求,直到`RDB`过程完成为止。如果存在旧的`RDB`文件,一旦新的`RDB`文件生成完毕,`Redis`会用该文件覆盖旧的`RDB`文件,以实现数据持久化。


连接`Redis`服务器的客户端可能都是几万或者是几十万,这种方式会有性能问题,不推荐使用。


#### 10.2.2 自动触发


自动触发是通过配置文件来实现的。在`redis.conf`配置文件中,里面有触发`save`命令的配置,可以去设置。


比如“`save m n`”。表示`m`秒内数据集存在`n`次修改时,自动触发`RDB`快照。  
 **默认如下配置:**


* `save 900 1`:表示900秒内如果至少有1个`key`的值变化;
* `save 300 10`:表示300秒内如果至少有10个`key`的值变化;
* `save 60 10000`:表示60秒内如果至少有10000个`key`的值变化;


可以同时配置多个。如果不需要自动持久化,可以注释掉所有的`save`行来停用保存功能。


**其他参数:**


* `stop-writes-on-bgsave-error`:默认值为`yes`。当启用了`RDB`且最后一次后台保存数据失败,`Redis`是否停止接收数据。这会让用户意识到数据没有正确持久化到磁盘上,否则没有人会注意到灾难(`disaster`)发生了。如果`Redis`重启了,那么又可以重新开始接收数据了。
* `rdbcompression`:默认值是`yes`。对于存储到磁盘中的快照,可以设置是否进行压缩存储。
* `rdbchecksum`:默认值是`yes`。在存储快照后,我们还可以让`Redis`使用`CRC64`算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。
* `dbfilename`:设置快照的文件名,默认是`dump.rdb`。
* `dir`:设置快照文件的存放路径,这个配置项一定是个目录,而不能是文件名。


#### 10.2.3 主从复制刚开始时触发


当`Redis`的从节点与主节点进行初次同步(复制)时,主节点会生成一个`RDB`快照,并将该快照发送给从节点。这有助于加速从节点的初始化同步过程。


#### 10.2.4 save和bgsave命令的区别


1. **`save`**:命令会阻塞当前`Redis`服务器,因为它会在生成`RDB`文件时暂停处理其他命令请求,直到`RDB`过程完成为止。如果存在旧的`RDB`文件,一旦新的`RDB`文件生成完毕,`Redis`会用该文件覆盖旧的`RDB`文件,以实现数据持久化。
2. **`bgsave`**:执行该命令时,`Redis`会在后台异步进行快照操作,快照同时还可以响应客户端请求。  
 具体操作是`Redis`进程会执行`fork`操作创建子进程,`RDB`持久化过程由子进程负责,完成后自动结束。阻塞只发生在`fork`阶段,一般时间很短。基本上`Redis`内部所有的`RDB`操作都是采用`bgsave`命令。


#### 10.2.5 RDB的优缺点


**优点:**


1. **紧凑的文件格式**:`RDB`文件是一个紧凑的二进制文件,包含了整个`Redis`数据库的数据快照。这使得`RDB`文件适用于备份和迁移数据,以及在恢复时占用较小的存储空间。
2. **恢复速度快**:由于`RDB`文件是一个完整的数据快照,恢复速度通常比`AOF`日志文件要快。这对于在灾难恢复情况下尤为重要。
3. **适合冷备份**:`RDB`适合用于生成冷备份,可以在需要备份时手动触发`RDB`生成。这有助于在数据库状态稳定的情况下进行备份,以避免备份过程中的数据变动。
4. **性能较低的环境下更友好**:`RDB`生成过程可以在`Redis`子进程中进行,这有助于减轻主进程的负担,使其在高负载或性能较低的环境下更加稳定。


**缺点:**


1. **数据丢失风险**:由于`RDB`是基于时间或写入操作次数来触发的,如果在最近一次`RDB`生成和`Redis`宕机之间发生了数据写入,那么这部分数据将会丢失。
2. **不适合持续更新**:`RDB`适用于周期性的数据快照,而不适合需要实时持续更新的应用。由于生成`RDB`文件会导致`Redis`主进程在一段时间内阻塞,这可能会影响应用程序的性能。
3. **RDB适应性差**:`RDB`文件仅包含数据快照,如果`Redis`在上次`RDB`生成和宕机之间崩溃,那么`AOF`日志文件将会是更可靠的数据恢复来源。
4. **文件变大**:随着时间的推移,`RDB`文件会变得相对较大,因为它包含了所有数据的快照。这会影响备份和恢复的速度。


### 10.3 AOF机制


`AOF(Append-Only File)`是`Redis`的另一种持久化机制,因为`RDB`全量备份是比较耗时的,所以`Redis`提供一种更加高效的方式`AOF`,工作机制就是`Redis`会将每一个收到的写命令都通过`write`函数追加到日志文件中,以实现数据的持久化。`AOF`机制相对于`RDB`机制,更加持久,允许在`Redis`重启时通过重新执行写入操作来恢复数据。


#### 10.3.1 文件重写原理(针对AOF文件比较大的情况,Redis做的优化)


`AOF`的方式也同时带来了另一个问题。持久化文件会变的越来越大。为了压缩`AOF`的持久化文件。`Redis`提供了`bgrewriteaof`命令。将内存中的数据以命令的方式保存到临时文件中,同时会`fork`出一条新进程来将文件重写。


**具体优化:**


1. **`AOF`重写**:`Redis`引入了`AOF`重写机制,它允许在不中断服务的情况下创建一个新的`AOF`文件,其中只包含从数据库重启后的写入操作。这可以通过运行一个后台进程来完成,该进程分析现有`AOF`文件,然后根据数据库的当前状态重写新的`AOF`文件。这样可以减小`AOF`文件的大小并且不会影响性能。
2. **`AOF`重写触发**: 可以通过配置`auto-aof-rewrite-percentage`和`auto-aof-rewrite-min-size`参数来触发`AOF`重写。当`AOF`文件的大小达到`auto-aof-rewrite-min-size`并且文件增长量超过了当前文件大小的`auto-aof-rewrite-percentage`时,`Redis`会自动触发`AOF`重写。
3. **`AOF`重写后合并**: 在`AOF`重写过程中,如果发现有多条命令对同一个键执行了多次操作,那么只保留最终状态的写入操作,从而避免重复写入导致的数据冗余。
4. **压缩指令**: 在`AOF`文件中,`Redis`可以使用一些优化技术来减小指令的存储空间,从而降低`AOF`文件的大小。


#### 10.3.2 AOF也有三种触发机制


1. **always(始终同步)**:`Redis`会将每次执行的操作命令追加到`AOF`文件中,从而确保每次操作都被持久化到磁盘。这是最高级别的数据保护,**但也会产生显著的性能开销,因为每个操作都需要同步到磁盘**。  
 在配置文件中配置方式:`appendfsync always`
2. **everysec(每秒同步)**:`Redis`会每秒执行一次,将已缓冲的操作命令同步追加到`AOF`文件。这种方式在性能和持久性之间取得了平衡,一般来说,数据丢失的风险较小。  
 在配置文件中配置方式:`appendfsync everysec`
3. **no(异步)**:`Redis`将操作命令追加到`AOF`文件,但不会主动将数据同步到磁盘,而是由操作系统来处理同步。这提供了最高的性能,但也导致了数据丢失的风险,因为操作命令可能会在写入到磁盘之前存在于内存中。  
 在配置文件中配置方式:`appendfsync no`
4. `Redis`还支持自定义的`appendfsync`值,可以在配置文件中指定一个时间间隔,例如`appendfsync 100`,表示每隔`100`毫秒将缓冲的写入操作同步到磁盘。


选择适当的`AOF`触发机制取决于应用需求,需要根据性能和数据保护之间的权衡来进行选择。通常,**everysec是一种常见的配置,因为它在大多数情况下可以提供良好的性能和可靠的持久性。** 如果对性能要求非常高,**并且可以容忍一些数据丢失风险,那么可以考虑使用no触发机制。**


#### 10.3.3 AOF的优缺点


**优点:**


1. **高持久性**: `AOF`记录了每个写入操作,因此相对于`RDB`持久化,数据恢复的能力更强,数据丢失的风险更低。
2. **数据重放**: `AOF`文件可以被重新执行,使得在灾难恢复和故障转移的情况下更容易恢复数据。
3. **可读性**: `AOF`文件是一个文本文件,可以查看其内容,以了解`Redis`的历史写入操作。
4. **部分持久性**: 根据`appendfsync`的设置,可以在性能和持久性之间做出权衡。
5. **灵活性**: `AOF`支持多种同步选项,可以根据应用需求调整性能和数据保护级别。


**缺点:**


1. **文件大小**: 随着时间的推移,`AOF`文件可能会变得很大,影响备份、迁移和恢复的速度。
2. **写入延迟**: 在`everysec`或`no`模式下,数据可能会在一段时间内只存在于内存中,未持久化到磁盘,可能会导致数据丢失。
3. **性能开销**: 在较高的`appendfsync`设置下,`AOF`持久化会对性能产生一定影响。
4. **文件碎片**: `AOF`文件可能会出现碎片问题,特别是在文件大小频繁增加和重写时,这可能会影响磁盘空间的利用率。
5. **恢复速度**: 在某些情况下,使用`AOF`恢复数据可能比使用`RDB`恢复数据更慢,因为`AOF`文件中的写入操作需要重新执行。


**总结:**  
 `AOF`持久化适用于需要高持久性和较小数据丢失风险的情况。如果更关注性能和较短的恢复时间,可以考虑在适当情况下使用`RDB`持久化。在实际应用中,可以根据业务需求和性能要求来选择适合的持久化机制或结合使用两种机制以达到平衡。


### 10.4 RDB和AOF的选择


选择的话,两者加一起才更好。因为两个持久化机制明白了,剩下的就是看自己的需求了,需求不同选择的也不一定,但是通常都是结合使用。有一张图可供总结:


**RDB和AOF对比图:**  
 ![RDB和AOF对比图](https://img-blog.csdnimg.cn/65060866f1834581953e706c756d1927.jpeg#pic_center)


## 11 Redis的主从复制


参考1:<https://www.cnblogs.com/FatalFlower/p/15952843.html>


### 11.1 主从复制介绍


`Redis`的主从复制允许将一个`Redis`服务器(主节点)的数据复制到多个其他`Redis`服务器(从节点)上,以实现数据的分发、备份和高可用性。主从复制在许多场景下都非常有用,例如高可用、提高读取性能、数据备份和灾难恢复。


**Redis主从复制的一般工作原理和一些关键概念:**


1. **主节点(Master)**:主节点是数据源,它负责接收客户端的写入操作,并将这些操作应用于自身的数据集。主节点将自己的数据变更记录在`AOF`日志或`RDB`文件中,然后将这些变更通过网络发送给从节点。
2. **从节点(Slave)**:从节点是主节点的复制品,它通过连接到主节点并接收主节点发送的写入操作变更,来保持自身的数据与主节点同步。从节点不能进行写入操作,只能处理读取请求,从而分担主节点的读取压力。
3. **数据同步**:从节点在初始连接时会执行全量复制(全量同步),获取主节点上的所有数据。之后,从节点会持续地从主节点获取增量变更(增量同步),以保持自身数据的一致性。
4. **复制的类型**:
	* **全量复制(Full Resynchronization)**:在初始连接或数据丢失的情况下,从节点会进行全量复制,获取主节点上的所有数据。
	* **增量复制(Incremental Replication)**:在全量复制之后,从节点会持续接收主节点的写入操作,保持数据的同步。
5. **自动重连**:如果从节点与主节点的连接中断,从节点会自动尝试重新连接,并从最近的数据点开始增量复制,以保持数据的一致性。
6. **读写分离**:由于从节点能够处理读取请求,主从复制也可以用于实现读写分离架构,将读请求分发到从节点,从而减轻主节点的负载。
7. **高可用性**:如果主节点发生故障,可以将其中一个从节点提升为新的主节点,实现故障转移,以确保服务的可用性。


**总结:**  
 主从复制为`Redis`提供了高可用性、数据备份、负载均衡等优势。然而,需要注意的是,主从复制并不适用于所有场景,因为从节点仅能提供与主节点相同的数据,不能实现跨主节点的数据查询。


### 11.2 主从复制的目的及优点


主从复制(`Master-Slave Replication`)是`Redis`中一种重要的数据复制机制,其目的是将一个`Redis`服务器(主节点)的数据实时复制到其他多个`Redis`服务器(从节点),以实现数据的分发、备份、高可用性和负载均衡等目标。主从复制的优点如下:


1. **高可用性**:主从复制可以提高系统的可用性。当主节点出现故障时,可以将一个从节点升级为新的主节点,使系统能够继续提供服务,减少停机时间。
2. **数据备份**:从节点保存了主节点的数据的副本,因此可以在数据丢失、破坏或误删除时用作备份。这对于数据的恢复和业务连续性非常重要。
3. **读写分离**:从节点可以用于处理读取请求,从而分担主节点的负载。这有助于提高主节点的写入性能,并实现负载均衡。
4. **数据分发**:主从复制可以将数据在多个节点之间分发,这在分布式系统中非常有用。从而,各个节点可以提供就近的服务,减少网络延迟。
5. **灾难恢复**:如果主节点遭受硬件故障、数据损坏或其他灾难,从节点可以用作快速的灾难恢复手段。
6. **升级和维护**:当需要对主节点进行升级、维护或配置更改时,可以先将其降级为从节点,然后升级从节点。这样可以避免服务中断。
7. **实时数据复制**:主从复制可以实现实时的数据同步,从而保持从节点的数据与主节点的数据一致。
8. **实验和分析**:可以使用从节点来进行实验、分析和性能测试,而不影响主节点的生产环境。


虽然主从复制具有许多优点,但也需要注意它的一些限制。比如:**从节点的数据与主节点的数据相同,不能进行跨主节点的数据查询。** 此外,**主节点出现故障时,进行故障转移可能需要一些手动操作,具体取决于配置和部署。** 综合考虑业务需求和系统架构,决定是否采用主从复制以及如何配置和管理它。


### 11.3 主从复制的实现过程


`Redis`的主从复制分为以下几个阶段:


1. **连接阶段**:从节点首先与主节点建立连接。从节点发送一个`SYNC`命令给主节点,表示它希望进行全量同步。
2. **全量同步阶段**:在连接建立后,主节点开始执行全量同步过程。
3. **增量同步阶段**: 一旦全量同步完成,从节点进入增量同步阶段。在此阶段,主节点会持续地将写入操作发送给从节点,从节点接收并执行这些操作,以保持数据的实时一致性。
4. **持续同步和保持连接**: 从节点持续地向主节点发送心跳信号,以保持连接活跃。如果连接断开,从节点会尝试重新连接,然后继续进行增量同步。
5. **故障转移阶段(可选)**: 如果主节点发生故障,从节点可以被提升为新的主节点,以实现故障转移和高可用性。


#### 11.3.1 全量同步具体步骤


在`Redis`主从复制中,**全量同步(Full Resynchronization)是在从节点初始连接到主节点时所执行的过程,用于将主节点上的数据完整地传输到从节点,以确保从节点的数据与主节点的数据一致**。以下是`Redis`主从复制中全量同步的具体步骤:


1. **从节点连接到主节点**: 当从节点启动或重新连接时,它会发送一个`SYNC`命令给主节点,表示它希望进行全量同步。
2. **主节点接受`SYNC`命令**: 主节点收到从节点的`SYNC`命令后,会准备进行全量同步。
3. **主节点执行`BGSAVE`(可选)**: 主节点在开始全量同步之前,可以执行一个后台持久化操作,即执行`BGSAVE`命令生成`RDB`快照文件。这是为了确保在全量同步期间主节点的数据不会发生大的变化。
4. **生成`RDB`文件**: 如果主节点执行了`BGSAVE`,它会生成一个`RDB`快照文件,其中包含了当前主节点的数据。
5. **发送`RDB`文件和`AOF`偏移量**: 主节点将生成的`RDB`文件发送给从节点,并发送当前的`AOF`偏移量,表示数据变更在`AOF`日志中的位置。
6. **从节点载入`RDB`文件**: 从节点接收到`RDB`文件后,会载入该文件,将其中的数据加载到自己的内存中。
7. **发送`PSYNC`命令**: 从节点完成`RDB`文件的载入后,会向主节点发送`PSYNC`命令,携带上次复制的偏移量信息,以告知主节点从哪个位置开始进行增量同步。
8. **主节点进行增量同步**: 主节点接收到从节点的`PSYNC`命令后,根据携带的偏移量信息,开始将从该位置开始的写入操作发送给从节点。
9. **从节点应用增量操作**: 从节点接收并应用主节点发送的增量操作,以使从节点的数据与主节点的数据保持一致。
10. **完成同步**: 一旦从节点赶上主节点的数据,全量同步完成。从节点现在是主节点的复制品,并可以处理读取请求。


需要注意的是,**全量同步是一个初始过程,仅在从节点刚连接到主节点时执行一次**。之后,主节点会持续地将增量写入操作发送给从节点,以保持数据的同步。全量同步的过程确保了从节点的数据与主节点的数据一致性,并为之后的增量同步奠定了基础。


### 11.4 Redis如何保证主从的一致性


1. **全量同步**:在主从复制刚开始时,从节点会进行全量同步。主节点会将自己的整个数据集发送给从节点,确保从节点的数据与主节点一致。
2. **增量同步**:全量同步完成后,主节点会将写入操作记录在`AOF`日志中,并将这些操作发送给从节点。从节点会持续地接收并执行这些写入操作,以保持数据一致性。
3. **命令复制**:从节点接收到主节点的写入操作后,会将这些操作执行到自己的数据集中。这意味着从节点上的数据会按照主节点的操作顺序进行更新,从而保持一致性。
4. **复制偏移量**:主节点会记录每个写入操作的偏移量,表示该操作在`AOF`日志中的位置。从节点会定期向主节点发送自己的复制偏移量,以确保从正确的位置进行增量同步。
5. **断点续传**:如果从节点与主节点的连接中断,从节点会尝试重新连接并从断点位置继续同步。这避免了重复同步已经应用的数据。
6. **异步复制**:主从复制默认是异步的,主节点不会等待从节点应用写入操作。这可能导致从节点的数据略有滞后,但不影响一致性。


需要注意的是,尽管`Redis`通过上述机制来维护主从数据的一致性,但在某些情况下,由于网络延迟、故障等原因,从节点的数据可能会稍微滞后于主节点。因此,在主从复制中,不同节点之间的数据同步是一个近似一致性的概念,而不是强一致性。如果需要更严格的一致性,可以考虑使用`Redis`的哨兵机制或者集群模式。


### 11.5 Redis主从复制为什么不使用AOF?


主从复制中的`AOF(Append-Only File)`持久化和主节点的`AOF`持久化是两个不同的概念。在主从复制中,主节点的`AOF`持久化仍然可以被启用,但在从节点上通常会禁用`AOF`持久化。


**为什么在从节点上不使用`AOF`持久化:**


1. **数据同步的方式**:主从复制的主要目的是将主节点的数据同步到从节点,以实现数据的分发、备份和高可用性。从节点通过执行与主节点一样的写入操作来实现数据同步,因此不需要`AOF`文件来记录写入操作。
2. **数据保护和持久化**:从节点的数据是通过执行主节点的写入操作来同步的,而不是通过`AOF`文件。从节点并不直接处理客户端写入请求,因此不需要保留`AOF`文件来确保数据的持久性和恢复能力。
3. **性能和延迟**:从节点的主要任务是处理读取请求,它需要尽可能快速地响应这些请求。启用`AOF`会增加写入操作的开销,可能会影响从节点的读取性能,并且可能引入额外的写入延迟。
4. **节省空间**:从节点的`AOF`文件不会被用于数据恢复,因为它的数据是通过复制而来。因此,禁用从节点的`AOF`可以节省存储空间。
5. **简化配置和管理**:禁用从节点的`AOF`可以简化配置和管理,减少潜在的错误和混淆。


虽然在从节点上禁用`AOF`持久化是常见的做法,但这不是硬性规定。如果应用场景需要从节点也进行`AOF`持久化,仍然可以在从节点上启用`AOF`。然而,在主从复制的情况下,`AOF`文件在从节点上通常不会被使用,因为数据同步是通过主节点发送写入操作来实现的。


## 12 Redis哨兵机制


`Redis`哨兵(`Sentinel`)是用于监控和管理`Redis`主从复制和高可用性的系统。它可以自动检测主节点故障,进行故障转移,并重新配置从节点以提供高可用性的服务。


哨兵机制是`Redis`高可用性架构的一部分,允许在主节点发生故障时实现自动故障转移,以确保服务的连续性。


**哨兵节点本质上也是一个`Redis`节点**,但是和主节点和从节点不同,哨兵节点只是监视主节点和从节点,并不执行相关的业务操作。


### 12.1 哨兵机制的工作原理


1. **故障检测**:哨兵会定期向`Redis`的主节点和从节点发送心跳检测,以监测它们的状态。如果一个节点不再响应心跳,哨兵首先会将其标记为"主观下线"。
2. **选举新主节点**: 当主节点被标记为"主观下线"后,哨兵会通过`SENTINEL is-masterdown-by-addr`指令获取其它哨兵节点对于当前主节点的判断情况,如果哨兵节点对于当前主节点的主观下线判断数量超过了在配置文件中定义的票数,那么该主节点就被判定为`“ODOWN”(客观下线)`。然后与其他哨兵进行投票,以选择一个从节点升级为新的主节点。
3. **自动故障转移**: 一旦新主节点被选出,哨兵会执行自动故障转移过程。主要为以下步骤:
	* 哨兵会向其他从节点发送命令,让它们将复制目标从旧主节点切换到新主节点。
	* 哨兵会更新客户端的配置,将连接指向新的主节点。
	* 哨兵会更新所有相关节点的配置,以确保节点间的通信正确地指向新主节点。
4. **数据同步**: 一旦新主节点选举出来,从节点会向新主节点进行全量同步,以获取丢失的数据并保持一致性。
5. **故障恢复**: 当故障转移完成后,整个集群会恢复正常运行。新的主节点将负责处理客户端的写入请求,而从节点将继续处理读取请求。
6. **监控和报警**: 哨兵会持续监控集群的状态,如内存使用情况、连接数等,它还可以配置报警,在发生故障转移或其他问题时触发报警,以便在出现问题时通知管理员。
7. **多哨兵支持**: 在大规模的系统中,可以部署多个哨兵实例来提高监控和决策的可靠性。


由于使用单个的哨兵来监视`Redis`集群的节点不是完全可靠的,因为哨兵节点也有可能会出现故障,所以一般情况下会使用多个哨兵节点来监视整个`Redis`集群,如下图所示:  
 ![Redis的哨兵集群图](https://img-blog.csdnimg.cn/7547c81df2d148e7906bc79bb4e0e3de.png#pic_center)  
 **当存在多个`哨兵`节点时,在`Redis`哨兵机制中,对于节点的下线也有区分:**


1. **主观下线(Subjectively Down,即 SDOWN)**:指单个哨兵节点对集群中的节点作出主观下线判断。
2. **客观下线(Objectively Down,即 ODOWN)**:指多个哨兵节点对集群中的节点作出主观下线判断,并且通过`SENTINEL is-master-down-by-addr`命令互相交流之后,作出`Redis`节点下线的判断。一个哨兵节点可以通过向另一个哨兵节点发送`SENTINEL is-master-down-by-addr`命令来询问对方是否认为给定的节点已经下线。


需要注意的是,哨兵机制的目标是实现集群的高可用性,确保在主节点故障时仍然能够提供服务。但哨兵机制并不是完美的,因此在使用过程中需要正确配置和管理,以确保其能够正常工作。另外,考虑使用`Redis`的集群模式也是实现高可用性的一种选择。


### 12.2 Redis哨兵机制中哨兵节点个数设置原则


1. **奇数个节点**:哨兵节点的数量通常选择奇数个,以确保在进行选举和决策时能够达成多数共识。奇数个节点能够更好地处理节点故障、拆分等问题,避免出现僵局情况。
2. **至少三个节点**:最少应该配置三个哨兵节点,以确保能够实现故障检测和自动故障转移。两个节点可能会导致选举过程中出现不确定性。
3. **节点分布**:哨兵节点应该分布在不同的物理机器或虚拟机上,以确保即使一部分节点出现故障,仍然能够保持足够的监控和故障检测能力。
4. **偶数节点不推荐**:避免使用偶数个哨兵节点,因为在故障情况下可能出现投票平局,导致选举无法达成多数共识。
5. **多于三个节点**:在生产环境中,通常建议配置多于三个哨兵节点,例如五个或七个。多个哨兵节点可以提高监控的可靠性,减少误判和误操作。
6. **考虑资源和维护**:哨兵节点也会消耗资源,包括内存和网络带宽。在设置节点数量时,考虑可用的资源和维护成本。


**总结:**  
 设置哨兵节点的数量需要权衡可靠性、资源消耗和管理复杂性。选择适当的奇数个哨兵节点,并确保它们分布在不同的位置,以实现高可用性、故障检测和自动故障转移的目标。


### 12.3 在从节点中选出新的主节点是随机的吗


不是,哨兵在进行主节点故障转移时,选出新的主节点是经过一定的投票和共识过程的,以确保选出的节点是合适的、可用的,并且能够保持数据一致性。


**在投票过程中,哨兵会考虑以下因素:**


1. **健康状态**:哨兵会选择一个被标记为"主观上线"(即正常状态)的从节点作为新的主节点。被标记为"主观下线"的节点不会参与选举。
2. **复制偏移量**:哨兵会考虑每个从节点的复制偏移量,即从节点在`AOF`日志中的位置。复制偏移量较大的从节点可能更适合被选为新主节点,以确保数据的一致性。
3. **投票数**:哨兵会向其他哨兵发送投票请求,然后会根据收到的投票数来决定是否选举某个从节点为新主节点。
4. **优先级**:在一些情况下,可以为从节点配置不同的优先级。优先级高的从节点可能更有可能被选为新的主节点。
5. **其他因素**:哨兵还可以考虑其他因素,如节点的配置、性能等。


## 13 缓存击穿、缓存雪崩、缓存穿透区别及解决方案


参考1:[Redis的三大缓存异常原因分析和解决方案]( )


### 13.1 缓存击穿


`Redis`的缓存击穿是一种性能问题,通常发生在具有高并发访问的系统中。它的产生原因是在缓存中存储了某个热点数据,但在某一时刻,大量并发请求同时访问该热点数据,而此时该热点数据的缓存数据刚好过期或被删除,导致每个请求都需要重新查询数据库或重新生成数据,从而导致数据库负载剧增,系统性能急剧下降。


1. **产生原因**:缓存击穿的情况,经常是发生在热点数据过期失效的情况。
2. **解决方案**:
	* 对于访问很频繁的热点数据,就不需要设置过期时间了。这样对热点数据的访问可以直接在缓存中进行处理,`Redis`的数万级别的高吞吐量可以很好的应对大量的并发请求。
	* **使用布隆过滤器(Bloom Filter)**:布隆过滤器可以用来判断某个数据是否存在于缓存中,可以在查询缓存之前快速排除不存在于缓存中的数据,减少对数据库的请求。
	* **使用缓存预热(Cache Warming)**:在系统启动或数据更新之前,可以通过预先加载常用数据到缓存中,以减少冷启动时的缓存击穿风险。


### 13.2 缓存雪崩


`Redis`的缓存雪崩通常发生在大规模缓存中。它指在某一时刻,大量的缓存数据同时失效或被清除,导致大量请求同时落到数据库上,从而引起数据库负载急剧增加,甚至引发系统崩溃。


1. **产生原因**:产生原因一般有两种情况:


	* **缓存中有大量的数据同时过期,导致请求无法得到处理**。当多个缓存键的失效时间(过期时间)设置得非常接近时,它们可能在同一时刻失效,导致大量请求同时落到数据库上。
	* **`Redis`缓存实例发生故障,宕机了,导致大量请求积压到数据库**。一般来说,一个`Redis`实例可以支持数万级别的请求处理吞吐量,而单个数据库可能只能支持数千级别的请求处理吞吐量,它们两个的处理能力可能相差了近十倍。由于缓存雪崩,`Redis`缓存失效,所以,数据库就可能要承受近十倍的请求压力,从而因为压力过大而崩溃。
2. **解决方案**:  
 **针对缓存中有大量的数据同时过期的情况,可以提供两种解决方案。**


	* **随机失效时间**:给数据的到期时间增加一个较小的随机数,避免给大量的数据设置相同的过期的时间。
	* **使用缓存预热(Cache Warming)**:在系统启动或数据更新之前,可以通过预先加载常用数据到缓存中,以减少冷启动时的缓存击穿风险。
	* **服务降级**:当发生缓存雪崩时,针对不同的数据采取不同的处理方式。  
	 ① **非核心数据**:暂时停止从缓存中查询这些数据,而是直接返回预定义信息、空值或者是错误信息。  
	 ② **核心数据**:缓存数据丢失时通过数据库读取。  
	 使用服务降级的方式,只有部分的数据请求会被发送到数据库,则数据库的压力就没有那么大了。
	* **监控和报警**:实施监控系统,定期检查缓存的状态,当发现缓存出现异常或过期集中时,及时发出警报并采取措施,以防止缓存雪崩。


**针对Redis缓存实例发生故障宕机的情况,同样也有两点建议。**


* **在业务系统中实现服务熔断或者请求限流机制:**  
 ① **服务熔断**:在发生缓存雪崩时,为了防止引发连锁的数据库雪崩,甚至是整个系统的崩溃,可以暂停业务应用对缓存系统的接口访问。  
 ② **请求限流**:在请求入口前端只允许每秒进入系统的请求数为1000个,再多的请求就会在入口前端被直接拒绝服务。
* **提前预防**:通过主从节点的方式构建`Redis`缓存高可靠集群。如果`Redis`缓存的主节点故障宕机了,从节点还可以切换成为主节点,继续提供缓存服务,避免了由于缓存实例宕机而导致的缓存雪崩问题。


### 13.3 缓存穿透


缓存穿透指的是要访问的数据既不在`Redis`中,也不在数据库中,导致请求访问缓存缓缺失。这样一来应用无法从数据库中读取写入缓存,缓存成了摆设,同时给数据库和缓存都带来巨大的压力。


1. **产生原因**:
	* **业务层误操作**:缓存中的数据和数据库中的数据被误删除了,所以缓存中和数据库中都没有数据。
	* **恶意攻击**:恶意用户或攻击者可能会故意发送查询不存在的数据的请求,以耗尽系统资源或尝试破坏应用程序。
2. **解决方案**:
	* **缓存空值或者缺省值**:一旦发生缓存穿透,可针对查询的数据在`Redis`缓存中设置一个短期的空值或者缺省值,当应用发送后续请求进行查询的时候就可以从`Redis`中读取到空值或者缺省值返回,避免大量请求数据库。
	* **使用布隆过滤器快速判断数据是否存在,避免从数据库中查询数据,减轻数据库压力**:通过查询布隆过滤器快速判断数据是否存在,如果不存在就不需要再去数据库中查询了。
	* **在请求入口的前端进行请求检测**:缓存穿透很大一部分原因是有大量的恶意请求访问不存在的数据,所以对业务系统接收到的请求进行合法性检测,把恶意的请求直接过滤掉,不让它们访问后端缓存和数据库。
	* **使用缓存预热(Cache Warming)**:在系统启动或数据更新之前,可以通过预先加载常用数据到缓存中,以减少冷启动时的缓存击穿风险。
	* **监控和报警**:实施监控系统,定期检查缓存的状态,当发现缓存出现异常或过期集中时,及时发出警报并采取措施,以防止缓存穿透。


跟缓存雪崩、缓存击穿这两类问题相比,缓存穿透的影响更大一些。从预防的角度来说,我们需要避免误删除数据库和缓存中的数据;从应对角度来说,我们可以在业务系统中使用缓存空值或缺省值、使用布隆过滤器,以及进行恶意请求检测等方法。


### 13.4 布隆过滤器


布隆过滤器(`Bloom Filter`)是一种用于高效判断一个元素是否属于一个集合的数据结构,它可以快速检查一个元素是否可能在集合中,但具有一定的概率性和容错性。布隆过滤器在一些应用中能够有效地减少不必要的查询,节省计算资源。


**优点:**


1. **空间效率高**:布隆过滤器使用位数组,相对于存储实际元素,它的空间消耗很小。
2. **查询效率高**:布隆过滤器的查询操作非常快速,只需计算哈希并检查位值。
3. **支持大规模数据集**:布隆过滤器适用于处理大规模的数据集,因为它的空间和时间复杂度都与数据集大小无关。
4. **保密性强**:因为布隆过滤器不存储元素本身。


**缺点:**


1. **存在误判**:布隆过滤器可以产生误判,即它可能认为元素在集合中但实际上不在,但不会产生漏判(如果它认为元素不在集合中,那么一定不在)。
2. **无法删除元素**:布隆过滤器中一旦将位设置为1,就无法删除元素。如果需要删除元素,通常需要使用其他技巧,如定时重建布隆过滤器。
3. 无法获取元素本身。


**工作原理:** 布隆过滤器的核心是一个二进制向量数组和一组哈希函数。


1. 初始化:创建一个长度为`m`的位数组,所有位都初始化为0。
2. 添加元素:将待添加的元素通过多个哈希函数映射为位数组中的多个位置,并将这些位置的对应位设置为1。
3. 查询元素:对待查询的元素使用相同的哈希函数,检查对应位置的位值。如果所有位置的位值都为1,说明元素可能存在于集合中;如果有任何一个位置的位值为0,说明元素一定不存在于集合中。


**使用场景:**


1. 解决`Redis`缓存穿透问题。
2. 做邮件的黑名单过滤。
3. 对爬虫网址做过滤,爬过的不在爬。
4. 解决新闻推荐过的不再推荐。
5. `HBase/RocksDB/LevelDB`等数据库内置布隆过滤器,用于判断数据是否存在,可以减少数据库的`IO`请求。


**`Redis`集成布隆过滤器:**  
 用`Redis`可以集成布隆过滤器,版本推荐6.x,最低4.x版本,下载安装插件后在`redis.config`配置文件中加入`redisbloom.so`文件的地址并重启。(若是`redis`集群则每个配置文件中都需要加入`redisbloom.so`文件的地址)。


**主要指令:**


1. `bf.add`:添加一个元素。
2. `bf.exists`:判断一个元素是否存在。
3. `bf.madd`:添加多个元素。
4. `bf.mexists`:判断多个元素是否存在。


## 14 使用String和hash存储对象的对比


**使用字符串(String):**


1. **简单数据**:字符串适用于存储简单的键值对数据,例如配置信息、计数器等。
2. **性能**:字符串的读写操作非常高效,适用于对单个字段进行频繁的读写操作。
3. **扩展性**:当需要存储多个对象的不同字段时,可以使用多个字符串键,每个键存储一个字段,这种方式较为简单。
4. **内存效率**:对于小对象,字符串通常比散列更内存效率高,因为它不需要存储字段名。


**使用哈希(Hash):**


1. **结构化数据**:哈希适用于存储结构化的数据对象,其中包含多个字段,每个字段都有自己的名称和值。
2. **高效存储**:哈希可以存储多个字段,每个字段都可以单独进行读写操作,这使得它适用于需要频繁操作对象的不同部分的情况。
3. **字段命名**:哈希允许为每个字段指定一个名称,这有助于更好地组织和理解存储的数据。
4. **查询和更新**:如果只需要读取或更新散列中的一个或少数几个字段,而不是整个对象,哈希更为高效。
5. **节省内存**:对于大量具有相同字段的对象,使用哈希可以节省内存,因为字段名只存储一次。


## 15 Redis如何保证缓存的一致性


参考:[Redis之缓存一致性]( )


## 16 Redis怎么实现分布式锁


两种方式:


* 加锁/释放锁;
* `Redlock`算法;


### 16.1 加锁/释放锁


1. **加锁**:在需要获取锁的客户端向`Redis`发送一个`SET`命令,尝试在`Redis`中设置一个特定的键(锁键)并赋予一个唯一的值,通常是客户端的标识符(如`UUID`)。



SET lock_key unique_value NX PX 30000


* **`lock_key`**:锁的名称,可以是任何唯一的标识符。
* **`unique_value`**:是唯一的值,通常由客户端生成,用于标识这个锁的持有者。
* **`NX`**:表示仅当`lock_key`不存在时才设置成功,即获取锁的操作是原子的。
* **`PX 30000`**:表示设置锁的超时时间为30秒,以防止锁被永久占用。


2. **释放锁**: 在需要释放锁的客户端可以使用`DEL`命令来删除锁键,确保只有锁的持有者可以释放锁。



DEL lock_key


可以在释放锁时检查锁的持有者是否是当前客户端,以确保不会释放其他客户端持有的锁。


这种基本的分布式锁实现有一些限制和注意事项:


* 获取锁的超时时间需要谨慎设置,过短可能导致竞争激烈,过长可能导致客户端长时间等待。
* 如果锁的持有者执行时间较长,需要确保锁不会在执行期间自动过期,可以通过不断延长锁的超时时间来实现。
* 释放锁时需要谨慎,确保只有锁的持有者可以释放锁,以避免误操作。


### 16.2 Redlock算法


`Redlock`是一种用于分布式系统中的分布式锁算法,它是由`Redis`的作者`Antirez`在`Redis`官方文档中提出的一种思路。虽然`Redlock`提供了一种可行的分布式锁实现方法,但它仍然有一些局限性,需要谨慎使用。以下是使用`Redlock`算法在`Redis`中实现分布式锁的基本步骤:


1. **选择锁服务器**:在分布式环境中,选择多个独立的`Redis`节点,通常是奇数个(例如3、5、7个)。这些节点可以是`Redis`的主节点或者具有持久性和同步机制的`Redis`哨兵节点,作为锁服务器。
2. **获取当前时间戳**:所有客户端需要获取一个相同的当前时间戳,可以使用`Unix`时间戳或者其他合适的时间单位。
3. **尝试获取锁**:客户端使用相同的`key`和具有唯一性的`value`(例如`UUID`)获取锁,在每个`Redis`实例上请求获取锁,使用`SET`命令并设置锁的超时时间。



SET lock_key unique_value NX PX 30000


* **`lock_key`**:锁的名称,可以是任何唯一的标识符。
* **`unique_value`**:是唯一的值,通常由客户端生成,用于标识这个锁的持有者。
* **`NX`**:表示仅当`lock_key`不存在时才设置成功,即获取锁的操作是原子的。
* **`PX 30000`**:表示设置锁的超时时间为30秒,以防止锁被永久占用。


4. **计算获取锁的时间**:如果在大多数节点上成功获取锁,客户端可以计算获取锁的时间(即当前时间戳减去第二步中获取的时间戳)。`key`的真正有效时间等于有效时间减去获取锁所使用的时间。
5. **判断锁是否有效**:客户端需要判断获取锁的时间是否小于锁的有效时间(例如锁的超时时间的一半),当且仅当从大多数(N/2+1)的`Redis`节点都取到锁,并且使用的时间小于锁失效时间时,锁才算获取成功。
6. **释放锁**:当客户端完成任务后,可以使用`DEL`命令在所有节点上根据`key`和`请求ID`删除锁。
7. **处理锁失效**:如果因为某些原因,获取锁失败(没有在至少N/2+1个`Redis`实例取到锁或者取锁时间已经超过了有效时间),客户端应该在所有的`Redis`实例上进行解锁(即便某些`Redis`实例根本就没有加锁成功,防止某些节点获取到锁但是客户端没有得到响应而导致接下来的一段时间不能被重新获取锁)。


需要注意的是,`Redlock`算法并不是绝对可靠的,它对网络分区等异常情况可能不够健壮。在使用`Redlock`时,建议谨慎考虑以下因素:


1. 在网络分区(网络隔离)发生时,可能导致部分节点无法通信,进而可能导致锁的失效。
2. `Redlock`的性能相对较低,因为它需要在多个节点上进行多次尝试。


### 16.3 分布式锁和单机锁得区别


参考1:[分布式锁:概述篇]( )


**单机锁**:解决的是进程内线程间的问题;  
 **分布式锁**:则是解决的进程间的一个问题;


## 17 Redis给一个键值对设置过期时间


参考1:[Redis Setex 命令]( )


`Redis`的`setex`命令为指定的`key`设置值及其过期时间。如果`key`已经存在, `setex`命令将会替换旧的值。


`SETNX key value`只有在`key`不存在时设置`key`的值。


示例:



redis 127.0.0.1:6379> SETEX KEY_NAME TIMEOUT VALUE


## 18 Redis的事务


`Redis`支持事务,事务允许将多个命令打包成一个单一的执行单元,要么全部执行成功,要么全部执行失败,保持了原子性。`Redis`使用`MULTI`、`EXEC`、`DISCARD`和`WATCH`命令来实现事务。以下是关于`Redis`事务的一些重要信息:


1. **开启事务**:通过`MULTI`命令来开启一个事务。一旦开启事务,后续的命令都会被放入事务队列中,但不会立即执行。



MULTI


2. **添加命令到事务队列**:在事务开启后,所有后续的命令都会被添加到事务队列中,但不会立即执行。



SET key1 value1
SET key2 value2


3. **执行事务**:通过`EXEC`命令来执行事务中的所有命令。一旦执行`EXEC`,`Redis`会按照事务队列中的顺序执行所有命令。



EXEC


如果事务中的任何命令出现了错误,`Redis`将执行失败,并返回一个包含错误信息的数组。否则,事务将被成功执行,返回每个命令的结果。  
 4. **取消事务**:如果在执行事务之前需要取消事务,可以使用`DISCARD`命令。`DISCARD`命令会清空事务队列中的所有命令。



DISCARD


5. **事务中的错误处理**:如果事务中的某个命令执行失败,不会影响其他命令的执行,`Redis`会继续执行后续的命令。因此,需要在客户端代码中检查每个命令的执行结果,以判断是否有错误。
6. **事务的原子性**:`Redis`事务是原子性的,意味着在事务执行期间,其他客户端无法插入命令,也无法读取事务中的未执行命令。
7. **WATCH命令**:`Redis`还提供了`WATCH`命令,它用于监视一个或多个键。如果在事务执行前被监视的键发生了变化,事务将被取消。这可以用于实现乐观锁的机制。


`Redis`事务非常适合一次性执行多个命令,从而确保这些命令的原子性。然而,需要注意的是,`Redis`事务不同于传统数据库的事务,它不提供隔离性,不支持回滚,且在一些情况下可能表现出预期之外的行为。因此,在使用`Redis`事务时,需要充分了解其特性和限制,并根据具体需求进行设计和使用。


## 19 Redis集群方案


参考1:[Redis:集群方案]( )


常见的`Redis`集群方案:


1. 主从复制;
2. 哨兵模式;
3. 官网的`Redis Cluster`;


## 20 Redis删除数据的方式


### 20.1 Redis的key过期之后是立即删除吗?是什么机制?


不是,`Redis`中的键过期后并不会立即被立刻删除。过期键的删除是通过`Redis`的定期任务来进行的,具体的删除时间取决于`Redis`的策略和配置。


**`Redis`使用两种主要策略来管理过期键的删除:**


1. **定期删除**:`Redis`会定期检查一定数量的键,看它们是否过期,然后删除过期的键。这个检查过程不是立即发生,而是由`Redis`配置中的`hz`参数控制的,表示每秒执行检查的次数。默认情况下,`hz`设置为10,即每秒检查10次。
2. **惰性删除**:当客户端尝试访问一个键时,`Redis`会首先检查该键是否过期。如果键过期了,`Redis`会删除它,然后返回一个不存在的值。这意味着过期键只有在访问时才会被删除。


总的来说,`Redis`的过期键并不是立即删除的,而是在一定的时间内(取决于`hz`参数和键的访问频率)被定期或惰性删除。这种策略可以有效减少删除操作的开销,同时保证了`Redis`的高性能。


需要注意的是,过期键的删除是基于`LRU`(最近最少使用)算法实现的,因此在某些情况下可能会有一些不确定性,例如可能出现一些已过期但尚未删除的键。但总体来说,`Redis`会尽力确保过期键的及时删除。如果需要精确控制键的生命周期,可以使用`EXPIRE`或`PEXPIRE`命令来设置键的过期时间。


### 20.2 delete是阻塞还是非阻塞,或者Redis有没有非阻塞删除的方式


`Redis`的删除操作通常是非阻塞的,当执行删除命令时,`Redis`会立即返回,并在后台异步地执行删除操作,而不会阻塞其他操作的执行。


这意味着即使执行了删除操作,`Redis`仍然可以同时响应其他读取和写入请求,不会因为删除操作而停止响应其他命令。这种非阻塞的特性有助于保持`Redis`的高吞吐量和低延迟特性。


需要注意的是,虽然`Redis`的删除操作是非阻塞的,但在删除大量数据时,删除操作可能会占用一定的`CPU`和`I/O`资源,从而对其他操作产生一定的影响。因此,在大规模删除操作时,需要谨慎考虑对系统性能的影响,可以采用分批次删除或者在低负载时段进行删除操作,以减轻影响。


此外,如果需要在删除某个键后立即获取该键的值,可以使用`DEL`命令删除键,并立即访问该键,`Redis`会在删除后返回`"nil"`值。这可以用于原子地删除并获取键的值,但请注意,如果键不存在或已经过期,它也会返回`"nil"`值。


### 20.3 假如Redis有一个大键,这个键存储了很多数据,我们想把这个键删除,你会一般是用什么删


因为`Redis`是或者是单线程的,我们删除这个键的话,会肯定会导致其他的请求进不来,但是我们就想删除这个键,你知道该怎么办吗?


如果希望删除一个大键,而又不希望删除操作导致阻塞,可以使用`Redis`的`UNLINK`命令,它执行非阻塞删除操作。`UNLINK`命令是`Redis 4.0`版本引入的。这个命令会将指定的键放入一个删除队列,然后在后台异步删除这些键。


**以下是使用`UNLINK`命令删除大键的示例:**



UNLINK your_large_key


这个命令会立即返回,而后台线程会异步删除指定的键。这意味着删除操作不会阻塞当前客户端或其他`Redis`操作。


**注意**:`UNLINK`命令适用于`Redis 4.0`及更高版本。如果使用较旧版本的`Redis`,可以尝试使用`DEL`命令来删除大键,但这可能会在删除大键时阻塞其他操作,具体取决于大键的大小。如果非阻塞删除对于你的应用程序至关重要,那么在升级到`Redis 4.0`或更高版本之前,可以考虑使用其他方式来分割或拆分大键,以减小删除的开销。


### 20.4 Redis怎么发现这种大key问题?


在`Redis`中,大`Key`问题是指存储在数据库中的某些键值对占用的内存过大,可能会导致性能问题,降低`Redis`的效率。以下是一些方法可以用来发现和解决大Key问题:


1. **使用`OBJECT`命令查看键的信息**:`Redis`提供了`OBJECT`命令,可以查看键的信息,包括类型、大小等。例如,可以使用`OBJECT`命令的`ENCODING`子命令来检查一个键使用的编码方式,以及`IDLETIME`子命令来查看键的空闲时间。



查看键的编码方式

OBJECT ENCODING key

查看键的空闲时间

OBJECT IDLETIME key


2. **使用`SCAN`命令扫描大`Key`**:使用`SCAN`命令可以迭代数据库中的键,适用于查找大`Key`。通过设置合适的游标值,可以逐步扫描整个数据库。以下是一个简单的示例:



使用 SCAN 命令迭代键

SCAN 0 COUNT 100


上述命令表示从游标为0开始扫描数据库,每次扫描100个键。需要多次执行`SCAN`命令,逐步扫描数据库中的键。


## 21 Redis限流的几种方式


1. **基于Redis的setnx的操作**  
 比如我们需要在10秒内限定20个请求,那么我们在`setnx`的时候可以设置过期时间10,当请求的`setnx`数量达到20时候即达到了限流效果。代码比较简单就不做展示了。


**这种做法的弊端有很多弊端,比如当统计1-10秒的时候,无法统计2-11秒之内,如果需要统计N秒内的M个请求,那么我们的Redis中需要保持N个key等等问题。**


2. **基于Redis的数据结构zset**  
 其实限流涉及的最主要的就是滑动窗口,上面也提到1-10怎么变成2-11。其实也就是起始值和末端值都各+1即可。


可以将请求构造成一个`zset`数组,当每一次请求进来的时候,`value`保持唯一,可以用`UUID`或者时间戳,而`score`可以用当前时间戳表示,因为`score`我们可以用来计算当前时间戳之内有多少的请求数量。而`zset`数据结构也提供了`zrange`方法让我们可以很轻易的获取到2个时间戳内有多少请求。


通过上述代码可以做到滑动窗口的效果,并且能保证每N秒内至多M个请求,缺点就是`zset`的数据结构会越来越大。实现方式相对也是比较简单的。


3. **基于`Redis`的令牌桶算法**  
 提令牌桶算法涉及到输入速率和输出速率,当输出速率大于输入速率,那么就是超出流量限制了。也就是说我们每访问一次请求的时候,可以从`Redis`中获取一个令牌,如果拿到令牌了,那就说明没超出限制,而如果拿不到,则结果相反。

 根据这个思想,可以结合`Redis`的`list`数据结构很轻易的做到这样的代码,只是简单实现依靠`list`的`leftPop`来获取令牌。


## 22 Redis的stream是否用过


`Redis Streams`是`Redis 5.0`引入的新数据结构,用于处理有序流式数据。它们提供了类似于消息队列的功能,但有更多的功能,允许轻松地添加、读取和处理事件流。`Streams`主要用于日志处理、消息传递和事件驱动的应用程序中。


**以下是有关`Redis Streams`的关键概念和操作:**


1. **Stream**:`Stream`是一个有序的、不断增长的事件序列,它包含了一个或多个条目(`entry`)。每个条目都有一个唯一的`ID`来标识,可以是整数或时间戳。`Stream`的特点是可以保留和查询历史数据。
2. **Entry**:`Stream`中的每个事件都称为一个`entry`,它包含了一个关键字(`field`)和一个值(`value`)。通常,值是一个包含了事件数据的`JSON`对象。
3. **消费者(Consumer)**:消费者是指应用程序或客户端,它可以订阅一个或多个`Stream`,以便接收事件。`Redis`允许多个消费者订阅相同的`Stream`,并可以独立读取事件。
4. **消费者组(Consumer Group)**:消费者组是一组消费者的集合,用于协作处理事件。它可以确保在多个消费者之间平衡事件的处理负载。
5. **XADD**:`XADD`命令用于向`Stream`添加新的条目,它接收一个`Stream`名称、一个条目`ID`和一个包含字段值对的数据。条目`ID`可以是自动生成的或者用户指定的。
6. **XREAD**:`XREAD`命令用于从`Stream`读取事件。可以根据条件读取事件,例如,读取未读取的事件、读取指定数量的事件等。
7. **XGROUP**:`XGROUP`命令用于创建和管理消费者组,以及将消费者加入组中。
8. **XACK 和 XDEL**:`XACK`用于确认消费者已经成功处理了一个或多个事件,而`XDEL`用于删除事件。
9. **XPENDING**:`XPENDING`用于查询未处理的事件以及与事件相关的信息,如消费者信息、未处理事件的数量等。


`Redis Streams`是一个强大的数据结构,可用于实现各种实时数据处理任务。它允许应用程序通过事件流进行通信,并且可以在流中保留历史数据以进行后续分析和查询。`Streams`还支持多个消费者之间的负载均衡,以确保事件能够高效地处理。


## 23 Redis架构如何设计及优化


设计和优化`Redis`架构通常依赖于应用程序的需求和负载情况。`Redis`是一个高性能的键值存储系统,可以用于多种用途,包括缓存、会话存储、计数器等。以下是设计和优化`Redis`架构的一些常见考虑因素和策略:


1. **数据模型**:
	* 确定数据的存储结构,例如使用字符串、列表、集合、哈希或有序集合等数据类型。
	* 考虑如何组织和命名键,以便于查询和管理。
2. **数据分区**:
	* 对于大规模负载,考虑使用`Redis`的分片机制将数据分布到多个`Redis`实例中,以减轻单个实例的负载。
	* 使用一致性哈希算法来确定数据应存储在哪个分片上。
3. **数据持久化**:
	* 根据可接受的数据丢失程度,选择适当的持久化方式,如快照(`RDB`)和日志(`AOF`)。
	* 配置持久化的频率,以平衡性能和数据安全性。
4. **内存优化**:
	* 监控`Redis`的内存使用情况,确保不会超出可用内存。
	* 使用`Redis`内置的`LRU`算法或手动删除过期数据来管理内存。
5. **集群和高可用性**:
	* 对于关键应用,使用`Redis`高可用性解决方案,如主从复制、哨兵或`Redis`集群。
	* 考虑数据备份和恢复策略,以应对故障。
6. **访问控制和安全性**:
	* 实现适当的访问控制,限制对`Redis`实例的访问。
	* 使用密码认证或者更高级的安全机制来保护数据。
7. **性能优化**:
	* 针对特定的使用情况和负载特性,优化`Redis`的配置参数,如最大连接数、超时设置、并发客户端数等。
	* 使用`Redis`的事务和管道功能,减少客户端与服务器之间的通信开销。
8. **缓存策略**:
	* 为缓存数据定义合适的过期时间,以确保数据不会无限期占用内存。
	* 使用`LRU`或`LFU`算法来淘汰不常用的缓存数据。
9. **监控和性能分析**:
	* 使用监控工具来实时监视`Redis`的性能和健康状态。
	* 使用性能分析工具来识别性能瓶颈和优化机会。
10. **版本升级**:
	* 定期升级`Redis`版本,以获取性能改进、安全性修复和新特性。在升级之前,确保进行充分的测试,以防止应用程序出现不兼容问题。
11. **客户端优化**:
	* 优化客户端代码,避免不必要的`Redis`查询和连接开销。
	* 使用连接池和重用连接,以减少连接的创建和销毁开销。
12. **分布式缓存**:
	* 如果在应用程序中使用`Redis`作为分布式缓存,请考虑如何处理缓存失效、热点数据和缓存预热等问题。
13. **容量规划**:
	* 根据预期的数据量和负载,规划足够的硬件资源,包括内存、`CPU`和存储。


**总结:**  
 `Redis`的设计和优化应该是根据具体的应用需求和负载情况来进行的。不同的应用可能需要不同的策略和配置。定期监控和性能调优是保持`Redis`高性能和可用性的关键。同时,随着`Redis`和应用的发展,也需要不断地进行容量规划和架构调整。


## 24 排行榜,有一个积分,在相同的数值时要按照那个时间进行排序,是怎么处理?


在Redis中,ZSET(有序集合)是一个适合实现排行榜的数据结构。ZSET 允许每个成员关联一个分数(score),并按照分数的顺序进行排序。如果分数相同,则可以使用成员的插入时间(或其他标识)来进行次级排序。



ZADD leaderboard 100 player1
ZADD leaderboard 150 player2
ZADD leaderboard 100 player3


这里,`leaderboard`是有序集合的键名,`player1`、`player2`和`player3`是成员,分别关联的分数是100、150和100。


## 25 Redis缓存的命中率问题:


假设`MySQL`有几千万的数据,`Redis`是几十万的数据,如何保证`MySQL`几千万的数据到`Redis`几十万的数据,`Redis`可以有较高的命中率?


1. **数据同步策略**:确保`MySQL`中的数据同步到`Redis`中。可以采用以下几种方式:
	* 定期同步:定期从`MySQL`中导出数据,更新到`Redis`中。这可以通过定时任务或触发器来实现。
	* 实时同步:使用数据库触发器或者应用层监听数据库变更事件,在数据发生变化时立即同步到`Redis`。
2. **数据结构选择**:选择适当的数据结构以最大程度利用`Redis`的性能。例如,如果数据具有层次结构或关联关系,考虑使用`Redis`的`Hash`或者有序集合(`SortedSet`)等数据结构。
3. **缓存策略**:采用合适的缓存策略,包括缓存淘汰策略(`LRU`、`LFU`等)和过期策略。这有助于保持`Redis`中的数据集合较小,提高命中率。
4. **数据切分**:如果`Redis`中的数据较大,可以考虑对数据进行分片或者切分,将不同的数据集合存储在不同的`Redis`实例中。这有助于减轻单个`Redis`实例的压力。
5. **使用BloomFilter**:`BloomFilter`是一种概率型数据结构,可以用于判断一个元素是否可能存在于一个集合中。在`Redis`中,可以使用布隆过滤器来过滤掉一些不太可能存在于缓存中的数据,从而减轻`Redis`的负担。
6. **分布式缓存**:考虑采用分布式缓存方案,如`Redis`集群。通过将数据分布在多个节点上,可以提高整体的读写性能和容量。
7. **合理设置缓存过期时间**:对于不常变化的数据,可以设置较长的缓存过期时间,减少缓存的更新频率。对于频繁变化的数据,可以采用较短的过期时间,确保缓存及时更新。
8. **性能监控和调优**:定期监控`Redis`的性能指标,包括命中率、内存使用等,根据监控结果进行调优。可以采用`Redis`的性能分析工具,如`redis-cli`的`INFO`命令,或者第三方的监控工具。
9. **合理使用缓存预热**:在系统启动或重启时,可以通过缓存预热的方式,将热点数据提前加载到`Redis`中,提高缓存的初始化命中率。


综合采用上述策略,可以有效地保证`MySQL`中的大量数据同步到`Redis`中,并提高`Redis`的命中率,从而加速对数据的访问。在实际应用中,根据具体的业务场景和数据特点,可以调整和优化这些策略。


## 26 Redis是怎么检查出哪些key过期了?


`Redis`缓存过期删除的三种方式:


1. **定时删除**:`key`设置了过期时间,一旦过期立即删除。
2. **惰性删除**:过期`key`不会马上被删除,而是继续保存在内存中,当`key`被访问时检查`key`的过期时间,若已过期则删除。
3. **定期删除**:每隔一段时间(时间可以自行设置,`Redis`配置文件的`hz`参数表示1s执行多少次定期删除策略,默认值10),随机抽取设置了过期时间的`key`检查它们的过期时间,删除已过期的`key`。


## 27 Redis的ZSet往跳表里面插一条数据,然后插一个节点,它具体的过程是啥?就比如说它层高怎么确定的,然后怎么找到它那原来那个位置的。


在`Redis`中,有序集合(`Sorted Set`)是通过跳表(`Skip List`)实现的。跳表是一种基于链表的数据结构,它通过层级的方式组织元素,以提高查找效率。以下是向有序集合(`ZSet`)插入数据的简要过程:


1. **跳表节点的创建**:当向有序集合插入一条数据时,首先会创建一个新的跳表节点,这个节点包含了元素的分值(`score`)和成员(`member`)。
2. **决定节点的层级**:插入节点时,需要决定节点的层级。节点的层级决定了节点在跳表中的高度,影响了跳表的查找效率。通常,节点的层级是通过随机算法确定的,其中每个节点的层级是一个介于1和最大层级之间的随机数。
3. **插入节点到跳表**:将新创建的节点插入到跳表中。对于每一层,找到该层上比当前节点小的节点,将当前节点插入到这个节点之后。这一过程需要在每一层进行。
4. **更新索引**:在跳表中,为了加速查找,通常会在每一层维护一个索引。索引指向每一层上的最后一个节点。在插入新节点后,需要更新索引,确保索引正确地指向每一层的最后一个节点。
5. **查找过程**:查找元素时,从最顶层开始,逐层向右移动,找到比目标元素小的节点。然后进入下一层,继续向右移动。重复这个过程,直到找到目标元素或者到达最底层。如果找到目标元素,则返回该元素;如果到达最底层而仍未找到,则表示该元素不存在。  
 这个过程中,跳表通过不断地在每一层中跳过一些节点,从而减少查找的时间。跳表的平均查找时间复杂度为O(log n)。


在`Redis`中,`ZSet`的跳表实现是为了支持有序集合的快速插入、删除和范围查询等操作。


## 28 Redis的高可用有哪些方式?


1. **主从复制(Master-Slave Replication)**:
	* Redis支持主从复制机制,其中一个Redis节点作为主节点(Master),而其他节点作为从节点(Slave)。主节点负责写入操作,而从节点复制主节点的数据,提供读取服务。在主节点发生故障时,可以将一个从节点晋升为新的主节点。
	* 主从复制是一种简单而有效的高可用性方案,但在主节点故障后,需要手动进行故障切换。
2. **哨兵模式(Sentinel)**:
	* 哨兵模式是一种由Redis提供的自动故障切换和监控机制。多个哨兵进程监视所有节点,当 主节点宕机时,哨兵会自动选举新的主节点。哨兵还能够检测并处理从节点的故障。
	* 哨兵模式提供了自动化的高可用性解决方案,但需要额外的配置和管理哨兵节点。
3. **集群模式(Redis Cluster)**:
	* Redis Cluster是一种分布式解决方案,将数据分片存储在多个节点上,并提供自动的故障切换和负载均衡。每个节点既可以处理读取操作,也可以处理部分写入操作。
	* Redis Cluster是一个全自动的解决方案,适用于大规模的Redis部署。它支持节点的动态添加和移除,并且在节点故障时能够自动进行重新分片。
4. **持久化(AOF 和 RDB)**:
	* Redis 支持两种持久化方式,分别是Append-Only File(AOF)和Redis Database File(RDB)。AOF记录每个写操作,RDB则保存数据的快照。
	* 通过合理配置持久化机制,可以在节点重启后快速恢复数据,提高可用性。
5. **使用第三方工具**:
	* 一些第三方工具和平台可以帮助管理Redis的高可用性,例如使用云服务提供商的Redis服务,或使用像Twemproxy、Codis等代理层工具来提供额外的高可用性支持。


选择合适的高可用性方案通常取决于具体的业务需求、部署环境和管理复杂性。不同的场景可能选择不同的方案,或者将多种方式组合使用以达到更高的可用性水平。


## 29 Redis在使用的过程中,可能会有哪些问题,或者注意事项有那些?


回答:首先这个数据的一致性,就是说从MySQL他的数据一致这块,基于MySQL的 Bing log 订阅,就是 MySQL 更新完之后把这个更新的写入到这个 Bing log 日志里面,然后再通过消息中间件,有这个消息的重试,一次不成功也能多次保证这个数据我更新过来。



![img](https://img-blog.csdnimg.cn/img_convert/6161e370b8a17209b3fa65b5babb1a40.png)
![img](https://img-blog.csdnimg.cn/img_convert/96c6f81049bc4a5e88b96c191938193e.png)
![img](https://img-blog.csdnimg.cn/img_convert/15dab894fd330d2f82c1c320b02623e6.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618658159)**

的高可用性解决方案,但需要额外的配置和管理哨兵节点。
3. **集群模式(Redis Cluster)**:
	* Redis Cluster是一种分布式解决方案,将数据分片存储在多个节点上,并提供自动的故障切换和负载均衡。每个节点既可以处理读取操作,也可以处理部分写入操作。
	* Redis Cluster是一个全自动的解决方案,适用于大规模的Redis部署。它支持节点的动态添加和移除,并且在节点故障时能够自动进行重新分片。
4. **持久化(AOF 和 RDB)**:
	* Redis 支持两种持久化方式,分别是Append-Only File(AOF)和Redis Database File(RDB)。AOF记录每个写操作,RDB则保存数据的快照。
	* 通过合理配置持久化机制,可以在节点重启后快速恢复数据,提高可用性。
5. **使用第三方工具**:
	* 一些第三方工具和平台可以帮助管理Redis的高可用性,例如使用云服务提供商的Redis服务,或使用像Twemproxy、Codis等代理层工具来提供额外的高可用性支持。


选择合适的高可用性方案通常取决于具体的业务需求、部署环境和管理复杂性。不同的场景可能选择不同的方案,或者将多种方式组合使用以达到更高的可用性水平。


## 29 Redis在使用的过程中,可能会有哪些问题,或者注意事项有那些?


回答:首先这个数据的一致性,就是说从MySQL他的数据一致这块,基于MySQL的 Bing log 订阅,就是 MySQL 更新完之后把这个更新的写入到这个 Bing log 日志里面,然后再通过消息中间件,有这个消息的重试,一次不成功也能多次保证这个数据我更新过来。



[外链图片转存中...(img-xcIMgYkX-1715743437559)]
[外链图片转存中...(img-qI9YdqQi-1715743437559)]
[外链图片转存中...(img-tvOam52E-1715743437559)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618658159)**

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值