告别慢查询!Spring Boot整合Redis打造高性能缓存,这篇真的够了​

一、前言

在当今的互联网应用开发中,业务性能优化是至关重要的一环。随着用户数量的增加和业务复杂度的提升,数据库的压力也越来越大,响应时间变长,用户体验随之下降。而缓存技术的出现,为解决这些问题提供了有效的途径。其中,Redis凭借其高性能、多数据结构的特性,成为缓存、分布式锁、限流等场景的首选中间件。Spring Boot作为主流的Java开发框架,通过自动配置机制简化了Redis的集成流程,让开发者无需关注复杂的底层实现,即可快速上手。

二、环境准备

2.1 安装Redis服务

Redis支持Windows、Linux、Mac多平台部署,推荐使用Docker快速搭建(避免环境配置冲突):

# 拉取Redis镜像
docker pull redis:6.2.6

# 启动容器
docker run -d -p 6379:6379 --name redis-demo redis --requirepass "123456"

# 验证连接
redis-cli -h localhost -p 6379 -a 123456

输入ping返回PONG即表示连接成功。

2.2 前置条件

确保Redis服务端已部署(本地或云服务器,推荐Redis 6.x+),并满足以下条件:

  • Redis服务正常运行,IP、端口(默认6379)、密码正确
  • 服务器防火墙开放6379端口(云服务器需配置安全组)
  • 客户端与Redis版本兼容(Jedis 3.8.0兼容Redis 6.x+,Redisson 3.25.2兼容Redis 6.x+)

三、项目依赖配置

3.1 基础依赖配置

创建Spring Boot项目(推荐2.7.x版本,兼容性更优),在pom.xml中添加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>

spring-boot-starter-data-redis包含Redis自动配置类和核心API,commons-pool2提供连接池支持,优化Redis连接性能。

3.2 客户端选型

Spring Boot 2.x+默认内置Lettuce客户端,无需额外排除依赖。完整POM依赖如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

若业务需使用Jedis客户端,需先排除默认Lettuce依赖,再引入Jedis和连接池依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <exclusions>
        <exclusion>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

对于分布式场景,推荐集成Redisson,可与Lettuce/Jedis共存,直接引入Starter实现自动配置:

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.25.2</version>
</dependency>

四、Redis连接配置

4.1 基础连接配置

在application.yml中配置Redis连接信息,覆盖默认自动配置:

spring:
  redis:
    # 连接信息
    host: localhost
    port: 6379
    password: 123456
    database: 0  # 选择第0个数据库(Redis默认16个数据库)
    
    # 连接池配置(Lettuce)
    lettuce:
      pool:
        max-active: 8      # 最大连接数
        max-idle: 8        # 最大空闲连接
        min-idle: 2        # 最小空闲连接
        max-wait: 1000ms   # 连接等待超时时间
        timeout: 5000ms    # 命令执行超时时间

数据库索引database用于隔离不同业务数据,避免键名冲突。连接池参数需根据业务压力调整,避免连接泄露或资源浪费。

4.2 集群模式配置

适用于Redis集群部署场景(至少3主3从),配置需包含所有主从节点:

spring:
  redis:
    cluster:
      nodes:
        - 192.168.1.101:6379
        - 192.168.1.102:6379
        - 192.168.1.103:6379
      max-redirects: 3  # 最大重定向次数
    password: 123456
    timeout: 3000ms

4.3 Redisson配置

Redisson需单独配置连接信息,支持单机/集群模式,且默认已集成Jackson序列化(无需额外处理Key乱码):

spring:
  redis:
    redisson:
      file: classpath:/redisson-config.yml

在src/main/resources下创建redisson-config.yml:

singleServerConfig:
  address: "redis://localhost:6379"
  password: "123456"
  database: 0
  connectionPoolSize: 64
  idleConnectionTimeout: 10000
  connectTimeout: 3000
  timeout: 3000
  codec: !<org.redisson.codec.JsonJacksonCodec> {}

五、序列化配置优化

5.1 默认序列化问题

