0

0

Redis 缓存与 Java 集成应用实战 (全网最新颖教程)

星夢妙者

星夢妙者

发布时间:2025-07-10 18:11:02

|

674人浏览过

|

来源于php中文网

原创

redis 缓存与 java 应用集成的核心目的是提升系统响应速度并减轻数据库压力。1. 最常用的方式是通过 spring data redis 实现 cache-aside 模式,应用代码手动控制缓存读写;2. 常见的缓存策略包括 cache-aside(旁路缓存)、read/write-through(读写穿透)、write-back(写回)和 refresh-ahead(刷新预加载),各自适用于不同业务场景;3. java 中主流的 redis 客户端为 jedis 和 lettuce,其中 lettuce 因支持非阻塞 i/o 和响应式编程,更适合高并发新项目;4. 缓存常见问题如缓存穿透可通过缓存空值或布隆过滤器防范,缓存雪崩可通过设置随机过期时间或多级缓存应对,缓存击穿则可通过互斥锁或永不过期机制解决。

Redis 缓存与 Java 集成应用实战 (全网最新颖教程)

Redis 缓存与 Java 应用集成,说白了,就是为了让你的系统跑得更快,响应更及时。当用户请求一个数据时,我们希望它能瞬间出现,而不是让数据库吭哧吭哧地去查。Redis 作为一个高性能的内存数据库,正是扮演这个“加速器”的角色。它能大幅减轻后端数据库的压力,提升整体吞吐量,这在流量大的应用里几乎是标配了。

Redis 缓存与 Java 集成应用实战 (全网最新颖教程)

解决方案

在 Java 应用中集成 Redis 缓存,最直接也是最常用的方式就是通过 Spring Data Redis。我们以一个典型的用户数据查询场景为例,看看如何让 Redis 介入。

首先,你的 Spring Boot 项目需要引入 spring-boot-starter-data-redis 依赖。

立即学习Java免费学习笔记(深入)”;

Redis 缓存与 Java 集成应用实战 (全网最新颖教程)

    org.springframework.boot
    spring-boot-starter-data-redis

接着,在 application.ymlapplication.properties 里配置 Redis 连接信息:

spring:
  redis:
    host: localhost # 或者你的 Redis 服务器地址
    port: 6379
    password: # 如果有密码
    database: 0 # 默认数据库

核心的集成思路是“Cache-Aside”模式,也就是我们常说的“旁路缓存”。应用程序代码自己决定何时从缓存读,何时写回缓存,以及何时更新数据库。

Redis 缓存与 Java 集成应用实战 (全网最新颖教程)

假设我们有一个 UserService,负责获取用户信息:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;

@Service
public class UserService {

    private final RedisTemplate redisTemplate;
    private final UserRepository userRepository; // 假设你有一个用户数据的 JPA Repository

    @Autowired
    public UserService(RedisTemplate redisTemplate, UserRepository userRepository) {
        this.redisTemplate = redisTemplate;
        this.userRepository = userRepository;
    }

    /**
     * 根据用户ID获取用户信息,优先从Redis缓存获取
     */
    public User getUserById(Long userId) {
        String cacheKey = "user:" + userId;
        User user = (User) redisTemplate.opsForValue().get(cacheKey);

        if (user != null) {
            System.out.println("Cache hit for user ID: " + userId);
            return user; // 缓存命中,直接返回
        }

        // 缓存未命中,查询数据库
        System.out.println("Cache miss for user ID: " + userId + ", querying database...");
        user = userRepository.findById(userId).orElse(null); // 假设通过JPA从数据库获取

        if (user != null) {
            // 将查询到的数据放入缓存,并设置过期时间
            redisTemplate.opsForValue().set(cacheKey, user, 10, TimeUnit.MINUTES); // 缓存10分钟
            System.out.println("User ID: " + userId + " fetched from DB and cached.");
        } else {
            // 如果数据库中也不存在,为了防止缓存穿透,也可以考虑缓存一个空值(短时间)
            redisTemplate.opsForValue().set(cacheKey, "null", 1, TimeUnit.MINUTES);
            System.out.println("User ID: " + userId + " not found, caching 'null' to prevent penetration.");
        }
        return user;
    }

    /**
     * 更新用户信息后,同步更新缓存
     */
    public User updateUser(User user) {
        User updatedUser = userRepository.save(user); // 更新数据库
        String cacheKey = "user:" + updatedUser.getId();
        redisTemplate.opsForValue().set(cacheKey, updatedUser, 10, TimeUnit.MINUTES); // 更新缓存
        System.out.println("User ID: " + updatedUser.getId() + " updated in DB and cache.");
        return updatedUser;
    }

