2858 字
约 9 分钟
13
缓存配置

Spring Boot Redis 集成概述

Spring Boot 为 Redis 集成提供了优秀的自动配置支持,通过 Spring Data Redis 可以轻松地在 Spring Boot 应用中使用 Redis。Spring Data Redis 支持 Jedis 和 Lettuce 两种客户端,并提供了统一的编程模型。

🔧 自动配置

Spring Boot 提供开箱即用的 Redis 自动配置

📝 RedisTemplate

强大的 Redis 操作模板,支持各种数据类型

🗂️ Repository 支持

类似 JPA 的 Repository 编程模型

💾 缓存抽象

Spring Cache 抽象,注解驱动的缓存

环境准备

Maven 依赖

<!-- Spring Boot Starter -->
<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>

<!-- JSON 序列化(可选) -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

<!-- Spring Boot Web(用于示例) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

配置文件

application.ymlapplication.properties集群配置

spring:
  redis:
    # 单机配置
    host: localhost
    port: 6379
    password: # 密码(如果有)
    database: 0
    timeout: 3000ms
    
    # 连接池配置(Lettuce)
    lettuce:
      pool:
        max-active: 100    # 最大连接数
        max-idle: 20       # 最大空闲连接数
        min-idle: 5        # 最小空闲连接数
        max-wait: 3000ms   # 最大等待时间
        
    # Jedis 连接池配置(如果使用 Jedis)
    jedis:
      pool:
        max-active: 100
        max-idle: 20
        min-idle: 5
        max-wait: 3000ms

# 缓存配置
spring:
  cache:
    type: redis
    redis:
      time-to-live: 3600000  # 默认过期时间(毫秒)
      cache-null-values: false
# Redis 基本配置
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=
spring.redis.database=0
spring.redis.timeout=3000ms

# Lettuce 连接池配置
spring.redis.lettuce.pool.max-active=100
spring.redis.lettuce.pool.max-idle=20
spring.redis.lettuce.pool.min-idle=5
spring.redis.lettuce.pool.max-wait=3000ms

# 缓存配置
spring.cache.type=redis
spring.cache.redis.time-to-live=3600000
spring.cache.redis.cache-null-values=false
spring:
  redis:
    cluster:
      nodes:
        - 192.168.1.100:7000
        - 192.168.1.100:7001
        - 192.168.1.101:7000
        - 192.168.1.101:7001
        - 192.168.1.102:7000
        - 192.168.1.102:7001
      max-redirects: 3
    password: # 集群密码
    timeout: 3000ms
    
    lettuce:
      pool:
        max-active: 100
        max-idle: 20
        min-idle: 5
        max-wait: 3000ms

Redis 配置类

@Configuration
@EnableCaching
public class RedisConfig {
    
