Skip to content

Spring 整合 Redis

1. Redis 概述

1.1 什么是 Redis

Redis(Remote Dictionary Server)是一个开源的内存数据结构存储系统,可用作数据库、缓存、消息代理和流处理引擎。

1.2 Redis 数据类型

数据类型说明应用场景
String字符串缓存、计数器、分布式锁
Hash哈希表对象存储、购物车
List列表消息队列、最新列表
Set集合标签、共同好友
Sorted Set有序集合排行榜、延时队列
Stream消息队列、事件流
Bitmap位图签到、在线状态
HyperLogLog基数统计UV 统计

2. 快速入门

2.1 添加依赖

xml
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>3.3.0</version>
</dependency>
<dependency>
    <groupId>io.lettuce</groupId>
    <artifactId>lettuce-core</artifactId>
    <version>6.3.0.RELEASE</version>
</dependency>

2.2 配置 Redis

java
@Configuration
public class RedisConfig {
    
    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
        config.setHostName("localhost");
        config.setPort(6379);
        config.setPassword("password");
        config.setDatabase(0);
        return new LettuceConnectionFactory(config);
    }
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(
            LettuceConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        
        template.afterPropertiesSet();
        return template;
    }
    
    @Bean
    public StringRedisTemplate stringRedisTemplate(
            LettuceConnectionFactory connectionFactory) {
        return new StringRedisTemplate(connectionFactory);
    }
}

2.3 基本操作

java
@Service
public class RedisService {
    
    private final RedisTemplate<String, Object> redisTemplate;
    
    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);
    }
    
    public Boolean delete(String key) {
        return redisTemplate.delete(key);
    }
    
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }
    
    public Boolean expire(String key, long timeout, TimeUnit unit) {
        return redisTemplate.expire(key, timeout, unit);
    }
    
    public Long getExpire(String key) {
        return redisTemplate.getExpire(key);
    }
}

3. String 操作

3.1 基本操作

java
@Service
public class StringRedisService {
    
    private final StringRedisTemplate redisTemplate;
    
    public void set(String key, String value) {
        redisTemplate.opsForValue().set(key, value);
    }
    
    public void setEx(String key, String value, long seconds) {
        redisTemplate.opsForValue().set(key, value, seconds, TimeUnit.SECONDS);
    }
    
    public void setNx(String key, String value) {
        redisTemplate.opsForValue().setIfAbsent(key, value);
    }
    
    public String get(String key) {
        return redisTemplate.opsForValue().get(key);
    }
    
    public String getAndSet(String key, String newValue) {
        return redisTemplate.opsForValue().getAndSet(key, newValue);
    }
    
    public Long increment(String key) {
        return redisTemplate.opsForValue().increment(key);
    }
    
    public Long increment(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, delta);
    }
    
    public Long decrement(String key) {
        return redisTemplate.opsForValue().decrement(key);
    }
    
    public Long append(String key, String value) {
        return redisTemplate.opsForValue().append(key, value);
    }
    
    public Long size(String key) {
        return redisTemplate.opsForValue().size(key);
    }
}

3.2 分布式锁

java
@Service
public class DistributedLockService {
    
    private final StringRedisTemplate redisTemplate;
    
    public boolean tryLock(String key, String value, long expireTime, TimeUnit unit) {
        Boolean result = redisTemplate.opsForValue()
            .setIfAbsent(key, value, expireTime, unit);
        return Boolean.TRUE.equals(result);
    }
    
    public boolean releaseLock(String key, String value) {
        String script = 
            "if redis.call('get', KEYS[1]) == ARGV[1] then " +
            "    return redis.call('del', KEYS[1]) " +
            "else " +
            "    return 0 " +
            "end";
        
        RedisScript<Long> redisScript = RedisScript.of(script, Long.class);
        Long result = redisTemplate.execute(redisScript, List.of(key), value);
        return result != null && result == 1L;
    }
}

3.3 计数器

java
@Service
public class CounterService {
    
    private final StringRedisTemplate redisTemplate;
    
    public Long increment(String key) {
        return redisTemplate.opsForValue().increment(key);
    }
    
    public Long increment(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, delta);
    }
    
    public Long getCount(String key) {
        String value = redisTemplate.opsForValue().get(key);
        return value != null ? Long.parseLong(value) : 0L;
    }
    
    public void reset(String key) {
        redisTemplate.opsForValue().set(key, "0");
    }
}

4. Hash 操作

4.1 基本操作

java
@Service
public class HashRedisService {
    
    private final RedisTemplate<String, Object> redisTemplate;
    
    public void hSet(String key, String field, Object value) {
        redisTemplate.opsForHash().put(key, field, value);
    }
    
