Skip to content

Spring Data Redis

1. Redis简介

Redis (Remote Dictionary Server) 是一个开源的内存数据结构存储系统,可用作数据库、缓存和消息代理。它支持多种数据类型,如字符串、哈希、列表、集合、有序集合、位图、HyperLogLog等。

2. Spring Data Redis概述

Spring Data Redis是Spring Data家族的一部分,提供了与Redis交互的高级抽象,简化了Redis操作的代码编写,并将Redis集成到Spring应用程序中。它提供了一致的Spring编程模型,支持原生Redis数据类型操作,同时保留了Redis的特性。

3. 主要特性

  1. 连接管理:支持多种Redis客户端(Lettuce, Jedis)
  2. 操作抽象:提供RedisTemplate,简化Redis数据操作
  3. 自动序列化/反序列化:对象与Redis数据类型的自动转换
  4. Redis Repositories:类似JPA Repository的抽象
  5. 分布式锁:基于Redis的分布式锁实现
  6. 缓存集成:与Spring的缓存抽象集成
  7. 发布/订阅支持:对Redis pub/sub的原生支持
  8. 事务支持:Redis命令事务处理

4. 依赖配置

Maven

xml
<!-- Spring Boot方式 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- 使用lettuce作为客户端 -->
<dependency>
    <groupId>io.lettuce</groupId>
    <artifactId>lettuce-core</artifactId>
</dependency>

<!-- 非Spring Boot方式 -->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>3.1.0</version>
</dependency>

5. Redis连接配置

Spring Boot配置

properties
# application.properties
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=
spring.redis.database=0
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0
spring.redis.lettuce.pool.max-wait=-1ms

手动配置RedisConnectionFactory

java
@Configuration
public class RedisConfig {
    
    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(new RedisStandaloneConfiguration("localhost", 6379));
    }
    
    // 或使用集群配置
    @Bean
    public LettuceConnectionFactory redisClusterConnectionFactory() {
        RedisClusterConfiguration clusterConfig = new RedisClusterConfiguration();
        clusterConfig.addClusterNode(new RedisNode("127.0.0.1", 6379));
        clusterConfig.addClusterNode(new RedisNode("127.0.0.1", 6380));
        // 添加更多节点...
        
        return new LettuceConnectionFactory(clusterConfig);
    }
    
    // 配置连接池
    @Bean
    public LettuceConnectionFactory redisConnectionFactoryWithPool() {
        LettucePoolingClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
                .commandTimeout(Duration.ofSeconds(2))
                .poolConfig(poolConfig())
                .build();
        
        return new LettuceConnectionFactory(
                new RedisStandaloneConfiguration("localhost", 6379), 
                clientConfig
        );
    }
    
    private GenericObjectPoolConfig poolConfig() {
        GenericObjectPoolConfig config = new GenericObjectPoolConfig();
        config.setMaxTotal(8);
        config.setMaxIdle(8);
        config.setMinIdle(0);
        return config;
    }
}

6. RedisTemplate配置

java
@Configuration
public class RedisTemplateConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        
        // 使用Jackson2JsonRedisSerializer序列化对象
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, 
                ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        serializer.setObjectMapper(mapper);
        
        // 设置String键的序列化方式
        template.setKeySerializer(new StringRedisSerializer());
        // 设置String值的序列化方式
        template.setValueSerializer(serializer);
        // 设置Hash键的序列化方式
        template.setHashKeySerializer(new StringRedisSerializer());
        // 设置Hash值的序列化方式
        template.setHashValueSerializer(serializer);
        
        template.afterPropertiesSet();
        return template;
    }
    
    // 如果只需要处理String类型
    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory connectionFactory) {
        return new StringRedisTemplate(connectionFactory);
    }
}

7. 基本操作

字符串操作

java
@Service
public class StringOperationsExample {
    
    private final StringRedisTemplate redisTemplate;
    
    public StringOperationsExample(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    
    public void stringOperations() {
        ValueOperations<String, String> ops = redisTemplate.opsForValue();
        
        // 设置值
        ops.set("key", "value");
        
        // 设置值并设置过期时间
        ops.set("key-with-ttl", "value", Duration.ofMinutes(10));
        
        // 获取值
        String value = ops.get("key");
        
        // 设置过期时间
        redisTemplate.expire("key", Duration.ofHours(1));
        
        // 检查键是否存在
        Boolean exists = redisTemplate.hasKey("key");
        
        // 删除键
        redisTemplate.delete("key");
        
        // 批量操作
        Map<String, String> map = new HashMap<>();
        map.put("key1", "value1");
        map.put("key2", "value2");
        ops.multiSet(map);
        
        List<String> keys = Arrays.asList("key1", "key2");
        List<String> values = ops.multiGet(keys);
    }
}

哈希操作

java
@Service
public class HashOperationsExample {
    
