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