🍃 Spring Boot 集成 Redis

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>

配置文件

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 RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); // JSON 序列化配置 Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = 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 RedisTemplate redisTemplate; @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") public T get(String key, Class clazz) { 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 Map hGetAll(String key) { return redisTemplate.opsForHash().entries(key); } /** * 批量设置哈希字段 */ public void hMSet(String key, Map map) { 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 List lRange(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 Set sMembers(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 Set zRevRange(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(Collection keys) { 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 List getUsersByStatus(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(); // 特定缓存配置 Map cacheConfigurations = 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; Map sessionData = 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); Map response = 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; Map sessionData = 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 ResponseEntity logout(@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; DefaultRedisScript script = 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); } /** * 使用锁执行业务逻辑 */ public T executeWithLock(String lockKey, long expireTime, Supplier business) { 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"; DefaultRedisScript redisScript = 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"; DefaultRedisScript redisScript = 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 的集成为开发者提供了强大而灵活的缓存和数据存储解决方案。通过合理的配置和使用,可以显著提升应用的性能和用户体验。在实际项目中,建议根据具体需求选择合适的客户端和配置策略。

高性能 易配置 可扩展 生产就绪