Spring Boot默认使用JdkSerializationRedisSerializer序列化对象,存在可读性差、占用空间大的问题。推荐自定义序列化配置,使用Jackson2JsonRedisSerializer实现JSON序列化。

5.2 自定义RedisTemplate配置

创建配置类,配置RedisTemplate序列化方式和各种Operations:

@Configuration
@EnableCaching
public class RedisConfig {
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        
        // 设置Key序列化器
        template.setKeySerializer(RedisSerializer.string());
        template.setHashKeySerializer(RedisSerializer.string());
        
        // 设置Value序列化器(JSON序列化,推荐)
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        
        template.afterPropertiesSet();
        return template;
    }
    
    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(factory);
        return stringRedisTemplate;
    }
}

六、数据操作实战

6.1 字符串操作

@Service
public class RedisService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // 存入键值对(无过期时间)
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }
    
    // 存入键值对(设置过期时间)
    public void set(String key, Object value, long timeout, TimeUnit unit) {
        redisTemplate.opsForValue().set(key, value, timeout, unit);
    }
    
    // 获取值
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }
    
    // 自增操作(计数器场景)
    public Long increment(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, delta);
    }
    
    // 批量操作
    public void multiSet(Map<String, Object> map) {
        redisTemplate.opsForValue().multiSet(map);
    }
}

6.2 哈希操作

@Service
public class RedisHashService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // 存入哈希数据
    public void put(String key, String hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }
    
    // 获取单个属性
    public Object get(String key, String hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }
    
    // 获取所有属性
    public Map<Object, Object> entries(String key) {
        return redisTemplate.opsForHash().entries(key);
    }
    
    // 删除指定字段
    public Long delete(String key, Object... hashKeys) {
        return redisTemplate.opsForHash().delete(key, hashKeys);
    }
}

6.3 列表操作

@Service
public class RedisListService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // 左侧插入
    public Long leftPush(String key, Object value) {
        return redisTemplate.opsForList().leftPush(key, value);
    }
    
    // 右侧插入
    public Long rightPush(String key, Object value) {
        return redisTemplate.opsForList().rightPush(key, value);
    }
    
    // 获取列表范围
    public List<Object> range(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }
    
    // 获取列表长度
    public Long size(String key) {
        return redisTemplate.opsForList().size(key);
    }
}

6.4 集合操作

@Service
public class RedisSetService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // 添加元素
    public Long add(String key, Object... values) {
        return redisTemplate.opsForSet().add(key, values);
    }
    
    // 获取所有元素
    public Set<Object> members(String key) {
        return redisTemplate.opsForSet().members(key);
    }
    
    // 判断元素是否存在
    public Boolean isMember(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }
    
    // 获取集合大小
    public Long size(String key) {
        return redisTemplate.opsForSet().size(key);
    }
}

6.5 有序集合操作

@Service
public class RedisZSetService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // 添加元素(带分数)
    public Boolean add(String key, Object value, double score) {
        return redisTemplate.opsForZSet().add(key, value, score);
    }
    
    // 获取分数范围内的元素
    public Set<Object> rangeByScore(String key, double min, double max) {
        return redisTemplate.opsForZSet().rangeByScore(key, min, max);
    }
    
    // 获取排名
    public Long rank(String key, Object value) {
        return redisTemplate.opsForZSet().rank(key, value);
    }
    
    // 获取分数
    public Double score(String key, Object value) {
        return redisTemplate.opsForZSet().score(key, value);
    }
}

七、缓存注解使用

7.1 启用缓存支持

在启动类或配置类上添加@EnableCaching注解:

@SpringBootApplication
@EnableCaching
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

7.2 常用缓存注解

@Cacheable – 缓存查询

@Service
@CacheConfig(cacheNames = "user")
public class UserService {
    
    @Cacheable(key = "#id", unless = "#result == null")
    public User findById(Long id) {
        // 如果缓存中没有,则执行方法并将结果存入缓存
        return userRepository.findById(id).orElse(null);
    }
}