    /**
     * RedisTemplate 配置
     */
    @Bean
    public RedisTemplateredisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplatetemplate = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        
        // JSON 序列化配置
        Jackson2JsonRedisSerializerjackson2JsonRedisSerializer = 
            new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, 
            ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        
        // String 序列化配置
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        
        // key 采用 String 的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        
        // value 采用 JSON 的序列化方式
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        
        template.afterPropertiesSet();
        return template;
    }
    
    /**
     * StringRedisTemplate 配置(用于字符串操作)
     */
    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory connectionFactory) {
        return new StringRedisTemplate(connectionFactory);
    }
    
    /**
     * 缓存管理器配置
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofHours(1))  // 默认过期时间 1 小时
            .serializeKeysWith(RedisSerializationContext.SerializationPair
                .fromSerializer(new StringRedisSerializer()))
            .serializeValuesWith(RedisSerializationContext.SerializationPair
                .fromSerializer(new Jackson2JsonRedisSerializer<>(Object.class)))
            .disableCachingNullValues();
        
        return RedisCacheManager.builder(connectionFactory)
            .cacheDefaults(config)
            .build();
    }
}

RedisTemplate 使用

基础操作服务

@Service
public class RedisService {
    
    @Autowired
    private RedisTemplateredisTemplate;
    
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    // ========== 字符串操作 ==========
    
    /**
     * 设置字符串值
     */
    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);
    }
    
    /**
     * 获取字符串值(指定类型)
     */
    @SuppressWarnings("unchecked")
    publicT get(String key, Classclazz) {
        Object value = redisTemplate.opsForValue().get(key);
        return value != null ? (T) value : null;
    }
    
    /**
     * 递增
     */
    public Long increment(String key) {
        return redisTemplate.opsForValue().increment(key);
    }
    
    /**
     * 递增指定值
     */
    public Long increment(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, delta);
    }
    
    // ========== 哈希操作 ==========
    
    /**
     * 设置哈希字段
     */
    public void hSet(String key, String field, Object value) {
        redisTemplate.opsForHash().put(key, field, value);
    }
    
    /**
     * 获取哈希字段值
     */
    public Object hGet(String key, String field) {
        return redisTemplate.opsForHash().get(key, field);
    }
    
    /**
     * 获取所有哈希字段
     */
    public MaphGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }
    
    /**
     * 批量设置哈希字段
     */
    public void hMSet(String key, Mapmap) {
        redisTemplate.opsForHash().putAll(key, map);
    }
    
    // ========== 列表操作 ==========
    
    /**
     * 左侧推入
     */
    public Long lPush(String key, Object... values) {
        return redisTemplate.opsForList().leftPushAll(key, values);
    }
    
    /**
     * 右侧弹出
     */
    public Object rPop(String key) {
        return redisTemplate.opsForList().rightPop(key);
    }
    
    /**
     * 获取列表范围
     */
    public ListlRange(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }
    
    // ========== 集合操作 ==========
    
    /**
     * 添加集合成员
     */
    public Long sAdd(String key, Object... values) {
        return redisTemplate.opsForSet().add(key, values);
    }
    
    /**
     * 获取集合所有成员
     */
    public SetsMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }
    
    /**
     * 判断是否是集合成员
     */
    public Boolean sIsMember(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }
    
    // ========== 有序集合操作 ==========
    
    /**
     * 添加有序集合成员
     */
    public Boolean zAdd(String key, Object value, double score) {
        return redisTemplate.opsForZSet().add(key, value, score);
    }
    
    /**
     * 获取有序集合范围(按分数倒序)
     */
    public SetzRevRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().reverseRange(key, start, end);
    }
    
    /**
     * 获取有序集合范围(带分数)
     */
    public Set> zRevRangeWithScores(String key, long start, long end) {
        return redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);
    }
    
    // ========== 通用操作 ==========
    
    /**
     * 删除键
     */
    public Boolean delete(String key) {
        return redisTemplate.delete(key);
    }
    
    /**
     * 批量删除键
     */
    public Long delete(Collectionkeys) {
        return redisTemplate.delete(keys);
    }
    
    /**
     * 检查键是否存在
     */
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }
    
    /**
     * 设置过期时间
     */
    public Boolean expire(String key, long timeout, TimeUnit unit) {
        return redisTemplate.expire(key, timeout, unit);
    }
    
    /**
     * 获取过期时间
     */
    public Long getExpire(String key) {
        return redisTemplate.getExpire(key);
    }
}

Spring Cache 注解使用

用户服务示例

@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    /**
     * 缓存用户信息
     */
    @Cacheable(value = "users", key = "#userId")
    public User getUserById(Long userId) {
        System.out.println("从数据库查询用户: " + userId);
        return userRepository.findById(userId).orElse(null);
    }
    
    /**
     * 缓存用户列表(条件缓存)
     */
    @Cacheable(value = "userList", key = "#status", condition = "#status != null")
    public ListgetUsersByStatus(String status) {
        System.out.println("从数据库查询用户列表: " + status);
        return userRepository.findByStatus(status);
    }
    
    /**
     * 更新用户信息(更新缓存)
     */
    @CachePut(value = "users", key = "#user.id")
    public User updateUser(User user) {
        System.out.println("更新用户信息: " + user.getId());
        return userRepository.save(user);
    }
    
    /**
     * 删除用户(清除缓存)
     */
    @CacheEvict(value = "users", key = "#userId")
    public void deleteUser(Long userId) {
        System.out.println("删除用户: " + userId);
        userRepository.deleteById(userId);
    }
    
    /**
     * 清除所有用户缓存
     */
    @CacheEvict(value = {"users", "userList"}, allEntries = true)
    public void clearAllUserCache() {
        System.out.println("清除所有用户缓存");
    }
    
    /**
     * 复杂缓存逻辑
     */
    @Caching(
        cacheable = @Cacheable(value = "userProfile", key = "#userId"),
        put = @CachePut(value = "users", key = "#userId")
    )
    public User getUserProfile(Long userId) {
        User user = userRepository.findById(userId).orElse(null);
        if (user != null) {
            // 加载用户详细信息
            user.setProfile(loadUserProfile(userId));
        }
        return user;
    }
    
    private UserProfile loadUserProfile(Long userId) {
        // 模拟加载用户详细信息
        return new UserProfile();
    }
}

