热门八股-Redis

基础概念

1.Redis是什么?为什么叫高性能?

Redis是一个基于内存的高性能Key-Value数据库,也常被用作缓存、分布式锁、消息队列、排行榜、计数器等

它之所以高性能,主要因为:

  • 数据主要存在内存里,读写速度远高于磁盘
  • 核心命令执行是单线程,避免了多线程锁竞争和上下文切换
  • 使用IO多路复用,一个线程可以同时处理大量连接
  • 数据结构设计得很精巧,不同场景有不同的底层编码
  • 使用操作系统零拷贝,减少数据拷贝开销

面试可以说:Redis快,不只是因为内存,还因为单线程模型、多路IO复用和高效数据结构一起发挥作用

2.Redis为什么快?单线程模型、多路IO复用、内存操作、数据结构优化

  • 纯内存操作。大部分请求直接在内存里完成,不需要频繁访问磁盘
  • 单线程执行命令。Redis的核心命令处理是单线程,避免了加锁、线程切换等开销
  • IO多路复用。Redis用一个线程监听多个socket连接,谁有事件就处理,不需要一个线程一个连接
  • 底层数据结构优化。比如List用quicklist,Hash小数据用listpack大数据再转hashtable,既省内存又保证性能

3.Redis单线程为什么还能抗高并发?

Redis单线程指的是命令执行主流程单线程,不是整个Redis只有一个线程

它能抗高并发,原因是:

  • 请求大多数是内存操作,执行非常快
  • 单线程避免了锁竞争
  • IO多路复用可以同时管理大量连接
  • Redis命令通常很短,不会长时间占用CPU

但也要注意,如果执行大key删除、复杂Lua、大范围查询等慢命令,单线程会被阻塞,影响所有请求。

4.Redis支持的数据类型有哪些?五大基础+3大特殊+高级类型

Redis常见5大基础数据类型:

  • String:字符串、数字、二进制
  • List:列表
  • Hash:哈希
  • Set:无序集合
  • ZSet:有序集合

3大特殊数据类型:

  • Bitmap:位图
  • HyperLogLog:基数统计
  • Geo:地理位置

高级或扩展类型:

  • Stream:消息队列
  • Bitfield:位域操作
  • Bloom Filter:布隆过滤器,通常来自RedisBloom模块

5.Redis常用应用场景有哪些

  • 缓存:缓存热点数据,减轻数据库压力
  • 分布式锁:用 set nx ex 实现互斥
  • 计数器:文章阅读数、点赞数、库存扣减
  • 排行榜:ZSet实现积分榜、热度榜
  • Session共享:分布式系统保存登录态
  • 消息队列:List、Stream实现异步消息
  • 限流:计数器、滑动窗口、令牌桶
  • 去重统计:Bitmap、HyperLogLog、布隆过滤器

Redis适合高频、低延迟、数据结构友好的场景

基础数据结构

1.String底层结构、最大容量、使用场景

Redis String底层使用SDS,也就是Simple Dynamic String。

SDS相比C字符有几个优势:

  • 记录字符串长度,获取长度为O(1)
  • 二进制安全,可以存图片、序列化对象等
  • 预分配空间,减少频繁扩容
  • 防止缓冲区溢出

String最大容量是512MB,但实际开发不建议存这么大,否则会造成网络、内存和阻塞问题

常见场景:

  • 缓存JSON字符串
  • 计数器incr
  • 分布式锁
  • 验证码、token、session

2.List底层quicklist原理

Redis早期List底层用ziplist和linkedlist,后来统一改成quicklist。

quicklist可以理解为"链表+压缩列表"的组合。整体是一个双向链表,每个链表节点里存一段连续内存结构

这样做的好处:

  • 比纯链表更省内存,因为减少大量指针开销
  • 比纯链表数组更适合两端插入,删除
  • 兼顾空间和性能

List常用于队列、栈、简单消息队列等场景,但现在更推荐Stream做可靠消息队列

