Spring Boot 集成 Redis 全方位详解

文章目录

  • [一、Redis 与 Spring Boot 集成概述](#一、Redis 与 Spring Boot 集成概述)
    • [1.1 Redis 简介](#1.1 Redis 简介)
    • [1.2 Spring Boot 集成 Redis 的优势](#1.2 Spring Boot 集成 Redis 的优势)
    • [1.3 应用场景](#1.3 应用场景)
  • 二、环境准备与搭建
    • [2.1 环境要求](#2.1 环境要求)
    • [2.2 Redis 安装与配置](#2.2 Redis 安装与配置)
      • [2.2.1 Windows 系统安装 Redis](#2.2.1 Windows 系统安装 Redis)
      • [2.2.2 Linux 系统安装 Redis](#2.2.2 Linux 系统安装 Redis)
      • [2.2.3 Redis 基本配置](#2.2.3 Redis 基本配置)
    • [2.3 创建 Spring Boot 项目](#2.3 创建 Spring Boot 项目)
      • [2.3.1 使用 Spring Initializr 创建项目](#2.3.1 使用 Spring Initializr 创建项目)
      • [2.3.2 手动配置 pom.xml](#2.3.2 手动配置 pom.xml)
  • [三、Redis 核心配置](#三、Redis 核心配置)
    • [3.1 基本配置](#3.1 基本配置)
    • [3.2 集群配置](#3.2 集群配置)
    • [3.3 哨兵模式配置](#3.3 哨兵模式配置)
    • [3.4 自定义 RedisTemplate 配置](#3.4 自定义 RedisTemplate 配置)
    • [3.5 序列化器详解](#3.5 序列化器详解)
  • [四、Redis 数据结构操作](#四、Redis 数据结构操作)
    • [4.1 StringRedisTemplate 操作](#4.1 StringRedisTemplate 操作)
    • [4.2 RedisTemplate 操作对象](#4.2 RedisTemplate 操作对象)
    • [4.3 操作 Hash 类型](#4.3 操作 Hash 类型)
    • [4.4 操作 List 类型](#4.4 操作 List 类型)
    • [4.5 操作 Set 类型](#4.5 操作 Set 类型)
    • [4.6 操作 ZSet 类型](#4.6 操作 ZSet 类型)
  • [五、Spring 缓存抽象集成](#五、Spring 缓存抽象集成)
    • [5.1 开启缓存支持](#5.1 开启缓存支持)
    • [5.2 配置缓存管理器](#5.2 配置缓存管理器)
    • [5.3 缓存注解详解](#5.3 缓存注解详解)
    • [5.4 缓存注解使用示例](#5.4 缓存注解使用示例)
    • [5.5 SpEL 表达式在缓存中的应用](#5.5 SpEL 表达式在缓存中的应用)
  • [六、Redis 高级特性](#六、Redis 高级特性)
    • [6.1 分布式锁](#6.1 分布式锁)
    • [6.2 发布 / 订阅](#6.2 发布 / 订阅)
      • [6.2.1 消息发布者](#6.2.1 消息发布者)
      • [6.2.2 消息订阅者](#6.2.2 消息订阅者)
      • [6.2.3 配置消息监听容器](#6.2.3 配置消息监听容器)
    • [6.3 管道操作](#6.3 管道操作)
    • [6.4 事务](#6.4 事务)
  • 七、实战案例
    • [7.1 缓存用户信息](#7.1 缓存用户信息)
      • [7.1.1 控制器](#7.1.1 控制器)
      • [7.1.2 测试](#7.1.2 测试)
    • [7.2 实现分布式计数器](#7.2 实现分布式计数器)
  • 八、问题排查与最佳实践
    • [8.1 常见问题及解决方案](#8.1 常见问题及解决方案)
    • [8.2 最佳实践](#8.2 最佳实践)
  • 九、总结

一、Redis 与 Spring Boot 集成概述

1.1 Redis 简介

Redis(Remote Dictionary Server)是一个开源的、高性能的键值对存储数据库,它以其出色的性能、丰富的数据结构和灵活的使用方式,在现代软件开发中占据了重要地位。

Redis 具有以下核心特性:

  • 基于内存存储,读写速度极快,单机每秒可处理数十万次操作
  • 支持多种数据结构,包括字符串(String)、哈希(Hash)、列表(List)、集合(Set)、有序集合(ZSet)等
  • 支持数据持久化,可将内存中的数据定期写入磁盘,避免数据丢失
  • 支持主从复制、哨兵模式和集群模式,保证高可用性和扩展性
  • 提供丰富的功能,如事务、Lua 脚本、发布 / 订阅、过期策略等

1.2 Spring Boot 集成 Redis 的优势

Spring Boot 作为当前流行的 Java 开发框架,简化了 Java 应用的开发配置过程。将 Spring Boot 与 Redis 集成,具有以下优势:

  • 自动配置:Spring Boot 提供了 Redis 自动配置模块,减少手动配置工作
  • 模板化操作:通过 RedisTemplate 等模板类,简化 Redis 操作
  • 缓存支持:无缝集成 Spring 缓存抽象,轻松实现数据缓存
  • 连接池管理:内置连接池支持,优化 Redis 连接性能
  • 与 Spring 生态完美融合:可与 Spring MVC、Spring Data 等组件无缝协作

1.3 应用场景

Spring Boot 集成 Redis 后,可应用于多种场景:

  • 数据缓存:减轻数据库压力,提高数据访问速度
  • 会话存储:分布式系统中共享会话信息
  • 计数器:实现访问量、下载量等计数功能
  • 分布式锁:解决分布式系统中的并发问题
  • 消息队列:基于发布 / 订阅功能实现简单的消息通信
  • 排行榜:利用 ZSet 数据结构实现实时排行榜

二、环境准备与搭建

2.1 环境要求

  • JDK:1.8 及以上版本
  • Spring Boot:2.x 或 3.x 版本(本文以 2.7.10 为例)
  • Redis:5.x 及以上版本
  • Maven:3.6 及以上版本(或 Gradle 7.0 及以上)

2.2 Redis 安装与配置

2.2.1 Windows 系统安装 Redis

  1. 访问 Redis 官方网站(https://redis.io/download)下载 Windows 版本的 Redis(可2. 通过 GitHub 上的微软维护版本获取 解压下载的压缩包到指定目录,如 D:\redis
  2. 打开命令提示符,进入 Redis 目录,执行以下命令启动 Redis 服务器:
java 复制代码
redis-server.exe redis.windows.conf
  1. 另开一个命令提示符窗口,执行以下命令启动 Redis 客户端:
java 复制代码
redis-cli.exe -h 127.0.0.1 -p 6379

2.2.2 Linux 系统安装 Redis

  1. 通过包管理器安装(以 Ubuntu 为例):
java 复制代码
sudo apt update
sudo apt install redis-server
  1. 启动 Redis 服务:
java 复制代码
sudo systemctl start redis-server
  1. 验证 Redis 是否启动成功:
java 复制代码
redis-cli ping

若返回 "PONG",则表示 Redis 启动成功。

2.2.3 Redis 基本配置

Redis 的主要配置文件为 redis.conf,常用配置项如下:

  • port 6379:默认端口号
  • bind 127.0.0.1:绑定的 IP 地址,默认只允许本地访问
  • requirepass yourpassword:设置访问密码
  • daemonize yes:以守护进程方式运行(Linux 系统)
  • maxmemory:设置 Redis 最大使用内存
  • maxmemory-policy:内存达到上限时的淘汰策略
    修改配置后,需重启 Redis 服务使配置生效。

2.3 创建 Spring Boot 项目

2.3.1 使用 Spring Initializr 创建项目

  1. 访问 https://start.spring.io/
  2. 填写项目信息:

Group:com.example

Artifact:springboot-redis-demo

Name:springboot-redis-demo

Description:Demo project for Spring Boot and Redis

Package name:com.example.redisdemo

Type:Maven

Java:8

Spring Boot:2.7.10

  1. 选择依赖:

Spring Web

Spring Data Redis

Lombok(可选,用于简化代码)

  1. 点击 "Generate" 按钮下载项目压缩包,解压后导入 IDE(如 IntelliJ IDEA 或 Eclipse)

2.3.2 手动配置 pom.xml

若手动创建项目,需在 pom.xml 中添加以下依赖:

java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.10</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>springboot-redis-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-redis-demo</name>
    <description>Demo project for Spring Boot and Redis</description>
    
    <properties>
        <java.version>1.8</java.version>
    </properties>
    
    <dependencies>
        <!-- Spring Web 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <!-- Spring Data Redis 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        
        <!-- 连接池依赖 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        
        <!-- Lombok 依赖(可选) -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        
        <!-- 测试依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

三、Redis 核心配置

3.1 基本配置

Spring Boot 提供了自动配置功能,我们只需在配置文件中添加 Redis 相关配置即可。常用的配置文件格式有两种:application.properties 和 application.yml,这里以 application.yml 为例。

在 src/main/resources 目录下创建 application.yml 文件,添加以下配置:

java 复制代码
spring:
  redis:
    # Redis 服务器地址
    host: 127.0.0.1
    # Redis 服务器端口
    port: 6379
    # Redis 服务器密码(若未设置密码,可省略)
    password: 
    # 数据库索引(0-15,默认为 0)
    database: 0
    # 连接超时时间(毫秒)
    timeout: 3000ms
    # Lettuce 连接池配置
    lettuce:
      pool:
        # 最大连接数
        max-active: 8
        # 最大空闲连接
        max-idle: 8
        # 最小空闲连接
        min-idle: 2
        # 最大等待时间(-1 表示无限制)
        max-wait: -1ms

3.2 集群配置

如果使用 Redis 集群,配置方式如下:

java 复制代码
spring:
  redis:
    password: 
    timeout: 3000ms
    cluster:
      # 集群节点列表
      nodes:
        - 192.168.1.101:6379
        - 192.168.1.102:6379
        - 192.168.1.103:6379
        - 192.168.1.104:6379
        - 192.168.1.105:6379
        - 192.168.1.106:6379
      # 最大重定向次数
      max-redirects: 3
    lettuce:
      pool:
        max-active: 16
        max-idle: 8
        min-idle: 4
        max-wait: 1000ms

3.3 哨兵模式配置

若 Redis 采用哨兵模式,配置如下:

java 复制代码
spring:
  redis:
    password: 
    timeout: 3000ms
    sentinel:
      # 主节点名称
      master: mymaster
      # 哨兵节点列表
      nodes:
        - 192.168.1.201:26379
        - 192.168.1.202:26379
        - 192.168.1.203:26379
    lettuce:
      pool:
        max-active: 16
        max-idle: 8
        min-idle: 4
        max-wait: 1000ms

3.4 自定义 RedisTemplate 配置

Spring Boot 自动配置的 RedisTemplate 使用 JdkSerializationRedisSerializer 进行序列化,这种方式会导致存储的数据带有序列化前缀,可读性差且占用空间。因此,通常需要自定义 RedisTemplate,使用 JSON 序列化方式。

创建 Redis 配置类:

java 复制代码
package com.example.redisdemo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    /**
     * 自定义 RedisTemplate,使用 JSON 序列化
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        // 创建 RedisTemplate 实例
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        
        // 设置连接工厂
        template.setConnectionFactory(factory);
        
        // 创建字符串序列化器
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        
        // 创建 JSON 序列化器
        GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
        
        // 设置 key 的序列化器
        template.setKeySerializer(stringSerializer);
        // 设置 hash key 的序列化器
        template.setHashKeySerializer(stringSerializer);
        // 设置 value 的序列化器
        template.setValueSerializer(jsonSerializer);
        // 设置 hash value 的序列化器
        template.setHashValueSerializer(jsonSerializer);
        
        // 初始化模板
        template.afterPropertiesSet();
        
        return template;
    }
}
    

上述配置中,我们使用 StringRedisSerializer 序列化 key,使用 GenericJackson2JsonRedisSerializer 序列化 value。GenericJackson2JsonRedisSerializer 基于 Jackson 库,能够将对象序列化为 JSON 格式,并且在反序列化时能够自动识别对象类型。

3.5 序列化器详解

Redis 序列化器用于将 Java 对象转换为可存储在 Redis 中的字节序列,以及将字节序列转换回 Java 对象。Spring Data Redis 提供了多种序列化器:

  1. StringRedisSerializer:将对象序列化为字符串,主要用于序列化 key
  2. JdkSerializationRedisSerializer:使用 JDK 自带的序列化机制,默认的序列化器
  3. GenericJackson2JsonRedisSerializer:使用 Jackson 库将对象序列化为 JSON 格式
  4. Jackson2JsonRedisSerializer:与 GenericJackson2JsonRedisSerializer 类似,但需要指定目标类
  5. OxmSerializer:使用 Spring O/X 映射功能进行序列化,支持 XML 格式
    各种序列化器的优缺点:

在实际开发中,推荐使用 StringRedisSerializer 作为 key 的序列化器,使用 GenericJackson2JsonRedisSerializer 或 Jackson2JsonRedisSerializer 作为 value 的序列化器。

四、Redis 数据结构操作

Spring Data Redis 提供了 RedisTemplate 和 StringRedisTemplate 两个模板类用于操作 Redis。StringRedisTemplate 是 RedisTemplate 的子类,专门用于处理字符串类型的数据。

4.1 StringRedisTemplate 操作

StringRedisTemplate 的 opsForValue() 方法返回 ValueOperations 对象,用于操作 String 类型的数据。

java 复制代码
package com.example.redisdemo.service;

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Service
public class StringRedisService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 设置字符串值
     */
    public void setString(String key, String value) {
        ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
        operations.set(key, value);
    }

    /**
     * 设置字符串值并指定过期时间
     */
    public void setStringWithExpire(String key, String value, long timeout, TimeUnit unit) {
        ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
        operations.set(key, value, timeout, unit);
    }

    /**
     * 获取字符串值
     */
    public String getString(String key) {
        ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
        return operations.get(key);
    }

    /**
     * 自增操作
     */
    public Long increment(String key, long delta) {
        ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
        return operations.increment(key, delta);
    }

    /**
     * 自减操作
     */
    public Long decrement(String key, long delta) {
        ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
        return operations.decrement(key, delta);
    }

    /**
     * 删除键
     */
    public Boolean delete(String key) {
        return stringRedisTemplate.delete(key);
    }
}
    

4.2 RedisTemplate 操作对象

RedisTemplate 可以操作各种类型的对象,下面以操作用户对象为例进行说明。

首先定义一个 User 实体类:

java 复制代码
package com.example.redisdemo.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}
    

然后创建一个服务类,使用 RedisTemplate 操作 User 对象:

java 复制代码
package com.example.redisdemo.service;

import com.example.redisdemo.entity.User;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Service
public class ObjectRedisService {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 存储用户对象
     */
    public void saveUser(String key, User user) {
        ValueOperations<String, Object> operations = redisTemplate.opsForValue();
        operations.set(key, user);
    }

    /**
     * 存储用户对象并指定过期时间
     */
    public void saveUserWithExpire(String key, User user, long timeout, TimeUnit unit) {
        ValueOperations<String, Object> operations = redisTemplate.opsForValue();
        operations.set(key, user, timeout, unit);
    }

    /**
     * 获取用户对象
     */
    public User getUser(String key) {
        ValueOperations<String, Object> operations = redisTemplate.opsForValue();
        return (User) operations.get(key);
    }

    /**
     * 删除用户对象
     */
    public Boolean deleteUser(String key) {
        return redisTemplate.delete(key);
    }
}
    

4.3 操作 Hash 类型

Hash 类型适合存储对象的多个属性,RedisTemplate 的 opsForHash() 方法返回 HashOperations 对象,用于操作 Hash 类型的数据。

java 复制代码
package com.example.redisdemo.service;

import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Map;
import java.util.Set;

@Service
public class HashRedisService {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 向 Hash 中添加一个字段
     */
    public void put(String key, String hashKey, Object value) {
        HashOperations<String, String, Object> operations = redisTemplate.opsForHash();
        operations.put(key, hashKey, value);
    }

    /**
     * 向 Hash 中添加多个字段
     */
    public void putAll(String key, Map<String, Object> map) {
        HashOperations<String, String, Object> operations = redisTemplate.opsForHash();
        operations.putAll(key, map);
    }

    /**
     * 获取 Hash 中指定字段的值
     */
    public Object get(String key, String hashKey) {
        HashOperations<String, String, Object> operations = redisTemplate.opsForHash();
        return operations.get(key, hashKey);
    }

    /**
     * 获取 Hash 中所有字段和值
     */
    public Map<String, Object> getAll(String key) {
        HashOperations<String, String, Object> operations = redisTemplate.opsForHash();
        return operations.entries(key);
    }

    /**
     * 获取 Hash 中所有字段
     */
    public Set<String> getKeys(String key) {
        HashOperations<String, String, Object> operations = redisTemplate.opsForHash();
        return operations.keys(key);
    }

    /**
     * 获取 Hash 中所有值
     */
    public Object[] getValues(String key) {
        HashOperations<String, String, Object> operations = redisTemplate.opsForHash();
        return operations.values(key).toArray();
    }

    /**
     * 删除 Hash 中指定字段
     */
    public Long delete(String key, String... hashKeys) {
        HashOperations<String, String, Object> operations = redisTemplate.opsForHash();
        return operations.delete(key, hashKeys);
    }

    /**
     * 判断 Hash 中是否存在指定字段
     */
    public Boolean hasKey(String key, String hashKey) {
        HashOperations<String, String, Object> operations = redisTemplate.opsForHash();
        return operations.hasKey(key, hashKey);
    }
}
    

4.4 操作 List 类型

List 类型是一个有序的字符串列表,RedisTemplate 的 opsForList() 方法返回 ListOperations 对象,用于操作 List 类型的数据。

java 复制代码
package com.example.redisdemo.service;

import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

@Service
public class ListRedisService {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 从列表左侧插入元素
     */
    public Long leftPush(String key, Object value) {
        ListOperations<String, Object> operations = redisTemplate.opsForList();
        return operations.leftPush(key, value);
    }

    /**
     * 从列表左侧插入多个元素
     */
    public Long leftPushAll(String key, Object... values) {
        ListOperations<String, Object> operations = redisTemplate.opsForList();
        return operations.leftPushAll(key, values);
    }

    /**
     * 从列表右侧插入元素
     */
    public Long rightPush(String key, Object value) {
        ListOperations<String, Object> operations = redisTemplate.opsForList();
        return operations.rightPush(key, value);
    }

    /**
     * 从列表右侧插入多个元素
     */
    public Long rightPushAll(String key, Object... values) {
        ListOperations<String, Object> operations = redisTemplate.opsForList();
        return operations.rightPushAll(key, values);
    }

    /**
     * 获取列表指定范围内的元素
     */
    public List<Object> range(String key, long start, long end) {
        ListOperations<String, Object> operations = redisTemplate.opsForList();
        return operations.range(key, start, end);
    }

    /**
     * 获取列表长度
     */
    public Long size(String key) {
        ListOperations<String, Object> operations = redisTemplate.opsForList();
        return operations.size(key);
    }

    /**
     * 从列表左侧弹出元素
     */
    public Object leftPop(String key) {
        ListOperations<String, Object> operations = redisTemplate.opsForList();
        return operations.leftPop(key);
    }

    /**
     * 从列表右侧弹出元素
     */
    public Object rightPop(String key) {
        ListOperations<String, Object> operations = redisTemplate.opsForList();
        return operations.rightPop(key);
    }

    /**
     * 根据索引设置列表元素的值
     */
    public void set(String key, long index, Object value) {
        ListOperations<String, Object> operations = redisTemplate.opsForList();
        operations.set(key, index, value);
    }

    /**
     * 删除列表中指定次数的元素
     */
    public Long remove(String key, long count, Object value) {
        ListOperations<String, Object> operations = redisTemplate.opsForList();
        return operations.remove(key, count, value);
    }
}
    

4.5 操作 Set 类型

Set 类型是一个无序的字符串集合,不允许重复元素,RedisTemplate 的 opsForSet() 方法返回 SetOperations 对象,用于操作 Set 类型的数据。

java 复制代码
package com.example.redisdemo.service;

import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Set;

@Service
public class SetRedisService {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 向集合中添加元素
     */
    public Long add(String key, Object... values) {
        SetOperations<String, Object> operations = redisTemplate.opsForSet();
        return operations.add(key, values);
    }

    /**
     * 获取集合中的所有元素
     */
    public Set<Object> members(String key) {
        SetOperations<String, Object> operations = redisTemplate.opsForSet();
        return operations.members(key);
    }

    /**
     * 判断元素是否在集合中
     */
    public Boolean isMember(String key, Object value) {
        SetOperations<String, Object> operations = redisTemplate.opsForSet();
        return operations.isMember(key, value);
    }

    /**
     * 获取集合的大小
     */
    public Long size(String key) {
        SetOperations<String, Object> operations = redisTemplate.opsForSet();
        return operations.size(key);
    }

    /**
     * 从集合中移除元素
     */
    public Long remove(String key, Object... values) {
        SetOperations<String, Object> operations = redisTemplate.opsForSet();
        return operations.remove(key, values);
    }

    /**
     * 随机从集合中弹出一个元素
     */
    public Object pop(String key) {
        SetOperations<String, Object> operations = redisTemplate.opsForSet();
        return operations.pop(key);
    }

    /**
     * 计算两个集合的交集
     */
    public Set<Object> intersect(String key1, String key2) {
        SetOperations<String, Object> operations = redisTemplate.opsForSet();
        return operations.intersect(key1, key2);
    }

    /**
     * 计算两个集合的并集
     */
    public Set<Object> union(String key1, String key2) {
        SetOperations<String, Object> operations = redisTemplate.opsForSet();
        return operations.union(key1, key2);
    }

    /**
     * 计算两个集合的差集
     */
    public Set<Object> difference(String key1, String key2) {
        SetOperations<String, Object> operations = redisTemplate.opsForSet();
        return operations.difference(key1, key2);
    }
}
    

4.6 操作 ZSet 类型

ZSet 类型是一个有序的字符串集合,每个元素都关联一个分数(score),RedisTemplate 的 opsForZSet() 方法返回 ZSetOperations 对象,用于操作 ZSet 类型的数据。

java 复制代码
package com.example.redisdemo.service;

import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Set;

@Service
public class ZSetRedisService {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 向有序集合中添加元素
     */
    public Boolean add(String key, Object value, double score) {
        ZSetOperations<String, Object> operations = redisTemplate.opsForZSet();
        return operations.add(key, value, score);
    }

    /**
     * 向有序集合中添加多个元素
     */
    public Long add(String key, Set<ZSetOperations.TypedTuple<Object>> tuples) {
        ZSetOperations<String, Object> operations = redisTemplate.opsForZSet();
        return operations.add(key, tuples);
    }

    /**
     * 获取有序集合中元素的分数
     */
    public Double score(String key, Object value) {
        ZSetOperations<String, Object> operations = redisTemplate.opsForZSet();
        return operations.score(key, value);
    }

    /**
     * 增加有序集合中元素的分数
     */
    public Double incrementScore(String key, Object value, double delta) {
        ZSetOperations<String, Object> operations = redisTemplate.opsForZSet();
        return operations.incrementScore(key, value, delta);
    }

    /**
     * 获取有序集合的大小
     */
    public Long size(String key) {
        ZSetOperations<String, Object> operations = redisTemplate.opsForZSet();
        return operations.size(key);
    }

    /**
     * 移除有序集合中的元素
     */
    public Long remove(String key, Object... values) {
        ZSetOperations<String, Object> operations = redisTemplate.opsForZSet();
        return operations.remove(key, values);
    }

    /**
     * 获取有序集合中指定排名范围的元素(按分数升序)
     */
    public Set<Object> range(String key, long start, long end) {
        ZSetOperations<String, Object> operations = redisTemplate.opsForZSet();
        return operations.range(key, start, end);
    }

    /**
     * 获取有序集合中指定分数范围的元素(按分数升序)
     */
    public Set<Object> rangeByScore(String key, double min, double max) {
        ZSetOperations<String, Object> operations = redisTemplate.opsForZSet();
        return operations.rangeByScore(key, min, max);
    }

    /**
     * 获取元素在有序集合中的排名(按分数升序)
     */
    public Long rank(String key, Object value) {
        ZSetOperations<String, Object> operations = redisTemplate.opsForZSet();
        return operations.rank(key, value);
    }

    /**
     * 获取元素在有序集合中的排名(按分数降序)
     */
    public Long reverseRank(String key, Object value) {
        ZSetOperations<String, Object> operations = redisTemplate.opsForZSet();
        return operations.reverseRank(key, value);
    }
}
    

五、Spring 缓存抽象集成

Spring 提供了缓存抽象,可以通过注解的方式轻松实现数据缓存功能。Spring Boot 与 Redis 集成后,可以将 Redis 作为缓存的存储介质。

5.1 开启缓存支持

要使用 Spring 缓存抽象,需要在配置类或启动类上添加 @EnableCaching 注解开启缓存支持。

java 复制代码
package com.example.redisdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@EnableCaching // 开启缓存支持
public class RedisDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(RedisDemoApplication.class, args);
    }
}
    

5.2 配置缓存管理器

Spring Boot 会自动配置 RedisCacheManager,但我们可以根据需要自定义缓存管理器,设置缓存过期时间、序列化方式等。

java 复制代码
package com.example.redisdemo.config;

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableCaching
public class CacheConfig {

    /**
     * 自定义 RedisCacheManager
     */
    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
        // 全局默认缓存配置
        RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
                // 设置 key 序列化器
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new StringRedisSerializer()))
                // 设置 value 序列化器
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new GenericJackson2JsonRedisSerializer()))
                // 默认过期时间 30 分钟
                .entryTtl(Duration.ofMinutes(30))
                // 不缓存 null 值
                .disableCachingNullValues();

        // 针对不同缓存名称设置不同的过期时间
        Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
        // 用户缓存过期时间 1 小时
        configMap.put("userCache", defaultConfig.entryTtl(Duration.ofHours(1)));
        // 商品缓存过期时间 10 分钟
        configMap.put("productCache", defaultConfig.entryTtl(Duration.ofMinutes(10)));
        // 订单缓存过期时间 5 分钟
        configMap.put("orderCache", defaultConfig.entryTtl(Duration.ofMinutes(5)));

        // 创建缓存管理器
        return RedisCacheManager.builder(factory)
                .cacheDefaults(defaultConfig) // 设置默认缓存配置
                .withInitialCacheConfigurations(configMap) // 设置自定义缓存配置
                .build();
    }
}
    

5.3 缓存注解详解

Spring 缓存抽象提供了多个注解用于实现缓存功能:

  • @Cacheable:用于查询操作,方法执行前先查询缓存,如果缓存中存在,则直接返回缓存结果,不执行方法;如果缓存中不存在,则执行方法,并将方法返回值存入缓存。
    主要属性:
    • value/cacheNames:缓存名称,必填,用于指定缓存的存储位置
    • key:缓存的 key,可以使用 SpEL 表达式,默认为方法参数组合
    • condition:缓存条件,满足条件才进行缓存
    • unless:排除条件,满足条件则不进行缓存
  • @CachePut:用于更新操作,方法执行后将返回值存入缓存,无论缓存中是否已存在该 key。
    主要属性与 @Cacheable 相同。
  • @CacheEvict:用于删除操作,方法执行后删除缓存中的指定数据。
    主要属性:
    • value/cacheNames:缓存名称,必填
    • key:要删除的缓存 key
    • allEntries:是否删除缓存中的所有数据,默认为 false
    • beforeInvocation:是否在方法执行前删除缓存,默认为 false
  • @Caching:用于组合多个缓存注解,可以同时指定 @Cacheable、@CachePut 和 @CacheEvict。
  • @CacheConfig:用于类级别,统一配置该类中所有缓存注解的公共属性,如 value、keyGenerator 等。

5.4 缓存注解使用示例

下面以用户服务为例,展示缓存注解的使用:

java 复制代码
package com.example.redisdemo.service;

import com.example.redisdemo.entity.User;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class UserCacheService {

    // 模拟数据库存储用户信息
    private static final Map<Long, User> userMap = new HashMap<>();

    static {
        // 初始化一些用户数据
        userMap.put(1L, new User(1L, "张三", 20, "zhangsan@example.com"));
        userMap.put(2L, new User(2L, "李四", 25, "lisi@example.com"));
        userMap.put(3L, new User(3L, "王五", 30, "wangwu@example.com"));
    }

    /**
     * 根据 ID 查询用户
     * 使用 @Cacheable 注解,缓存名称为 userCache,key 为 "user:" + id
     */
    @Cacheable(value = "userCache", key = "'user:' + #id")
    public User getUserById(Long id) {
        System.out.println("从数据库查询用户,id=" + id);
        return userMap.get(id);
    }

    /**
     * 添加用户
     * 使用 @CachePut 注解,将新添加的用户存入缓存
     */
    @CachePut(value = "userCache", key = "'user:' + #result.id")
    public User addUser(User user) {
        System.out.println("向数据库添加用户,user=" + user);
        userMap.put(user.getId(), user);
        return user;
    }

    /**
     * 更新用户
     * 使用 @CachePut 注解,更新缓存中的用户信息
     */
    @CachePut(value = "userCache", key = "'user:' + #user.id")
    public User updateUser(User user) {
        System.out.println("更新数据库中的用户,user=" + user);
        userMap.put(user.getId(), user);
        return user;
    }

    /**
     * 删除用户
     * 使用 @CacheEvict 注解,删除缓存中的用户信息
     */
    @CacheEvict(value = "userCache", key = "'user:' + #id")
    public void deleteUser(Long id) {
        System.out.println("从数据库删除用户,id=" + id);
        userMap.remove(id);
    }

    /**
     * 清空所有用户缓存
     * 使用 @CacheEvict 注解,allEntries = true 表示删除缓存中的所有数据
     */
    @CacheEvict(value = "userCache", allEntries = true)
    public void clearUserCache() {
        System.out.println("清空所有用户缓存");
    }
}
    

5.5 SpEL 表达式在缓存中的应用

在缓存注解中,key、condition、unless 等属性支持使用 SpEL 表达式,使缓存配置更加灵活。

常用的 SpEL 表达式:

示例:

java 复制代码
// 使用方法参数作为 key
@Cacheable(value = "userCache", key = "#id")
public User getUserById(Long id) { ... }

// 使用方法参数的属性作为 key
@Cacheable(value = "userCache", key = "#user.id")
public User getUserByUser(User user) { ... }

// 组合多个参数作为 key
@Cacheable(value = "userCache", key = "#name + ':' + #age")
public List<User> getUsersByNameAndAge(String name, Integer age) { ... }

// 根据条件进行缓存
@Cacheable(value = "userCache", key = "#id", condition = "#id > 10")
public User getUserById(Long id) { ... }

// 根据返回值决定是否缓存
@Cacheable(value = "userCache", key = "#id", unless = "#result == null")
public User getUserById(Long id) { ... }

六、Redis 高级特性

6.1 分布式锁

在分布式系统中,多个进程可能同时操作共享资源,为了保证数据一致性,需要使用分布式锁。Redis 可以通过 setIfAbsent 方法(即 SET NX 命令)实现分布式锁。

java 复制代码
package com.example.redisdemo.service;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Service
public class RedisDistributedLockService {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    // 锁的前缀
    private static final String LOCK_PREFIX = "lock:";

    /**
     * 获取分布式锁
     *
     * @param lockKey 锁的 key
     * @param value 锁的 value,用于标识锁的持有者
     * @param expireTime 锁的过期时间
     * @param unit 时间单位
     * @return 是否获取到锁
     */
    public boolean tryLock(String lockKey, String value, long expireTime, TimeUnit unit) {
        String key = LOCK_PREFIX + lockKey;
        // 使用 setIfAbsent 方法实现分布式锁,该方法是原子操作
        return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, unit));
    }

    /**
     * 释放分布式锁
     *
     * @param lockKey 锁的 key
     * @param value 锁的 value,用于验证是否为锁的持有者
     */
    public void unlock(String lockKey, String value) {
        String key = LOCK_PREFIX + lockKey;
        try {
            // 获取锁的 value
            Object currentValue = redisTemplate.opsForValue().get(key);
            // 验证是否为锁的持有者
            if (currentValue != null && currentValue.equals(value)) {
                // 释放锁
                redisTemplate.delete(key);
            }
        } catch (Exception e) {
            // 处理异常
            e.printStackTrace();
        }
    }
}
    

上述实现存在一个问题:如果持有锁的进程在释放锁之前崩溃,可能导致锁无法释放。为了解决这个问题,可以给锁设置过期时间,确保锁最终会被释放。

更完善的分布式锁实现可以使用 Redisson 框架,它提供了更可靠的分布式锁实现,支持自动续期、公平锁等功能。

6.2 发布 / 订阅

Redis 提供了发布 / 订阅功能,可以实现简单的消息通信。Spring Data Redis 对 Redis 的发布 / 订阅功能进行了封装,使用起来更加方便。

6.2.1 消息发布者

java 复制代码
package com.example.redisdemo.service;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class RedisMessagePublisher {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 发布消息
     *
     * @param channel 频道
     * @param message 消息内容
     */
    public void publish(String channel, Object message) {
        redisTemplate.convertAndSend(channel, message);
    }
}
    

6.2.2 消息订阅者

java 复制代码
package com.example.redisdemo.service;

import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.stereotype.Service;

@Service
public class RedisMessageSubscriber implements MessageListener {

    /**
     * 接收消息
     *
     * @param message 消息
     * @param pattern 模式
     */
    @Override
    public void onMessage(Message message, byte[] pattern) {
        // 获取消息内容
        String content = new String(message.getBody());
        // 获取频道
        String channel = new String(message.getChannel());
        // 处理消息
        System.out.println("收到来自频道 " + channel + " 的消息:" + content);
    }
}
    

6.2.3 配置消息监听容器

java 复制代码
package com.example.redisdemo.config;

import com.example.redisdemo.service.RedisMessageSubscriber;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;

@Configuration
public class RedisMessageConfig {

    /**
     * 配置消息监听适配器
     */
    @Bean
    public MessageListenerAdapter messageListenerAdapter(RedisMessageSubscriber subscriber) {
        return new MessageListenerAdapter(subscriber);
    }

    /**
     * 配置消息监听容器
     */
    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer(
            RedisConnectionFactory factory, MessageListenerAdapter adapter) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(factory);
        // 订阅 "news" 频道
        container.addMessageListener(adapter, new PatternTopic("news"));
        // 可以订阅多个频道
        container.addMessageListener(adapter, new PatternTopic("notice"));
        return container;
    }
}
    

6.3 管道操作

Redis 管道(Pipeline)允许客户端一次性发送多个命令,减少网络往返次数,提高批量操作的效率。

java 复制代码
package com.example.redisdemo.service;

import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

@Service
public class RedisPipelineService {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 批量设置键值对
     *
     * @param keys 键列表
     * @param values 值列表
     * @return 操作结果
     */
    public List<Object> batchSet(List<String> keys, List<Object> values) {
        if (keys.size() != values.size()) {
            throw new IllegalArgumentException("keys 和 values 的大小必须相等");
        }

        // 使用管道进行批量操作
        return redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            for (int i = 0; i < keys.size(); i++) {
                String key = keys.get(i);
                Object value = values.get(i);
                // 序列化 key 和 value
                byte[] keyBytes = redisTemplate.getKeySerializer().serialize(key);
                byte[] valueBytes = redisTemplate.getValueSerializer().serialize(value);
                // 设置键值对
                connection.set(keyBytes, valueBytes);
            }
            return null;
        });
    }

    /**
     * 批量获取值
     *
     * @param keys 键列表
     * @return 值列表
     */
    public List<Object> batchGet(List<String> keys) {
        // 使用管道进行批量操作
        return redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            for (String key : keys) {
                // 序列化 key
                byte[] keyBytes = redisTemplate.getKeySerializer().serialize(key);
                // 获取值
                connection.get(keyBytes);
            }
            return null;
        }, redisTemplate.getValueSerializer());
    }
}
    

6.4 事务

Redis 支持事务,可以将多个命令打包成一个事务,一次性执行,要么全部执行,要么全部不执行。

java 复制代码
package com.example.redisdemo.service;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

@Service
public class RedisTransactionService {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 执行 Redis 事务
     */
    public List<Object> executeTransaction() {
        // 使用 SessionCallback 执行事务
        return redisTemplate.execute((SessionCallback<List<Object>>) session -> {
            // 开启事务
            session.multi();

            // 向事务中添加命令
            session.opsForValue().set("name", "张三");
            session.opsForValue().set("age", 20);
            session.opsForHash().put("user", "id", 1);
            session.opsForHash().put("user", "name", "张三");

            // 执行事务
            return session.exec();
        });
    }

    /**
     * 带条件的事务(使用 Watch 命令)
     */
    public List<Object> executeTransactionWithCondition(String key, Object expectedValue) {
        // 使用 SessionCallback 执行事务
        return redisTemplate.execute((SessionCallback<List<Object>>) session -> {
            // 监视 key
            session.watch(key);

            // 获取当前值
            Object currentValue = session.opsForValue().get(key);

            // 判断当前值是否与预期值一致
            if (expectedValue.equals(currentValue)) {
                // 开启事务
                session.multi();

                // 向事务中添加命令
                session.opsForValue().set(key, "new value");
                session.opsForValue().set("otherKey", "other value");

                // 执行事务
                return session.exec();
            } else {
                // 取消监视
                session.unwatch();
                return null;
            }
        });
    }
}
    

七、实战案例

7.1 缓存用户信息

下面实现一个用户信息管理系统,使用 Redis 缓存用户信息,提高查询效率。

7.1.1 控制器

java 复制代码
package com.example.redisdemo.controller;

import com.example.redisdemo.entity.User;
import com.example.redisdemo.service.UserCacheService;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/users")
public class UserController {

    private final UserCacheService userCacheService;

    public UserController(UserCacheService userCacheService) {
        this.userCacheService = userCacheService;
    }

    /**
     * 根据 ID 查询用户
     */
    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        return userCacheService.getUserById(id);
    }

    /**
     * 添加用户
     */
    @PostMapping
    public User addUser(@RequestBody User user) {
        return userCacheService.addUser(user);
    }

    /**
     * 更新用户
     */
    @PutMapping
    public User updateUser(@RequestBody User user) {
        return userCacheService.updateUser(user);
    }

    /**
     * 删除用户
     */
    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Long id) {
        userCacheService.deleteUser(id);
    }

    /**
     * 清空用户缓存
     */
    @DeleteMapping("/cache")
    public void clearUserCache() {
        userCacheService.clearUserCache();
    }
}
    

7.1.2 测试

  1. 启动应用后,可以使用 Postman 或 curl 等工具测试接口:
  2. 查询用户:GET http://localhost:8080/users/1
  3. 添加用户:POST http://localhost:8080/users,请求体为 JSON 格式的用户信息
  4. 更新用户:PUT http://localhost:8080/users,请求体为 JSON 格式的用户信息
  5. 删除用户:DELETE http://localhost:8080/users/1
  6. 清空缓存:DELETE http://localhost:8080/users/cache

7.2 实现分布式计数器

使用 Redis 的自增操作实现一个分布式计数器,可用于统计网站访问量、下载量等。

java 复制代码
package com.example.redisdemo.service;

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Service
public class DistributedCounterService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 增加计数器的值
     *
     * @param key 计数器 key
     * @param delta 增加的值
     * @return 增加后的值
     */
    public Long increment(String key, long delta) {
        return stringRedisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 减少计数器的值
     *
     * @param key 计数器 key
     * @param delta 减少的值
     * @return 减少后的值
     */
    public Long decrement(String key, long delta) {
        return stringRedisTemplate.opsForValue().decrement(key, delta);
    }

    /**
     * 获取计数器的值
     *
     * @param key 计数器 key
     * @return 计数器的值
     */
    public Long getCount(String key) {
        String value = stringRedisTemplate.opsForValue().get(key);
        return value == null ? 0 : Long.parseLong(value);
    }

    /**
     * 重置计数器的值
     *
     * @param key 计数器 key
     */
    public void reset(String key) {
        stringRedisTemplate.delete(key);
    }

    /**
     * 设置计数器的过期时间
     *
     * @param key 计数器 key
     * @param timeout 过期时间
     * @param unit 时间单位
     * @return 是否设置成功
     */
    public Boolean expire(String key, long timeout, TimeUnit unit) {
        return stringRedisTemplate.expire(key, timeout, unit);
    }
}
    

八、问题排查与最佳实践

8.1 常见问题及解决方案

  • 连接超时
    • 检查 Redis 服务器是否启动
    • 检查 Redis 服务器地址和端口是否正确
    • 检查防火墙是否允许连接 Redis 端口
    • 检查网络是否通畅
  • 密码错误
    • 检查 Redis 配置的密码是否正确
    • 检查应用配置中的密码是否与 Redis 配置一致
  • 序列化异常
    • 确保使用的序列化器能够正确序列化和反序列化对象
    • 对于自定义对象,确保其具有无参构造函数
    • 检查对象是否实现了序列化接口(如果使用 JDK 序列化)
  • 缓存与数据库不一致
    • 确保更新数据库后及时更新或删除缓存
    • 考虑使用事务保证缓存和数据库操作的原子性
    • 对于并发场景,可使用分布式锁保证数据一致性
  • Redis 内存溢出
    • 合理设置缓存过期时间
    • 配置 Redis 的内存淘汰策略
    • 定期清理无用缓存

8.2 最佳实践

  • 合理设置过期时间
    • 根据业务需求为缓存设置合适的过期时间,避免 Redis 内存溢出
    • 对于热点数据,可以适当延长过期时间
    • 对于变化频繁的数据,应缩短过期时间
  • 选择合适的序列化方式
    • 推荐使用 StringRedisSerializer 作为 key 的序列化器
    • 对于 value 的序列化,推荐使用 JSON 序列化器,如 GenericJackson2JsonRedisSerializer
    • 避免使用 JDK 序列化器,除非有特殊需求
  • 使用连接池
    • 配置合理的连接池参数,提高连接复用率
    • 根据业务并发量调整最大连接数、最大空闲连接数等参数
  • 缓存穿透防护
    • 对于不存在的 key,也进行缓存(缓存 null 值)
    • 使用布隆过滤器过滤不存在的 key
  • 缓存击穿防护
    • 对于热点 key,设置永不过期或延长过期时间
    • 使用互斥锁防止缓存击穿
  • 缓存雪崩防护
    • 为不同的 key 设置随机的过期时间,避免大量缓存同时过期
    • 使用熔断降级机制,当 Redis 不可用时,直接访问数据库
    • 部署 Redis 集群,提高可用性
  • 监控与告警
    • 监控 Redis 的内存使用情况、连接数、命中率等指标
    • 设置告警阈值,当指标超过阈值时及时告警
  • 数据备份与恢复
    • 配置 Redis 持久化机制(RDB 或 AOF)
    • 定期备份 Redis 数据,确保数据可恢复

九、总结

本文详细介绍了 Spring Boot 集成 Redis 的全过程,包括环境搭建、核心配置、数据结构操作、缓存注解使用、高级特性及实战案例。通过本文的学习,读者可以掌握 Spring Boot 与 Redis 集成的基本方法和最佳实践。

Redis 作为一款高性能的缓存数据库,在现代软件开发中有着广泛的应用。Spring Boot 提供了简洁的配置和丰富的模板类,使得集成 Redis 变得非常简单。在实际开发中,应根据业务需求合理使用 Redis 的各种功能,充分发挥其高性能的优势,同时注意解决缓存一致性、分布式锁等问题,确保系统的稳定运行。

随着业务的发展,还可以进一步学习 Redis 集群、哨兵模式、Redisson 框架等高级内容,以应对更高的并发和更复杂的业务场景。

相关推荐
神云瑟瑟4 小时前
spring boot拦截器获取requestBody的巨坑
java·spring boot·拦截器
Q_Q5110082854 小时前
python+django/flask在线问诊系统 医院就诊 医生推荐系统
spring boot·python·django·flask·node.js·php
xiaoye37086 小时前
Spring Boot 详细介绍
java·spring boot·后端
毕业设计制作和分享6 小时前
springboot523基于Spring Boot的大学校园生活信息平台的设计与实现
前端·vue.js·spring boot·后端·生活
FrankYoou7 小时前
Spring Boot 自动配置之 TaskExecutor
java·spring boot
计算机学姐8 小时前
基于微信小程序的智能在线预约挂号系统【2026最新】
java·vue.js·spring boot·mysql·微信小程序·小程序·tomcat
野犬寒鸦9 小时前
今日面试之快问快答:Redis篇
java·数据库·redis·后端·缓存·面试·职场和发展