一、Redis 基础认知

1. 核心定位

Redis 是基于内存的键值型 NoSQL 数据库,Java 开发中核心用于:缓存(减轻 MySQL 压力)、分布式锁、计数器、排行榜、限流等场景,是 Java 后端高性能架构的核心组件。

2. 为什么快(Java 面试必答)

Redis 高性能的核心原因,需结合 Java 开发视角理解:

  1. 纯内存操作:数据存储在内存,读写延迟远低于磁盘(Java 中需注意 Redis 内存占用,避免 OOM);
  2. 单线程模型(核心命令):Redis 核心命令执行为单线程,避免 Java 多线程的上下文切换、锁竞争开销;
    • 补充:Redis 6.0 + 引入多线程 IO(仅处理网络读写),命令执行仍为单线程,Java 客户端无需适配多线程逻辑;
  3. IO 多路复用:基于 epoll 实现,单线程处理数千个网络连接,对应 Java NIO 的 Reactor 模式,避免阻塞 IO 的性能瓶颈;
  4. 数据结构优化:底层编码(如 ziplist、跳表)适配高频操作,Java 开发无需关心底层,但需知道不同数据结构的性能特性。

3.  开发选型:Redis vs Memcached

维度RedisMemcachedJava 开发选型建议
数据结构支持 String/Hash/List/ZSet 等仅支持 StringJava 复杂业务(如排行榜、对象缓存)选 Redis
持久化支持 RDB/AOF不支持Java 项目需数据落地(如缓存重启不丢失)选 Redis
集群方案原生 Cluster / 哨兵,Java 客户端(Redisson/Jedis)原生支持需第三方集群方案,Java 客户端支持弱分布式 Java 项目选 Redis
客户端生态Jedis(基础)、Redisson(高级,封装分布式锁 / 集合)成熟Java 客户端生态薄弱优先 Redis

二、核心数据结构(Java 开发高频场景)

数据结构核心特点Java 开发典型场景底层编码(面试深问)
String二进制安全,支持原子增减(incr/decr)1. 分布式 ID(incr 生成);2. 缓存简单对象(JSON 序列化);3. 计数器(点赞数 / 阅读量)短字符串→embstr,长字符串→raw,整数→int,Java 序列化对象需控制长度避免编码切换
Hash键值对集合,适合存储对象(减少 Key 数量)缓存用户 / 商品对象(如 user:1001 的 name/age/phone),避免 String 序列化 / 反序列化开销少量字段→ziplist,大量字段→hashtable,Java 中需控制字段数量避免性能下降
List双向链表,支持阻塞读写1. 简单消息队列(异步通知);2. 最新消息列表少量元素→ziplist,大量元素→linkedlist,Java 中阻塞读取(blpop)需处理超时
Set无序唯一,支持交集 / 并集 / 差集1. 共同好友;2. 抽奖去重;3. 数据去重整数元素→intset,其他→hashtable,Java 中集合运算(如 sinter)需注意性能
ZSet有序唯一(按 score 排序),支持范围查询1. 排行榜(游戏积分 / 商品热度);2. 延迟队列少量元素→ziplist,大量元素→跳表 + hashtable,Java 中 zrevrange 是高频操作

特殊数据结构(拓展场景)

  1. Bitmap:按位存储,极致省空间,Java 中用于用户签到、在线状态统计;
  2. HyperLogLog:极小空间统计海量数据基数,Java 中用于 UV 统计(容忍 0.81% 误差);
  3. Geospatial:存储经纬度,Java 中用于「附近的人」等 LBS 场景;
  4. Stream:持久化消息队列,Java 中替代 List 解决消息丢失问题,支持消费组(类似 Kafka)。

三、Redis 核心特性

1. 过期策略与内存淘汰

(1)过期策略

Redis 采用「惰性删除 + 定期删除」:

  • 惰性删除:访问 Key 时检查过期,需注意:过期 Key 若长期不访问,会导致内存泄漏;
  • 定期删除:每 100ms 随机扫描部分 Key,Java 开发无需干预,但需合理设置过期时间。