参数说明:

  • value/cacheNames:缓存名称
  • key:缓存键,支持SpEL表达式
  • condition:条件缓存,满足条件才缓存
  • unless:条件不缓存,满足条件不缓存

@CachePut – 更新缓存

@CachePut(key = "#user.id")
public User save(User user) {
    // 每次都会执行方法,并将结果更新到缓存
    return userRepository.save(user);
}

@Cacheable不同的是,@CachePut每次都会触发真实查询方法的调用。

@CacheEvict – 删除缓存

@CacheEvict(key = "#id")
public void deleteById(Long id) {
    // 删除数据库记录后,同时删除缓存
    userRepository.deleteById(id);
}

参数说明:

  • allEntries:是否清空所有缓存内容,默认为false
  • beforeInvocation:是否在方法执行前清空,默认为false

@Caching – 组合多个缓存操作

@Caching(
    put = @CachePut(key = "#user.id"),
    evict = @CacheEvict(key = "'list'", allEntries = true)
)
public User update(User user) {
    return userRepository.save(user);
}

7.3 自定义Key生成器

可以自定义缓存键生成策略:

@Configuration
public class RedisConfig {
    
    @Bean
    public KeyGenerator customKeyGenerator() {
        return (target, method, params) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(target.getClass().getName());
            sb.append(method.getName());
            for (Object obj : params) {
                sb.append(obj.toString());
            }
            return sb.toString();
        };
    }
}

使用自定义Key生成器:

@Cacheable(value = "users", keyGenerator = "customKeyGenerator")
public User getUserByEmail(String email) {
    return userRepository.findByEmail(email);
}

八、常见问题与解决方案

8.1 连接配置问题

问题现象Unable to connect to Redis server: 127.0.0.1:6379

解决方案

  1. 确保Redis服务已启动:redis-cli ping返回PONG
  2. 检查防火墙是否开放6379端口
  3. 验证配置文件的host、port、password是否正确
  4. 关闭Redis保护模式(仅测试环境):config set protected-mode no

8.2 序列化异常

问题现象SerializationException: Cannot serialize

解决方案

  1. 实体类实现Serializable接口
  2. 使用JSON序列化器替代默认JDK序列化
  3. 配置自定义RedisTemplate,指定Jackson序列化器

8.3 连接池异常

问题现象Could not get a resource from the pool

解决方案

  1. 优化连接池参数配置
  2. 检查连接泄漏问题
  3. 增加连接池最大连接数
  4. 检查Redis服务端maxclients限制

8.4 缓存穿透、雪崩、击穿

缓存穿透:查询不存在的数据,导致请求直接打到数据库

  • 解决方案:缓存空值、布隆过滤器

缓存雪崩:大量Key同时过期

  • 解决方案:设置随机过期时间、热点数据永不过期

缓存击穿:热点Key过期瞬间,大量请求涌入数据库

  • 解决方案:加互斥锁、永不过期

九、性能优化建议

  1. 连接池优化:根据业务压力调整连接池参数,避免连接泄露或资源浪费
  2. 序列化优化:使用JSON序列化替代JDK序列化,提升性能和可读性
  3. 缓存策略:采用本地缓存+Redis二级缓存架构,热点数据采用本地缓存
  4. 数据一致性:重要数据配置AOF持久化策略,确保数据安全
  5. 监控告警:通过Spring Boot Actuator监控Redis连接池状态,设置告警阈值

十、总结

Spring Boot集成Redis是一个简单而强大的技术组合,通过自动配置和丰富的API,开发者可以快速实现高性能的缓存系统。本文从环境准备、依赖配置、连接管理、序列化优化、数据操作、缓存注解、常见问题等多个维度,详细介绍了Spring Boot集成Redis的完整流程。在实际项目中,建议根据业务场景选择合适的缓存策略,并注意数据一致性和性能优化,才能构建出稳定可靠的分布式缓存系统。


作 者:南烛
链 接:https://www.itnotes.top/archives/1246
来 源:IT笔记
文章版权归作者所有,转载请注明出处!


上一篇
下一篇