自定义缓存配置

@Configuration
public class CacheConfig {
    
    /**
     * 自定义缓存管理器
     */
    @Bean
    public CacheManager customCacheManager(RedisConnectionFactory connectionFactory) {
        // 默认配置
        RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(30))
            .serializeKeysWith(RedisSerializationContext.SerializationPair
                .fromSerializer(new StringRedisSerializer()))
            .serializeValuesWith(RedisSerializationContext.SerializationPair
                .fromSerializer(new Jackson2JsonRedisSerializer<>(Object.class)))
            .disableCachingNullValues();
        
        // 特定缓存配置
        MapcacheConfigurations = new HashMap<>();
        
        // 用户缓存 - 1小时过期
        cacheConfigurations.put("users", defaultConfig.entryTtl(Duration.ofHours(1)));
        
        // 用户列表缓存 - 10分钟过期
        cacheConfigurations.put("userList", defaultConfig.entryTtl(Duration.ofMinutes(10)));
        
        // 会话缓存 - 30分钟过期
        cacheConfigurations.put("sessions", defaultConfig.entryTtl(Duration.ofMinutes(30)));
        
        // 配置缓存 - 1天过期
        cacheConfigurations.put("config", defaultConfig.entryTtl(Duration.ofDays(1)));
        
        return RedisCacheManager.builder(connectionFactory)
            .cacheDefaults(defaultConfig)
            .withInitialCacheConfigurations(cacheConfigurations)
            .build();
    }
}

实际应用场景

1. 分布式会话管理

@RestController
@RequestMapping("/api/session")
public class SessionController {
    
    @Autowired
    private RedisService redisService;
    
    /**
     * 用户登录
     */
    @PostMapping("/login")
    public ResponseEntity> login(@RequestBody LoginRequest request) {
        // 验证用户凭据
        User user = authenticateUser(request.getUsername(), request.getPassword());
        if (user == null) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }
        
        // 创建会话
        String sessionId = UUID.randomUUID().toString();
        String sessionKey = "session:" + sessionId;
        
        MapsessionData = new HashMap<>();
        sessionData.put("userId", user.getId());
        sessionData.put("username", user.getUsername());
        sessionData.put("loginTime", System.currentTimeMillis());
        sessionData.put("lastAccessTime", System.currentTimeMillis());
        
        // 存储会话(30分钟过期)
        redisService.set(sessionKey, sessionData, 30, TimeUnit.MINUTES);
        
        Mapresponse = new HashMap<>();
        response.put("sessionId", sessionId);
        response.put("message", "登录成功");
        
        return ResponseEntity.ok(response);
    }
    
    /**
     * 获取会话信息
     */
    @GetMapping("/info")
    public ResponseEntity> getSessionInfo(
            @RequestHeader("Session-Id") String sessionId) {
        
        String sessionKey = "session:" + sessionId;
        MapsessionData = redisService.get(sessionKey, Map.class);
        
        if (sessionData == null) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }
        
        // 更新最后访问时间
        sessionData.put("lastAccessTime", System.currentTimeMillis());
        redisService.set(sessionKey, sessionData, 30, TimeUnit.MINUTES);
        
        return ResponseEntity.ok(sessionData);
    }
    
    /**
     * 用户登出
     */
    @PostMapping("/logout")
    public ResponseEntitylogout(@RequestHeader("Session-Id") String sessionId) {
        String sessionKey = "session:" + sessionId;
        redisService.delete(sessionKey);
        return ResponseEntity.ok("登出成功");
    }
    
    private User authenticateUser(String username, String password) {
        // 实现用户认证逻辑
        return null;
    }
}