    /**
     * 删除用户信息后,删除缓存
     */
    public void deleteUser(Long userId) {
        userRepository.deleteById(userId); // 删除数据库数据
        String cacheKey = "user:" + userId;
        redisTemplate.delete(cacheKey); // 删除缓存
        System.out.println("User ID: " + userId + " deleted from DB and cache.");
    }
}

// 假设的User实体类和UserRepository接口
// public class User implements Serializable { ... }
// public interface UserRepository extends JpaRepository { ... }

这个例子展示了最基础的“读写缓存”逻辑。实际项目中,你可能还会用到 Spring 的 @Cacheable, @CachePut, @CacheEvict 等注解,它们能大大简化这些缓存操作,让你更专注于业务逻辑,但底层原理其实是类似的。当然,注解用起来很爽,但有时候,当缓存策略变得复杂,或者需要更精细的控制时,直接操作 RedisTemplate 也是个非常灵活的选择。

Redis 在 Java 应用中常见的缓存策略有哪些?

谈到缓存策略,这可不是一刀切的事情,不同的业务场景,对缓存的要求也千差万别。

首先,最常见的,也是上面例子里用的,是旁路缓存(Cache-Aside)。这是最灵活的一种,你的应用代码自己负责维护缓存和数据库的一致性。读取时,先查缓存,没有再查数据库,然后把数据回填到缓存。写入时,先更新数据库,再更新(或删除)缓存。这种模式控制力强,但需要开发者手动处理缓存逻辑,代码会多一些。我个人觉得,对于那些缓存更新频率不高,或者需要精确控制缓存生命周期的场景,旁路缓存是个不错的选择。

然后是读写穿透(Read-Through / Write-Through)。这个通常由缓存框架(比如 Spring Cache)来帮你实现。当应用从缓存中读取数据时,如果缓存中没有,缓存框架会负责从数据源(比如数据库)加载数据并放入缓存,再返回给应用。写入时,数据会先写入缓存,然后缓存框架再负责将数据同步到数据源。这种模式对应用代码来说更透明,你只需要声明式地配置好缓存规则就行。用 Spring Cache 的 @Cacheable@CachePut 注解就是典型的读写穿透。它简化了开发,但一旦出了问题,排查起来可能需要深入了解框架的实现细节。

还有一种是写回(Write-Back)。这种模式下,数据会先写入缓存,然后异步地写入到数据源。它能显著提升写入性能,因为应用不需要等待数据写入数据库。但风险也很明显:如果缓存服务在数据同步到数据库之前宕机了,那么这部分数据可能就丢失了。所以,除非你的应用对数据一致性要求不高,或者有非常完善的持久化机制来弥补,否则一般不推荐在核心业务中使用。比如日志系统、消息队列的缓冲层,可能会考虑这种模式。

最后,提一下刷新预加载(Refresh-Ahead)。这是一种更高级的优化策略,尤其适用于那些热点数据。它会在缓存项即将过期之前,就提前异步地去加载最新数据并更新缓存。这样,当用户请求时,数据始终是新鲜的,避免了缓存失效时瞬间的“击穿”问题。实现起来会复杂一些,通常需要定时任务或者消息队列配合。

PictoGraphic
PictoGraphic

AI驱动的矢量插图库和插图生成平台

下载

选择哪种策略,很大程度上取决于你对数据一致性、性能和开发复杂度的权衡。

如何选择合适的 Java Redis 客户端?Jedis 还是 Lettuce?

在 Java 生态里,提到 Redis 客户端,绕不开的就是 Jedis 和 Lettuce。它们都是非常优秀的库,但设计理念和适用场景却有些不同。

Jedis Jedis 是一个比较老牌、成熟的客户端。它的设计哲学比较直观,基本上是同步阻塞 I/O 的。这意味着当你执行一个 Redis 命令时,线程会等待 Redis 服务器的响应。 它的优点是:

  • 简单直观: API 映射 Redis 命令,上手快,理解成本低。
  • 成熟稳定: 经过长时间的考验,社区支持也很好。
  • 线程安全: 通过连接池(如 Apache Commons Pool)来管理连接,保证了多线程环境下的安全使用。

但缺点也很明显:

  • 阻塞 I/O: 在高并发场景下,如果连接池配置不当或者 Redis 响应慢,可能会导致线程阻塞,影响吞吐量。每个请求都需要一个独立的连接,连接数可能会成为瓶颈。

Lettuce Lettuce 是一个相对较新的客户端,它基于 Netty 框架,实现了非阻塞 I/O (NIO)。这意味着它可以在一个连接上处理多个并发请求,而不需要为每个请求都分配一个独立的线程。 它的优点是:

  • 非阻塞 I/O: 性能更高,在高并发场景下表现优异,能够更好地利用系统资源。
  • 响应式编程友好: 天然支持 Reactive Streams,非常适合 Spring WebFlux 这类响应式编程框架。
  • 连接复用: 能够高效地复用连接,减少连接创建和销毁的开销。
  • 集群支持: 对 Redis Cluster、Sentinel 等高级特性支持更好。

