Redis.全部面试题
Redis 热key
| 方法 | 优点 | 缺点 |
|---|---|---|
| redis-cli --hotkeys | 简单易用,快速定位热点 | 扫描时间限制,性能影响 |
| Keyspace Notifications | 实时监控,灵活 | 性能开销,复杂度较高 |
| 慢查询日志 | 发现耗时热点 | 只关注慢查询,不全 |
| 采样分析与监控系统 | 全面,结合业务数据 | 需要搭建监控系统 |
| 自定义访问计数器 | 精准,业务可控 | 额外开销,需要业务配合 |
Redis Cluster 故障转移流程
Redis Cluster 支持自动故障转移(Failover)机制,在主节点故障时由其从节点接管,以保证服务的高可用性。
故障转移流程
主节点 A 挂了
↓
从节点 A1 检测到主节点 A 不可达(PFAIL)
↓
多个节点标记 A 为 FAIL(确认故障)
↓
从节点 A1 准备发起故障转移(此时进入随机延迟等待⏱️)
↓
延迟结束 → 发起选举(请求投票)
↓
其他主节点投票支持 A1 升级为主
↓
A1 成为新主节点,接管槽位
↓
集群更新槽位映射,完成转移
故障转移步骤
1. 故障检测(Failure Detection)
- 节点之间使用 Gossip 协议定期发送
PING。 - 若某节点超过
cluster-node-timeout未响应 → 标记为PFAIL(疑似故障)。 - 若多个节点将该节点标记为
PFAIL,则达成共识标记为FAIL(确认故障),并广播此状态。
2. 从节点发起故障转移(Failover)
- 发现主节点
FAIL的从节点,在随机延迟后准备发起转移。 - 满足条件的从节点开始请求授权进行主从切换。
3. 投票选举新主节点
- 发起选举的从节点向其他主节点发送投票请求(
FAILOVER_AUTH_REQUEST)。 - 若获得半数以上主节点的投票支持 → 当选为新主节点。
4. 主从角色切换
- 被选中的从节点脱离旧主节点关系,升级为主节点。
- 接管原主节点负责的槽位(Slot)。
5. 槽位与拓扑更新
- 新主节点广播角色变更。
- 所有节点更新其槽位到节点的映射表,保持集群一致。
相关注意事项
| 项目 | 说明 |
|---|---|
| 随机延迟 | 防止多个从节点同时发起故障转移导致冲突 |
| 投票机制 | 需获得半数以上主节点支持(包括自己) |
| 优先级 | slave-priority 值越高,从节点越优先成为主节点 |
| 主节点数量要求 | 故障转移要求至少 3 个主节点(保证多数派) |
| 手动触发 | 可使用 CLUSTER FAILOVER 命令主动执行切换 |
Redis Cluster 两个节点脑裂问题
什么是脑裂?
脑裂指的是集群因网络分区或通信故障,导致集群分裂成两个或多个子集群,每个子集群都认为自己是主集群,进而导致数据不一致和服务混乱。
Redis Cluster 两个节点的脑裂问题
| 情况 | 说明 |
|---|---|
| 主节点数量 = 2 | 集群无法形成“多数派”,因为多数派至少需要超过半数节点。 |
| 网络分区发生 | 假设两个主节点 A、B 之间网络断开,两边各自认为对方失联。 |
| 每个节点单独判定对方 FAIL | 节点 A 认为 B 挂了,节点 B 也认为 A 挂了。 |
| 双方都认为自己是正常主节点 | 由于没有多数派原则,两边都不会放弃自己的主节点身份。 |
| 结果 | 产生两个“活跃主节点”,数据写入不同步,导致数据严重不一致。 |
为什么两个节点无法避免脑裂?
- 没有多数派投票,只有两个节点时,故障判定需要 2/2 节点同意,网络断开后无法达成共识。
- 没有第三方裁决节点,缺乏“仲裁者”角色,无法判定真正的故障方。
Redis Cluster 扩容和缩容
一、Redis Cluster 扩容(增加节点)
1. 新节点加入集群
- 新节点启动时,先以空槽分配状态加入集群。
- 通过
CLUSTER MEET <ip> <port>命令,将新节点加入集群。
2. 分配槽到新节点
- 现有节点需要将部分槽迁移给新节点,通常通过客户端工具或手动命令操作。
- 迁移过程:
- 发送
CLUSTER SETSLOT <slot> MIGRATING <node_id>给源节点,表示该槽开始迁移。 - 发送
CLUSTER SETSLOT <slot> IMPORTING <new_node_id>给目标节点,表示该槽开始导入。 - 将该槽相关的数据(key)逐个迁移到新节点。
- 迁移完成后,更新槽映射,槽归新节点所有。
- 发送
3. 重新平衡槽分布
- 通过迁移槽实现集群负载均衡,避免数据热点。
二、Redis Cluster 缩容(移除节点)
1. 将待移除节点槽迁移走
- 将待移除节点负责的槽,逐步迁移到其他节点。
- 过程同扩容的槽迁移过程。
2. 从集群中剔除节点
- 迁移完成后,通过
CLUSTER FORGET <node_id>命令让其他节点忘记该节点。 - 该节点退出集群。
三、槽迁移细节
- 槽是 Redis Cluster 的数据分片单位,总共 16384 个槽。
- 扩容和缩容都是通过槽的转移实现数据的动态调整。
- 槽迁移过程中,集群仍可提供读写服务,迁移数据过程支持在线状态。
Redis 的 Twemproxy(也叫 Nutcracker)是一个开源的代理中间件,它的主要作用是:
Twemproxy 的作用
- 代理客户端请求
- Twemproxy 作为客户端和多个 Redis 服务器之间的代理层,接收客户端请求后,根据配置将请求路由到对应的 Redis 实例。
- 客户端只连接 Twemproxy,简化了客户端对多 Redis 节点的管理。
- 实现统一的分片(sharding)机制
- Twemproxy 支持基于哈希的分片,将 key 映射到不同的 Redis 节点,实现数据的分片分布。
- 透明地支持多节点 Redis 集群模式,客户端无需感知多个 Redis 实例。
- 简化客户端连接管理
- 客户端只需连接 Twemproxy 一个地址,避免管理多个 Redis 连接。
- 减少客户端连接数,减轻 Redis 服务器的压力。
- 支持读写分离
- Twemproxy 支持将读请求路由到从节点,写请求路由到主节点,提升性能。
- 提升系统扩展性
- 支持动态添加和移除 Redis 节点,方便扩展和维护。
Redis 内存分配碎片
Redis 的内存碎片是指:Redis 实际分配的内存空间大于其存储的数据所需内存的情况,主要是由于内存分配、回收不均造成的。这种碎片可能导致 Redis 占用大量系统内存,即使实际数据量并不大。
🧱 一、内存碎片的产生原因
1. 内存分配器特性
Redis 使用不同版本的内存分配器(如 jemalloc、glibc),这些分配器为性能优化会预分配或对齐内存块,导致:
| 场景 | 描述 |
|---|---|
| 小对象分配 | 分配器按最小粒度(如 8B、16B)对齐,浪费空间 |
| 对象删除 | 被删除对象的内存未及时释放,形成“空洞” |
| 内存池空闲 | 分配器维护的 arena、chunk 等可能长期保持未用但保留状态 |
2. 键值变更频繁
- Redis 中大量键频繁更新、删除、重建,导致内存分配释放不均衡;
- 特别是使用复杂结构(如 list、set、zset)时更容易产生碎片。
3. 大对象释放
- Redis 释放大型对象后,分配器未及时将内存归还系统,而是“保留”下来,等待再次复用。
4. 内存碎片与 RDB/AOF 重写
- Redis 在 RDB 保存或 AOF 重写时会分配新内存空间临时存储数据;
- 重写结束后旧内存释放,新内存保留,会增加碎片率。
📊 二、如何查看内存碎片率?
使用 Redis 命令:
INFO MEMORY
关键字段:
used_memory: 实际使用的内存(字节)
used_memory_rss: 向操作系统申请的总物理内存
mem_fragmentation_ratio = used_memory_rss / used_memory
- 正常范围:1.0 ~ 1.5
- 超过 1.5:说明碎片严重,可能需要优化
🧰 三、解决或缓解内存碎片的方式
| 方法 | 说明 |
|---|---|
| 重启 Redis | 释放全部内存,由操作系统重新分配,效果显著但有停机风险 |
| 使用 jemalloc | 默认配置更优,管理碎片更好(推荐替代 glibc) |
手动触发 MEMORY PURGE(Redis 4.0+) |
清理 jemalloc 中空闲未使用的 arena 空间 |
| 内存监控与热点管理 | 减少频繁删除/创建对象,控制内存抖动行为 |
| 合理配置 maxmemory 策略 | 避免 Redis 内存占用无限扩张(使用 LFU、LRU 等) |
✏️ 四、相关命令说明
MEMORY STATS # 查看 jemalloc 内存使用详情
MEMORY PURGE # 主动请求释放 jemalloc 的未使用内存
Pipeline 和 Multi / Exec 的区别
| 特性 | Pipeline | Multi / Exec |
|---|---|---|
| 作用 | 减少网络往返次数,提高吞吐 | 实现事务,保证命令原子性执行 |
| 事务支持 | ❌ 不是事务,不保证原子性 | ✅ 支持事务,命令按序原子执行 |
| 错误处理 | 单个命令出错不影响其他命令 | 编译阶段出错不执行,执行阶段部分失败不回滚 |
| 命令执行 | 一次发送多个命令,服务端依次执行并返回结果 | MULTI 开启事务,EXEC 一次性提交所有命令执行 |
| 返回机制 | 所有命令的结果一起返回 | 所有命令打包在 EXEC 后返回 |
| 客户端资源占用 | 占用较多内存(命令缓冲) | 占用少量内存 |
| 使用场景 | 高性能批量读写 | 需要多个命令一致性(如账户转账) |
AOF重写期间的增量数据如何处理?
AOF 重写期间新增的数据会被写入一个“增量缓冲区(AOF rewrite buffer)”,等子AOF重写期间的增量数据如何处理?进程重写完毕后,主进程会把这些增量写操作追加到新 AOF 文件的末尾,确保最终新 AOF 文件完整无缺。
Redis的jemalloc分配器有什么特点?
| 特点 | 说明 |
|---|---|
| 1. 低内存碎片率 | jemalloc 能更好地管理内存块,避免频繁分配和释放带来的碎片问题,提升内存使用效率。 |
| 2. 多线程优化(多 Arena) | 支持多个 arena,避免线程间竞争;虽然 Redis 主线程是单线程的,但后台 AOF、RDB、集群等线程也受益于此。 |
| 3. 快速分配与释放 | 采用固定大小内存池(bin + slab)技术,适用于 Redis 中大量小对象的频繁分配与释放。 |
| 4. 更好的内存可观测性 | 提供丰富的统计与监控接口(如 je_malloc_stats_print),便于追踪内存使用。 |
| 5. 稳定性高 | jemalloc 在长期运行的大型 Redis 服务中表现出更稳定的内存使用和性能。 |
used_memory:Redis 实际使用的内存
used_memory_rss:Redis 进程从操作系统申请的总内存(可能包含碎片)
allocator_allocated:jemalloc 实际分配的总内存
allocator_active:jemalloc 申请的活跃内存
allocator_frag_ratio:碎片比
big key 有什么性能问题
| 问题类型 | 影响描述 |
|---|---|
| 阻塞主线程 | Redis 是单线程模型,访问 bigkey 会导致主线程长时间阻塞,影响整个实例响应。 |
| 删除慢 | DEL 或 UNLINK bigkey 时,尤其是集合类键(如一个包含百万元素的 Hash),DEL 会瞬时清理,阻塞明显。 |
| 网络传输开销大 | 读取 bigkey(如 GET 一个 10MB 的 String)会造成 带宽占用高、延迟大。 |
| 主从同步压力大 | 主库发送 bigkey 到从库,会拖慢同步速度,甚至引发复制中断。 |
| AOF 或 RDB 持久化成本高 | 保存或加载 bigkey 成本大,尤其是在重启恢复时显著拉长冷启动时间。 |

浙公网安备 33010602011771号