3.Hash底层结构,为什么适合hash

Hash是field-value结构,很适合存对象

Hash底层在数据少、字段短时使用listpack,节省内存;数据变多后会转为hashtable,提高查询效率

它适合存对象的原因:

  • 可以单独修改某个字段,不用整体反序列化
  • 相比多个String key,更省key的空间
  • 结构清晰,适合用户信息、商品信息、配置对象等

但如果对象字段很多,嵌套复杂,还是要注意序列化和维护成本

4.Set底层、特点、应用场景

Set是无序不重复集合。

底层根据数据情况选择:

  • 整体且数量少时,用intset,省内存
  • 其他情况或数量变大时,用hashtable

特点:

  • 元素唯一
  • 支持交集、并集、差集
  • 查询元素是否存在很快

常见场景:

  • 标签系统
  • 好友共同关注
  • 抽奖去重
  • 黑名单、白名单
  • 用户去重集合

5.ZSet底层跳表skiplist原理,为什么用跳表不用红黑树

ZSet是有序集合,每个元素有一个score,用于排序。

Redis ZSet底层通常由dict+skiplidt组成:

  • dict用来按member快速查score
  • skiplist用来按score排序和范围查询

跳表可以理解为多层有序链表,底层链表保存全部数据,上层链表保存部分索引。查找时从高层往低层走,平均复杂度O(logN)

为什么不用红黑树

  • 跳表实现比红黑树简单
  • 范围查询很方便,只有找到起点后沿链表向后遍历
  • 插入删除也比较容易维护
  • 性能接近平衡树,工程实现更友好

ZSet常用于排行榜、延迟队列、权重排序、热度榜

特殊数据结构

1.HyperLogLog作用、误差范围、适用场景

HyperLogLog用于基数统计,也就是统计不重复元素个数

典型场景是统计UV:一天有多少独立用户访问

它的优点是非常省内存。Redis中一个HyperLogLog大约只需要12KB,就能统计海量数据基数

缺点是有误差,标准误差大约是0.81%。所以它适合允许少量误差的统计场景,不适合精确计数

常用命令:

复制代码
PFADD key user1 user2
PFCOUNT key
PFMERGE dest source1 source2

2.Bitmap位图原理、签到、统计活跃用户

Bitmap本质上还是String,只是按bit位操作

每一位只有0或1,非常适合表示是否存在、是否签到、是否活跃

比如用户签到:

  • key:sign:1001:202608
  • 第一天对应offset 0,setbit key offset value
  • 签到设置为1,未签到设置为0

常用场景:

  • 用户签到
  • 活跃用户统计'布尔状态记录
  • 连续打卡天数

Bitmap很省内存,但offset如果特别大,中间空洞也会占用空间,所以要设计好偏移量

3.Geo地理位置实现原理

Redis Geo用来存储经纬度并计算地理位置距离

底层实际是ZSet。Redis会把经纬度编码为geohash,在作为score存到ZSet里

常见命令:

复制代码
GEOADD city 116.40 39.90 beijing
GEODIST city beijing shanghai km
GEOSEARCH city FROMLONLAT 116.40 39.90 BYRADIUS 10 KM

适用场景:

  • 附近的人
  • 附近门店
  • 网约车附近司机
  • 地理围栏粗筛

注意:Geo适合快速筛选,不适合替代专业GIS系统

4.Stream消息队列原理、消费组、偏移量

Stream是redis 5.0引入的消息队列结构,比List更适合做可靠消息。

每条消息都有一个递增ID,格式类似:时间戳-序号

Stream支持:

  • 消息队列持久保存
  • 消费组Consumer Group
  • 多消费者分担消费
  • ACK确认机制
  • Pending List记录已投递但未确认的消息

消费组里每个消费者读取消息后,需要XACK确认,如果消费者挂了,消息会留在Pending List,可被其他消费者认领

它适合轻量级消息队列,但如果对高吞吐、复杂路由、跨机房可靠性要求很高,还是更推荐Kafka RocketMQ更专业MQ