缺点嘛,可能就是:

  • 学习曲线: 对于习惯了同步编程的开发者来说,非阻塞和响应式编程的思维方式可能需要适应。
  • 配置相对复杂: 相较于 Jedis,其配置和使用可能需要更多一点的理解。

我的看法: 在绝大多数新的 Spring Boot 项目中,尤其是 Spring Boot 2.x 以后,Spring Data Redis 默认集成的就是 Lettuce。如果你正在构建一个全新的、需要处理高并发、追求极致性能的应用,或者你的技术栈偏向响应式编程,那么 Lettuce 绝对是首选。它的非阻塞特性在高吞吐量场景下能带来显著优势。

而如果你的项目是老项目,或者对性能要求没那么极致,更看重开发效率和简单性,并且已经习惯了 Jedis 的 API,那么继续使用 Jedis 也未尝不可。但即便如此,我也建议在 Jedis 上配置好连接池,避免一些不必要的坑。

总的来说,新项目我无脑推荐 Lettuce。

Redis 缓存穿透、雪崩、击穿,这些坑你踩过吗?

在实际生产环境中,光是把 Redis 集成进去远远不够,你还得知道怎么避开那些常见的“坑”。缓存这东西,用好了是神器,用不好就是定时炸弹。最典型的三个问题就是缓存穿透、雪崩和击穿。

1. 缓存穿透 (Cache Penetration) 这玩意儿,说白了就是查询一个根本不存在的数据,而且这个数据永远也不会存在。比如,你的系统被恶意攻击,或者程序有个 bug,总是去查 user:999999999 这种不存在的用户 ID。每次请求都会穿透缓存,直接打到数据库上。如果这种请求量非常大,数据库可能就扛不住了。

怎么防?

  • 缓存空值: 最简单有效的方法。如果数据库查询结果为空,也把这个空结果(比如一个特定的“null”字符串或空对象)缓存起来,并设置一个很短的过期时间。这样下次再查这个不存在的数据时,就能直接从缓存返回,避免打到数据库。当然,过期时间要短,不然缓存里一堆空值也挺占内存的。
  • 布隆过滤器 (Bloom Filter): 对于那种海量请求查询不存在数据的场景,布隆过滤器是个更高级的方案。它是一个空间效率极高的概率型数据结构,可以判断一个元素是否“可能存在”或“一定不存在”。请求过来,先通过布隆过滤器判断,如果过滤器说“一定不存在”,那直接返回,连 Redis 都不用查。但它有个小缺点,就是存在误判率,可能会把极少数不存在的判断为存在,然后还是会穿透到 Redis 或数据库。不过这个误判率通常可以接受。

2. 缓存雪崩 (Cache Avalanche) 想象一下,你的缓存服务器突然挂了,或者大量的缓存 key 在同一时间集中失效了。这时,所有的请求都会直接涌向数据库,就像雪崩一样,数据库瞬间被压垮。这在很多大型促销活动,或者系统上线初期,缓存策略不当的时候特别容易发生。

怎么防?

  • 给缓存过期时间加随机值: 这是最直接有效的办法。不要让所有 key 都设置成固定的过期时间,比如 1 小时。可以在 1 小时的基础上,加上一个随机的几分钟,比如 expire = 60 * 60 + random(0, 300) 秒。这样,即使大量 key 同时创建,它们也不会在同一时间点失效。
  • 多级缓存: 部署多层缓存,比如本地缓存 + Redis 缓存。当 Redis 挂掉时,至少本地缓存还能顶一阵。
  • 熔断和限流: 在应用层面做一些保护措施。当数据库负载过高时,可以暂时停止部分请求,或者直接返回默认值/错误信息,避免数据库彻底崩溃。
  • 高可用 Redis 集群: 部署 Redis Sentinel 或 Redis Cluster,保证 Redis 服务本身的高可用性,减少宕机风险。

3. 缓存击穿 (Cache Breakdown) 和雪崩不同,击穿针对的是一个“热点 key”。当某个非常热门的 key,在它失效的瞬间,大量的并发请求同时涌入,这些请求都会穿透缓存,直接打到数据库上,导致数据库压力骤增。虽然只有一个 key,但因为它是热点,瞬间的并发量可能非常高。