2. 分布式锁实现

@Component
public class DistributedLockService {
    
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    private static final String LOCK_PREFIX = "lock:";
    private static final String UNLOCK_SCRIPT = 
        "if redis.call('get', KEYS[1]) == ARGV[1] then " +
        "return redis.call('del', KEYS[1]) " +
        "else return 0 end";
    
    /**
     * 获取分布式锁
     */
    public boolean tryLock(String lockKey, String requestId, long expireTime) {
        String key = LOCK_PREFIX + lockKey;
        Boolean result = stringRedisTemplate.opsForValue()
            .setIfAbsent(key, requestId, Duration.ofSeconds(expireTime));
        return Boolean.TRUE.equals(result);
    }
    
    /**
     * 释放分布式锁
     */
    public boolean releaseLock(String lockKey, String requestId) {
        String key = LOCK_PREFIX + lockKey;
        DefaultRedisScriptscript = new DefaultRedisScript<>();
        script.setScriptText(UNLOCK_SCRIPT);
        script.setResultType(Long.class);
        
        Long result = stringRedisTemplate.execute(script, 
            Collections.singletonList(key), requestId);
        return Long.valueOf(1).equals(result);
    }
    
    /**
     * 使用锁执行业务逻辑
     */
    publicT executeWithLock(String lockKey, long expireTime, 
                                Supplierbusiness) {
        String requestId = UUID.randomUUID().toString();
        boolean locked = tryLock(lockKey, requestId, expireTime);
        
        if (!locked) {
            throw new RuntimeException("获取锁失败: " + lockKey);
        }
        
        try {
            return business.get();
        } finally {
            releaseLock(lockKey, requestId);
        }
    }
}

// 使用示例
@Service
public class OrderService {
    
    @Autowired
    private DistributedLockService lockService;
    
    /**
     * 创建订单(防止重复提交)
     */
    public Order createOrder(CreateOrderRequest request) {
        String lockKey = "order:create:" + request.getUserId();
        
        return lockService.executeWithLock(lockKey, 30, () -> {
            // 检查是否已存在订单
            if (existsRecentOrder(request.getUserId())) {
                throw new RuntimeException("请勿重复提交订单");
            }
            
            // 创建订单逻辑
            return doCreateOrder(request);
        });
    }
    
    private boolean existsRecentOrder(Long userId) {
        // 检查最近是否有订单
        return false;
    }
    
    private Order doCreateOrder(CreateOrderRequest request) {
        // 实际创建订单逻辑
        return new Order();
    }
}

3. 限流器实现

@Component
public class RateLimiterService {
    
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    /**
     * 滑动窗口限流
     */
    public boolean isAllowed(String key, int limit, int windowSeconds) {
        long now = System.currentTimeMillis();
        long windowStart = now - windowSeconds * 1000L;
        
        String script = 
            "redis.call('zremrangebyscore', KEYS[1], 0, ARGV[1]) " +
            "local count = redis.call('zcard', KEYS[1]) " +
            "if count < tonumber(ARGV[3]) then " +
                "redis.call('zadd', KEYS[1], ARGV[2], ARGV[2]) " +
                "redis.call('expire', KEYS[1], ARGV[4]) " +
                "return 1 " +
            "else " +
                "return 0 " +
            "end";
        
        DefaultRedisScriptredisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(script);
        redisScript.setResultType(Long.class);
        
        Long result = stringRedisTemplate.execute(redisScript,
            Collections.singletonList(key),
            String.valueOf(windowStart),
            String.valueOf(now),
            String.valueOf(limit),
            String.valueOf(windowSeconds));
        
        return Long.valueOf(1).equals(result);
    }
    