5.Bitmap和HyperLogLog区别

Bitmap适合精确记录某个用户是否存在,比如"用户今天是否签到"。它可以精确统计,但需要能把用户映射到合理的offset

HyperLogLog适合统计不重复数量,比如"今天UV是多少",它非常省内存,但有误差,不能知道具体有哪些用户

  • Bitmap:精确,可判断某个元素是否存在,占用空间和最大offset有关
  • HyperLogLog:近似,只统计数量,不能反查元素,占用空间固定且很小

6.Redis布隆过滤器原理、优缺点、误判问题、使用场景

布隆过滤器用于判断一个元素"可能存在"或"一定不存在"

它由一个位数组和多个哈希函数组成。添加元素时,用多个哈希函数算出多个位置,把这些位置设为1.查询元素时,如果这些位置都为1,就认为可能存在;只有有一个位置为0,就一定不存在

优点:

  • 非常省内存
  • 查询速度快
  • 适合海量数据判重

缺点:

  • 有误判:不存在的数据可能被判断为存在
  • 一般不支持直接删除,除非使用计数布隆过滤器

使用场景:

  • 缓存穿透防护
  • 黑名单过滤
  • 爬虫URL去重
  • 推荐系统去重

缓存经典问题

1.缓存穿透是什么?产生原因+四种解决方案

缓存穿透指查询一个缓存和数据库都不存在的数据。请求每次都打到数据库,缓存不起作用

产生原因:

  • 恶意请求大量不存在的key
  • 业务查询了非法ID
  • 数据本身确实不存在

解决方案:

  1. 缓存空值。数据库查不到,也把空结果缓存一小段时间
  2. 布隆过滤器,请求先经过布隆过滤器,不存在是key直接拦截
  3. 参数校验,明显非法id、格式直接拒绝
  4. 接口限流和风控,防止恶意高频请求

实际开发常用"参数校验+缓存空值+布隆过滤器"

2.缓存击穿是什么?产生原因+解决方案

缓存击穿指某个热点key过期瞬间,大量请求同时访问这个key,全部打到数据库

它和缓存穿透不同:击穿访问的是热点存在数据,只是缓存刚好失效

解决方案:

  • 热点key不设置过期时间,后台异步刷新
  • 互斥锁,只允许一个线程查数据库并重建缓存
  • 逻辑过期,缓存里存过期时间,过期后先返回旧值,再异步刷新
  • 热点key提前续期

3.缓存雪崩是什么?产生原因+事前/事中/事后解决方案

缓存雪崩指大量key同一时间失效,或者Redis整体不可用,导致请求集中打到数据库

产生原因:

  • 大量key设置相同过期时间
  • Redis宕机
  • 缓存集群故障

事前防御:

  • key过期时间加随机值
  • 热点数据永不过期或逻辑过期
  • Redis高可用部署
  • 做限流、降级、熔断

事中处理:

  • 限流保护数据库
  • 降级返回旧数据或默认值
  • 快速扩容和恢复Redis

事后优化:

  • 排查过期时间设计
  • 完善监控告警
  • 做缓存预热

4.Redis大key问题:本质就是做拆分

大key指value很大,或者集合元素特别多的key

常见例子:

  • 一个String存几MB的JSON
  • 一个Hash有几十万个field
  • 一个List、Set、ZSet有上百万元素

危害:

  • 网络传输慢
  • 删除阻塞
  • 迁移和扩容慢
  • 单线程处理时间长,影响其他请求

解决思路:

  • 拆key,比如按用户、时间、分片拆分
  • 大对象压缩或只存必要字段
  • 集合分页读取,避免一次取全量
  • 删除用UNLINK异步删除
  • 定期扫描识别大key

5.Redis热key问题:如何发现和解决?

热key指某个key被大量访问,导致单节点压力过高