怎么防?

  • 互斥锁 (Mutex Lock): 当一个热点 key 失效时,第一个请求去查询数据库并重建缓存。这时,可以用分布式锁(比如基于 Redis 的 Redisson 锁)来保证只有一个线程去查询数据库,其他线程则等待这个线程查询并回填缓存后,再从缓存中获取数据。

      // 伪代码
      String lockKey = "lock:" + cacheKey;
      if (redisLock.tryLock(lockKey, 10, TimeUnit.SECONDS)) { // 尝试获取锁
          try {
              // 再次检查缓存,可能在等待锁的过程中其他线程已经重建了
              Object data = redisTemplate.opsForValue().get(cacheKey);
              if (data != null) return data;
    
              // 缓存确实没有,查询数据库并重建
              data = queryFromDB();
              redisTemplate.opsForValue().set(cacheKey, data, ...);
              return data;
          } finally {
              redisLock.unlock(lockKey); // 释放锁
          }
      } else {
          // 获取锁失败,说明有其他线程正在重建,等待一小会儿重试,或者从数据库读取(降级)
          Thread.sleep(50); // 简单等待
          return (Object) redisTemplate.opsForValue().get(cacheKey); // 再次尝试从缓存获取
      }
  • 永不过期: 对于一些绝对的热点数据,可以考虑将其设置为永不过期(或设置一个非常长的过期时间),然后通过异步的方式(比如定时任务或消息队列)去更新缓存中的数据。这样,即使数据有更新,也不会出现缓存失效导致击穿的情况。

这些问题,说实话,都是我在实际项目中真真切切踩过的坑。理解它们,并在设计之初就考虑好相应的防御策略,能让你在后续的运维中省去不少麻烦。缓存虽好,但用起来也得小心翼翼。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
spring框架介绍
spring框架介绍

本专题整合了spring框架相关内容,想了解更多详细内容,请阅读专题下面的文章。

114

2025.08.06

Java Spring Security 与认证授权
Java Spring Security 与认证授权

本专题系统讲解 Java Spring Security 框架在认证与授权中的应用,涵盖用户身份验证、权限控制、JWT与OAuth2实现、跨站请求伪造(CSRF)防护、会话管理与安全漏洞防范。通过实际项目案例,帮助学习者掌握如何 使用 Spring Security 实现高安全性认证与授权机制,提升 Web 应用的安全性与用户数据保护。

29

2026.01.26

spring boot框架优点
spring boot框架优点

spring boot框架的优点有简化配置、快速开发、内嵌服务器、微服务支持、自动化测试和生态系统支持。本专题为大家提供spring boot相关的文章、下载、课程内容,供大家免费下载体验。

135

2023.09.05

spring框架有哪些
spring框架有哪些

spring框架有Spring Core、Spring MVC、Spring Data、Spring Security、Spring AOP和Spring Boot。详细介绍:1、Spring Core,通过将对象的创建和依赖关系的管理交给容器来实现,从而降低了组件之间的耦合度;2、Spring MVC,提供基于模型-视图-控制器的架构,用于开发灵活和可扩展的Web应用程序等。

390

2023.10.12

Java Spring Boot开发
Java Spring Boot开发

本专题围绕 Java 主流开发框架 Spring Boot 展开,系统讲解依赖注入、配置管理、数据访问、RESTful API、微服务架构与安全认证等核心知识,并通过电商平台、博客系统与企业管理系统等项目实战,帮助学员掌握使用 Spring Boot 快速开发高效、稳定的企业级应用。

70

2025.08.19

Java Spring Boot 4更新教程_Java Spring Boot 4有哪些新特性
Java Spring Boot 4更新教程_Java Spring Boot 4有哪些新特性

Spring Boot 是一个基于 Spring 框架的 Java 开发框架,它通过 约定优于配置的原则,大幅简化了 Spring 应用的初始搭建、配置和开发过程,让开发者可以快速构建独立的、生产级别的 Spring 应用,无需繁琐的样板配置,通常集成嵌入式服务器(如 Tomcat),提供“开箱即用”的体验,是构建微服务和 Web 应用的流行工具。

34

2025.12.22

Java Spring Boot 微服务实战
Java Spring Boot 微服务实战

本专题深入讲解 Java Spring Boot 在微服务架构中的应用,内容涵盖服务注册与发现、REST API开发、配置中心、负载均衡、熔断与限流、日志与监控。通过实际项目案例(如电商订单系统),帮助开发者掌握 从单体应用迁移到高可用微服务系统的完整流程与实战能力。

135

2025.12.24

什么是分布式
什么是分布式

分布式是一种计算和数据处理的方式,将计算任务或数据分散到多个计算机或节点中进行处理。本专题为大家提供分布式相关的文章、下载、课程内容,供大家免费下载体验。

328

2023.08.11

俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

158

2026.01.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
进程与SOCKET
进程与SOCKET

共6课时 | 0.4万人学习

Redis+MySQL数据库面试教程
Redis+MySQL数据库面试教程

共72课时 | 6.5万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号