【面试题系列】 Redis 核心面试题(二)&答案

本文主要介绍Redis 的面试题,涵盖持久化、集群、缓存策略、事务等方面

一、持久化机制

1. RDB 与 AOF 的核心区别及适用场景?

答案:

特性 RDB AOF
存储内容 内存快照(二进制文件) 写命令日志(文本格式)
数据完整性 可能丢失最后一次快照后的数据 可配置 everysec 保证秒级丢失
恢复速度 快(直接加载二进制文件) 慢(需重放所有命令)
文件大小 小(压缩) 大(未压缩)
适用场景 定期全量备份,快速恢复 实时备份,数据完整性要求高

混合持久化(Redis 4.0+):

AOF 重写时会将 RDB 快照写入文件开头,结合两者优势。

2. RDB 快照触发机制有哪些?

答案:

  • 自动触发
    • 满足 save 配置条件(如 save 60 1000)。
    • 执行 flushall 命令。
    • 主从复制时主节点生成快照。
  • 手动触发
    • SAVE(阻塞主线程)。
    • BGSAVE(后台 fork 子进程生成快照)。

二、集群与高可用

3. Redis Cluster 节点通信机制是什么?

答案:

  • Gossip 协议 :节点通过周期性广播 PING/PONG 消息同步集群状态。
  • 消息类型
    • MEET:邀请新节点加入集群。
    • PONG:响应请求或广播自身状态。
    • FAIL:标记节点为下线。
  • 节点握手:新节点加入时需与至少一个已知节点通信,逐步同步整个集群的槽位映射。

4. Redis Sentinel 如何实现故障转移?

答案:

  1. 监控 :Sentinel 定期向主从节点发送 PING 检测存活状态。
  2. 主观下线(SDOWN):若节点超时未响应,标记为 SDOWN。
  3. 客观下线(ODOWN):半数以上 Sentinel 确认节点不可达时,标记为 ODOWN。
  4. 选举领导者:Sentinel 通过 Raft 协议选举主节点。
  5. 故障转移
    • 提升某个从节点为主节点。
    • 其他从节点重新指向新主节点。
    • 通知客户端新主节点地址。

三、缓存策略

5. 缓存穿透、缓存雪崩、缓存击穿的区别与解决方案?

答案:

问题 描述 解决方案
穿透 查询不存在的数据,直接打到数据库 布隆过滤器、空值缓存(设置短过期时间)
雪崩 大量缓存同时过期,数据库压力激增 随机过期时间、加锁限流、二级缓存
击穿 热点数据过期瞬间,大量请求打到数据库 互斥锁(SETNX)、永不过期(异步更新)

Java 示例(布隆过滤器):

java 复制代码
BloomFilter<String> bloomFilter = BloomFilter.create(
    Funnels.stringFunnel(Charset.defaultCharset()), 
    1000000, 0.01); // 100万容量,误差率1%
if (!bloomFilter.mightContain(key)) {
    return null; // 直接返回,避免查数据库
}

6. Redis 内存淘汰策略中 LRU 和 LFU 的区别?

答案:

  • LRU(Least Recently Used):淘汰最久未使用的键。
  • LFU(Least Frequently Used):淘汰访问频率最低的键。
  • Redis 实现
    • LRU 使用近似算法(随机采样+淘汰最旧)。
    • LFU 通过 logc(访问频率)lru(最近访问时间) 综合判断。
  • 适用场景
    • LRU 适合热点数据不明显的场景。
    • LFU 适合长期低频但短期高频的键(如促销活动商品)。

四、事务与并发控制

7. Redis 事务的原子性如何保证?

答案:

Redis 事务不保证原子性,但通过以下机制提供部分保证:

  1. 入队阶段:所有命令先放入队列,若语法错误则整个事务被取消。
  2. 执行阶段:命令按顺序执行,中途出错不回滚,但后续命令仍执行。
  3. WATCH 机制:通过乐观锁监控键变化,若被修改则事务失败。

示例(WATCH 实现 CAS):

java 复制代码
jedis.watch("balance");
String balance = jedis.get("balance");
if (Integer.parseInt(balance) >= 100) {
    Transaction tx = jedis.multi();
    tx.decrBy("balance", 100);
    List<Object> result = tx.exec();
    if (result == null) {
        // 数据被修改,重试逻辑
    }
}
jedis.unwatch();

8. Redis 如何实现乐观锁?

答案:

  • WATCH/MULTI/EXEC
    • 监控指定键,若事务执行前键被修改,EXEC 返回 null
  • Check-and-Set(CAS)
    • 使用 GETSETSET key value NX 实现条件更新。
  • 版本号机制
    • 对每个键维护版本号,更新时校验版本号是否匹配。

五、实战优化

9. Redis 连接池的配置参数有哪些?

答案:

  • MaxTotal:最大连接数(默认 8)。
  • MaxIdle:最大空闲连接数(默认 8)。
  • MinIdle:最小空闲连接数(默认 0)。
  • MaxWaitMillis:获取连接的最大等待时间(默认 -1,无限等待)。
  • TestOnBorrow :获取连接时是否测试连通性(默认 false)。

Java 配置示例(JedisPoolConfig):

java 复制代码
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(100);
config.setMaxIdle(20);
config.setTestOnBorrow(true);
JedisPool jedisPool = new JedisPool(config, "localhost", 6379);

10. 如何监控 Redis 的内存使用情况?

答案:

  • 命令行工具
    • INFO memory:查看内存使用统计。
    • MEMORY USAGE key:查看单个键的内存占用。
  • 可视化工具
    • RedisInsight、Prometheus + Grafana。
  • 关键指标
    • used_memory:已使用内存。
    • mem_fragmentation_ratio:内存碎片率(理想值 1.0-1.5)。
    • evicted_keys:被淘汰的键数量。

总结

Redis 的核心设计理念(单线程、内存优先、异步持久化)是理解其高性能的关键。面试中需结合具体场景分析问题,如缓存策略需区分穿透/雪崩/击穿的不同解决方案,集群需掌握节点通信和故障转移原理。建议通过 Redis-cli 实操和源码阅读加深理解。

相关推荐
小陈工1 小时前
Python Web开发入门(十七):Vue.js与Python后端集成——让前后端真正“握手言和“
开发语言·前端·javascript·数据库·vue.js·人工智能·python
科技小花6 小时前
数据治理平台架构演进观察:AI原生设计如何重构企业数据管理范式
数据库·重构·架构·数据治理·ai-native·ai原生
一江寒逸6 小时前
零基础从入门到精通MySQL(中篇):进阶篇——吃透多表查询、事务核心与高级特性,搞定复杂业务SQL
数据库·sql·mysql
D4c-lovetrain6 小时前
linux个人心得22 (mysql)
数据库·mysql
阿里小阿希7 小时前
CentOS7 PostgreSQL 9.2 升级到 15 完整教程
数据库·postgresql
荒川之神7 小时前
Oracle 数据仓库雪花模型设计(完整实战方案)
数据库·数据仓库·oracle
做个文艺程序员7 小时前
MySQL安全加固十大硬核操作
数据库·mysql·安全
不吃香菜学java7 小时前
Redis简单应用
数据库·spring boot·tomcat·maven
一个天蝎座 白勺 程序猿7 小时前
Apache IoTDB(15):IoTDB查询写回(INTO子句)深度解析——从语法到实战的ETL全链路指南
数据库·apache·etl·iotdb
不知名的老吴7 小时前
Redis的延迟瓶颈:TCP栈开销无法避免
数据库·redis·缓存