当前位置: 首页> 教育> 幼教 > 免费24小时在线心理医生_网络建站网网络推广_百度搜索引擎营销如何实现_北京学电脑的培训机构

免费24小时在线心理医生_网络建站网网络推广_百度搜索引擎营销如何实现_北京学电脑的培训机构

时间:2025/9/14 9:09:37来源:https://blog.csdn.net/m0_52388979/article/details/147474472 浏览次数:0次
免费24小时在线心理医生_网络建站网网络推广_百度搜索引擎营销如何实现_北京学电脑的培训机构

​​​​​​目录

一、缓存穿透

二、布隆过滤器

三、缓存预热、雪崩、穿透、击穿分别是什么?你遇到过那几个情况?

四、缓存预热你是怎么做到的?

五、如何避免或者减少缓存雪崩(缓存击穿的升级版)?

六、穿透和击穿有什么区别?它俩一个意思还是截然不同?

七、穿透和击穿你有什么解决方案?如何避免?

八、假如出现了缓存不一致,你有哪些修补方案?

九、缓存击穿

十、缓存雪崩

十一、延迟双删

十二、共享锁与排他锁

十三、redis分布式锁,是如何实现的?

十四、Redisson实现分布式锁如何合理的控制锁的有效时长?

十五、Redisson的这个锁,可以重入吗?

十六、Redisson锁能解决主从数据一致的问题吗

十七、Redis集群有哪些方案,知道嘛

十八、redis主从数据同步的流程是什么?

十九、主从同步流程(全量+增量)

二十、你们使用redis是单点还是集群,哪种集群?

二十一、Redis分片集群中数据是怎么存储和读取的?

二十二、Redis集群脑裂,该怎么解决呢?

二十三、Redis集群的最大槽数为什么是16384个?

二十五、介绍一下redis的主从同步

二十六、Redis哨兵领导者选举机制?

二十七、Redis脑裂

二十八、哨兵结点的数量应该为奇数,这是为什么?

二十九、怎么保证Redis的高并发高可用

三十、Redis分片集群。解决海量数据存储问题以及高并发写的问题。

三十一、Redis的分片集群有什么作用?

三十二、Redis分片集群中数据是怎么存储和读取的?

三十三、Redis是单线程的,但是为什么还那么快?

三十四、I/O多路复用模型

三十五、Redis网络模型

三十六、能解释一下I/O多路复用模型?


  • 缓存 -- 缓存三兄弟(穿透、击穿、雪崩)、双写一致性、持久化、数据过期策略、数据淘汰策略

  • 分布式锁 -- setnx redisson

  • 消息队列,延迟队列 -- 数据类型

一、缓存穿透

比如:疯狂,大量请求查一个不在redis中也不在db中的数据,导致每次请求都会区查数据库---宕机

  • 一般情况下,先查询缓存Redis是否有该条数据,缓存中没有时,再查询数据库

  • 当数据库也不存在该条数据时,每次查询都要访问数据库,这就是缓存穿透

  • 缓存穿透带来的问题就是,当有大量请求查村数据库不存在的数据时,就会给数据库带来压力,甚至会拖垮数据库

解决:

  1. redis缓存一个空数据,下次查反空 -- 内存消耗,可能还有数据不一致的情况

  2. 加一层布隆过滤器 -- 缓存预热,添加到缓存的时候也加到布隆里里

二、布隆过滤器

--依赖bitmap(位图) --应用 黑名单 垃圾邮箱

可以使用布隆过滤器解决缓存穿透的问题

  • 把已存在数据的key存在布隆过滤器中,相当于Redis前面挡着一个布隆过滤器

  • 当有新的请求时,先到布隆过滤器中查询是否存在

  • 如果布隆过滤器中不存在该条数据就直接返回

  • 如果布隆过滤器中已存在,采取查询缓存Redis,如果Redis里没有查询到再去查询数据库。

三、缓存预热、雪崩、穿透、击穿分别是什么?你遇到过那几个情况?

  • 缓存预热:

    • 缓存预热是指在系统正式上线之前,提前将一些热点数据加载到缓存中,以减少首次请求时对数据库的压力。这可以通过批量请求或定时任务来实现。

  • 缓存雪崩:

    • 缓存雪崩是指在某个时间点,大量缓存失效或被清空,造成大量请求直接访问数据库,从而引起数据库的瞬时高负载,可能导致系统崩溃。

    • redis主机挂了,Redis全盘崩溃,偏硬件运维

  • 缓存穿透:

    • 缓存穿透是指请求的数据在缓存和数据库中都不存在(例如,查询一个错误的ID),每次请求都会直接打到数据库,无法利用缓存,从而给数据库带来很大的压力。

  • 缓存击穿:

    • 缓存击穿是指某个热点数据在缓存中失效,同时有大量请求同时访问这个数据,造成请求直接访问数据库。虽然这与缓存雪崩类似,但击穿主要是单个热点数据,而雪崩是多个数据同时失效

遇到过哪几种情况?