    public void hSetAll(String key, Map<String, Object> map) {
        redisTemplate.opsForHash().putAll(key, map);
    }
    
    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 hDelete(String key, Object... fields) {
        return redisTemplate.opsForHash().delete(key, fields);
    }
    
    public Boolean hExists(String key, String field) {
        return redisTemplate.opsForHash().hasKey(key, field);
    }
    
    public Long hIncrement(String key, String field, long delta) {
        return redisTemplate.opsForHash().increment(key, field, delta);
    }
    
    public Set<Object> hKeys(String key) {
        return redisTemplate.opsForHash().keys(key);
    }
    
    public Long hSize(String key) {
        return redisTemplate.opsForHash().size(key);
    }
}

4.2 对象缓存

java
@Service
public class UserCacheService {
    
    private final RedisTemplate<String, Object> redisTemplate;
    private static final String USER_KEY_PREFIX = "user:";
    
    public void saveUser(User user) {
        String key = USER_KEY_PREFIX + user.getId();
        Map<String, Object> userMap = new HashMap<>();
        userMap.put("id", user.getId());
        userMap.put("username", user.getUsername());
        userMap.put("email", user.getEmail());
        userMap.put("createdAt", user.getCreatedAt().toString());
        
        redisTemplate.opsForHash().putAll(key, userMap);
        redisTemplate.expire(key, 1, TimeUnit.HOURS);
    }
    
    public User getUser(Long id) {
        String key = USER_KEY_PREFIX + id;
        Map<Object, Object> entries = redisTemplate.opsForHash().entries(key);
        
        if (entries.isEmpty()) {
            return null;
        }
        
        return User.builder()
            .id(Long.parseLong(entries.get("id").toString()))
            .username(entries.get("username").toString())
            .email(entries.get("email").toString())
            .createdAt(LocalDateTime.parse(entries.get("createdAt").toString()))
            .build();
    }
    
    public void updateUserField(Long id, String field, Object value) {
        String key = USER_KEY_PREFIX + id;
        redisTemplate.opsForHash().put(key, field, value);
    }
    
    public void deleteUser(Long id) {
        redisTemplate.delete(USER_KEY_PREFIX + id);
    }
}

4.3 购物车

java
@Service
public class CartService {
    
    private final RedisTemplate<String, Object> redisTemplate;
    private static final String CART_KEY_PREFIX = "cart:";
    
    public void addItem(Long userId, Long productId, int quantity) {
        String key = CART_KEY_PREFIX + userId;
        redisTemplate.opsForHash().put(key, productId.toString(), quantity);
    }
    
    public void removeItem(Long userId, Long productId) {
        String key = CART_KEY_PREFIX + userId;
        redisTemplate.opsForHash().delete(key, productId.toString());
    }
    
    public void updateQuantity(Long userId, Long productId, int quantity) {
        String key = CART_KEY_PREFIX + userId;
        if (quantity <= 0) {
            redisTemplate.opsForHash().delete(key, productId.toString());
        } else {
            redisTemplate.opsForHash().put(key, productId.toString(), quantity);
        }
    }
    
    public Map<Object, Object> getCart(Long userId) {
        String key = CART_KEY_PREFIX + userId;
        return redisTemplate.opsForHash().entries(key);
    }
    
    public void clearCart(Long userId) {
        redisTemplate.delete(CART_KEY_PREFIX + userId);
    }
}

5. List 操作

5.1 基本操作

java
@Service
public class ListRedisService {
    
    private final RedisTemplate<String, Object> redisTemplate;
    
    public Long lPush(String key, Object value) {
        return redisTemplate.opsForList().leftPush(key, value);
    }
    
    public Long rPush(String key, Object value) {
        return redisTemplate.opsForList().rightPush(key, value);
    }
    
    public Object lPop(String key) {
        return redisTemplate.opsForList().leftPop(key);
    }
    
    public Object rPop(String key) {
        return redisTemplate.opsForList().rightPop(key);
    }
    
    public Object lIndex(String key, long index) {
        return redisTemplate.opsForList().index(key, index);
    }
    
    public Long lSize(String key) {
        return redisTemplate.opsForList().size(key);
    }
    
    public List<Object> lRange(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }
    
    public void lTrim(String key, long start, long end) {
        redisTemplate.opsForList().trim(key, start, end);
    }
}

5.2 消息队列

java
@Service
public class MessageQueueService {
    
    private final RedisTemplate<String, Object> redisTemplate;
    private static final String QUEUE_KEY = "message:queue";
    
    public void sendMessage(Object message) {
        redisTemplate.opsForList().rightPush(QUEUE_KEY, message);
    }
    
    public Object receiveMessage() {
        return redisTemplate.opsForList().leftPop(QUEUE_KEY);
    }
    
