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

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介: 本文总结了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


相关文章
|
29天前
|
缓存 监控 NoSQL
Redis 实操要点:Java 最新技术栈的实战解析
本文介绍了基于Spring Boot 3、Redis 7和Lettuce客户端的Redis高级应用实践。内容包括:1)现代Java项目集成Redis的配置方法;2)使用Redisson实现分布式可重入锁与公平锁;3)缓存模式解决方案,包括布隆过滤器防穿透和随机过期时间防雪崩;4)Redis数据结构的高级应用,如HyperLogLog统计UV和GeoHash处理地理位置。文章提供了详细的代码示例,涵盖Redis在分布式系统中的核心应用场景,特别适合需要处理高并发、分布式锁等问题的开发场景。
147 38
|
4天前
|
存储 NoSQL 定位技术
Redis数据类型面试给分情况
Redis常见数据类型包括:string、hash、list、set、zset(有序集合)。此外还包含高级结构如bitmap、hyperloglog、geo。不同场景可选用合适类型,如库存用string,对象存hash,列表用list,去重场景用set,排行用zset,签到用bitmap,统计访问量用hyperloglog,地理位置用geo。
19 5
|
3天前
|
存储 缓存 NoSQL
Redis 核心知识与项目实践解析
本文围绕 Redis 展开,涵盖其在项目中的应用(热点数据缓存、存储业务数据、实现分布式锁)、基础数据类型(string 等 5 种)、持久化策略(RDB、AOF 及混合持久化)、过期策略(惰性 + 定期删除)、淘汰策略(8 种分类)。 还介绍了集群方案(主从复制、哨兵、Cluster 分片)及主从同步机制,分片集群数据存储的哈希槽算法。对比了 Redis 与 Memcached 的区别,说明了内存用完的情况及与 MySQL 数据一致性的保证方案。 此外,详解了缓存穿透、击穿、雪崩的概念及解决办法,如何保证 Redis 中是热点数据,Redis 分布式锁的实现及问题解决,以及项目中分布式锁
|
1月前
|
存储 Java 数据库
应届生面试高频 Java 基础问题及详细答案解析
摘要: Java数据类型分为基本类型(如int、float等)和引用类型(如类、数组)。final可修饰类、方法和变量,使其不可继承、重写或修改。static用于类级别的变量和方法,共享于所有实例。"=="比较基本类型的值或引用类型的地址,而equals比较对象内容(需重写)。Java只有值传递,对于引用类型传递的是地址副本。String对象不可变,拼接操作会创建新对象而非修改原对象。Java 10的var支持类型推断,Java 16的Record提供不可变类简化。
54 0
|
1月前
|
存储 安全 Java
应届生面试高频 Java 基础问题及实操示例解析
本文总结了Java基础面试中的高频考点,包括数据类型分类、final修饰符的三种用途、static关键字特性、==与equals的区别、Java只有值传递的特性、String的不可变性、Error与Exception的差异、程序初始化顺序规则,以及IO流的字节流/字符流分类。每个问题都配有简明定义和典型示例,如用final修饰变量示例、static方法调用限制说明等,帮助应聘者快速掌握核心概念和实际应用场景。
40 0
|
11月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
8月前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
8月前
|
存储 缓存 算法
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
本文介绍了多线程环境下的几个关键概念,包括时间片、超线程、上下文切换及其影响因素,以及线程调度的两种方式——抢占式调度和协同式调度。文章还讨论了减少上下文切换次数以提高多线程程序效率的方法,如无锁并发编程、使用CAS算法等,并提出了合理的线程数量配置策略,以平衡CPU利用率和线程切换开销。
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
|
8月前
|
存储 缓存 Java
大厂面试必看!Java基本数据类型和包装类的那些坑
本文介绍了Java中的基本数据类型和包装类,包括整数类型、浮点数类型、字符类型和布尔类型。详细讲解了每种类型的特性和应用场景,并探讨了包装类的引入原因、装箱与拆箱机制以及缓存机制。最后总结了面试中常见的相关考点,帮助读者更好地理解和应对面试中的问题。
194 4
|
9月前
|
算法 Java 数据中心
探讨面试常见问题雪花算法、时钟回拨问题,java中优雅的实现方式
【10月更文挑战第2天】在大数据量系统中,分布式ID生成是一个关键问题。为了保证在分布式环境下生成的ID唯一、有序且高效,业界提出了多种解决方案,其中雪花算法(Snowflake Algorithm)是一种广泛应用的分布式ID生成算法。本文将详细介绍雪花算法的原理、实现及其处理时钟回拨问题的方法,并提供Java代码示例。
471 2

相关产品

  • 云数据库 Tair(兼容 Redis)