在实际生产环境中,遇到过以下几个情况:

  • 缓存雪崩:由于没有合理设置缓存的过期时间,导致在特定时刻大量缓存同时失效,数据库瞬间承受了很大压力。

  • 缓存穿透:存在大量恶意请求查询不存在的数据,没有有效地过滤,这导致数据库频繁访问。

四、缓存预热你是怎么做到的?

实现缓存预热的方法包括:

  • 在系统上线前,通过脚本将热点数据预先加载到缓存中。

  • 使用定时任务,在系统的低峰时段定期更新缓存中的热点数据。

  • 在发布新版本时,可以在部署过程中,提前访问一遍热点数据以填充缓存

五、如何避免或者减少缓存雪崩(缓存击穿的升级版)?

为了避免或减少缓存雪崩,可以采取以下措施:

  • 设置不同的过期时间:不要让所有缓存数据在同一时间失效,可以采用随机的过期时间。

  • 使用互斥锁:在缓存失效后,只有一个请求可以去查询数据库并更新缓存,其他请求则等待。

  • 监控和扩容:监控数据库性能,当发现异常流量时及时扩容,以应对突发流量。

六、穿透和击穿有什么区别?它俩一个意思还是截然不同?

穿透和击穿是截然不同的概念:

  • 缓存穿透:请求的数据在缓存和数据库中均不存在,不会利用到缓存

  • 缓存击穿:请求的数据在缓存中失效,导致大量请求直接访问数据库。

七、穿透和击穿你有什么解决方案?如何避免?

  • 缓存穿透(缓存和数据库中都不存在):

    • 使用布隆过滤器:可以快速判断请求的数据是否存在,避免无效请求访问数据库。

    • 对于非法请求进行限流和拦截。

  • 缓存击穿(某个热点数据在缓存中失效):

    • 设置互斥锁:确保只有一个请求在缓存失效时去加载数据库数据,其余请求等待。

    • 采用“请求合并”技术,将多个同时请求合并为一个请求。

八、假如出现了缓存不一致,你有哪些修补方案?

对于缓存不一致的问题,可以考虑以下修补方案:

  • 延迟双删:在更新数据库后,先删除缓存,再稍等几秒后再删除一次缓存,以降低缓存和数据库不一致的概率。

  • 使用消息队列:在数据写入操作后,将变更信息发送到消息队列,随后由消费者监听该消息并更新缓存。

  • 定期刷新缓存:实施定时任务,周期性地从数据库中重新加载数据到缓存,以确保数据的一致性。

九、缓存击穿

十、缓存雪崩

十一、延迟双删

  1. 删除Redis缓存

  2. 更新数据库

  3. 延迟一段时间后再次删除Redis缓存。

双写一致性,你先动缓存redis还是数据库?为什么?

  • 更新数据库后,如果立即删除Redis缓存,可能会有其他请求查询到旧数据并将其写回Redis。

  • 如果先删除Redis缓存,再更新数据库,可能会有请求在缓存删除后查询到数据库的旧数据并将其写回Redis。

更新数据库 删除缓存 更新缓存

  1. 更新数据库,删除缓存

    • 理想情况:

    • 问题:旧数据+数据不一致。线程一去查数据,缓存未命中,查数据库拿到旧数据,回写的时候,此时有线程二去更新数据库为另一个值,又删除了缓存。线程一回写了一个旧值。

  2. 删除缓存,更新数据库

    • 理想情况:

    • 问题:旧数据,数据不一致。缓存删除后,业务直接查数据库,如果此时数据库还没有更新完,会读到旧数据,且会将旧值回写至缓存,就在这时,数据库更新完了,但是实际上跟缓存的数据是不一致的。导致数据不一致,第一步删除缓存白干了。

十二、共享锁与排他锁

锁与读写操作的直接对应关系

共享锁(S锁) = 读锁 排他锁(X锁) = 写锁

这种命名是因为:

  • 多个"读操作"可以同时进行(共享)

  • "写操作"必须独占资源(排他)

重新梳理四种情况的互斥关系

1. 读读不互斥 (共享锁之间兼容)

  • 事务A:SELECT ... LOCK IN SHARE MODE (获取S锁)

  • 事务B:可以同时执行相同的S锁查询

  • ✅ 多个事务可以同时读取同一数据

2. 读写互斥 (共享锁与排他锁不兼容)

  • 事务A:持有S锁(读锁)

  • 事务B:尝试UPDATE... (需要X锁/写锁)

  • ❌ 事务B会被阻塞,直到事务A释放S锁

3. 写读互斥 (排他锁与共享锁不兼容)

  • 事务A:SELECT ... FOR UPDATE (获取X锁/写锁)

  • 事务B:尝试SELECT ... LOCK IN SHARE MODE (需要S锁/读锁)

  • ❌ 事务B会被阻塞,直到事务A释放X锁

4. 写写互斥 (排他锁之间不兼容)

  • 事务A:持有X锁(写锁)

  • 事务B:尝试另一个写操作(需要X锁)

  • ❌ 事务B会被阻塞,直到事务A释放X锁