发现方式:

  • Redis hotkeys采样探测
  • 业务埋点统计访问频次
  • 代理层或网关统计
  • 监控Redis单节点CPU、QPS、带宽异常

解决方案:

  • 本地缓存,减少Redis访问
  • 热key拆分成多个副本key,随机读
  • 读写分离,让从节点分担读请求
  • 热点数据提前预热
  • 对极端热点做限流和降级

京东hotkey探测机制的思路也是通过客户端、代理或服务端采样识别热点key,在推送到本地缓存,减少集中访问

过期/淘汰策略

1.Redis键过期删除三种策略:定时删除、惰性删除、定期删除

  • 定时删除:给每个key设置定时器,到期立刻删。优点是内存释放及时,缺点是定时器太多,CPU压力大
  • 惰性删除:访问key时才判断是否过期,过期就删除。优点是省CPU,缺点是过期key如果没有访问,会一直占内存
  • 定期删除:Redis定期抽样检查一批key,删除过期数据。它是CPU和内存之间的折中

Redis实际采用"惰性删除+定期删除"

2.Redis内存满后八大淘汰策略分别是什么?

Redis内存达到maxmemory后,会根据淘汰策略处理

  • noeviction:不淘汰,新写入报错
  • allkeys-lru:所有key中淘汰最近最少使用
  • volatile-lru:设置过期时间的key中淘汰最近最少使用
  • allkeys-random:所有key中随机淘汰
  • volatile-random:设置过期时间的key中随机淘汰
  • volatile-ttl:设置过期时间的key中优先淘汰快过期的
  • allkeys-lfu:所有key中淘汰最近最不常用
  • volatile-lfu:设置过期时间的key中淘汰最近最不常用

缓存场景常用allkeys-lru或allkeys-lfu

3.LRU底层实现原理,Redis近似LRU怎么做

LRU:Least Recently Used最近最少使用

标准LRU通常用"哈希表+双向链表"实现

  • 哈希表:key映射链表节点,快速找到节点
  • 双向链表:维护访问顺序

访问某个key时,把它移动到链表头;淘汰时删除链表尾

但Redis没有严格维护全局LRU链表,因为成本太高

Redis使用近似LRU:随机采样一批key,从中淘汰最久未访问的key。采样数量由配置控制

近似LRU的好处是:

  • 实现简单
  • 开销低
  • 效果接近真实LRU

4.过期键会不会主动占用内存?主从间过期怎么同步?

过期key如果没有被访问,可能会在内存里停留一段时间,直到定期删除扫描到它。所以过期key可能短暂占用内存。

主从同步方面,为了保持一致性,一般由主节点负责过期删除。主节点删除过期key后,会向从节点发出删除命令,从节点执行删除。

从节点通常不会独立判断并删除过期key,以避免主从数据不一致

缓存与数据库一致性

1.缓存和数据库双写一致性几种策略

常见策略有:

  • 先更新数据库,再删除缓存
  • 先删除缓存,再更新数据库
  • 先更新数据库,再更新缓存
  • 延时双删
  • 订阅binlog异步更新缓存

生产中最常见的是"先更新数据库,再删除缓存",因为直接更新缓存容易受并非写影响,导致旧数据覆盖新数据

2.先更新数据库再删缓存、先删缓存再更新数据库优缺点

  • 先更新数据库再删缓存:

优点是更符合数据源以数据库为准的思想。删除缓存后,下次查询会从数据库加载新数据

缺点是删除缓存失败,可能留下旧缓存,所以需要重试、消息队列或订阅binlog兜底

  • 先删除缓存再更新数据库:

缺点更明显,删除缓存后,如果有查询请求进来,可能读到旧数据库值并写回缓存。随后数据库才更新,缓存就变成旧值

所以一般不推荐简单使用"先删缓存再更新数据库"。

3.延时双删策略原理

延时双删的流程:

  1. 先删除缓存
  2. 更新数据库
  3. 等待一小段时间
  4. 再删除一次缓存

第二次删除是为了清理并发读请求可能写入的旧缓存

