一、基础概念&核心特性(必问)
1. 什么是Redis?它的核心定位和应用场景是什么?
核心知识点
Redis(Remote Dictionary Server)是开源的、高性能的键值对(Key-Value)NoSQL数据库,基于内存运行,支持持久化,采用单线程核心IO模型,提供丰富数据结构和扩展功能。
原理详解
-
核心定位:内存数据库(优先操作内存,性能极高)+ 持久化存储(避免内存数据丢失)+ 分布式协调工具(支持分布式锁、发布订阅等)。
-
核心优势(原理层面):
-
高性能:内存操作无磁盘IO瓶颈,读QPS可达10万+/s,写QPS可达5万+/s;单线程模型避免多线程上下文切换、锁竞争开销,同时用IO多路复用(epoll/kqueue)处理大量并发连接,解决单线程无法应对多连接的问题。
-
灵活扩展:支持主从复制、哨兵、集群模式,可实现高可用和水平扩展。
-
功能丰富:原生支持多种数据结构、持久化、事务、Lua脚本、发布订阅等,无需额外开发。
- 典型应用场景:
-
缓存:热点数据(商品、用户信息)缓存,减少数据库压力;
-
会话存储:分布式会话(如Spring Session集成Redis);
-
分布式锁:基于SET NX EX实现,解决分布式系统并发竞争问题;
-
计数器/限流:incr/decr实现点赞、阅读量计数,结合过期时间实现接口限流;
-
消息队列:Stream/List结构实现简单的消息生产消费(替代轻量级MQ);
-
地理位置:Geo结构实现附近的人、门店定位功能。
2. Redis支持哪些数据结构?每种结构的底层实现原理是什么?(高频重点)
核心知识点
Redis支持8种核心数据结构:String(字符串)、Hash(哈希)、List(列表)、Set(集合)、ZSet(有序集合)、Bitmap(位图)、HyperLogLog(基数统计)、Geo(地理位置),每种结构底层有专属实现,决定其性能和适用场景。
原理详解(分结构拆解)
(1)String(字符串)
-
底层实现:简单动态字符串(SDS) ,而非C语言原生字符串(char*)。
-
SDS原理:SDS由「len(已用长度)、free(空闲长度)、buf(字节数组)」三部分组成,优势如下:
-
避免缓冲区溢出:修改字符串时,先检查len+新增长度是否超过buf容量,不足则自动扩容(扩容规则:小于1MB时翻倍,大于1MB时每次增加1MB);
-
快速获取长度:直接读取len属性,时间复杂度O(1)(C语言需遍历,O(n));
-
减少内存分配:free属性记录空闲空间,支持预分配(减少频繁扩容)和惰性释放(删除字符串时不立即释放内存,留作后续复用);
-
二进制安全:buf存储的是字节流,可存储图片、视频等二进制数据(C语言字符串以\0结尾,无法存储含\0的二进制数据)。
- 适用场景:缓存、计数器、分布式锁、存储简单字符串(如token)。
(2)Hash(哈希)
-
底层实现:两种结构自适应切换——ziplist(压缩列表) → hashtable(哈希表) 。
-
切换条件(Redis 7.0前):当哈希中元素个数 ≤ 512 且每个元素的key和value长度 ≤ 64字节时,用ziplist;否则切换为hashtable。(Redis 7.0后,ziplist被listpack替代,逻辑类似,更高效)
-
各自原理:
-
ziplist(压缩列表):一种紧凑的内存结构,将所有键值对紧凑存储在连续内存中,减少内存碎片,适合小数据量场景;缺点是插入/删除需移动元素,时间复杂度O(n)。
-
hashtable(哈希表):Redis的核心哈希结构,由「数组+链表」组成(类似Java的HashMap),解决哈希冲突的方式是「链地址法」(冲突的元素挂在同一个数组下标对应的链表上);Redis 3.0后引入「渐进式rehash」,解决扩容时的性能瓶颈(避免一次性rehash导致单线程阻塞)。
- 适用场景:存储对象(如用户信息、商品详情),可单独操作对象的某个字段(hset/hget),比String存储对象更节省内存、更灵活。
(3)List(列表)
-
底层实现:同样自适应切换——ziplist(压缩列表) → quicklist(快速列表) 。
-
切换条件:元素个数 ≤ 512 且每个元素长度 ≤ 64字节时用ziplist;否则用quicklist。(Redis 3.2后,用quicklist替代了原有的linkedlist,优化内存和性能)
-
原理:
-
quicklist:由多个ziplist组成的双向链表,每个ziplist是一个紧凑的内存块,既保留了链表的高效插入/删除(两端操作O(1)),又通过ziplist减少了内存碎片;
-
核心特性:双向链表,支持从头部(lpush/lpop)、尾部(rpush/rpop)插入/删除元素,按索引访问元素(lindex)时间复杂度O(n)。
- 适用场景:消息队列(简单生产者-消费者模型)、最新消息列表(如朋友圈点赞列表)、栈/队列实现。
(4)Set(集合)
-
底层实现:自适应切换——intset(整数集合) → hashtable(哈希表) 。
-
切换条件:当集合中所有元素都是整数,且元素个数 ≤ 512时,用intset;否则用hashtable。
-
原理:
-
intset:紧凑的整数存储结构,元素按升序排列,支持快速查找(二分查找,O(log n)),插入/删除需移动元素(O(n));
-
hashtable:用哈希表存储元素,key是集合元素,value为null,利用哈希表的特性实现元素的快速查找、插入、删除(O(1)),且自动去重。
-
核心特性:无重复元素、支持交集(sinter)、并集(sunion)、差集(sdiff)操作。
-
适用场景:去重(如用户标签去重)、共同好友、抽奖活动(srandmember/spop)。
(5)ZSet(有序集合)
-
底层实现:自适应切换——ziplist(压缩列表) → skiplist(跳表)+ hashtable。
-
切换条件:元素个数 ≤ 128 且每个元素的key和value长度 ≤ 64字节时用ziplist;否则用skiplist+hashtable。
-
原理(重点,面试高频):
-
ziplist:元素按分数升序存储,每个元素由「分数+成员」组成,紧凑存储,适合小数据量;
-
skiplist+hashtable(核心):
-
skiplist(跳表):一种有序数据结构,通过“分层”实现快速查找,类似平衡树但实现更简单;每个节点有多个层级,高层节点是低层节点的“索引”,查找时从高层开始,快速定位到目标范围,时间复杂度O(log n);用于维护元素的有序性,支持按分数范围查询(zrangebyscore);
-
hashtable:用于快速根据成员查找分数(zscore),时间复杂度O(1);两者结合,既保证了有序性,又保证了快速查询。
-
核心特性:元素有序(按分数排序)、无重复成员、支持按分数/排名查询。
-
适用场景:排行榜(如商品销量、用户积分)、带权重的消息队列、范围查询(如查询分数在80-100的用户)。
(6)Bitmap(位图)
-
底层实现:基于String实现,本质是字节数组(每个字节的8个bit位存储0/1),利用bit位节省内存。
-
原理:每个bit位对应一个状态(0=未发生,1=已发生),通过bitop(与/或/非/异或)、bitcount(统计1的个数)、setbit/getbit(设置/获取bit位)等命令操作,时间复杂度O(1)。
-
适用场景:签到统计(每天一个bit位,一年仅需45字节)、用户在线状态(1=在线,0=离线)、连续活跃天数统计。
(7)HyperLogLog(基数统计)
-
底层实现:基于概率算法,通过存储元素的哈希值的高位特征,估算集合的基数(不重复元素个数),占用内存固定(约12KB),与集合大小无关。
-
原理:核心是「伯努利试验」,通过统计哈希值中“前导0的最大个数”,估算基数;存在极小误差(约0.81%),但无需存储所有元素,适合海量数据的基数统计。
-
适用场景:UV统计(网站独立访客)、用户行为去重统计(如浏览商品的独立用户数)。
(8)Geo(地理位置)
-
底层实现:基于ZSet实现,将经纬度转换为「geohash值」(一个64位整数)作为ZSet的分数,成员为地理位置名称;通过geohash值的特性,实现附近的人、距离计算等功能。
-
原理:geohash算法将二维的经纬度( longitude,latitude)转换为一维的整数,相邻的地理位置的geohash值也相邻,因此可以通过ZSet的范围查询(zrangebyscore),找到附近的地理位置。
-
适用场景:附近的人、门店定位、距离计算(geodist)。
3. Redis和Memcached的核心区别是什么?(高频对比)
核心知识点
两者都是内存缓存工具,但Redis功能更全面、支持持久化和更多数据结构,Memcached更轻量、专注于简单缓存,核心区别集中在数据结构、持久化、原子性等维度。
原理详解(对比表格+重点补充)
| 对比维度 | Redis | Memcached |
|---|---|---|
| 数据结构 | 支持String、Hash、List等8种结构,功能丰富 | 仅支持String类型,功能单一 |
| 持久化 | 支持RDB、AOF两种持久化方式,可将内存数据落盘,重启不丢失 | 不支持持久化,数据仅存于内存,重启即丢失 |
| 原子性 | 原生支持单个操作原子性,支持事务(MULTI/EXEC)、Lua脚本,保证复杂操作原子性 | 仅支持简单的CAS原子操作,不支持事务和Lua脚本 |
| 内存管理 | 采用动态内存分配,支持内存淘汰策略(如LRU),可设置最大内存 | 采用预分配内存池机制,内存分配后无法回收(易产生内存碎片),不支持内存淘汰 |
| 并发连接 | 单线程IO模型+IO多路复用,支持海量并发连接 | 多线程模型,处理并发连接的方式与Redis不同 |
| 高可用 | 支持主从复制、哨兵、集群,可实现故障自动切换和水平扩展 | 不支持原生高可用,需依赖第三方工具(如Keepalived)实现主从切换 |
| 适用场景 | 复杂缓存、分布式锁、会话存储、消息队列等多场景 | 简单的字符串缓存(如静态页面缓存),追求极致轻量 |
补充:Redis单线程 vs Memcached多线程——Redis单线程避免了多线程的上下文切换和锁竞争,而Memcached多线程可利用多核CPU,但多线程的开销可能抵消部分性能优势;在高频简单读写场景,两者性能接近,复杂场景Redis更有优势。
二、持久化机制(核心重点,必问)
1. Redis的持久化机制有哪些?各自的实现原理、优缺点是什么?
核心知识点
Redis提供两种持久化方式:RDB(Redis Database)和AOF(Append Only File),可单独使用,也可组合使用;核心目的是将内存中的数据落盘,避免Redis重启后数据丢失。
原理详解(分两种方式拆解)
(1)RDB(快照持久化)
-
定义:在指定的时间间隔内,将Redis内存中的所有数据生成一份快照(二进制文件,.rdb),保存到磁盘中;Redis重启时,加载.rdb文件,恢复数据。
-
实现原理(触发机制+执行流程):
- 触发机制:
-
手动触发:执行save(同步)、bgsave(异步)命令;
-
自动触发:通过redis.conf配置触发,如「save 900 1」(900秒内有1次写操作,触发bgsave)、「save 300 10」(300秒内有10次写操作)、「save 60 10000」(60秒内有10000次写操作);
-
其他触发:Redis关闭(shutdown)时,会自动执行save(同步),保证数据不丢失;主从复制时,主节点会执行bgsave,将快照发送给从节点。
- 执行流程(bgsave,核心异步流程):
-
- 客户端发送bgsave命令,Redis主进程fork(创建子进程),子进程会复制主进程的内存数据(写时复制,COW);
-
- 子进程将内存中的数据写入临时的.rdb文件,写入完成后,替换原有的.rdb文件;
-
- 子进程执行完成后,向主进程发送信号,主进程更新持久化状态;
-
注:fork过程会阻塞主进程(时间很短,取决于内存大小),子进程执行期间,主进程可正常处理客户端请求,写入的数据会被记录到缓冲区,待子进程完成后,再同步到新的.rdb文件。
-
优点:
-
快照文件体积小,加载速度快(二进制文件,直接解析加载);
-
异步触发(bgsave)时,不影响主进程处理请求(仅fork时短暂阻塞);
-
适合全量备份、灾难恢复(如定期备份.rdb文件)。
-
缺点:
-
数据一致性差:RDB是间隔性快照,若Redis在两次快照之间崩溃,会丢失这段时间内的所有写操作;
-
fork子进程开销大:若内存数据量大(如10GB),fork过程会阻塞主进程,影响服务可用性;
-
不适合实时持久化场景(如金融类数据,要求数据零丢失)。
(2)AOF( Append Only File,追加文件持久化)
-
定义:将Redis的每一条写操作命令(如set、hset、incr),以文本格式追加到.aof文件中;Redis重启时,重新执行.aof文件中的所有命令,恢复数据。
-
实现原理(触发机制+执行流程+重写机制):
- 触发机制:
-
开启AOF:在redis.conf中配置「appendonly yes」(默认关闭);
-
命令追加:每执行一条写操作命令,Redis会将命令追加到.aof缓冲区(而非直接写入磁盘);
-
缓冲区同步到磁盘:通过「appendfsync」配置同步策略(核心,面试高频):
-
appendfsync always:每执行一条写命令,立即将缓冲区数据写入磁盘(最安全,数据零丢失,但IO开销最大,性能最差);
-
appendfsync everysec:每秒将缓冲区数据写入磁盘(默认配置,平衡安全性和性能,最多丢失1秒数据);
-
appendfsync no:由操作系统决定何时将缓冲区数据写入磁盘(最不安全,可能丢失大量数据,性能最好)。
- 执行流程:
- 客户端执行写命令 → 命令被追加到AOF缓冲区 → 按appendfsync策略同步到.aof文件 → Redis重启时,读取.aof文件,逐行执行命令,恢复数据。
- AOF重写(核心优化,解决.aof文件过大问题):
-
问题:AOF文件会随着写操作增多而不断增大,不仅占用磁盘空间,还会导致Redis重启时加载速度变慢;
-
原理:重写时,Redis会扫描内存中的所有数据,生成对应的写命令(如将100次incr key命令,合并为set key 100),替换原有的.aof文件,减少文件体积;
-
触发机制:
-
手动触发:执行bgrewriteaof命令(异步,不阻塞主进程);
-
自动触发:通过配置「auto-aof-rewrite-percentage 100」(当AOF文件体积比上次重写后增大100%)和「auto-aof-rewrite-min-size 64mb」(AOF文件体积至少64MB),满足两个条件自动触发重写。
-
优点:
-
数据一致性高:可配置为每秒同步或每命令同步,最多丢失1秒数据(甚至零丢失);
-
恢复精度高:记录每一条写命令,重启时可完整恢复数据;
-
重写机制可优化文件体积,避免磁盘占用过大。
-
缺点:
-
AOF文件体积比RDB大(文本格式,命令冗余),加载速度比RDB慢;
-
同步IO开销大(尤其是appendfsync always),性能比RDB略差;
-
可能出现AOF文件损坏(如磁盘故障),导致数据恢复失败(可通过redis-check-aof工具修复)。
(3)RDB和AOF的对比总结
-
数据一致性:AOF > RDB(AOF可实现近实时持久化);
-
文件体积:RDB < AOF;
-
加载速度:RDB > AOF;
-
性能开销:RDB(异步)< AOF(同步IO);
-
适用场景:RDB适合全量备份、灾难恢复;AOF适合实时持久化、数据零丢失场景;生产环境通常组合使用(RDB做全量备份,AOF做增量备份,重启时优先加载AOF,保证数据完整性)。
2. Redis重启时,如何选择加载RDB还是AOF文件?
核心知识点
Redis重启时,加载持久化文件的优先级由配置和文件存在性决定,核心原则是「优先加载AOF文件,保证数据完整性」。
原理详解
-
若AOF功能开启(appendonly yes),且.aof文件存在:Redis优先加载AOF文件,因为AOF文件的数据更完整(丢失数据更少);
-
若AOF功能关闭(appendonly no),或.aof文件不存在、损坏且无法修复:Redis会加载.rdb文件;
-
若既没有.aof文件,也没有.rdb文件:Redis重启后为空白,无任何数据;
补充:若AOF文件损坏,可通过「redis-check-aof --fix 文件名」工具修复,修复后再重启Redis加载。
三、内存管理&淘汰策略(高频)
1. Redis的内存淘汰策略有哪些?各自的原理和适用场景是什么?
核心知识点
Redis是内存数据库,当内存使用达到「maxmemory」(配置文件中设置的最大内存)时,会触发内存淘汰策略,删除部分数据,释放内存,避免内存溢出;Redis 4.0后支持8种淘汰策略,分为三大类。
原理详解(分大类拆解,重点记常用策略)
(1)淘汰策略分类(按淘汰范围)
- 只淘汰过期键(3种):
-
volatile-lru:淘汰「过期键」中,最近最少使用(LRU,Least Recently Used)的键;
-
volatile-ttl:淘汰「过期键」中,剩余过期时间(TTL)最短的键;
-
volatile-random:淘汰「过期键」中,随机选择的键;
-
适用场景:仅希望淘汰过期数据,保留未过期数据(如缓存热点数据,未过期的需保留)。
- 淘汰所有键(4种):
-
allkeys-lru:淘汰「所有键」中,最近最少使用的键(最常用策略);
-
allkeys-random:淘汰「所有键」中,随机选择的键;
-
allkeys-lfu:淘汰「所有键」中,最近最不常用(LFU,Least Frequently Used)的键(Redis 4.0新增,比LRU更精准,考虑使用频率);
-
volatile-lfu:淘汰「过期键」中,最近最不常用的键;
-
适用场景:allkeys-lru适合缓存场景(优先保留热点数据);allkeys-lfu适合使用频率不均匀的场景(如用户访问,部分数据高频访问,部分低频)。
- 不淘汰(1种):
-
noeviction:不淘汰任何键,当内存达到maxmemory后,拒绝所有写操作(返回OOM错误),读操作正常;
-
适用场景:不允许丢失任何数据,如Redis作为数据库使用(非缓存),需手动处理内存溢出。
(2)核心策略原理补充(LRU和LFU,面试重点)
- LRU(最近最少使用):
-
原理:假设“最近使用过的数据,未来被使用的概率更高”,淘汰最久未被使用的数据;
-
Redis实现:Redis并未维护完整的LRU链表(会占用大量内存),而是采用「采样LRU」——每次淘汰时,随机采样N个键(默认5个),淘汰其中最久未使用的键;通过配置「maxmemory-samples」调整采样数量,采样越多,淘汰越精准,但性能开销越大。
- LFU(最近最不常用):
-
原理:在LRU的基础上,增加“使用频率”维度,假设“使用频率越高的数据,未来被使用的概率更高”,淘汰使用频率最低的数据;
-
Redis实现:每个键的内存结构中,用24位记录「使用频率(counter)」,每次访问键时,counter会增加(增加幅度随频率变化),同时counter会定期衰减(避免旧数据长期占用高频率);淘汰时,采样N个键,淘汰counter最小的键。
(3)生产环境常用配置
-
缓存场景:优先配置「allkeys-lru」(最通用,保留热点数据);
-
数据访问频率差异大的场景:配置「allkeys-lfu」(更精准淘汰低频数据);
-
不允许淘汰未过期数据的场景:配置「volatile-lru」。
2. Redis的内存碎片是怎么产生的?如何解决?
核心知识点
内存碎片是指Redis占用的内存大于实际存储数据的内存,导致内存利用率降低;产生原因与内存分配、数据删除、数据结构变化有关,可通过主动整理或配置优化解决。
原理详解
(1)内存碎片产生的原因
-
内存分配机制:Redis使用jemalloc(默认)、tcmalloc等内存分配器,这些分配器会按固定大小的内存块(如8B、16B、32B)分配内存;当存储的数据大小与分配的内存块大小不匹配时,会产生碎片(如存储10B数据,分配16B内存块,剩余6B为碎片)。
-
数据频繁删除/修改:
-
删除数据时,Redis采用「惰性释放」(不立即回收内存,留作后续复用),若后续复用的内存块大小与碎片大小不匹配,碎片会一直存在;
-
数据修改(如String从10B扩容到20B),原内存块无法容纳,需分配新的内存块,原内存块变为碎片。
- 数据结构变化:如Hash从ziplist切换为hashtable,原ziplist占用的内存块变为碎片。
(2)解决方法
-
主动整理内存:执行「memory purge」命令(Redis 4.0+),主动回收惰性释放的内存碎片(需配合内存分配器支持,如jemalloc);
-
重启Redis:重启后,Redis会重新分配内存,碎片会被清除(简单直接,但会导致短暂服务不可用,需提前做好备份);
-
优化配置:
-
合理设置「maxmemory」,避免内存过度使用导致碎片增多;
-
优化数据结构,尽量使用紧凑的数据结构(如小数据量用ziplist、intset),减少内存分配不匹配的情况;
-
避免频繁删除、修改大量数据,减少碎片产生。
四、高可用机制(主从、哨兵、集群,高频重点)
1. Redis主从复制的原理是什么?有哪些复制模式?
核心知识点
主从复制是Redis高可用的基础,核心是「主节点(master)数据同步到从节点(slave)」,实现数据备份和读写分离(主写从读);支持全量复制和增量复制两种模式。
原理详解(分复制模式+执行流程)
(1)核心作用
-
数据备份:从节点是主节点的副本,主节点故障时,从节点可作为备用节点,避免数据丢失;
-
读写分离:主节点负责写操作,从节点负责读操作,分担主节点压力,提升并发读性能;
-
水平扩展:通过增加从节点,提升读并发能力。
(2)复制模式(全量复制+增量复制)
1. 全量复制(首次同步,核心流程)
当从节点首次连接主节点,或从节点与主节点断开连接后重新连接(超过复制偏移量),会触发全量复制,流程如下:
-
从节点发送「slaveof masterIP masterPort」命令,向主节点发起同步请求;
-
主节点收到请求后,执行bgsave命令,生成RDB快照文件,同时将期间的所有写操作记录到「复制缓冲区」;
-
主节点将RDB快照文件发送给从节点,从节点接收完成后,清空自身内存,加载RDB文件,恢复数据;
-
主节点将复制缓冲区中的写操作命令,逐一向从节点发送,从节点执行这些命令,实现与主节点数据一致;
-
同步完成后,主节点后续的写操作,会实时发送给从节点,保持数据同步。
缺点:全量复制会占用大量网络带宽(RDB文件传输)和主/从节点资源(主节点bgsave、从节点加载RDB),适合首次同步。
2. 增量复制(后续同步,核心流程)
全量复制完成后,主节点与从节点保持连接,主节点会将所有写操作实时同步到从节点,这就是增量复制;核心依赖「复制偏移量」和「复制缓冲区」:
-
主节点和从节点各自维护一个「复制偏移量」(offset):主节点每执行一次写操作,offset+1;从节点每接收并执行一次主节点的命令,offset+1;
-
主节点维护一个「复制缓冲区」(环形缓冲区),存储最近执行的写操作命令;
-
若从节点因网络波动短暂断开连接,重新连接后,会向主节点发送自己的offset;
-
主节点判断从节点的offset是否在复制缓冲区的范围内:
-
若在范围内:主节点将offset之后的命令发送给从节点,从节点执行,完成增量同步;
-
若不在范围内(缓冲区已覆盖该offset):触发全量复制。
(3)注意事项
-
主节点默认开启写操作,从节点默认只读(slave-read-only yes),不可执行写操作;
-
一个主节点可对应多个从节点,一个从节点只能对应一个主节点(不支持链式复制,Redis 4.0+支持从节点作为其他从节点的主节点,但不推荐);
-
复制过程中,主节点的写操作不会阻塞(bgsave异步执行,复制缓冲区缓存命令),从节点加载RDB时会阻塞,无法处理读请求。
2. Redis哨兵(Sentinel)的原理和作用是什么?核心流程有哪些?
核心知识点
哨兵是Redis的高可用解决方案,核心作用是「监控主从节点、自动故障切换、通知客户端」,解决主从复制中主节点故障后需手动切换的问题;哨兵本身是一个分布式集群,至少需要3个哨兵节点(避免单点故障)。
原理详解
(1)核心作用
-
监控(Monitoring):哨兵节点实时监控主节点(master)和从节点(slave)的运行状态(是否在线、是否正常响应);
-
故障检测(Failure Detection):当主节点故障时,哨兵集群会通过“投票”机制,判定主节点为「客观下线」(ODOWN);
-
自动故障切换(Automatic Failover):主节点下线后,哨兵集群从所有从节点中选举一个作为新的主节点,将其他从节点切换为新主节点的从节点,同时通知客户端新的主节点地址;
-
通知(Notification):当节点状态变化(主节点下线、故障切换完成)时,哨兵会通过配置的脚本,通知管理员或其他系统(如监控系统)。
(2)核心流程(故障检测+故障切换)
1. 故障检测(主观下线+客观下线)
-
主观下线(SDOWN):单个哨兵节点检测到主节点未响应(如ping命令无回复),且超过「down-after-milliseconds」配置的时间(默认30000ms),则判定主节点为主观下线;
-
客观下线(ODOWN):单个哨兵判定主节点主观下线后,会向其他哨兵节点发送请求,询问是否也认为该主节点下线;当超过「quorum」(配置的法定人数,默认1)个哨兵判定主节点主观下线时,主节点被判定为客观下线(确认故障)。
2. 故障切换(核心步骤)
-
选举哨兵leader:哨兵集群中,通过Raft算法选举一个哨兵作为leader,由leader负责执行故障切换(避免多个哨兵同时执行切换,导致混乱);
-
选择新主节点:leader哨兵从所有健康的从节点中,选择一个作为新的主节点,选择规则(优先级从高到低):
-
优先选择「slave-priority」(从节点优先级)最高的从节点;
-
优先级相同,选择复制偏移量最大的从节点(数据最完整);
-
偏移量相同,选择运行时间最久的从节点;
-
以上都相同,选择节点ID最小的从节点。
- 执行切换:
-
向新主节点发送「slaveof no one」命令,让其成为主节点;
-
向其他从节点发送「slaveof 新主节点IP 新主节点端口」命令,让它们切换为新主节点的从节点;
-
记录原主节点的信息,当原主节点恢复后,将其切换为新主节点的从节点(不再作为主节点)。
- 通知客户端:哨兵将新主节点的地址通知给所有连接的客户端,客户端后续的写操作会指向新主节点。
(3)哨兵集群配置要点
-
哨兵集群至少3个节点(奇数个,避免投票平局),每个哨兵节点配置相同的监控规则;
-
配置「quorum」:建议设置为哨兵节点数的一半+1(如3个哨兵,quorum=2),保证故障判定的准确性;
-
哨兵节点与主从节点之间、哨兵节点之间,需保证网络连通(避免哨兵无法监控节点)。
3. Redis Cluster(集群)的原理是什么?如何实现高可用和水平扩展?
核心知识点
Redis Cluster是Redis的分布式解决方案,核心是「分片存储+主从复制」,将数据分片存储在多个节点上,实现水平扩展(支持海量数据),同时每个分片节点有主从备份,实现高可用;集群至少需要3个主节点,每个主节点至少1个从节点(共6个节点)。
原理详解
(1)核心设计:分片存储(一致性哈希)
-
哈希槽(Hash Slot):Redis Cluster将整个键空间分为16384个哈希槽(0-16383),每个主节点负责一部分哈希槽(如3个主节点,分别负责0-5460、5461-10922、10923-16383);
-
键的分配规则:对键进行CRC16哈希计算,得到哈希值后,对16384取模,得到对应的哈希槽,该键就存储在负责该哈希槽的主节点上;
- 示例:key=user:123,CRC16(user:123) = 12345,12345 % 16384 = 12345,若该哈希槽由主节点A负责,则key存储在A节点;
- 优势:哈希槽固定,可灵活调整主节点负责的哈希槽(如新增主节点时,将其他主节点的部分哈希槽迁移到新节点),实现水平扩展。
(2)高可用实现(主从复制+自动故障切换)
-
节点结构:每个主节点(master)对应至少1个从节点(slave),主节点负责哈希槽的读写操作,从节点负责备份主节点的数据;
-
故障检测:集群中所有节点互相监控(通过Gossip协议交换节点状态),当某个主节点故障时,其从节点会检测到,并发起故障切换;
-
故障切换流程:
-
主节点故障后,其从节点中会通过选举(类似哨兵的Raft算法),选出一个作为新的主节点;
-
新主节点接管原主节点的所有哈希槽,集群中其他节点更新哈希槽与主节点的对应关系;
-
原主节点恢复后,成为新主节点的从节点。
(3)客户端访问原理
-
客户端首次连接集群时,会获取集群的「哈希槽分配信息」(所有主节点及其负责的哈希槽);
-
客户端对键进行哈希计算,确定对应的哈希槽和主节点,直接向该主节点发送请求;
-
若客户端发送请求的节点,不负责该键的哈希槽,节点会返回「MOVED」命令,告知客户端该键对应的正确主节点地址,客户端收到后,重新向正确节点发送请求(一次重定向);
-
若哈希槽正在迁移(如扩容、缩容),节点会返回「ASK」命令,告知客户端临时的主节点地址,客户端临时向该节点发送请求。
(4)集群的优势与局限
优势:
-
水平扩展:支持新增主节点,分担哈希槽,突破单节点内存限制;
-
高可用:每个主节点有从节点备份,故障自动切换,不影响服务;
-
分布式部署:节点可分布在不同服务器,提升并发处理能力。
局限:
-
不支持跨节点事务(如MULTI/EXEC命令,只能在单个节点内执行);
-
不支持跨节点的聚合操作(如sinter、zunion,只能在单个节点内执行);
-
配置和维护比主从、哨兵更复杂。