为什么叫"共享"和"排他"?

这种命名是从锁的共享特性角度描述的:

  • 共享锁(S锁):可以被多个事务"共享"持有(对应多个读操作)

  • 排他锁(X锁):必须"排除"其他所有锁(对应写操作需要独占)

-- 事务1获取共享锁(读锁) BEGIN; SELECT * FROM accounts WHERE id = 1 LOCK IN SHARE MODE;

-- 此时事务2可以同时获取共享锁 SELECT * FROM accounts WHERE id = 1 LOCK IN SHARE MODE;

-- 但事务3尝试获取排他锁(写锁)会被阻塞 UPDATE accounts SET balance = 100 WHERE id = 1;

十三、redis分布式锁,是如何实现的?

  • 先按照自己简历上的业务进行描述分布式锁使用的场景

  • 我们当使用的redisson实现的分布式锁,底层是setnx和lua脚本(保证原子性)

十四、Redisson实现分布式锁如何合理的控制锁的有效时长?

  • 在redisson的分布式锁中,提供了一个WatchDog(看门狗),-个线程获取锁成功以后,WatchDog会给持有锁的线程续期(默认是每隔10秒续期一次)

十五、Redisson的这个锁,可以重入吗?

  • 可以重入,多个锁重入需要判断是否是当前线程,在redis中进行存储的时候使用的hash结构来存储线程信息和重入的次数

十六、Redisson锁能解决主从数据一致的问题吗

  • 不能解决,但是可以使用redisson提供的红锁来解决,但是这样的话,性能就太低了

  • 如果业务中非要保证数据的强一致性,建议采用zookeeper实现的分布式锁

十七、Redis集群有哪些方案,知道嘛

在Redis中提供的集群方案总共有三种

  • 主从复制

  • 哨兵模式

  • 分片集群

介绍一下redis的主从同步 单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。-般都是一主多从,主节点负责写数据,从节点负责读数据

十八、redis主从数据同步的流程是什么?

  • 全量同步:

    1. 从节点请求主节点同步数据(replicationid、offset)

    2. 主节点判断是否是第一次请求,是第一次就与从节点同步版本信息(replication id和offset)

    3. 主节点执行bgsave,生成rdb文件后,发送给从节点去执行

    4. 在rdb生成执行期间,主节点会以命令的方式记录到缓冲区(一个日志文件 repl-backlog))

    5. 把生成之后的命令日志文件发送给从节点进行同步

  • 增量同步:

    1. 从节点请求主节点同步数据,主节点判断不是第一次请求,不是第一次就获取从节点的offset值

    2. 主节点从命令日志中获取offset值之后的数据,发送给从节点进行数据同步

十九、主从同步流程(全量+增量)

全量同步(第一次同步)的处理 replid 不一致

当判断需要全量同步时:

  1. 主节点执行BGSAVE

    • 生成RDB快照文件

    • 如果配置了repl-diskless-sync则直接通过网络发送

  2. 传输RDB文件

    bash

    复制

    # 主节点日志示例
    Background saving started by pid 2645
    Synchronization with replica 127.0.0.1:6380 succeeded
  3. 从节点加载RDB

    • 清空自身数据

    • 加载接收到的RDB文件

  4. 开始增量同步

    • 主节点将新写入命令存入复制积压缓冲区

    • 从节点持续接收并执行这些命令

增量同步的处理

当可以进行增量同步时:

  1. 主节点发送积压命令

    • 从积压缓冲区(repl-backlog)中发送从节点缺失的命令

    • 积压缓冲区是环形缓冲区,默认大小1MB

  2. 持续同步

    • 主节点将每个写命令发送给从节点

    • 从节点按顺序执行这些命令

  3. 偏移量更新

    • 主从节点都会持续更新master_repl_offset

    • 用于下次断线重连时判断同步位置

核心概念:Replication ID和offset

  1. Replication ID

    • 一个伪随机的40字符字符串(如:"fccb3c8c80a6096b9a7b8f1e9a7b8f1e9a7b8f1e9")

    • 标识主节点的复制流历史

  2. offset

    • 单调递增的64位整数

    • 记录复制流中的字节位置

二十、你们使用redis是单点还是集群,哪种集群?

主从(1主1从)+哨兵就可以了。单节点不超过10G内存,如果Redis内存不足则可以给不同服务分配独立的Redis主从节点

二十一、Redis分片集群中数据是怎么存储和读取的?

二十二、Redis集群脑裂,该怎么解决呢?

在分布式系统中,两个或多个主节点(Master)同时存在并接受写入,导致数据冲突和不一致

  • 主节点(Master)和从节点(Slave)被隔离在不同的网络分区,导致它们无法通信。

  • 哨兵(Sentinel)集群也被分割,部分哨兵认为旧主节点仍然存活,而另一部分哨兵选举了新主节点。

  • 最终,两个主节点同时接受写入,导致数据冲突。

脑裂的具体过程