    /**
     * 令牌桶限流
     */
    public boolean tryAcquire(String bucketKey, int capacity, double refillRate) {
        String script = 
            "local bucket = KEYS[1] " +
            "local capacity = tonumber(ARGV[1]) " +
            "local refillRate = tonumber(ARGV[2]) " +
            "local now = tonumber(ARGV[3]) " +
            
            "local bucket_data = redis.call('HMGET', bucket, 'tokens', 'lastRefill') " +
            "local tokens = tonumber(bucket_data[1]) or capacity " +
            "local lastRefill = tonumber(bucket_data[2]) or now " +
            
            "local elapsed = (now - lastRefill) / 1000 " +
            "local tokensToAdd = elapsed * refillRate " +
            "tokens = math.min(capacity, tokens + tokensToAdd) " +
            
            "if tokens >= 1 then " +
                "tokens = tokens - 1 " +
                "redis.call('HMSET', bucket, 'tokens', tokens, 'lastRefill', now) " +
                "redis.call('EXPIRE', bucket, 3600) " +
                "return 1 " +
            "else " +
                "redis.call('HMSET', bucket, 'tokens', tokens, 'lastRefill', now) " +
                "redis.call('EXPIRE', bucket, 3600) " +
                "return 0 " +
            "end";
        
        DefaultRedisScriptredisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(script);
        redisScript.setResultType(Long.class);
        
        Long result = stringRedisTemplate.execute(redisScript,
            Collections.singletonList(bucketKey),
            String.valueOf(capacity),
            String.valueOf(refillRate),
            String.valueOf(System.currentTimeMillis()));
        
        return Long.valueOf(1).equals(result);
    }
}

// 限流注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
    String key() default "";
    int limit() default 10;
    int window() default 60;
    String message() default "请求过于频繁,请稍后再试";
}

// 限流切面
@Aspect
@Component
public class RateLimitAspect {
    
    @Autowired
    private RateLimiterService rateLimiterService;
    
    @Around("@annotation(rateLimit)")
    public Object around(ProceedingJoinPoint point, RateLimit rateLimit) throws Throwable {
        String key = rateLimit.key();
        if (key.isEmpty()) {
            key = point.getSignature().toShortString();
        }
        
        boolean allowed = rateLimiterService.isAllowed(key, rateLimit.limit(), rateLimit.window());
        if (!allowed) {
            throw new RuntimeException(rateLimit.message());
        }
        
        return point.proceed();
    }
}

⚙️ 配置参数详解

Spring Boot Redis 核心配置参数说明与推荐值

配置项 默认值 说明 推荐值
spring.redis.host localhost Redis 服务器地址 实际服务器地址
spring.redis.port 6379 Redis 服务器端口 6379
spring.redis.timeout 2000ms 连接超时时间 3000ms
spring.redis.lettuce.pool.max-active 8 最大连接数 100
spring.redis.lettuce.pool.max-idle 8 最大空闲连接数 20
spring.redis.lettuce.pool.min-idle 0 最小空闲连接数 5
spring.redis.lettuce.pool.max-wait -1ms 最大等待时间 3000ms

💡 最佳实践

Redis 配置与使用的核心建议
🔧

序列化选择

根据需求选择合适的序列化方式,JSON适合可读性,二进制适合性能
🏊‍♂️

连接池配置

合理配置连接池参数,避免连接不足或浪费资源
⏰

缓存策略

设置合理的过期时间,避免缓存雪崩和内存溢出
🛡️

异常处理

妥善处理 Redis 连接异常,提供降级方案保证服务可用性
📊

监控告警

监控 Redis 连接数、响应时间、错误率等关键指标
🏷️

键命名规范

建立统一的键命名规范,便于管理和维护
🎯

总结

Spring Boot 与 Redis 的集成为开发者提供了强大而灵活的缓存和数据存储解决方案。通过合理的配置和使用,可以显著提升应用的性能和用户体验。在实际项目中,建议根据具体需求选择合适的客户端和配置策略。
高性能易配置可扩展生产就绪

缓存配置
https://clxhxhhr.top/posts/2842/
作者
clxstart
发布于
2026-09-19
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。