    private final RedisTemplate<String, Object> redisTemplate;
    
    public HashOperationsExample(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    
    public void hashOperations() {
        HashOperations<String, String, Object> ops = redisTemplate.opsForHash();
        
        // 单个字段设置
        ops.put("user:1", "name", "John");
        ops.put("user:1", "age", 30);
        
        // 获取单个字段
        Object name = ops.get("user:1", "name");
        
        // 批量设置字段
        Map<String, Object> fields = new HashMap<>();
        fields.put("email", "john@example.com");
        fields.put("active", true);
        ops.putAll("user:1", fields);
        
        // 获取所有字段和值
        Map<String, Object> entries = ops.entries("user:1");
        
        // 获取所有字段名
        Set<String> fieldNames = ops.keys("user:1");
        
        // 检查字段是否存在
        Boolean hasField = ops.hasKey("user:1", "email");
        
        // 删除字段
        ops.delete("user:1", "active");
        
        // 增加数字值
        ops.increment("user:1", "age", 1);
    }
}

列表操作

java
@Service
public class ListOperationsExample {
    
    private final RedisTemplate<String, Object> redisTemplate;
    
    public ListOperationsExample(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    
    public void listOperations() {
        ListOperations<String, Object> ops = redisTemplate.opsForList();
        
        // 右侧插入
        ops.rightPush("list", "item1");
        ops.rightPushAll("list", "item2", "item3");
        
        // 左侧插入
        ops.leftPush("list", "item0");
        
        // 获取列表范围
        List<Object> items = ops.range("list", 0, -1); // 获取所有元素
        
        // 修改指定位置的值
        ops.set("list", 1, "modified-item");
        
        // 获取指定位置的元素
        Object item = ops.index("list", 0);
        
        // 获取列表长度
        Long size = ops.size("list");
        
        // 弹出元素
        Object leftItem = ops.leftPop("list");
        Object rightItem = ops.rightPop("list");
        
        // 阻塞弹出(适用于消息队列场景)
        Object value = ops.leftPop("list", Duration.ofSeconds(10));
        
        // 列表修剪
        ops.trim("list", 0, 2); // 只保留前3个元素
    }
}

集合操作

java
@Service
public class SetOperationsExample {
    
    private final RedisTemplate<String, Object> redisTemplate;
    
    public SetOperationsExample(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    
    public void setOperations() {
        SetOperations<String, Object> ops = redisTemplate.opsForSet();
        
        // 添加元素
        ops.add("set1", "member1", "member2", "member3");
        ops.add("set2", "member3", "member4", "member5");
        
        // 获取集合所有成员
        Set<Object> members = ops.members("set1");
        
        // 判断成员是否存在
        Boolean isMember = ops.isMember("set1", "member1");
        
        // 获取集合大小
        Long size = ops.size("set1");
        
        // 删除成员
        ops.remove("set1", "member1");
        
        // 随机获取成员
        Object randomMember = ops.randomMember("set1");
        List<Object> randomMembers = ops.randomMembers("set1", 2);
        
        // 集合操作
        Set<Object> diff = ops.difference("set1", "set2");       // 差集
        Set<Object> union = ops.union("set1", "set2");           // 并集
        Set<Object> intersect = ops.intersect("set1", "set2");   // 交集
        
        // 将集合操作结果存储到新集合
        ops.differenceAndStore("set1", "set2", "diff-result");
        ops.unionAndStore("set1", "set2", "union-result");
        ops.intersectAndStore("set1", "set2", "intersect-result");
    }
}

有序集合操作

java
@Service
public class ZSetOperationsExample {
    
    private final RedisTemplate<String, Object> redisTemplate;
    