    public Object receiveMessageBlocking(long timeout, TimeUnit unit) {
        return redisTemplate.opsForList().leftPop(QUEUE_KEY, timeout, unit);
    }
}

5.3 最新列表

java
@Service
public class LatestListService {
    
    private final RedisTemplate<String, Object> redisTemplate;
    
    public void addLatest(String key, Object value, int maxSize) {
        redisTemplate.opsForList().leftPush(key, value);
        redisTemplate.opsForList().trim(key, 0, maxSize - 1);
    }
    
    public List<Object> getLatest(String key, int count) {
        return redisTemplate.opsForList().range(key, 0, count - 1);
    }
}

6. Set 操作

6.1 基本操作

java
@Service
public class SetRedisService {
    
    private final RedisTemplate<String, Object> redisTemplate;
    
    public Long sAdd(String key, Object... values) {
        return redisTemplate.opsForSet().add(key, values);
    }
    
    public Long sRemove(String key, Object... values) {
        return redisTemplate.opsForSet().remove(key, values);
    }
    
    public Object sPop(String key) {
        return redisTemplate.opsForSet().pop(key);
    }
    
    public Boolean sIsMember(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }
    
    public Set<Object> sMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }
    
    public Long sSize(String key) {
        return redisTemplate.opsForSet().size(key);
    }
    
    public Set<Object> sIntersect(String key1, String key2) {
        return redisTemplate.opsForSet().intersect(key1, key2);
    }
    
    public Set<Object> sUnion(String key1, String key2) {
        return redisTemplate.opsForSet().union(key1, key2);
    }
    
    public Set<Object> sDifference(String key1, String key2) {
        return redisTemplate.opsForSet().difference(key1, key2);
    }
}

6.2 标签系统

java
@Service
public class TagService {
    
    private final RedisTemplate<String, Object> redisTemplate;
    private static final String TAG_PREFIX = "tag:";
    private static final String ARTICLE_TAGS_PREFIX = "article:tags:";
    
    public void addTags(Long articleId, Set<String> tags) {
        String articleKey = ARTICLE_TAGS_PREFIX + articleId;
        redisTemplate.opsForSet().add(articleKey, tags.toArray());
        
        for (String tag : tags) {
            redisTemplate.opsForSet().add(TAG_PREFIX + tag, articleId);
        }
    }
    
    public Set<Object> getArticlesByTag(String tag) {
        return redisTemplate.opsForSet().members(TAG_PREFIX + tag);
    }
    
    public Set<Object> getCommonArticles(String tag1, String tag2) {
        return redisTemplate.opsForSet().intersect(TAG_PREFIX + tag1, TAG_PREFIX + tag2);
    }
    
    public Set<Object> getArticleTags(Long articleId) {
        return redisTemplate.opsForSet().members(ARTICLE_TAGS_PREFIX + articleId);
    }
}

7. Sorted Set 操作

7.1 基本操作

java
@Service
public class SortedSetRedisService {
    
    private final RedisTemplate<String, Object> redisTemplate;
    
    public Boolean zAdd(String key, Object value, double score) {
        return redisTemplate.opsForZSet().add(key, value, score);
    }
    
    public Long zRemove(String key, Object... values) {
        return redisTemplate.opsForZSet().remove(key, values);
    }
    
    public Double zScore(String key, Object value) {
        return redisTemplate.opsForZSet().score(key, value);
    }
    
    public Long zRank(String key, Object value) {
        return redisTemplate.opsForZSet().rank(key, value);
    }
    
    public Long zReverseRank(String key, Object value) {
        return redisTemplate.opsForZSet().reverseRank(key, value);
    }
    
    public Long zSize(String key) {
        return redisTemplate.opsForZSet().size(key);
    }
    
    public Set<Object> zRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().range(key, start, end);
    }
    
    public Set<Object> zReverseRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().reverseRange(key, start, end);
    }
    
    public Set<ZSetOperations.TypedTuple<Object>> zRangeWithScores(
            String key, long start, long end) {
        return redisTemplate.opsForZSet().rangeWithScores(key, start, end);
    }
    
    public Long zCount(String key, double min, double max) {
        return redisTemplate.opsForZSet().count(key, min, max);
    }
    
    public Long zRemoveRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().removeRange(key, start, end);
    }
    
    public Long zRemoveRangeByScore(String key, double min, double max) {
        return redisTemplate.opsForZSet().removeRangeByScore(key, min, max);
    }
}

7.2 排行榜

java
@Service
public class LeaderboardService {
    
    private final RedisTemplate<String, Object> redisTemplate;
    private static final String LEADERBOARD_KEY = "game:leaderboard";
    