但延时时间不好控制,太短可能无效,太长影响一致性窗口。因此它不是银弹,适合对一致性要求不是极端高的场景

4.如何保证强一致性?Canal订阅binlog方案

严格强一致性很难靠缓存做到,因为缓存和数据库是两个系统。

如果业务必须强一致,建议:

  • 不使用缓存,直接查数据库
  • 或者更新时加锁,串性化处理
  • 或者使用事务消息、可靠队列保证最终一致

Canal方案属于最终一致性,它模拟MySQL从库订阅binlog,监听数据库变更,再异步删除或更新Redis缓存

流程:

  1. 业务写数据
  2. MySQL产生binlog
  3. Canal监听binlog
  4. Canal投递变更事件
  5. 消费者删除或更新缓存

它的优点是解耦业务代码,缺点是链路变长,需要处理消息延迟、重复消费和失败重试。

持久化RDB&AOF(超级高频)

1.RDB是什么?原理、触发方式、优缺点

RDB是Redis的快照持久化。它会在某个时间点把内存数据生成一个快照文件,通常叫dump.rdb

原理:

  • 执行RDB时,Redis调用fork()创建子进程
  • fork:采用写时复制COW
  • 子进程把内存全量数据写入临时rdb文件
  • 写入完成,替换旧的dump.rdb文件AOF

触发方式:

  • 配置规则自动触发
  • 手动执行save
  • 手动执行bgsave
  • 主从复制是可能触发

save会阻塞主线程,不推荐线上使用。

bgsave会fork子进程生成RDB,主进程继续处理请求

优点:

  • 文件紧凑,恢复速度快
  • 适合全量备份
  • 对运行时影响相对较小

缺点:

  • 两次快照之间的数据可能丢失
  • fork子进程会有开销,大内存实例可能有抖动

2.AOF是什么?日志刷写策略三种

AOF是Append Only File,追加写日志。它会把写命令追加到AOF文件里,重启时通过重放命令恢复数据。

AOF刷盘策略有三种:

  • always:每次写命令都刷盘,最安全,性能最低
  • everysec:每秒刷盘一次,最多丢一秒数据,默认常用
  • no:由操作系统决定刷盘,性能高,但丢数据风险大

生产中通常使用everysec,在性能和可靠性之间折中

3.AOF重写机制原理,为什么要重写?

AOF会不断追加写命令,时间久了会越来越大,很多历史命令可以合并

比如

1 set count 1

2 set count 2

3 set count 3

最终只需要保存

set count 3

AOF重写不是简单压缩旧文件,而是根据当前内存数据生成一份新的最小化AOF文件

重写期间Redis会fork子进程生成新的AOF,同时主进程继续处理写请求,并把新增写入记录到缓冲区。重写完成后,把缓冲区内容追加到新AOF,再替换旧文件

4.RDB和AOF对比区别、各自适用场景

RDB是快照,AOF是命令日志

RDB文件小,恢复快,适合备份,但可能丢失最近一段数据

AOF数据更完整,最多通常丢一秒,但文件大,恢复可能更慢。

适用场景:

  • 只做缓存,适合丢数据:可以不开持久化或只开RDB
  • 希望恢复较快:RDB更合适
  • 希望数据尽量少丢:AOF更合适
  • 既要备份又要可靠:RDB+AOF混合持久化

5.混合持久化原理(RDB+AOF混合)

混合持久化是Redis 4.0后支持的能力

开启后,AOF重写生成的新文件前半部分是RDB格式的全量快照,后半部分是AOF格式的增量命令。

这样既能利用RDB恢复快的优点,也能利用AOF丢数据少的优点

简单说:先用RDB快速恢复大部分数据,再用AOF回放后续增量

6.Redis宕机后的恢复流程

Redis重启恢复时大致按优先级加载持久化文件。

如果开启AOF。通常优先加载AOF,因为AOF数据更完整

如果没开启AOF,则加载RDB