    public ZSetOperationsExample(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    
    public void zsetOperations() {
        ZSetOperations<String, Object> ops = redisTemplate.opsForZSet();
        
        // 添加元素及分数
        ops.add("zset", "member1", 1.0);
        ops.add("zset", "member2", 2.0);
        ops.add("zset", "member3", 3.0);
        
        // 批量添加
        Set<ZSetOperations.TypedTuple<Object>> tuples = new HashSet<>();
        tuples.add(ZSetOperations.TypedTuple.of("member4", 4.0));
        tuples.add(ZSetOperations.TypedTuple.of("member5", 5.0));
        ops.add("zset", tuples);
        
        // 按分数获取元素
        Set<Object> range = ops.range("zset", 0, -1);           // 所有元素
        Set<Object> reverseRange = ops.reverseRange("zset", 0, -1);  // 反序所有元素
        
        // 按分数范围获取元素
        Set<Object> scoreRange = ops.rangeByScore("zset", 2.0, 4.0);
        
        // 获取元素的分数
        Double score = ops.score("zset", "member1");
        
        // 获取排名
        Long rank = ops.rank("zset", "member2");        // 从低到高排名
        Long revRank = ops.reverseRank("zset", "member2");  // 从高到低排名
        
        // 增加分数
        ops.incrementScore("zset", "member1", 2.5);
        
        // 统计分数范围内的元素数量
        Long count = ops.count("zset", 2.0, 4.0);
        
        // 删除元素
        ops.remove("zset", "member1");
        
        // 删除排名范围内的元素
        ops.removeRange("zset", 0, 1);
        
        // 删除分数范围内的元素
        ops.removeRangeByScore("zset", 4.0, 5.0);
    }
}

8. Redis Repositories

Spring Data Redis也支持Repository风格的抽象,类似于Spring Data JPA。

启用Redis Repositories

java
@Configuration
@EnableRedisRepositories(basePackages = "com.example.repository")
public class RedisRepositoryConfig {
    // Redis连接配置
}

定义实体类

java
@RedisHash("user")
public class User implements Serializable {
    
    @Id
    private String id;
    
    private String name;
    private String email;
    private int age;
    
    @TimeToLive
    private Long expiration; // 过期时间(秒)
    
    // 构造函数、getter和setter
}

创建Repository接口

java
public interface UserRepository extends CrudRepository<User, String> {
    
    List<User> findByName(String name);
    
    List<User> findByAgeGreaterThan(int age);
    
    @Query("{ ?0 : ?1 }")
    List<User> findByAttribute(String attribute, String value);
}

使用Repository

java
@Service
public class UserService {
    
    private final UserRepository userRepository;
    
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    public User save(User user) {
        return userRepository.save(user);
    }
    
    public Optional<User> findById(String id) {
        return userRepository.findById(id);
    }
    
    public List<User> findByName(String name) {
        return userRepository.findByName(name);
    }
    
    public Iterable<User> findAll() {
        return userRepository.findAll();
    }
    
    public void delete(String id) {
        userRepository.deleteById(id);
    }
}

9. 缓存抽象

Spring Data Redis与Spring Cache集成,提供了Redis作为缓存后端的实现。

配置Redis缓存

java
@Configuration
@EnableCaching
public class RedisCacheConfig {
    
    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(10))       // 默认缓存过期时间
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new GenericJackson2JsonRedisSerializer()))
                .disableCachingNullValues();             // 不缓存null值
        
        // 配置不同的缓存对应不同的过期时间
        Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
        cacheConfigurations.put("userCache", cacheConfig.entryTtl(Duration.ofMinutes(5)));
        cacheConfigurations.put("productCache", cacheConfig.entryTtl(Duration.ofHours(1)));
        
        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(cacheConfig)
                .withInitialCacheConfigurations(cacheConfigurations)
                .transactionAware()
                .build();
    }
}

使用缓存注解

java
@Service
public class CachedUserService {
    
    private final UserRepository userRepository;
    
    public CachedUserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    @Cacheable(value = "userCache", key = "#id")
    public User findById(String id) {
        return userRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("User not found"));
    }
    
    @CachePut(value = "userCache", key = "#user.id")
    public User save(User user) {
        return userRepository.save(user);
    }
    
    @CacheEvict(value = "userCache", key = "#id")
    public void delete(String id) {
        userRepository.deleteById(id);
    }
    
    @CacheEvict(value = "userCache", allEntries = true)
    public void clearCache() {
        // 清空整个缓存
    }
}

10. 分布式锁

Spring Data Redis提供了RedisLockRegistry实现基于Redis的分布式锁。

java
@Configuration
public class RedisLockConfig {
    
    @Bean
    public RedisLockRegistry redisLockRegistry(RedisConnectionFactory connectionFactory) {
        return new RedisLockRegistry(connectionFactory, "locks", 30000);
    }
}

使用分布式锁:

