第 193 章:Spring Data Redis 实战
学习目标
- 集成 Spring Data Redis
- 掌握 RedisTemplate 用法
- 学会 Spring Cache + Redis
- 了解 Spring Boot 3.x 配置
一、Spring Data Redis 简介
Spring Data Redis 是 Spring 对 Redis 客户端(Lettuce/Jedis)的封装,提供统一的 RedisTemplate。
二、引入依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 连接池(可选,Lettuce 默认自带) -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>三、配置(application.yml)
yaml
spring:
data:
redis:
host: localhost
port: 6379
password: yourpass
database: 0
timeout: 2000ms
lettuce:
pool:
max-active: 20
max-idle: 10
min-idle: 2
max-wait: 1000ms
shutdown-timeout: 200ms四、RedisTemplate 基础
4.1 注入使用
java
@Service
public class UserService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public void set(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
public Object get(String key) {
return redisTemplate.opsForValue().get(key);
}
}4.2 五种数据结构 API
java
// String
redisTemplate.opsForValue().set("k", "v");
redisTemplate.opsForValue().get("k");
redisTemplate.opsForValue().increment("counter");
// Hash
redisTemplate.opsForHash().put("user:1", "name", "Tom");
redisTemplate.opsForHash().get("user:1", "name");
redisTemplate.opsForHash().entries("user:1");
// List
redisTemplate.opsForList().leftPush("list", "a");
redisTemplate.opsForList().range("list", 0, -1);
// Set
redisTemplate.opsForSet().add("tags", "java", "redis");
redisTemplate.opsForSet().members("tags");
// ZSet
redisTemplate.opsForZSet().add("rank", "Tom", 100.0);
redisTemplate.opsForZSet().reverseRange("rank", 0, 9);五、序列化配置
5.1 默认 JDK 序列化的问题
默认 JdkSerializationRedisSerializer 会把对象变成不可读的二进制流:
key: \xac\xed\x00\x05t\x00\x04user5.2 推荐:String + JSON
java
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
StringRedisSerializer keySerializer = new StringRedisSerializer();
GenericJackson2JsonRedisSerializer valueSerializer = new GenericJackson2JsonRedisSerializer();
template.setKeySerializer(keySerializer);
template.setHashKeySerializer(keySerializer);
template.setValueSerializer(valueSerializer);
template.setHashValueSerializer(valueSerializer);
template.afterPropertiesSet();
return template;
}
}5.3 Jackson 配置(支持 LocalDateTime)
java
@Bean
public GenericJackson2JsonRedisSerializer jsonSerializer() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.activateDefaultTyping(
mapper.getPolymorphicTypeValidator(),
ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.PROPERTY
);
return new GenericJackson2JsonRedisSerializer(mapper);
}六、StringRedisTemplate
只用于 String 类型,无需 JSON 序列化:
java
@Autowired
private StringRedisTemplate stringRedisTemplate;
// 存字符串
stringRedisTemplate.opsForValue().set("token:abc", "user123");
// 哈希(字段值也是 String)
stringRedisTemplate.opsForHash().put("user:1", "name", "Tom");七、Spring Cache + Redis
7.1 启用
java
@SpringBootApplication
@EnableCaching
public class App { }7.2 配置
yaml
spring:
cache:
type: redis
redis:
time-to-live: 60000
cache-null-values: false
key-prefix: "cache:"
use-key-prefix: true7.3 注解使用
java
@Service
public class UserService {
@Cacheable(value = "user", key = "#id")
public User findById(Long id) {
return userMapper.selectById(id);
}
@CachePut(value = "user", key = "#user.id")
public User update(User user) {
userMapper.updateById(user);
return user;
}
@CacheEvict(value = "user", key = "#id")
public void delete(Long id) {
userMapper.deleteById(id);
}
@Caching(evict = {
@CacheEvict(value = "user", key = "#user.id"),
@CacheEvict(value = "userList", allEntries = true)
})
public User create(User user) {
userMapper.insert(user);
return user;
}
}7.4 自定义 Key 生成
java
@Configuration
public class CacheConfig implements CachingConfigurer {
@Override
public KeyGenerator keyGenerator() {
return (target, method, params) -> {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getSimpleName()).append(":");
sb.append(method.getName()).append(":");
for (Object p : params) sb.append(p).append(":");
return sb.toString();
};
}
}7.5 自定义 TTL
java
@Bean
public RedisCacheManagerBuilderCustomizer customizer() {
return builder -> builder.withCacheConfiguration("user",
RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30))
.serializeValuesWith(SerializationPair.fromSerializer(jsonSerializer()))
);
}八、Redis 工具类
java
@Component
public class RedisUtils {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 通用
public boolean expire(String key, long seconds) {
return Boolean.TRUE.equals(redisTemplate.expire(key, seconds, TimeUnit.SECONDS));
}
public Long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
public Boolean hasKey(String key) {
return redisTemplate.hasKey(key);
}
public Boolean delete(String key) {
return redisTemplate.delete(key);
}
// String
public void set(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
public void set(String key, Object value, long seconds) {
redisTemplate.opsForValue().set(key, value, seconds, TimeUnit.SECONDS);
}
@SuppressWarnings("unchecked")
public <T> T get(String key) {
return (T) redisTemplate.opsForValue().get(key);
}
// Hash
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<Object, Object> hgetAll(String key) {
return redisTemplate.opsForHash().entries(key);
}
// 自增
public Long incr(String key, long delta) {
return redisTemplate.opsForValue().increment(key, delta);
}
}九、Lua 脚本
9.1 defaultRedisScript
java
@Bean
public DefaultRedisScript<Long> limitScript() {
DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setLocation(new ClassPathResource("lua/limit.lua"));
script.setResultType(Long.class);
return script;
}lua
-- limit.lua
local current = redis.call('INCR', KEYS[1])
if tonumber(current) == 1 then
redis.call('EXPIRE', KEYS[1], ARGV[1])
end
return current9.2 调用
java
@Autowired
private DefaultRedisScript<Long> limitScript;
public boolean tryAcquire(String key, int period) {
Long count = redisTemplate.execute(limitScript,
Collections.singletonList(key),
String.valueOf(period));
return count != null && count <= LIMIT;
}十、Pipeline 与事务
10.1 Pipeline
java
List<Object> results = redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
StringRedisConnection sconn = (StringRedisConnection) connection;
sconn.set("k1", "v1");
sconn.set("k2", "v2");
sconn.get("k1");
return null;
});10.2 事务(简单场景)
java
redisTemplate.execute(new SessionCallback<>() {
@Override
public Object execute(RedisOperations operations) {
operations.multi();
operations.opsForValue().set("k1", "v1");
operations.opsForValue().set("k2", "v2");
return operations.exec();
}
});十一、连接池监控
java
@Bean
public RedisTemplate<String, Object> redisTemplate(...) {
// ...
template.setEnableTransactionSupport(true);
return template;
}监控指标(Micrometer):
yaml
management:
endpoints:
web:
exposure:
include: health, metrics
metrics:
tags:
application: myapp访问 http://localhost:8080/actuator/metrics/redis.connections.active。
十二、本章小结
| 类 | 用途 |
|---|---|
| RedisTemplate | 通用 |
| StringRedisTemplate | String 专用 |
| RedisCacheManager | Spring Cache |
| DefaultRedisScript | Lua 脚本 |
| 序列化器 | 特点 |
|---|---|
| StringRedisSerializer | 可读 key |
| GenericJackson2JsonRedisSerializer | JSON 值 |
| JdkSerializationRedisSerializer | 二进制(不推荐) |
动手练习
- 配置 Spring Data Redis + Lettuce 池
- 写一个 RedisUtils 工具类
- 用 @Cacheable 缓存用户查询
- 写 Lua 脚本实现简单限流
推荐阅读
下一章:第 194 章:Redis 缓存模式与一致性 →