如果使用混合持久化,加载AOF文件会先读取其中的RDB部分,再回放后面的AOF增量命令

如果文件损坏,可以使用redis-check-aof或redis-check-rdb工具尝试修复

并发&分布式锁

1.Redis分布式锁基础实现原理(SET NX EX)

SET NX EX:nx只有key不存在时才设置成功;key存在时,获取锁失败,用来保证互斥,同一时间只能一个客户端拿到锁

ex seconds:给key设置过期时间,防止死锁

不能直接del lock:order。value存唯一标识,释放是判断value是不是自己的,才删除。释放锁需要Lua脚本保证原子性

存在的问题:

  • 锁过期,业务还没执行完。解决方案:看门狗(续期,Redission)
  • 主从切换锁丢失。解决方案:Redlock(业务很少用)

2.分布式锁超时释放问题怎么解决

业务执行时间超过锁过期时间,锁提前释放,锁失效

解决:看门狗 Redission实现

  • 获取锁不手动设置过期时间
  • 后台线程每10秒检测,锁还被当前线程持有,就把过期时间重置回30秒
  • 业务执行完释放锁,看门狗停止;客户端宕机则不再续期,锁自动过期

注意:手动指定leaseTime,看门狗失效

3.锁续约/续命怎么做?Redission看门狗原理

看门狗是Redission后台守护线程,实现锁自动续约

默认锁超时30秒,每10秒执行Lua脚本;锁还被当前线程持有,重置过期时间为30秒

底层锁使用Hash结构,同时支持可重入

4.主从架构下分布式锁锁失效问题

主从失效原因:主拿到锁,锁还未异步同步到从,主宕机,从升主,锁消失,其他客户端获取锁,锁失效。

解决:

  • Redlock:N个独立Redis,半数以上节点获取锁才算成功;但是成本高,性能差,生产很少使用
  • 业务兜底:Redis锁做前置控制,数据库乐观锁做最终防护

5.Redission可重入锁原理

Redission可重入锁底层是Hash结构;key锁名,field线程唯一标识,value可重入计数器

锁不存在则创建;同一线程再次加锁,计数器加1,实现重入

释放锁:计数器减1;计数器大于0不删key;计数器为0真正释放锁

全部逻辑放在Lua脚本,保证Redis端原子执行

主从复制&集群

1.Redis主从复制原理,全量同步/增量同步

主从复制就是把主节点数据同步到从节点,实现读扩展和高可用基础

第一次同步通常是全量同步:

  1. 从节点发送同步请求
  2. 主节点生成RDB快照
  3. 主节点把RDB发给从节点
  4. 从节点加载RDB
  5. 主节点把同步期间的新写命令继续发给从节点

断线重连时,如果复制积压缓冲区还保留这缺失数据,可以做增量同步,只补发断开期间的命令

2.主从复制缓冲区和积压缓冲区作用

复制缓冲区是每个从节点对应的输出缓冲区,用来暂存主节点要发给从节点的数据。如果从节点太慢,缓冲区可能变大,甚至导致连接断开

复制积压缓冲区是主节点维护的一块环形缓冲区,保存最近一段写命令。它用于从节点短暂断线后的增量同步

区别:

  • 辅助缓冲区:面向单个从节点
  • 积压缓冲区:主节点全局共享,用于断线续传

3.主从同步断线后怎么增量同步

Redis通过复制偏移量和主节点runid判断能不能增量同步

从节点断线重连后,会告诉主节点自己的复制偏移量。如果主节点runid没变,并且缺失的数据还在复制积压缓冲区里,主节点就只发送缺失部分

如果条件不满足,比如主节点重启了,或者积压缓冲区被覆盖了,就只能全量同步

4.哨兵Sentinel作用、原理、故障转移流程

Sentinel用来监控Redis主从集群,并在主节点故障时自动故障转移。

它主要做三件事:

  • 监控:检查主从节点是否存活
  • 通知:发现异常后通知管理员或系统
  • 自动故障转移:主节点挂了,选一个从节点升级为新主

