1.7 java面试题:Redis 基本篇

好的,我按顺序逐题回答,对高频重点配上能直接落地的 Java 代码案例,让你面试时既能讲原理,又能亮代码。


1. 什么是 Redis?

Redis 是基于内存的高性能键值存储系统 ,C 语言编写,单机 QPS 可达 10 万+。它不只是缓存,更是一个数据结构服务器,支持 String、Hash、List、Set、Sorted Set 等丰富类型,并提供持久化、主从、集群、事务、Lua 脚本等高级功能。在银行核心系统中,我们用它做热点数据缓存、分布式锁、实时排行榜和消息队列。


2. Redis 的数据类型?

  • String:缓存对象、计数器、分布式锁值。
  • Hash:存对象(如客户信息),可部分更新。
  • List:消息队列、最新 N 条记录。
  • Set:去重、标签、共同好友。
  • Sorted Set:排行榜、延迟队列。
  • 高级:Bitmaps (签到)、HyperLogLog (UV 统计)、Geo (附近的人)、Stream(5.0+ 消息队列)。

代码示例:Hash 存对象 + 部分更新

java 复制代码
// 存入客户信息
redisTemplate.opsForHash().put("customer:1001", "name", "张三");
redisTemplate.opsForHash().put("customer:1001", "balance", "50000");
// 只更新余额
redisTemplate.opsForHash().increment("customer:1001", "balance", -1000);

3. 使用 Redis 有哪些好处?

  1. 极致性能:内存读写微秒级,缓解 DB 压力。
  2. 数据结构丰富:天然匹配缓存、队列、排行榜等业务。
  3. 分布式能力:分布式锁、Pub/Sub、Stream 支撑微服务。
  4. 持久化:RDB + AOF 保证数据安全。
  5. 原子操作:Lua 脚本打包多个命令,避免并发问题。
  6. 高可用:哨兵/集群模式,故障转移,水平扩展。

4. Redis 相比 Memcached 有哪些优势?

  • 数据结构:Memcached 仅 String,Redis 支持多种复杂结构。
  • 持久化:Redis 支持 RDB/AOF,Memcached 不支持,重启全丢。
  • 高可用:Redis 原生支持哨兵、集群,Memcached 需客户端一致性哈希。
  • 功能丰富:Redis 支持事务、Lua 脚本、发布订阅、过期策略等。

5. Memcache 与 Redis 的区别都有哪些?

维度 Redis Memcached
数据结构 丰富 仅 String
持久化 支持 不支持
线程模型 单线程 (命令执行) 多线程
内存管理 多种淘汰策略 仅 LRU
集群 原生支持 客户端实现
功能 事务、Lua、Pub/Sub 简单

6. Redis 是单进程单线程的?

命令执行是单线程的 ,但 4.0 后引入多线程处理持久化、异步删除;6.0 后支持多线程 I/O(网络读写),但命令执行依然是单线程。这种设计避免了锁竞争,简化了数据一致性。


7. 一个字符串类型的值能存储最大容量是多少?

512MB。但生产中建议单个 value 不超过 10KB,大对象应拆分或使用 Hash 存储。


8. Redis 的持久化机制是什么?各自的优缺点?

  • RDB(快照):定时全量备份。优点:恢复快,文件小。缺点:可能丢最后几分钟数据。
  • AOF(追加日志) :记录每个写命令。优点:最多丢 1 秒(appendfsync everysec)。缺点:文件大,恢复慢。
  • 混合持久化(4.0+) :RDB 全量 + AOF 增量,推荐。银行场景开启 AOF everysec

9. Redis 常见性能问题和解决方案:

  • 慢查询 :用 SLOWLOG GET 10 分析,优化大 key 或复杂命令。
  • 热 key:加随机前缀打散或本地缓存(Caffeine)。
  • 大 keyredis-cli --bigkeys 扫描,拆分。
  • 内存打满 :设置合理的 maxmemory-policy
  • RDB fork 阻塞:控制实例内存大小,避开高峰。

10. Redis 过期键的删除策略?

  • 惰性删除:访问 key 时检查是否过期,过期则删。
  • 定期删除:每 100ms 随机抽取一部分 key 检查删除。
  • 内存淘汰 :内存达上限时,按 maxmemory-policy 淘汰。

11. Redis 的回收策略(淘汰策略)?

  • noeviction:不淘汰,写操作报错(适合金融记录)。
  • allkeys-lru:所有 key 参与 LRU 淘汰(推荐缓存场景)。
  • volatile-lru:仅设置了过期时间的 key 参与。
  • allkeys-random:随机淘汰。
  • volatile-ttl:优先淘汰剩余 TTL 最短的 key。

12. 为什么 Redis 需要把所有数据放到内存中?

为了达到微秒级延迟,必须避免磁盘 I/O。内存读写比磁盘快 1000 倍以上。持久化只是数据安全的补充,日常操作完全基于内存。


13. Redis 的同步机制了解么?