    public void addScore(String playerId, double score) {
        redisTemplate.opsForZSet().add(LEADERBOARD_KEY, playerId, score);
    }
    
    public void incrementScore(String playerId, double delta) {
        redisTemplate.opsForZSet().incrementScore(LEADERBOARD_KEY, playerId, delta);
    }
    
    public Long getRank(String playerId) {
        return redisTemplate.opsForZSet().reverseRank(LEADERBOARD_KEY, playerId);
    }
    
    public Double getScore(String playerId) {
        return redisTemplate.opsForZSet().score(LEADERBOARD_KEY, playerId);
    }
    
    public List<LeaderboardEntry> getTopPlayers(int count) {
        Set<ZSetOperations.TypedTuple<Object>> entries = 
            redisTemplate.opsForZSet().reverseRangeWithScores(LEADERBOARD_KEY, 0, count - 1);
        
        List<LeaderboardEntry> result = new ArrayList<>();
        long rank = 1;
        for (ZSetOperations.TypedTuple<Object> entry : entries) {
            result.add(new LeaderboardEntry(
                rank++,
                entry.getValue().toString(),
                entry.getScore()
            ));
        }
        return result;
    }
    
    public List<LeaderboardEntry> getPlayersAround(String playerId, int range) {
        Long rank = redisTemplate.opsForZSet().reverseRank(LEADERBOARD_KEY, playerId);
        if (rank == null) {
            return Collections.emptyList();
        }
        
        long start = Math.max(0, rank - range);
        long end = rank + range;
        
        Set<ZSetOperations.TypedTuple<Object>> entries = 
            redisTemplate.opsForZSet().reverseRangeWithScores(LEADERBOARD_KEY, start, end);
        
        List<LeaderboardEntry> result = new ArrayList<>();
        long currentRank = start + 1;
        for (ZSetOperations.TypedTuple<Object> entry : entries) {
            result.add(new LeaderboardEntry(
                currentRank++,
                entry.getValue().toString(),
                entry.getScore()
            ));
        }
        return result;
    }
    
    public void removePlayer(String playerId) {
        redisTemplate.opsForZSet().remove(LEADERBOARD_KEY, playerId);
    }
}

public record LeaderboardEntry(
    long rank,
    String playerId,
    Double score
) {}

8. 发布订阅

8.1 消息发布

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

8.2 消息订阅

java
@Component
public class RedisMessageListener implements MessageListener {
    
    @Override
    public void onMessage(Message message, byte[] pattern) {
        String channel = new String(message.getChannel());
        String body = new String(message.getBody());
        
        System.out.println("Received message from " + channel + ": " + body);
    }
}

@Configuration
public class RedisSubscriberConfig {
    
    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer(
            RedisConnectionFactory connectionFactory,
            RedisMessageListener listener) {
        
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.addMessageListener(listener, new ChannelTopic("news"));
        container.addMessageListener(listener, new PatternTopic("chat:*"));
        return container;
    }
}

9. 缓存注解

9.1 启用缓存

java
@Configuration
@EnableCaching
public class CacheConfig {
    
    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(30))
            .serializeKeysWith(RedisSerializationContext.SerializationPair
                .fromSerializer(new StringRedisSerializer()))
            .serializeValuesWith(RedisSerializationContext.SerializationPair
                .fromSerializer(new GenericJackson2JsonRedisSerializer()))
            .disableCachingNullValues();
        
        return RedisCacheManager.builder(connectionFactory)
            .cacheDefaults(config)
            .build();
    }
}

9.2 使用缓存注解

java
@Service
public class UserService {
    
    @Cacheable(value = "users", key = "#id")
    public User findById(Long id) {
        return userRepository.findById(id).orElse(null);
    }
    
    @Cacheable(value = "users", key = "#username")
    public User findByUsername(String username) {
        return userRepository.findByUsername(username).orElse(null);
    }
    
    @CachePut(value = "users", key = "#user.id")
    public User save(User user) {
        return userRepository.save(user);
    }
    
    @CacheEvict(value = "users", key = "#id")
    public void deleteById(Long id) {
        userRepository.deleteById(id);
    }
    
    @CacheEvict(value = "users", allEntries = true)
    public void deleteAll() {
        userRepository.deleteAll();
    }
}

10. 小结

本章学习了 Spring 整合 Redis 的核心内容:

内容要点
基本配置LettuceConnectionFactory、RedisTemplate
String缓存、计数器、分布式锁
Hash对象存储、购物车
List消息队列、最新列表
Set标签系统、社交关系
Sorted Set排行榜、延时队列
发布订阅消息广播、实时通知
缓存注解@Cacheable、@CachePut、@CacheEvict

下一章将学习 JWT 认证实战。