(2)内存淘汰策略(面试必答)
策略Java 应用场景
volatile-lru业务缓存(仅淘汰带过期时间的 Key),Java 项目中缓存用户 / 商品数据优先选
allkeys-lru纯缓存场景(所有 Key 都是缓存),Java 中如缓存热点数据
noeviction不淘汰,拒绝写操作,Java 中金融类项目(不允许数据丢失)可选

2. 事务

Redis 事务特性:弱原子性、无回滚、无隔离级别,Java 开发中几乎不直接使用 Redis 事务,替代方案:

  1. 简单原子操作:使用 Redis 单命令(如 incr、hincrby,天然原子);
  2. 复杂原子操作:使用 Lua 脚本(Jedis/Redisson 支持),一次性执行多个命令;
  3. 分布式事务:Java 中用 Seata + Redis,或 Redisson 的分布式事务组件。

3. 管道(Pipeline)

用于批量操作(如批量插入数据),核心价值:减少网络往返次数(RTT),Jedis/Redisson 均支持 Pipeline,需注意:

  • Pipeline 无原子性,Java 中需处理部分命令失败的情况;
  • 批量操作时,Pipeline 性能远高于循环单命令。

四、Redis 持久化(Java 开发核心配置)

1. RDB vs AOF(Java 面试核心对比)

维度RDB(快照)AOF(追加日志)Java 项目选型建议
原理定时将内存数据快照写入磁盘记录所有写命令,重启重放-
数据安全性低(两次快照间数据丢失)高(最多丢 1 秒)Java 核心业务(如订单缓存)选 AOF
恢复速度Java 项目重启时,RDB 适合快速恢复
性能开销低(fork 子进程)高(写日志)Java 高并发场景需平衡性能与安全

2. Java 项目持久化配置原则

  1. 生产环境:开启「RDB + AOF(混合持久化)」,兼顾数据安全与恢复速度;
  2. AOF 同步策略:选 everysec(平衡安全与性能),Java 金融项目可考虑 always;
  3. 混合持久化:Redis 4.0 + 支持,Java 项目开启后,重启时先加载 RDB,再重放 AOF 增量日志。

五、Redis 集群(Java 开发核心方案)

1. 主从复制

  • 原理:主库写、从库读,Java 客户端可配置读写分离(JedisPool 分主从池);
  • 关注点:主库宕机后需手动切换,生产环境需配合哨兵。

2. 哨兵(Sentinel)

  • 核心功能:监控主从、自动故障转移,Java 客户端(Jedis/Redisson)原生支持哨兵模式,无需额外开发;
  • 关注点:配置哨兵地址列表,客户端自动感知主库切换,无需重启应用。

3. Redis Cluster(分片集群)

  • 原理:16384 个哈希槽,数据按槽分布,Java 客户端需支持槽位计算;
  • Java 适配:
    • Jedis:使用 JedisCluster,自动处理槽位重定向;
    • Redisson:原生支持 Cluster,封装更友好;
  • 注意:Java 中避免跨槽的批量操作(如 mset 操作不同槽的 Key),会降低性能。

六、缓存核心问题

1. 三大问题核心对比( 面试高频)

问题定义Java 核心解决方案
缓存穿透请求不存在的 Key,打穿到 DB1. 缓存空值(设置短过期);2. 布隆过滤器(Java 中用 Guava BloomFilter 或 Redis 布隆过滤器模块)
缓存击穿热点 Key 过期,大量请求打 DB1. 热点 Key 永不过期;2. 分布式锁(仅一个请求查 DB)
缓存雪崩大量 Key 同时过期 / Redis 宕机,所有请求打 DB1. 过期时间加随机值;2. Redis 集群高可用;3. 多级缓存(本地缓存 Caffeine + Redis)