主从复制:从节点发送 PSYNC,主节点生成 RDB 快照 + 增量缓冲区。断线重连后根据 replication backlog 尝试增量同步,否则全量同步。


14. Pipeline 有什么好处,为什么要用 pipeline?

将多个命令批量发送,减少网络往返(RTT)

代码示例

java 复制代码
// 无 pipeline:1000 次网络往返
for (int i = 0; i < 1000; i++) {
    redisTemplate.opsForValue().set("key:" + i, "value" + i);
}

// 有 pipeline:1 次网络往返
redisTemplate.executePipelined(new SessionCallback<Object>() {
    @Override
    public Object execute(RedisOperations operations) {
        for (int i = 0; i < 1000; i++) {
            operations.opsForValue().set("key:" + i, "value" + i);
        }
        return null;
    }
});

批量导入时性能提升数十倍。


15. 是否使用过 Redis 集群,集群的原理是什么?

使用过。Redis Cluster 采用虚拟哈希槽,CRC16(key) % 16384 映射到 16384 个 slot,每个节点负责部分 slot。客户端直连节点,若 key 不在该节点,返回 MOVED 重定向。每个节点有主从,保证高可用。

代码示例(Spring Boot + Lettuce 连接集群)

yaml 复制代码
spring:
  redis:
    cluster:
      nodes: 10.0.1.1:6379,10.0.1.2:6379,10.0.1.3:6379
      max-redirects: 3

16. Redis 集群方案什么情况下会导致整个集群不可用?

  • 所有主节点宕机,且从节点无法提升为主。
  • slot 不完整 :某个 slot 所在的主从全部不可用,如果配置 cluster-require-full-coverage yes(默认),集群整体停止服务。

17. Redis 支持的 Java 客户端都有哪些?官方推荐用哪个?

  • Jedis:同步、直连、简单。
  • Lettuce:基于 Netty,异步、线程安全,官方推荐。
  • Redisson:高级封装,提供分布式锁、队列、对象等。
  • 官方推荐 Lettuce,Spring Boot 2.x 默认集成。

18. Jedis 与 Redisson 对比有什么优缺点?

  • Jedis:轻量,直接操作 Redis 命令,性能高,但实例非线程安全,需连接池。
  • Redisson:高层抽象,像操作 Java 对象一样使用 Redis,提供分布式锁、队列、读写锁等,但额外序列化开销略高。
  • 银行项目:一般用 Lettuce 做底层连接,Redisson 做分布式锁。

19. Redis 如何设置密码及验证密码?

服务端(redis.conf):

复制代码
requirepass your_password

客户端连接

yaml 复制代码
spring:
  redis:
    password: your_password

命令验证

复制代码
AUTH your_password

20. 说说 Redis 哈希槽的概念?

Redis Cluster 将数据空间划分为 16384 个 slot ,每个 key 通过 CRC16(key) & 16383 计算 slot,每个节点负责一部分 slot。移动 slot 即可实现数据迁移和扩缩容。


21. Redis 集群的主从复制模型是怎样的?

每个主节点可以有多个从节点,数据从主异步复制到从。主节点宕机,从节点可被选举提升为新主,保证可用性。


22. Redis 集群会有写操作丢失吗?为什么?

可能会。因为主从复制是异步的 ,如果主节点宕机时数据还未同步到从,新主节点将丢失这部分数据。可通过 wait 命令强制等待同步,但会降低性能。银行项目中,我们通过业务幂等和最终一致性兜底。


23. Redis 集群之间是如何复制的?

从节点启动时发送 PSYNC,主节点发送快照和命令流。正常运行期间,主节点将写命令异步发送给所有从节点。


24. Redis 集群最大节点个数是多少?

理论上 16384 个主节点,但官方建议不超过 1000 个节点,实际生产中一个集群几十个节点已经很大。


25. Redis 集群如何选择数据库?

集群模式不支持多数据库 ,只有一个 DB0。SELECT 命令被禁用。


26. 怎么测试 Redis 的连通性?

使用 PING 命令,返回 PONG 即连通。

java 复制代码
String result = redisTemplate.getConnectionFactory().getConnection().ping();
System.out.println(result); // "PONG"

27. 怎么理解 Redis 事务?

Redis 事务(MULTI/EXEC)保证命令一次性、顺序执行,不会被打断,但不保证原子性:某条命令执行失败不会回滚其他命令。

代码示例(WATCH 乐观锁)

java 复制代码
redisTemplate.watch("product:stock:1001");
String stock = redisTemplate.opsForValue().get("product:stock:1001");
if (Integer.parseInt(stock) > 0) {
    redisTemplate.multi();
    redisTemplate.opsForValue().decrement("product:stock:1001");
    List<Object> result = redisTemplate.exec();
    if (result.isEmpty()) {
        // 事务被取消,重试
    }
}

28. Redis 事务相关的命令有哪几个?

  • MULTI:开启事务
  • EXEC:执行事务
  • DISCARD:取消事务
  • WATCH:监视 key,乐观锁
  • UNWATCH:取消监视

29. Redis key 的过期时间和永久有效分别怎么设置?

  • 过期时间:EXPIRE key 60(秒)、SETEX key 60 value
  • 永久有效:不设置过期时间,或 PERSIST key 移除已有过期时间。

30. Redis 如何做内存优化?

  • 使用短 key。
  • 使用压缩列表或紧凑编码(默认)。
  • 使用 Hash 存储对象,比 String 存 JSON 节省内存。
  • 开启内存淘汰策略,及时清理过期 key。
  • 对较大值使用 Stream 或分片存储。

31. Redis 回收进程如何工作的?

当内存达到 maxmemory 时,根据 maxmemory-policy 淘汰 key。淘汰过程阻塞命令执行,直到释放出足够内存,但通常很快。


32. 都有哪些办法可以降低 Redis 的内存使用情况呢?

  • 设置 maxmemory-policy allkeys-lru
  • 使用 hash-max-ziplist-entries 等参数开启紧凑编码。
  • 缩短 key 长度。
  • 将大对象拆分为多个小 key。
  • 使用 volatile-ttl 淘汰即将过期的 key。
  • 使用连接池减少连接开销。

33. Redis 的内存用完了会发生什么?

  • 如果 maxmemory-policy 不是 noeviction,会淘汰 key 释放空间。
  • 如果是 noeviction,所有写入操作返回错误,读操作不受影响。

34. 一个 Redis 实例最多能存放多少的 keys?List、Set、Sorted Set 他们最多能存放多少元素?

  • 单实例 key 数量上限:2^32-1(约 42 亿)。
  • List、Set、Sorted Set 每个集合内元素数量也是 2^32-1
  • 受限于内存大小,实际远小于理论值。

35. MySQL 里有 2000w 数据,redis 中只存 20w 的数据,如何保证 redis 中的数据都是热点数据?

配置 maxmemory-policy allkeys-lru,内存满时自动淘汰最少使用的数据,保留热点数据。配合 maxmemory 设置合适的内存容量。


36. Redis 最适合的场景?

  • 缓存:热点数据,减少 DB 压力。
  • 分布式锁:跨进程互斥。
  • 计数器INCR 原子自增。
  • 排行榜:Sorted Set。
  • 消息队列:List 阻塞弹出、Stream。
  • 实时系统:发布订阅。

37. 假如 Redis 里面有 1 亿个 key,其中有 10w 个 key 是以某个固定的已知的前缀开头的,如果将它们全部找出来?

不能用 KEYS prefix* ,会阻塞 Redis。

使用 SCAN cursor MATCH prefix* COUNT 1000 游标迭代,每次返回部分 key,不阻塞主线程。

java 复制代码
// Spring 中使用 scan
Set<String> keys = redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
    Set<String> set = new HashSet<>();
    Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions()
            .match("prefix*").count(1000).build());
    while (cursor.hasNext()) {
        set.add(new String(cursor.next()));
    }
    return set;
});

38. 如果有大量的 key 需要设置同一时间过期,一般需要注意什么?

需要避免同一时刻大量 key 同时过期引发缓存雪崩。解决方案:在过期时间上加上一个随机值,例如 5~15 分钟的随机浮动。


39. 使用过 Redis 做异步队列么,你是怎么用的?

使用过。基于 List 的阻塞弹出 实现:

java 复制代码
// 生产者
redisTemplate.opsForList().leftPush("task:queue", task);

// 消费者(阻塞等待)
while (true) {
    Object task = redisTemplate.opsForList().rightPop("task:queue", 
                                                       0, TimeUnit.SECONDS);
    // 处理任务
}

或使用 Redis Stream(更可靠,支持消费者组):

java 复制代码
// 发送消息
Map<String, Object> msg = new HashMap<>();
msg.put("orderId", "1001");
redisTemplate.opsForStream().add("order:stream", msg);

// 消费
List<MapRecord<String, Object, Object>> records = 
    redisTemplate.opsForStream().read(Consumer.from("group", "consumer1"),
                                       StreamReadOptions.empty().count(1),
                                       StreamOffset.create("order:stream", ReadOffset.lastConsumed()));

40. 使用过 Redis 分布式锁么,它是什么回事?

使用过。分布式锁是保证多个服务实例在操作共享资源时互斥访问的一种机制。基于 Redis 的 SETNX + Lua 或 Redisson 实现。

原理 :利用 Redis 单线程命令 SET key value NX EX 30 原子加锁,解锁时用 Lua 脚本校验持有者删除,避免误删。

Redisson 代码案例

java 复制代码
RLock lock = redissonClient.getLock("lock:order:1001");
try {
    if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
        // 处理业务,看门狗自动续期
    }
} finally {
    if (lock.isHeldByCurrentThread()) {
        lock.unlock();
    }
}

注意事项:锁超时问题要用看门狗或业务幂等兜底;主从切换可能丢失锁,需用红锁或幂等防线。


以上 40 个问题环环相扣,既有理论又有可落地的代码,足以应对绝大多数 Redis 面试。