假设一个 Redis 集群由 1主(Master)+ 2从(Slave)+ 3哨兵(Sentinel) 组成,网络分区后可能发生:

  1. 网络分区1(可访问外网):

    • 主节点(Master)

    • 1个哨兵(Sentinel)

    • 客户端仍然向旧主写入数据

  2. 网络分区2(内网隔离):

    • 2个从节点(Slave)

    • 2个哨兵(Sentinel)

    • 哨兵检测到主节点失联,选举新主

    • 客户端向新主写入数据

  3. 网络恢复后

    • 两个主节点数据不一致,Redis 会保留新主的数据,丢弃旧主在隔离期间的写入(取决于配置)

集群脑裂是由于主节点和从节点和sentinel处于不同的网络分区,使得sentinel没有能够心跳感知到主节点,所以通过选举的方式提升了一个从节点为主,这样就存在了两个master,就像大脑分裂了一样。

解决:修改redis的配置,

1.设置最少的从节点数量(min-replicas-to-write 1 表示最少的salve节点为1个)(至少要有N个从节点同步成功)

  • min-slaves-to-write 1 # 主节点必须至少有一个从节点确认同步,否则停止写入。

  • 如果主节点发现无法同步到足够的从节点,会拒绝写入,避免数据丢失。

2.以及缩短主从数据同步的延迟时间,达不到要求就拒绝请求就可以避免大量的数据丢失(min-replicas-max-lag5 表示数据复制和同步的延迟不能超过5秒)(从节点同步延迟不能超过N秒)

  • min-slaves-max-lag 10 # 从节点延迟超过10秒,主节点停止写入

  • 防止主节点在高延迟环境下继续写入不可靠数据

3.哨兵(Sentinel)的 quorum(仲裁机制)

  • 哨兵必须达到多数派(>N/2)才能执行主节点切换,避免少数派哨兵误判

二十三、Redis集群的最大槽数为什么是16384个?

1.1 问题描述

Redis集群并没有使用一致性hash而是引入了哈希槽的概念。Redis 集群有16384个哈希槽, 每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽。但为什么哈希槽的数量是16384(2^14)个呢?

CRC16算法产生的hash值有16bit,该算法可以产生2^16=65536个值。

换句话说值是分布在0~65535之间,有更大的65536不用为什么只用16384就够? 作者在做mod运算的时候,为什么不mod65536,而选择mod16384? HASH_SLOT = CRC16(key) mod 65536为什么没启用

1.2 问题解释

1.2.1 antirez回答

https://github.com/redis/redis/issues/2576

1.2.2 翻译

  • 正常的心跳数据包带有节点的完整配置,可以用幂等方式用旧的节点替换旧节点,以便更新旧的配置。

  • 这意味着它们包含原始节点的插槽配置,该节点使用2k的空间和16k的插槽,但是会使用8k的空间(使用65k的插槽)。

  • 同时,由于其他设计折衷,Redis集群不太可能扩展到1000个以上的主节点。

  • 因此16k处于正确的范围内,以确保每个主机具有足够的插槽,最多可容纳1000个矩阵,但数量足够少,可以轻松地将插槽配置作为原始位图传播。请注意,在小型群集中,位图将难以压缩,因为当N较小时,位图将设置的slot / N位占设置位的很大百分比。

1.2.3 梳理

(1)消息头控制。如果槽位为65536,发送心跳信息的消息头达8k,发送的心跳包过于庞大。

  • 在消息头中最占空间的是myslots[CLUSTER_SLOTS/8]。 当槽位为65536时,这块的大小是: 65536÷8÷1024=8kb

  • 在消息头中最占空间的是myslots[CLUSTER_SLOTS/8]。 当槽位为16384时,这块的大小是: 16384÷8÷1024=2kb

  • 因为每秒钟,redis节点需要发送一定数量的ping消息作为心跳包,如果槽位为65536,这个ping消息的消息头太大了,浪费带宽。

心跳包中包含节点的完整配置,如果哈希槽数为65536,心跳包的消息头大小会达到8KB(计算:65536 / 8 / 1024),而16384个槽时消息头仅为2KB。这有助于减少带宽浪费,因为Redis节点需要频繁发送心跳包。

(2)主节点数量限制。redis的集群主节点数量基本不可能超过1000个。

集群节点越多,心跳包的消息体内携带的数据越多。如果节点过1000个,也会导致网络拥堵。因此redis作者不建议redis cluster节点数量超过1000个。 那么,对于节点数在1000以内的redis cluster集群,16384个槽位够用了。没有必要拓展到65536个。

主节点数量限制 Redis集群的主节点数量不建议超过1000个,以防止网络拥堵。如果节点数在1000以内,16384个槽足够满足需求,没有必要扩展到65536个。

(3)高效的位图压缩。槽位越小,节点少的情况下,压缩比高,容易传输

Redis主节点的配置信息中它所负责的哈希槽是通过一张bitmap的形式来保存的,在传输过程中会对bitmap进行压缩,但是如果bitmap的填充率slots / N很高的话(N表示节点数),bitmap的压缩率就很低。 如果节点数很少,而哈希槽数量很多的话,bitmap的压缩率就很低。

