Skip to content
第 193 / 250 章中间件⏱ 12 分钟阅读

第 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\x04user

5.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: true

7.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 current

9.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通用
StringRedisTemplateString 专用
RedisCacheManagerSpring Cache
DefaultRedisScriptLua 脚本
序列化器特点
StringRedisSerializer可读 key
GenericJackson2JsonRedisSerializerJSON 值
JdkSerializationRedisSerializer二进制(不推荐)

动手练习

  1. 配置 Spring Data Redis + Lettuce 池
  2. 写一个 RedisUtils 工具类
  3. 用 @Cacheable 缓存用户查询
  4. 写 Lua 脚本实现简单限流

推荐阅读


下一章:第 194 章:Redis 缓存模式与一致性

本站基于 VitePress 构建 · 由 Codebook 团队维护