java
@Service
public class DistributedLockService {
    
    private final RedisLockRegistry lockRegistry;
    
    public DistributedLockService(RedisLockRegistry lockRegistry) {
        this.lockRegistry = lockRegistry;
    }
    
    public void executeWithLock(String lockKey, Runnable task) {
        Lock lock = lockRegistry.obtain(lockKey);
        try {
            boolean acquired = lock.tryLock(10, TimeUnit.SECONDS);
            if (acquired) {
                try {
                    task.run();
                } finally {
                    lock.unlock();
                }
            } else {
                throw new RuntimeException("Failed to acquire lock: " + lockKey);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("Interrupted while acquiring lock", e);
        }
    }
}

11. 发布/订阅

Spring Data Redis支持Redis的发布/订阅模式。

配置消息监听器

java
@Configuration
public class RedisMessageConfig {
    
    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer(
            RedisConnectionFactory connectionFactory,
            MessageListener messageListener) {
        
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        
        // 添加监听器和监听的频道
        container.addMessageListener(messageListener, new ChannelTopic("chat"));
        
        return container;
    }
    
    @Bean
    public MessageListener messageListener() {
        return (message, pattern) -> {
            System.out.println("Received message: " + new String(message.getBody()));
            System.out.println("From channel: " + new String(message.getChannel()));
        };
    }
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        // 配置RedisTemplate
        // ...
    }
}

消息发布

java
@Service
public class MessagePublisher {
    
    private final RedisTemplate<String, Object> redisTemplate;
    
    public MessagePublisher(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    
    public void publish(String channel, Object message) {
        redisTemplate.convertAndSend(channel, message);
    }
}

12. 会话管理

Spring Session可以与Spring Data Redis集成,实现基于Redis的HTTP会话管理。

配置

java
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)
public class RedisSessionConfig {
    // Redis连接配置
}

13. Redis事务

Spring Data Redis支持Redis的事务操作。

java
@Service
public class TransactionalRedisService {
    
    private final RedisTemplate<String, Object> redisTemplate;
    
    public TransactionalRedisService(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    
    public void executeInTransaction() {
        redisTemplate.execute(new SessionCallback<List<Object>>() {
            @Override
            public List<Object> execute(RedisOperations operations) {
                operations.multi();  // 开始事务
                
                operations.opsForValue().set("key1", "value1");
                operations.opsForValue().set("key2", "value2");
                operations.opsForList().leftPush("list", "item");
                
                return operations.exec();  // 提交事务
            }
        });
    }
    
    public void executeWithWatch() {
        redisTemplate.execute(new SessionCallback<Object>() {
            @Override
            public Object execute(RedisOperations operations) {
                operations.watch("watched-key");  // 监视key
                
                Object value = operations.opsForValue().get("watched-key");
                
                operations.multi();
                operations.opsForValue().set("watched-key", "new-value");
                
                try {
                    return operations.exec();  // 如果watched-key被修改,事务将失败
                } catch (Exception e) {
                    return null;  // 事务失败
                }
            }
        });
    }
}

14. 性能优化

1. 使用管道操作

java
List<Object> results = redisTemplate.executePipelined(new RedisCallback<Object>() {
    @Override
    public Object doInRedis(RedisConnection connection) throws DataAccessException {
        StringRedisConnection stringRedisConn = (StringRedisConnection) connection;
        
        for (int i = 0; i < 1000; i++) {
            stringRedisConn.set("key" + i, "value" + i);
        }
        
        return null;
    }
});

2. 使用Lua脚本

java
@Service
public class RedisScriptService {
    
    private final RedisTemplate<String, Object> redisTemplate;
    
    public RedisScriptService(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    
    public void executeScript() {
        DefaultRedisScript<Boolean> script = new DefaultRedisScript<>();
        script.setScriptSource(new ResourceScriptSource(new ClassPathResource("script.lua")));
        script.setResultType(Boolean.class);
        
        List<String> keys = Collections.singletonList("key");
        Boolean result = redisTemplate.execute(script, keys, "arg1", "arg2");
    }
}

15. 总结

Spring Data Redis提供了全面的Redis操作抽象,从低级连接管理到高级Repository模式,使得在Spring应用中集成Redis变得简单而高效。无论是用于缓存、会话管理、消息发布/订阅还是分布式锁,Spring Data Redis都提供了一致的编程模型和丰富的功能。

通过合理配置和使用Spring Data Redis,可以充分利用Redis的高性能特性,同时享受Spring框架的便利性。