哈希槽的配置信息以位图形式保存并进行压缩。在节点少的情况下,较小的槽位数量能够提高位图的压缩率;反之,如果槽位太多而节点较少,填充率高会导致压缩效果差,增加传输负担。

1.2.4 总结

16384个哈希槽的设计兼顾了消息大小、节点管理和数据传输效率,使得Redis集群在保持高性能的同时,避免了带宽浪费和管理复杂性的增加。这一数量恰好符合Redis集群的使用场景,使得集群能够有效支持最多1000个主节点的配置。

二十五、介绍一下redis的主从同步

  • 单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。-般都是一主多从,主节点负责写数据,从节点负责读数据

二十六、Redis哨兵领导者选举机制?

监视该主节点的所有哨兵都有可能被选为领导者,选举使用的算法是Raft算法

  1. 故障发现阶段

    • 当主节点被判定为客观下线(ODOWN)时,所有监视该主节点的哨兵都会收到通知。

  2. 选举初始化

    • 每个哨兵会随机等待一段时间(减少冲突),然后尝试发起选举。

    • 投票规则

      • 每个哨兵在每个任期只能投一票

      • 哨兵只会投票给:

        • 第一个向其拉票的候选者(在相同term内)

        • 并且该候选者的配置纪元(configuration epoch)不小于自己

3.成为领导者的条件

  • 候选哨兵需要获得多数票(超过半数哨兵节点的同意)

  • 当选票数 > N/2 (N是哨兵总数)

  1. 选举超时

  • 如果选举超时(通常300ms * 随机因子)仍未选出领导者,会开始新一轮选举。

哨兵选主规则

  1. 首先判断主与从节点断开时间长短,如超过指定值就排后该从节点

  2. 然后判断从节点的slave-priority值,越小优先级越高

  3. 如果slave-prority一样,则判断slave节点的offset值,越大优先级越高

  4. 最后是判断slave节点的运行id大小,越小优先级越高。

为什么这样设计?

  1. 避免脑裂:多数票确保只有一个领导者

  2. 容错性:允许部分节点故障

  3. 效率:随机等待减少冲突

  4. 一致性:高epoch的哨兵优先

二十七、Redis脑裂

“旧主(原Master)在网络隔离期间仍接受写入,恢复后被降级导致数据丢失”的问题。这是分布式系统中的“旧主隔离写入”问题,但严格来说,这不属于“脑裂”,而是数据一致性问题。下面详细解释:


1. 什么是脑裂(Split-Brain)?

脑裂的定义: 在分布式系统中,两个或多个主节点(Master)同时存在并接受写入,导致数据冲突和不一致。

案例 1:库存超卖(电商场景)

  • 初始库存stock = 10

  • 客户端 1 向主节点 A 发送:DECR stock (A 的 stock = 9)。

  • 客户端 2 向主节点 B 发送:DECR stock (B 的 stock = 9)。

  • 最终合并时

  • 如果简单相加:stock = 9 + 9 = 18(显然错误,实际应该 stock = 8)。

  • 如果取最小值:stock = 9(仍然错误,因为扣减了两次)。

关键特征

  • 多个主节点同时活跃。

  • 客户端可能向不同的主节点写入,导致数据无法合并。


2. 你的场景分析

假设 3 节点集群(1 Master + 2 Sentinel):

  1. 初始状态

    • Node1 是 Master,Node2Node3 是 Sentinel + Slave。

  2. 网络分区

    • Node1 被隔离(无法与 Node2Node3 通信)。

    • Node2Node3 检测到 Node1 失联,选举 Node2 为新 Master。

  3. 问题发生

    • 分区期间

      • 客户端仍可能向 Node1 写入(如果客户端未感知 Node1 已失联)。

      • Node2(新 Master)也接受写入。

    • 网络恢复后

      • Node1 重新加入集群,但发现自己已被降级为 Slave。

      • Node1 的数据会被清空,并从 Node2(新 Master)重新同步。

      • 结果:分区期间写入 Node1 的数据丢失。


3. 这是脑裂吗?

