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 认证实战。