故障转移流程:

  1. Sentinel主观判断主节点下线
  2. 多个Sentinel达成客观下线
  3. Sentinel之间选举leader
  4. leader选择一个合适从节点作为新主
  5. 其他从节点改为复制新主
  6. 客户端更新主节点地址

5.哨兵怎么选主、投票机制

Sentinel选主时会综合考虑:

  • 从节点是否在线
  • slave-priority优先级
  • 复制偏移量,数据越新越优先
  • runid字典序,作为兜底比较

投票机制上,Sentinel需要达到quorum判断客观下线;故障转移时,还需要Sentinel集群选出一个leader来执行切换。

6.Redis Cluster集群原理、哈希槽16384

Redis Cluster是Redis官方分片集群方案。

它把整个key空间划分为16384个哈希槽,每个key通过CRC16计算后,对16384取模,决定落在哪个槽

每个主节点负责一部分槽,客户端访问key时,如果请求发错节点,Redis会返回MOVED或ASK,让客户端重定向到正确节点

Cluster同时支持主从复制,每个主节点可以有从节点,用于故障转移

7.集群槽位分配、分配规则

Redis Cluster的分片单位是槽,不是单个key

比如:

  • 节点A负责0到5000
  • 节点B负责5001到10000
  • 节点C负责10001到16383

key根据哈希结果落到某个槽,再由负责该槽的节点处理

如果key使用{}哈希标签,比如user:{100}:name和order:{100}:list,Redis只对{100}计算哈希,这样可以让多个key落到同一个槽,方便多key操作

8.集群扩容、缩容原理

扩容时,新增节点加入集群,然后从已有节点迁移一部分槽到新节点

缩容时,先把要下线节点负责的槽迁移到其他节点,再把节点移出集群

槽迁移过程中,客户端可能收到ASK重定向,表示这个key正在迁移,需要临时去另一个节点访问。

整个过程本质是槽位重新分配,而不是直接按节点搬整个数据库

9.为什么是16384个哈希槽,不是更大?

16384是一个工程折中

槽太少,分片不够细;槽太多,集群节点之间需要交换的槽位状态信息会变大

Redis Cluster节点之间通过gossip协议传播集群信息,每个节点都要维护槽位分配状态。16384个槽既能满足绝大多数分片需求,也能控制元数据开销

10.集群高可用、脑裂问题怎么解决

Redis Cluster通过主从复制和故障转移实现高可用。主节点挂了,从节点可以被提升为新主

脑裂指网络分区时,旧主和新主可能同时对外提供写服务,导致数据冲突

缓解方案:

  • 设置合适的min-replicas-to-write和min-replicas-max-lag,主节点在从节点不足或复制延迟过高时拒绝写入
  • 部署奇数个主节点和合理副本
  • 保证Sentinel或Cluster节点分布在不同机器
  • 客户端正确处理故障转移和重定向

Redis无法像强一致数据库那样完全避免所有脑裂数据风险,所以核心账务类强一致场景要谨慎使用。

网络模型&底层原理

1. Redis多路复用IO模型:epoll原理

Redis使用IO多路复用处理大量客户端连接

在Linux上常用epoll,它可以让一个线程同时监听多个socket,当某个socket可读或可写时,内核通知Redis处理

相比一个连接一个线程,epoll的优势是:

  • 线程数量少
  • 上下文切换少
  • 可以支撑大量连接
  • 事件来了再处理,效率高

Redis的事件循环会不断处理文件事件和时间事件,从而完成网络读写、命令执行、定时任务等

2.Redis 6.0多线程做了什么?网络多线程、命令还是单线程

Redis 6.0引入多线程,主要用于网络IO读写和协议解析,不是把命令执行改成多线程

也就是说:

  • 读取请求可以多线程
  • 写回响应可以多线程
  • 命令执行仍然主要是单线程

这样设计是为了提升网络IO性能,同时保留单线程命令执行的简单性,避免复杂锁竞争