不是严格意义上的脑裂,因为:

  • 只有 Node2 是合法的主节点(由多数派 Sentinel 选举)。

  • Node1 虽然接受了写入,但它已经被集群多数派判定为失效,因此它的写入是“非法”的。

  • 恢复后,集群仍然只有一个主节点(Node2,没有“双主”情况。

更准确的说法:这是“旧主隔离写入”问题(Stale Master Writes),属于数据丢失,而非脑裂。


4. 为什么奇数节点能避免“脑裂”?

奇数节点的核心作用是确保多数派唯一性,从而:

  1. 防止“双主”

    • 1:2 分区时,只有 2 节点侧能选举新主,1 节点侧无法选举,因此不会有两个主节点同时存在

    • 如果是偶数节点(如 4 节点 2:2),双方都无法达成多数,可能导致无主状态(但也不会脑裂)。

  2. 避免冲突决策

    • 奇数节点确保只有一个子集能达成多数,因此决策(如主节点切换)是唯一的。


5. 如何解决“旧主隔离写入”问题?

虽然奇数节点能避免脑裂,但仍需额外措施防止数据丢失

(1) Redis 的 min-slaves-to-write 配置

min-slaves-to-write 1  # 主节点必须至少有 1 个从节点在线才能接受写入
  • 作用

    • 如果主节点(Node1)发现无法连接任何从节点(因网络分区),会拒绝写入

    • 这样即使客户端仍向 Node1 发送写入,也会失败,避免数据不一致。

(2) 客户端重试机制

  • 客户端应监控主节点变化,如果写入失败,应查询 Sentinel 获取新主节点。

(3) 数据修复(人工干预)

  • 如果 Node1 在隔离期间接受了写入,可以在恢复后:

    • 手动导出 Node1 的增量数据。

    • 合并到新主节点(Node2)。


6. 对比脑裂 vs 旧主隔离写入

问题脑裂(Split-Brain)旧主隔离写入(Stale Master Writes)
定义两个或多个主节点同时接受写入旧主在网络隔离期间仍接受写入,恢复后数据丢失
数据一致性数据冲突(无法自动合并)数据丢失(旧主写入未被同步)
原因多数派决策失败(如偶数节点 2:2客户端未感知主节点切换
解决方案使用奇数节点 + 多数派机制min-slaves-to-write + 客户端重试

7. 结论

  1. 奇数节点的作用

    • 避免脑裂(确保只有一个多数派能决策)。

    • 但不能完全防止“旧主隔离写入”问题(需额外配置)。

  2. 数据一致性保障

    • 依赖 min-slaves-to-write 和客户端重试机制。

  3. 生产建议

    • 使用 3/5 个 Sentinel(奇数)。

    • 配置 min-slaves-to-write 1

    • 客户端实现自动故障转移逻辑。

最终答案: 奇数节点能避免脑裂(防止双主),但“旧主隔离写入”问题需额外配置解决。两者是不同的问题,需分别处理。

二十八、哨兵结点的数量应该为奇数,这是为什么?

  1. 多数票原则(Quorum)

哨兵通过投票决定主节点是否下线或选举新领导者,必须获得超过半数的同意才能生效。奇数数量能明确形成多数派,避免平局:

  • 3个哨兵:至少需要 2 票(>1.5

  • 4个哨兵:同样需要 3 票(>2),但容错能力与3节点相同(只能容忍1节点故障),却多消耗资源。

奇数更高效: 3节点和4节点都能容忍1个故障,但3节点更节省资源;5节点可容忍2个故障,优于6节点(同样最多容忍2个故障)。

  1. 容错能力最大化

奇数数量能在相同节点数下提供最优的容错能力

  • 3节点:允许1个节点故障(剩余2个仍能形成多数)

  • 5节点:允许2个节点故障(剩余3个仍能形成多数)

如果是偶数(如4节点),实际容错能力与少1个的奇数相同(4节点也只能容忍1个故障,否则剩余2=2,无法形成多数)。

  1. 避免脑裂(Split-Brain)

网络分区时,奇数节点能明确划分多数派和少数派。例如:

  • 3节点分区为 1 vs 2:只有2节点的一侧能达成共识。

  • 4节点分区为 2 vs 2:双方都无法达成多数,导致服务僵局。

  1. 成本与性能的平衡

奇数节点在资源消耗可用性之间取得最佳平衡:

  • 偶数节点(如4个)不会比3个提供更高的容错,却增加通信开销。

  • 5节点比3节点容错更强,但需权衡部署成本。

当有 3 个节点(奇数)时,如果网络分区为 1:22 的两个节点是否能选举新的主节点?这样会不会导致脑裂?

核心结论

不会脑裂!1:2 的网络分区情况下:

  1. 2 节点的一侧可以选举新主(因为 2 > 3/2,满足多数派)。

  2. 1 节点的一侧无法选举(因为 1 ≤ 3/2,不满足多数派)。

  3. 最终只有一个主节点生效,因此不会脑裂

二十九、怎么保证Redis的高并发高可用

  • 哨兵模式:实现主从集群的自动故障恢复(监控、自动故障恢复、通知)

    • 监控:Sentinel会不断检查您的master和slave是否按预期工作

    • 自动故障恢复:如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主

    • 通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端

  • 哨兵选举:

    • 首先判断主与从节点断开时间长短,如超过指定值就排除该从节点

    • 然后判断从节点的slave-priority值,越小优先级越高

    • 如果slave-prority一样,则判断slave节点的offset值,越大优先级越高

      • 复制偏移量(offset)表示从节点从主节点复制的数据量(字节数),可以理解为从节点与主节点数据同步的“进度条”。

      • Offset越大,说明该从节点从旧主节点接收到的数据越多,即它的数据状态更接近故障前的旧主节点。

    • 最后是判断slave节点的运行id大小,越小优先级越高【Run ID

      1. 为什么选 Run ID 较小的节点?而不是较大的?

      Redis 并没有规定必须选较小的,它只是选择了一种确定性的比较方式,确保所有哨兵能达成一致。

      • 选较小或较大都可以,关键是要有一个统一的规则,避免不同哨兵投票给不同的节点。

      • Redis 选择了字典序较小的 Run ID,可能是出于以下原因:

        • 实现简单:按字典序排序后取第一个(类似 MIN() 操作),代码实现更直接。

        • 历史习惯:许多分布式系统(如 ZooKeeper、Etcd)在选举时也倾向于选择较小的 ID,以减少冲突。

        • 避免人为干扰:如果选较大的 Run ID,可能会有人故意配置更大的 ID 来影响选举,而较小的 Run ID 是随机生成的,更难预测。

      2. Run ID 的大小和 Redis 启动顺序有关系吗?

      没有直接关系!

      • Run ID 是 Redis 启动时随机生成的(16字节的十六进制字符串,如 a1b2c3d4...),并不代表启动顺序

      • 先启动的 Redis 实例的 Run ID 不一定比后启动的小,因为它是完全随机的

      • 所以,Run ID 小的节点 ≠ 先启动的节点,它仅仅是用于选举时的一个确定性比较依据

      3. 如果选较大的 Run ID 会怎样?

      技术上完全可以,但 Redis 选择了较小的,可能是为了:

      • 一致性:许多分布式系统(如 Raft、ZooKeeper)在选举时也倾向于选择较小的 ID,Redis 可能沿用了类似的设计思路。

      • 避免人为操控:如果选较大的 Run ID,管理员可能会故意配置更大的 ID 来影响选举,而较小的 Run ID 是随机的,更公平。

      4. 极端情况:如果 Run ID 相同怎么办?

      • 理论上不可能,因为 Run ID 是随机生成的,碰撞概率极低(16字节的随机数,冲突概率可以忽略)。

      • 如果真的相同(比如人为修改配置文件),哨兵可能会依赖其他机制(如哨兵投票的多数决)来决

三十、Redis分片集群。解决海量数据存储问题以及高并发写的问题。

在 Redis Cluster 中,{aaa} 是哈希标签(Hash Tag),它的作用是让不同的 key 在计算 CRC16 哈希值时,只对 { } 内的内容(即 aaa)进行哈希计算,从而确保这些 key 被分配到同一个 slot(槽位)。

  1. set {aaa}name itheima → CRC16("aaa") = 888888

  2. set {aaa}name itheima22 → CRC16("aaa") = 888888

这是正确的! 因为:

  • CRC16 计算的是 { } 里的内容 aaa,而不是整个 key

  • 只要 { } 里的字符串相同,计算出的哈希值就相同,因此这两个 key 会被分配到同一个 slot。

三十一、Redis的分片集群有什么作用?

  • 集群中有多个master,每个master保存不同数据

  • 每个master都可以有多个slave节点

  • master之间通过ping监测彼此健康状态

  • 客户端请求可以访问集群任意节点,最终都会被转发到正确节点

分片集群主要解决的是海量数据存储的问题,集群中有多个master,每个master保存不同数据,并且还可以给每个master设置多个slave节点,就可以继续增大集群的高并发能力。同时每个master之间通过ping监测彼此健康状态,就类似于哨兵模式了。当客户端请求可以访问集群任意节点,最终都会被转发到正确节点。

三十二、Redis分片集群中数据是怎么存储和读取的?

  • Redis 分片集群引入了哈希槽的概念,Redis 集群有 16384 个哈希槽

  • 将16384个插槽分配到不同的实例

  • 读写数据:根据key的有效部分计算哈希值,对16384取余(有效部分,如果key前面有大括号,大括号的内容就是有效部分,如果没有,则以key本身做为有效部分)余数做为插槽,寻找插所在的实例

三十三、Redis是单线程的,但是为什么还那么快?

  • Redis是纯内存操作,执行速度非常快 ,避免磁盘 I/O 瓶颈

    • 所有数据存储在内存中,读写操作直接在 RAM 中完成,速度比磁盘数据库(如 MySQL)快几个数量级。

    • 虽然支持持久化(RDB/AOF),但通过 异步刷盘 实现,不影响主线程性能。

  • 采用单线程,避免不必要的上下文切换可竞争条件,多线程还要考虑线程安全问题

    • 单线程模型 避免了多线程的锁竞争、线程切换开销,减少了 CPU 资源的浪费。

    • 无锁设计:所有操作按顺序执行,无需考虑并发冲突(如 CAS 操作)。

  • 使用I/O多路复用模型,非阻塞IO

    • 通过 非阻塞 I/O + 多路复用 处理海量客户端连接(如 10 万+ QPS)。

      • Linux 下使用 epoll,Mac 下使用 kqueue

      • 单线程监听多个 Socket 事件,高效调度网络请求。

  • 高效的数据结构和算法

    • 优化过的数据结构:如哈希表、跳表、压缩列表(ziplist)等,在时间和空间上均高度优化。

    • 渐进式 Rehash:扩容时不阻塞主线程。

  • 避免复杂事务和关联查询

    • 无 SQL 解析、连接查询 等复杂操作,命令执行路径极短(如 GET key 直接访问内存哈希表)。

  • 补充优化技术

    • 管道(Pipeline):客户端批量发送命令,减少网络往返时间。

    • Lua 脚本:将多个操作合并为一个原子操作,减少通信开销。

  • 为什么单线程反而更快?
    • CPU 不是瓶颈:Redis 的性能瓶颈通常是 内存或网络带宽,而非 CPU。

    • 多线程的代价:线程切换、锁竞争、缓存一致性等问题会抵消多核优势,而 Redis 的简单操作模型让单线程更高效。

适用场景

  • 高吞吐、低延迟:适合缓存、计数器、消息队列等场景。

  • 不适用场景:需要大量 CPU 计算的任务(如复杂数据分析)。

三十四、I/O多路复用模型

Redis是纯内存操作,执行速度非常快,它的性能瓶颈是网络延迟而不是执行速度,I/0多路复用模型主要就是实现了高效的网络请求。

  • 用户空间和内核空间

  • 常见的IO模型

    • 阻塞IO(Blocking lO)

    • 非阻塞IO(Nonblocking lO)

    • IO多路复用(10 Multiplexing)

  • Redis网络模型

1. 阻塞IO(Blocking IO)

场景类比:排队买奶茶

  • 你去奶茶店点单,服务员说:“稍等,奶茶做好后叫你。”(你只能干等,不能做其他事

  • 你一直站在柜台前等,直到奶茶做好,期间不能玩手机、不能离开。

计算机中的表现

  • 当程序发起一个 读/写操作(比如读取文件、网络请求),如果数据没准备好,线程会一直卡住(阻塞),直到数据就绪。

  • 缺点:浪费 CPU 时间,效率低(一个线程只能处理一个请求)。


2. 非阻塞IO(Non-blocking IO)

场景类比:不断问奶茶做好了没

  • 你去奶茶店点单,服务员说:“你先去逛逛,好了我叫你。”

  • 但你是个急性子,每隔5秒就跑回来问:“奶茶好了吗?”(而不是干等)

  • 如果没好,你就继续去逛;如果好了,你拿走奶茶。

计算机中的表现

  • 程序发起IO请求后,内核立即返回(不管数据是否准备好)。

  • 程序需要 不断轮询(循环检查) 数据是否就绪。

  • 优点:线程不会卡死,可以同时做其他事。

  • 缺点:轮询消耗CPU资源(一直问“好了吗?”很累)。


3. IO多路复用(IO Multiplexing)

场景类比:一个服务员管理多个顾客

  • 现在奶茶店有 一个智能服务员,你点单后,服务员给你一个号码牌。

  • 你坐在旁边玩手机,服务员会 同时监控多个订单

  • 当你的奶茶好了,服务员叫你的号码,你再去拿。

计算机中的表现

  • 使用 select/poll/epoll 机制,一个线程可以监听多个IO请求

  • 内核会主动通知程序哪些IO已经就绪,程序再去处理。

  • 优点

    • 单线程可处理成千上万的连接(高并发)。

    • 比非阻塞IO更高效(不需要无脑轮询)。

  • 典型应用:Redis、Nginx、Java NIO。

三者的核心区别

类型工作方式优点缺点适用场景
阻塞IO一直等,直到数据就绪简单易用效率低,线程被卡住低并发场景
非阻塞IO不断轮询,检查数据是否就绪线程不会卡死轮询消耗CPU需要实时响应的场景
IO多路复用一个线程监听多个IO,内核通知高并发,资源占用少编程复杂高并发服务器(Redis)

现实中的例子

  1. 阻塞IO → 你去银行柜台办业务,柜员处理时你只能干等。-- 简单但低效,适合低并发。

  2. 非阻塞IO → 你在银行取号后,每隔几分钟去问“到我了没?”(浪费体力)。-- 不阻塞线程,但需要轮询,消耗CPU。

  3. IO多路复用 → 银行有大屏幕叫号,你可以坐着玩手机,等叫到你再去。-- 高并发神器,一个线程管多个IO(Redis、Nginx都用它)。

三十五、Redis网络模型

三十六、能解释一下I/O多路复用模型?

1.I/O多路复用

是指利用单个线程来同时监听多个Socket ,并在某个Socket可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。目前的I/O多路复用都是采用的epoll模式实现,它会在通知用户进程Socket就绪的同时,把已就绪的Socket写入用户空间,不需要挨个遍历Socket来判断是否就绪,提升了性能。

2.Redis网络模型 就是使用 I/O多路复用 结合 事件的处理器 来应对多个Socket请求

  • 连接应答处理器

  • 命令回复处理器,在Redis6.0之后,为了提升更好的性能,使用了多线程来处理回复事件

  • 命令请求处理器,在Redis6.0之后,将命令的转换使用了多线程,增加命令转换速度,在命令执行的时候,依然是单线程

关键字:免费24小时在线心理医生_网络建站网网络推广_百度搜索引擎营销如何实现_北京学电脑的培训机构

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: