Java Redis 面试题集锦 常见高频面试题目及解析

本文涉及的产品
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
RDS AI 助手,专业版
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
简介: 本文总结了Redis在Java中的核心面试题,包括数据类型操作、单线程高性能原理、键过期策略及分布式锁实现等关键内容。通过Jedis代码示例展示了String、List等数据类型的操作方法,讲解了惰性删除和定期删除相结合的过期策略,并提供了Spring Boot配置Redis过期时间的方案。文章还探讨了缓存穿透、雪崩等问题解决方案,以及基于Redis的分布式锁实现,帮助开发者全面掌握Redis在Java应用中的实践要点。

我将围绕Redis在Java中的常见面试问题,结合实际应用场景与代码示例,为你全面梳理Redis相关知识,希望能助力你应对面试及实际开发需求。

Java中Redis面试题集锦

一、Redis基础

1. Redis支持哪些数据类型?在Java中如何操作?

Redis支持多种数据类型,包括String、List、Set、SortedSet、Hash、Bitmap等。以Jedis为例,在Java中操作Redis数据类型示例如下:

import redis.clients.jedis.Jedis;

public class RedisExample {
   
    public static void main(String[] args) {
   
        Jedis jedis = new Jedis("localhost", 6379);

        // String类型操作
        jedis.set("stringKey", "stringValue");
        String stringValue = jedis.get("stringKey");
        System.out.println("String value: " + stringValue);

        // List类型操作
        jedis.rpush("listKey", "value1", "value2", "value3");
        System.out.println("List values: " + jedis.lrange("listKey", 0, -1));

        // Set类型操作
        jedis.sadd("setKey", "value1", "value2", "value3");
        System.out.println("Set values: " + jedis.smembers("setKey"));

        // Hash类型操作
        jedis.hset("hashKey", "field1", "value1");
        jedis.hset("hashKey", "field2", "value2");
        System.out.println("Hash values: " + jedis.hgetAll("hashKey"));

        // SortedSet类型操作
        jedis.zadd("sortedSetKey", 1, "value1");
        jedis.zadd("sortedSetKey", 2, "value2");
        System.out.println("SortedSet values: " + jedis.zrange("sortedSetKey", 0, -1));

        jedis.close();
    }
}

2. Redis单线程为何高性能?

Redis单线程却具备高性能,主要原因如下:

  • 内存操作:数据存储在内存中,内存读写速度极快,减少了磁盘I/O的延迟。
  • IO多路复用:使用I/O多路复用技术,如epoll,可以同时监听多个套接字,在有事件发生时才进行处理,提高了对并发连接的处理能力。
  • 避免上下文切换:单线程避免了多线程环境下频繁的上下文切换开销,使得Redis能够高效地处理命令。同时,单线程处理命令保证了原子性,避免了多线程并发操作可能导致的竞态条件问题。

二、过期策略详解

1. Redis的键过期删除策略是什么?

Redis采用惰性删除和定期删除相结合的键过期删除策略:

  • 惰性删除:当客户端访问某个键时,Redis会检查该键是否过期。如果过期,则删除该键并返回相应结果。例如在Jedis访问时,就会触发惰性删除机制。如下面代码中,当再次获取已过期的键时,会返回null,表明该键已被惰性删除:
Jedis jedis = new Jedis("localhost", 6379);
jedis.setex("expireKey", 10, "expireValue");// 设置键10秒后过期
// 模拟等待10秒以上
try {
   
    Thread.sleep(11000);
} catch (InterruptedException e) {
   
    e.printStackTrace();
}
String expiredValue = jedis.get("expireKey");
System.out.println("Expired value: " + expiredValue);
jedis.close();
  • 定期删除:Redis会周期性地随机扫描过期键,默认每秒进行10次扫描。每次扫描会随机抽查20个键,将其中过期的键删除。如果过期键的比例超过25%,则继续进行下一轮扫描,直到过期键的比例低于25%或达到最大扫描次数。

2. Java中如何设置键过期?

在Java中使用Jedis设置键过期可以通过setex方法,该方法用于设置一个带有过期时间(单位为秒)的键值对。例如:

Jedis jedis = new Jedis("localhost", 6379);
jedis.setex("keyWithExpire", 60, "value");// 设置键60秒后过期
jedis.close();

也可以通过set方法设置键值后,再使用expire方法单独设置过期时间:

Jedis jedis = new Jedis("localhost", 6379);
jedis.set("anotherKey", "anotherValue");
jedis.expire("anotherKey", 30);// 设置键30秒后过期
jedis.close();

3. 内存淘汰策略如何与过期键交互?

Redis的内存淘汰策略在内存不足时发挥作用,与过期键相互配合。当内存达到设置的最大内存限制(通过maxmemory配置)时,Redis会根据设置的内存淘汰策略(如volatile - lruallkeys - lruvolatile - ttl等)来删除键,以释放内存。其中,volatile相关的策略只会从设置了过期时间的键中进行淘汰,而allkeys相关的策略则会从所有键(无论是否设置过期时间)中进行淘汰。例如,当使用volatile - lru策略时,Redis会从设置了过期时间的键中,选择最近最少使用的键进行删除,即使这些键还未过期,以此来腾出内存空间。

4. 主从模式下过期键如何处理?

在Redis主从模式中,主节点负责处理所有写操作,包括键的过期删除。当主节点发现某个键过期时,会删除该键,并将删除操作同步给从节点。从节点本身不会主动检查和删除过期键,而是依赖主节点的同步指令(DEL命令)来更新自身的数据状态。因此,在Java代码中操作过期键时,需要确保在主节点上进行操作,以保证整个集群中数据状态的一致性。例如在使用Jedis进行集群操作时,要确保连接到主节点执行设置键过期等相关操作,否则可能会出现从节点数据不一致的问题。

三、其他重要问题

1. 如何用Redis实现分布式锁?

在Java中可以借助Jedis实现一个简单的分布式锁:

import redis.clients.jedis.Jedis;

public class DistributedLock {
   
    private static final String LOCK_KEY = "distributed_lock";
    private static final String LOCK_VALUE = System.currentTimeMillis() + "_" + Thread.currentThread().getId();
    private static final int EXPIRE_TIME = 10000; // 锁过期时间,单位毫秒

    public static boolean tryLock(Jedis jedis) {
   
        String result = jedis.set(LOCK_KEY, LOCK_VALUE, "NX", "EX", EXPIRE_TIME / 1000);
        return "OK".equals(result);
    }

    public static void unlock(Jedis jedis) {
   
        String script = "if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) else return 0 end";
        jedis.eval(script, 1, LOCK_KEY, LOCK_VALUE);
    }
}

使用时:

Jedis jedis = new Jedis("localhost", 6379);
if (DistributedLock.tryLock(jedis)) {
   
    try {
   
        // 执行业务逻辑
    } finally {
   
        DistributedLock.unlock(jedis);
    }
}
jedis.close();

2. 缓存穿透/雪崩/击穿解决方案

  • 缓存穿透:指查询一个一定不存在的数据,由于缓存不命中,将一直查询数据库。解决方案可以使用布隆过滤器,在查询前先通过布隆过滤器判断数据是否存在,若不存在则直接返回,避免查询数据库。
  • 缓存雪崩:指大量缓存同时过期,导致瞬间大量请求直接访问数据库。可以通过设置不同的过期时间,避免缓存集中过期;或者使用二级缓存,一级缓存失效时从二级缓存获取数据。
  • 缓存击穿:指一个热点Key在过期瞬间,大量请求同时访问,导致大量请求落到数据库。可以使用互斥锁,在缓存过期时,只有一个请求能获取锁去查询数据库并更新缓存,其他请求等待。

3. Spring Boot中如何配置Redis过期时间?

在Spring Boot项目中,可以通过在配置文件application.yml中进行如下配置来设置Redis缓存的默认过期时间:

spring:
  redis:
    host: localhost
    port: 6379
    lettuce:
      pool:
        max - active: 8
        max - idle: 8
        min - idle: 0
        max - wait: -1ms
    cache:
      cache - names: defaultCache
      time - to - live: 3600000 # 过期时间,单位毫秒,这里设置为1小时

也可以在代码中通过@Cacheable注解的expire属性针对不同的缓存方法设置不同的过期时间:

@Service
public class UserService {
   
    @Cacheable(value = "userCache", key = "#id", expire = 1800000) // 设置该缓存2小时过期
    public User getUserById(Long id) {
   
        // 从数据库查询用户信息逻辑
    }
}

4. Redis事务 vs Lua脚本

Redis事务可以将多个命令打包成一个原子操作,通过MULTIEXECDISCARD等命令实现。事务中的命令要么全部执行成功,要么全部不执行。但事务不支持回滚,一旦事务中的某个命令执行失败,后续命令仍会继续执行。

Lua脚本则提供了更强大的功能,它可以在Redis服务器端执行一段Lua代码。Lua脚本具有原子性,在执行过程中不会被其他命令打断。相比事务,Lua脚本可以实现更复杂的业务逻辑,并且由于在服务器端执行,减少了网络开销。例如,在实现分布式锁的释放操作时,使用Lua脚本可以确保在判断锁的持有者和删除锁这两个操作的原子性,避免出现并发问题。

5. 大Key过期导致阻塞怎么办?

大Key(例如包含大量元素的List、Hash、Set等)过期删除时可能会导致Redis主线程阻塞,影响性能。解决方法如下:

  • 使用异步删除:从Redis 4.0开始支持异步删除,通过UNLINK命令代替DEL命令,将大Key的删除操作放到后台线程执行,避免阻塞主线程。在Java中使用Jedis时,可以这样调用:
Jedis jedis = new Jedis("localhost", 6379);
jedis.unlink("bigKey");
jedis.close();
  • 提前拆分大Key:在数据写入时,将大Key拆分成多个小Key存储,减少单个Key的大小和过期删除时的影响。例如,对于一个包含大量用户信息的Hash类型大Key,可以按用户ID范围拆分成多个小的Hash Key。

6. 如何监控过期键?

可以通过监控Redis的expired_keys指标来了解过期键的情况。在Redis客户端中,可以使用INFO stats命令获取相关统计信息,其中expired_keys表示累计过期的键的数量。在Java中,可以通过Jedis获取该指标:

Jedis jedis = new Jedis("localhost", 6379);
String info = jedis.info("stats");
String[] lines = info.split("\n");
for (String line : lines) {
   
    if (line.startsWith("expired_keys")) {
   
        System.out.println("Expired keys: " + line.split(":")[1]);
        break;
    }
}
jedis.close();

也可以使用一些监控工具,如Prometheus结合Redis Exporter,将Redis的各项指标收集起来进行可视化监控,实时了解过期键的变化趋势,以便及时发现和处理过期键相关的性能问题。

以上内容有没有覆盖到你想要重点了解的Redis面试知识呢?要是你希望深入探讨某个具体问题,或者想补充新的面试题,都能随时告诉我。


Java,Redis, 面试题,高频面试题,Redis 面试解析,Java Redis 整合,Redis 数据结构,Redis 持久化,Redis 集群,Redis 缓存穿透,Redis 缓存雪崩,Redis 缓存击穿,Redis 事务,Redis 过期策略,Redis 性能优化



代码获取方式
https://pan.quark.cn/s/14fcf913bae6


相关文章
|
5月前
|
算法 Java
50道java集合面试题
50道 java 集合面试题
|
5月前
|
存储 SQL NoSQL
Redis-常用语法以及java互联实践案例
本文详细介绍了Redis的数据结构、常用命令及其Java客户端的使用,涵盖String、Hash、List、Set、SortedSet等数据类型及操作,同时提供了Jedis和Spring Boot Data Redis的实战示例,帮助开发者快速掌握Redis在实际项目中的应用。
415 1
Redis-常用语法以及java互联实践案例
|
7月前
|
Java 数据库连接 数据库
Java 相关知识点总结含基础语法进阶技巧及面试重点知识
本文全面总结了Java核心知识点,涵盖基础语法、面向对象、集合框架、并发编程、网络编程及主流框架如Spring生态、MyBatis等,结合JVM原理与性能优化技巧,并通过一个学生信息管理系统的实战案例,帮助你快速掌握Java开发技能,适合Java学习与面试准备。
353 2
Java 相关知识点总结含基础语法进阶技巧及面试重点知识
|
5月前
|
算法 Java
50道java基础面试题
50道java基础面试题
|
9月前
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
|
4月前
|
缓存 负载均衡 监控
135_负载均衡:Redis缓存 - 提高缓存命中率的配置与最佳实践
在现代大型语言模型(LLM)部署架构中,缓存系统扮演着至关重要的角色。随着LLM应用规模的不断扩大和用户需求的持续增长,如何构建高效、可靠的缓存架构成为系统性能优化的核心挑战。Redis作为业界领先的内存数据库,因其高性能、丰富的数据结构和灵活的配置选项,已成为LLM部署中首选的缓存解决方案。
|
5月前
|
存储 缓存 NoSQL
Redis专题-实战篇二-商户查询缓存
本文介绍了缓存的基本概念、应用场景及实现方式,涵盖Redis缓存设计、缓存更新策略、缓存穿透问题及其解决方案。重点讲解了缓存空对象与布隆过滤器的使用,并通过代码示例演示了商铺查询的缓存优化实践。
279 1
Redis专题-实战篇二-商户查询缓存
|
4月前
|
缓存 运维 监控
Redis 7.0 高性能缓存架构设计与优化
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Redis 7.0高性能缓存架构,探索函数化编程、多层缓存、集群优化与分片消息系统,用代码在二进制星河中谱写极客诗篇。
|
9月前
|
缓存 NoSQL Java
Redis+Caffeine构建高性能二级缓存
大家好,我是摘星。今天为大家带来的是Redis+Caffeine构建高性能二级缓存,废话不多说直接开始~
1322 0

相关产品

  • 云数据库 Tair(兼容 Redis)