2. 缓存与数据库一致性(Java 核心方案)

  • 核心原则:禁止「先更缓存,再更 DB」(并发必出脏数据);
  • 基础方案:先更 DB,再删缓存(Java 中用 RedisTemplate.delete (),而非更新缓存);
  • 进阶方案:延迟双删(删缓存→更 DB→延迟 1-2 秒再删缓存),Java 中用线程池 / 定时任务实现延迟;
  • 强一致性场景:Java 中用分布式事务(Seata)或消息队列(RocketMQ)异步更新缓存。

七、Redis 分布式锁(Java 开发核心)

1. 基础实现(RedisTemplate)

核心逻辑:原子加锁(set nx ex)+ 原子释放(Lua 脚本),Java 中需注意:

  • 加锁:redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, expireTime, TimeUnit.SECONDS),requestId 避免误删;
  • 释放:Lua 脚本(一次性执行,原子性),避免「查值 + 删除」非原子导致的误删。

2. 进阶实现(Redisson)

Java 中优先使用 Redisson,封装了分布式锁的所有核心能力:

  • 可重入锁:RedissonLock 支持重入,适配 Java 多线程重入场景;
  • 锁续期:Watch Dog 机制自动续期,解决业务执行时间超过锁过期时间的问题;
  • 公平锁:避免线程饥饿,适配 Java 公平锁场景;
  • RedLock:多节点加锁,避免 Redis 单点故障导致锁失效,Java 中用 RedissonRedLock 实现。

3. Java 开发避坑点

  1. 必须设置过期时间,避免死锁;
  2. 释放锁必须校验 requestId,避免误删其他线程的锁;
  3. 高并发场景优先用 Redisson,而非手动实现(避免踩坑)。

八、面试高频题(核心答案)

1. Redis 单线程为什么能支撑高并发?

答:Redis 核心命令执行是单线程,但结合以下特性支撑高并发:

  1. 纯内存操作,无磁盘 IO 瓶颈;
  2. IO 多路复用处理大量网络连接,避免阻塞 IO;
  3. 单线程避免线程切换和锁竞争开销;
  4. 补充:Redis 6.0 + 多线程仅处理网络读写,命令执行仍为单线程,Java 客户端无需适配多线程逻辑。

2. 如何解决缓存穿透 / 击穿 / 雪崩?

答:

  • 穿透:缓存空值(短过期)+ 布隆过滤器(Guava/Redis)拦截无效请求;
  • 击穿:热点 Key 永不过期 + 分布式锁(Redisson)限制查 DB 的请求数;
  • 雪崩:过期时间加随机值 + Redis 集群高可用 + 多级缓存(Caffeine 本地缓存 + Redis)。

3. 如何实现 Redis 分布式锁?

答:

  1. 基础版:用 RedisTemplate 执行 set nx ex 原子加锁,Lua 脚本原子释放;
  2. 进阶版:使用 Redisson,支持可重入、锁续期、公平锁、RedLock;
  3. 注意事项:加锁设过期时间、释放锁校验 requestId、高可用场景用 RedLock。

4. 如何保证缓存和数据库一致性?

答:核心原则是「先更数据库,再删缓存」;

  • 基础场景:更新 DB 后调用 RedisTemplate.delete () 删除缓存;
  • 并发场景:延迟双删(删缓存→更 DB→延迟 1-2 秒再删);
  • 强一致性场景:用 Seata 分布式事务或 RocketMQ 异步更新缓存。

总结

  1. Java 开发中 Redis 核心应用场景:缓存、分布式锁、计数器、排行榜,需结合数据结构特性选型;
  2. 缓存三大问题的核心解决思路:拦截无效请求、避免批量过期、保证 Redis 高可用;
  3. 分布式锁优先用 Redisson,遵循「原子加锁、原子释放、设置过期时间」三大原则;
  4. 缓存与 DB 一致性核心:先更 DB 再删缓存,避免先更缓存的脏数据问题。
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