跳转到内容

Spring Data Redis 集成


Spring Data Redis 是 Spring 对 Redis 客户端的统一封装,提供模板类操作 Redis,屏蔽底层客户端差异。

SpringBoot 应用
Spring Data Redis
├── RedisTemplate<K, V> 通用模板,支持任意类型
└── StringRedisTemplate RedisTemplate<String, String> 的简化版
RedisConnectionFactory(连接工厂)
├── LettuceConnectionFactory 默认,基于 Netty,支持异步/响应式
└── JedisConnectionFactory 老牌同步客户端
对比项Lettuce(默认)Jedis
线程模型多线程共享单连接(Netty)每个线程独占连接
连接池可选(推荐开启)必须配置连接池
异步支持✅ 原生支持❌ 不支持
响应式支持✅ 支持❌ 不支持
推荐程度✅ 生产推荐仅兼容旧项目
<!-- Spring Data Redis(内置 Lettuce 客户端) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 连接池支持(Lettuce 需要 commons-pool2) -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
spring:
data:
redis:
host: localhost
port: 6379
password: 123456
database: 0 # 数据库编号,默认 0(共 16 个,0~15)
connect-timeout: 3000ms # 连接超时
timeout: 3000ms # 命令执行超时
# Lettuce 连接池(推荐开启)
lettuce:
pool:
max-active: 8 # 最大连接数(-1 表示无限制)
max-idle: 8 # 最大空闲连接
min-idle: 1 # 最小空闲连接(保持预热)
max-wait: 200ms # 获取连接的最大等待时间,超时抛出异常
对比项RedisTemplateStringRedisTemplate
泛型<K, V> 可自定义<String, String>
默认序列化JDK 序列化(❌ 不推荐)String 序列化
适用场景存储复杂 Java 对象存储字符串(JSON 字符串)
配置复杂度需自定义序列化配置开箱即用

3.2. RedisTemplate 默认序列化的问题

Section titled “3.2. RedisTemplate 默认序列化的问题”
// 使用默认 RedisTemplate 存储对象
redisTemplate.opsForValue().set("user:1", new User("Alice", 25));
// Redis 中实际存储的 key 和 value 都是 JDK 序列化的乱码:
// Key: \xac\xed\x00\x05t\x00\x06user:1
// Value: \xac\xed\x00\x05sr\x00\x04User...

这导致:key 在 Redis 管理台中不可读;跨语言消费无法反序列化;存储体积大。

3.3. 推荐方案一:自定义 RedisTemplate(JSON 序列化)

Section titled “3.3. 推荐方案一:自定义 RedisTemplate(JSON 序列化)”
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// Key 和 HashKey 使用 String 序列化(可读)
StringRedisSerializer stringSerializer = new StringRedisSerializer();
template.setKeySerializer(stringSerializer);
template.setHashKeySerializer(stringSerializer);
// Value 和 HashValue 使用 JSON 序列化
Jackson2JsonRedisSerializer<Object> jsonSerializer =
new Jackson2JsonRedisSerializer<>(Object.class);
template.setValueSerializer(jsonSerializer);
template.setHashValueSerializer(jsonSerializer);
template.afterPropertiesSet();
return template;
}
}
// 使用自定义 RedisTemplate 存储对象
redisTemplate.opsForValue().set("user:1", new User("Alice", 25));
// Redis 中实际存储:
// Key: user:1
// Value: {"name":"Alice","age":25}

3.4. 推荐方案二:StringRedisTemplate + 手动 JSON(更直观)

Section titled “3.4. 推荐方案二:StringRedisTemplate + 手动 JSON(更直观)”
@Service
@RequiredArgsConstructor
public class UserCacheService {
private final StringRedisTemplate redisTemplate;
private final ObjectMapper objectMapper;
public void save(User user) throws JsonProcessingException {
String key = "user:" + user.getId();
String value = objectMapper.writeValueAsString(user); // 手动序列化
redisTemplate.opsForValue().set(key, value, 1, TimeUnit.HOURS);
}
public User get(Long userId) throws JsonProcessingException {
String json = redisTemplate.opsForValue().get("user:" + userId);
if (json == null) return null;
return objectMapper.readValue(json, User.class); // 手动反序列化
}
}

RedisTemplate 通过 opsForXxx() 方法获取各数据类型的操作对象:

// 五种基础类型操作入口
ValueOperations<K, V> ops = template.opsForValue(); // String
HashOperations<K, F, V> ops = template.opsForHash(); // Hash
ListOperations<K, V> ops = template.opsForList(); // List
SetOperations<K, V> ops = template.opsForSet(); // Set
ZSetOperations<K, V> ops = template.opsForZSet(); // ZSet
// key 级别操作(通用命令)
template.delete(key); // DEL
template.expire(key, 1, TimeUnit.HOURS); // EXPIRE
template.getExpire(key, TimeUnit.SECONDS); // TTL
template.hasKey(key); // EXISTS
template.type(key); // TYPE
// Pipeline:一次网络往返发送多条命令,减少 RTT
List<Object> results = redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
StringRedisConnection conn = (StringRedisConnection) connection;
for (int i = 1; i <= 100; i++) {
conn.set("key:" + i, "value:" + i);
}
return null;
});
// 使用 Lua 脚本保证多命令原子性(如分布式锁释放)
String script = """
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end
""";
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
Long result = redisTemplate.execute(
redisScript,
Collections.singletonList("lock:order:1001"),
"unique-value-12345"
);
问题原因解决方案
key 出现乱码前缀使用了默认 JDK 序列化自定义 StringRedisSerializer
存对象后无法反序列化泛型擦除,Object.class 无法还原类型指定具体类型或用 TypeReference
@Autowired RedisTemplate 注入失败存在多个 RedisTemplate Bean@Qualifier 指定 Bean 名称
连接池耗尽报错高并发下连接不够用调大 max-active,检查是否有连接泄露
命令超时网络抖动或 Redis 执行大 key 命令设置合理 timeout,排查大 key