面试说清楚:Redis 6.0多线程不是所有操作都多线程,核心命令执行仍然保持单线程模型

3.Pipeline管道作用、原理、适用场景

Pipeline可以把多个命令一次性发给Redis,不要每条命令都等待响应后再发下一条

它优化的是网络往返时间RTT

比如原来执行100条命令,需要100次请求;使用Pipeline后,可以一次发送100条命令,再批量读取响应

适用场景:

  • 批量写入缓冲
  • 批量查询多个key
  • 初始化数据
  • 网络延迟较高时提升吞吐

注意:Pipeline不是事务,中间命令失败不会自动回滚。批量太大也会占用Redis和客户端内存

4.Lua脚本作用、原子性、批量操作

Redis支持执行Lua脚本,常用来把多个命令打包成一个原子操作

Redis执行Lua脚本时,脚本会作为一个整体执行,中间不会被其他命令插入,所以具备原子性

常见场景:

  • 分布式锁释放,判断value后再删除
  • 秒杀库存扣减
  • 限流脚本
  • 批量判断和更新

注意:Lua脚本不要写太复杂,不要执行太久,因为Redis单线程执行命令,长脚本会阻塞其他请求

总结

  • Redis快是因为内存操作、单线程命令执行、IO多路复用和高效数据结构
  • Redis单线程指核心命令执行单线程,不代表没有后台线程或网络辅助线程
  • String底层是SDS,List底层是quicklist,ZSet常用dict+skiplist
  • HyperLogLog用于近似UV统计,Bitmap用于精确状态标记
  • 缓存穿透差不存在的数据,缓存击穿是热点key失效,缓存雪崩是大量key失效或Redis故障
  • Redis过期删除采用惰性删除+定期删除
  • 内存淘汰常见有LRU、LFU、随机、TTL、noeviction
  • 缓存一致性常用先更新数据库再删除缓存,失败要有重试或binlog兜底
  • RDB是快照,AOF是命令日志,混合持久化兼顾恢复速度和数据完整性
  • Redis分布式锁STE NX EX,Redission看门狗解决分布式锁超时问题,自动续期
  • Redission可重入锁底层Hash,value为0时才删除锁
  • Cluster用16384个哈希槽做分片,哨兵负责主从自动故障转移

主线:

  • 基础概念:理解Redis为什么快,适合做什么
  • 数据结构:掌握基础数据结构及特殊结构的场景
  • 缓存问题:穿透、击穿、雪崩、大key、热key是必问
  • 过期淘汰:理解删除策略和内存淘汰策略
  • 一致性:知道缓存和数据库双写为什么难,以及常见方案
  • 持久化:RDB、AOF、混合持久化要能对比
相关推荐
程序员夏洛16 分钟前
Redis 数据过期后的删除策略是什么?
数据库·redis·缓存
这个DBA有点耶24 分钟前
SQL:2023新特性详解:JSON_TABLE、GREATEST/LEAST、QUALIFY 到底怎么用?
数据库·sql·mysql
AOI小白新手上路36 分钟前
江科协15-2 直流电机实验调试总结
数据库·mongodb
小王C语言37 分钟前
MySQL 用户管理:创建用户、给用户授权
数据库·mysql
程序员夏洛1 小时前
Redis 的持久化机制有哪些?
java·数据库·redis
l1t1 小时前
DeepSeek总结的修复pgrust v0.2 #83:generate_series 聚合及其底层的两个成本 - #84
数据库·postgresql
厦门德仔2 小时前
【YiFeiWebApi】易飞ERP与WMS系统接口对接实战:从0到1全流程指南
大数据·数据库
内存漫游2 小时前
Page 到底是什么:数据库如何把一条条记录放进磁盘
数据库
小镇敲码人2 小时前
【深入浅出】之Qt 事件系统实战:鼠标、键盘、滚轮、窗口与定时器事件
数据库·qt·网络协议·mysql·http