Spring Data Redis 使用详解
1. Spring Data Redis 核心组件
1.1 Maven 依赖配置
xml
<!-- Spring Boot Redis Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 连接池依赖(推荐使用 Lettuce,Spring Boot 2.x+ 默认使用) -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- Jackson 序列化(用于 JSON 序列化) -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
1.2 配置文件 (application.yml)
yaml
spring:
redis:
# Redis服务器地址
host: localhost
# Redis服务器端口
port: 6379
# 数据库索引(默认0)
database: 0
# 连接超时时间
timeout: 2000ms
# 密码(没有密码则不设置)
password:
# Lettuce 连接池配置
lettuce:
pool:
# 最大连接数(默认8,-1表示无限制)
max-active: 100
# 最大空闲连接数
max-idle: 10
# 最小空闲连接数
min-idle: 5
# 获取连接时的最大等待时间(默认-1,不超时)
max-wait: 1000ms
# 空闲连接检查时间间隔
time-between-eviction-runs: 30000ms
2. RedisTemplate 核心方法详解
2.1 RedisTemplate 配置类
java
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* Redis 配置类
* 配置序列化方式,防止乱码
*/
@Configuration
public class RedisConfig {
/**
* 配置 RedisTemplate
* 使用 Jackson2JsonRedisSerializer 替代默认的 JdkSerializationRedisSerializer
*
* @param redisConnectionFactory Redis连接工厂
* @return 配置好的 RedisTemplate
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
// 创建 JSON 序列化器
Jackson2JsonRedisSerializer<Object> jsonSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
// 配置 ObjectMapper,支持多态类型
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.activateDefaultTyping(
LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.PROPERTY
);
jsonSerializer.setObjectMapper(objectMapper);
// 设置 key 的序列化器为 String
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
// 设置 value 的序列化器为 JSON
template.setValueSerializer(jsonSerializer);
template.setHashValueSerializer(jsonSerializer);
template.afterPropertiesSet();
return template;
}
/**
* 配置 Redis String 序列化器(纯字符串操作时使用)
*/
@Bean
public RedisSerializer<String> stringRedisSerializer() {
return new StringRedisSerializer();
}
}
2.2 RedisTemplate 核心操作方法
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* RedisTemplate 核心操作方法示例
* 包含了常用的 Redis 操作
*/
@Component
public class RedisTemplateService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate; // Spring Boot 自动配置的
// ============ Key 相关操作 ============
/**
* 判断 key 是否存在
* @param key 键
* @return true-存在, false-不存在
*/
public Boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除 key
* @param key 可以传一个或多个
* @return 删除的 key 数量
*/
public Long delete(String... keys) {
if (keys == null || keys.length == 0) {
return 0L;
}
if (keys.length == 1) {
return redisTemplate.delete(keys[0]) ? 1L : 0L;
} else {
return redisTemplate.delete(Arrays.asList(keys));
}
}
/**
* 设置过期时间
* @param key 键
* @param time 时间
* @param unit 时间单位
* @return true-成功, false-失败
*/
public Boolean expire(String key, long time, TimeUnit unit) {
try {
if (time > 0) {
return redisTemplate.expire(key, time, unit);
}
return false;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 获取过期时间
* @param key 键
* @param unit 时间单位
* @return 剩余过期时间
*/
public Long getExpire(String key, TimeUnit unit) {
return redisTemplate.getExpire(key, unit);
}
/**
* 获取 key 的类型
* @param key 键
* @return 数据类型
*/
public DataType type(String key) {
return redisTemplate.type(key);
}
// ============ String 类型操作 ============
/**
* 设置 String 类型的值
* @param key 键
* @param value 值
*/
public void set(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
/**
* 设置 String 类型的值并设置过期时间
* @param key 键
* @param value 值
* @param time 过期时间
* @param unit 时间单位
*/
public void set(String key, Object value, long time, TimeUnit unit) {
redisTemplate.opsForValue().set(key, value, time, unit);
}
/**
* 获取 String 类型的值
* @param key 键
* @return 值
*/
public Object get(String key) {
return redisTemplate.opsForValue().get(key);
}
/**
* 获取 String 类型的值(指定类型)
* @param key 键
* @param clazz 返回值类型
* @return 转换后的值
*/
public <T> T get(String key, Class<T> clazz) {
Object value = redisTemplate.opsForValue().get(key);
return clazz.cast(value);
}
/**
* 递增(原子操作)
* @param key 键
* @param delta 增加的值
* @return 递增后的值
*/
public Long increment(String key, long delta) {
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减(原子操作)
* @param key 键
* @param delta 减少的值
* @return 递减后的值
*/
public Long decrement(String key, long delta) {
return redisTemplate.opsForValue().decrement(key, delta);
}
/**
* 设置新值并返回旧值(原子操作)
* @param key 键
* @param value 新值
* @return 旧值
*/
public Object getAndSet(String key, Object value) {
return redisTemplate.opsForValue().getAndSet(key, value);
}
// ============ Hash 类型操作 ============
/**
* 设置 Hash 类型的值
* @param key 键
* @param hashKey Hash键
* @param value 值
*/
public void hSet(String key, String hashKey, Object value) {
redisTemplate.opsForHash().put(key, hashKey, value);
}
/**
* 批量设置 Hash 值
* @param key 键
* @param map 多个Hash键值对
*/
public void hSetAll(String key, Map<String, Object> map) {
redisTemplate.opsForHash().putAll(key, map);
}
/**
* 获取 Hash 值
* @param key 键
* @param hashKey Hash键
* @return 值
*/
public Object hGet(String key, String hashKey) {
return redisTemplate.opsForHash().get(key, hashKey);
}
/**
* 获取所有 Hash 键值对
* @param key 键
* @return 所有键值对
*/
public Map<Object, Object> hGetAll(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* 删除 Hash 键值对
* @param key 键
* @param hashKeys 要删除的Hash键
* @return 删除的数量
*/
public Long hDelete(String key, Object... hashKeys) {
return redisTemplate.opsForHash().delete(key, hashKeys);
}
/**
* 判断 Hash 键是否存在
* @param key 键
* @param hashKey Hash键
* @return true-存在, false-不存在
*/
public Boolean hHasKey(String key, String hashKey) {
return redisTemplate.opsForHash().hasKey(key, hashKey);
}
/**
* 获取 Hash 中所有键
* @param key 键
* @return 所有Hash键
*/
public Set<Object> hKeys(String key) {
return redisTemplate.opsForHash().keys(key);
}
/**
* 获取 Hash 中所有值
* @param key 键
* @return 所有Hash值
*/
public List<Object> hValues(String key) {
return redisTemplate.opsForHash().values(key);
}
// ============ List 类型操作 ============
/**
* 从列表左侧插入值
* @param key 键
* @param value 值
* @return 插入后列表的长度
*/
public Long lLeftPush(String key, Object value) {
return redisTemplate.opsForList().leftPush(key, value);
}
/**
* 从列表右侧插入值
* @param key 键
* @param value 值
* @return 插入后列表的长度
*/
public Long lRightPush(String key, Object value) {
return redisTemplate.opsForList().rightPush(key, value);
}
/**
* 从列表左侧弹出值
* @param key 键
* @return 弹出的值
*/
public Object lLeftPop(String key) {
return redisTemplate.opsForList().leftPop(key);
}
/**
* 从列表右侧弹出值
* @param key 键
* @return 弹出的值
*/
public Object lRightPop(String key) {
return redisTemplate.opsForList().rightPop(key);
}
/**
* 获取列表指定范围的元素
* @param key 键
* @param start 开始索引(0表示第一个)
* @param end 结束索引(-1表示最后一个)
* @return 元素列表
*/
public List<Object> lRange(String key, long start, long end) {
return redisTemplate.opsForList().range(key, start, end);
}
/**
* 获取列表长度
* @param key 键
* @return 列表长度
*/
public Long lSize(String key) {
return redisTemplate.opsForList().size(key);
}
// ============ Set 类型操作 ============
/**
* 向集合添加元素
* @param key 键
* @param values 一个或多个值
* @return 成功添加的元素数量
*/
public Long sAdd(String key, Object... values) {
return redisTemplate.opsForSet().add(key, values);
}
/**
* 从集合移除元素
* @param key 键
* @param values 一个或多个值
* @return 移除的元素数量
*/
public Long sRemove(String key, Object... values) {
return redisTemplate.opsForSet().remove(key, values);
}
/**
* 判断元素是否在集合中
* @param key 键
* @param value 值
* @return true-存在, false-不存在
*/
public Boolean sIsMember(String key, Object value) {
return redisTemplate.opsForSet().isMember(key, value);
}
/**
* 获取集合所有元素
* @param key 键
* @return 所有元素
*/
public Set<Object> sMembers(String key) {
return redisTemplate.opsForSet().members(key);
}
/**
* 获取集合大小
* @param key 键
* @return 集合大小
*/
public Long sSize(String key) {
return redisTemplate.opsForSet().size(key);
}
// ============ ZSet 类型操作 ============
/**
* 向有序集合添加元素
* @param key 键
* @param value 值
* @param score 分数
* @return true-成功, false-失败
*/
public Boolean zAdd(String key, Object value, double score) {
return redisTemplate.opsForZSet().add(key, value, score);
}
/**
* 从有序集合移除元素
* @param key 键
* @param values 一个或多个值
* @return 移除的元素数量
*/
public Long zRemove(String key, Object... values) {
return redisTemplate.opsForZSet().remove(key, values);
}
/**
* 获取有序集合指定范围的元素(按分数从小到大)
* @param key 键
* @param start 开始位置
* @param end 结束位置
* @return 元素集合
*/
public Set<Object> zRange(String key, long start, long end) {
return redisTemplate.opsForZSet().range(key, start, end);
}
/**
* 获取有序集合指定范围的元素(按分数从大到小)
* @param key 键
* @param start 开始位置
* @param end 结束位置
* @return 元素集合
*/
public Set<Object> zReverseRange(String key, long start, long end) {
return redisTemplate.opsForZSet().reverseRange(key, start, end);
}
/**
* 获取元素的分数
* @param key 键
* @param value 值
* @return 分数
*/
public Double zScore(String key, Object value) {
return redisTemplate.opsForZSet().score(key, value);
}
// ============ 事务操作 ============
/**
* 执行事务操作
* @param callback 事务回调
* @return 执行结果
*/
public Object executeTransaction(RedisCallback<Object> callback) {
return redisTemplate.execute(callback);
}
/**
* 执行 SessionCallback(支持事务中的多个操作)
* @param callback 会话回调
* @return 执行结果
*/
public Object executeSession(SessionCallback<Object> callback) {
return redisTemplate.execute(callback);
}
// ============ 管道操作 ============
/**
* 执行管道操作(批量执行命令,减少网络开销)
* @param callback 管道回调
* @return 执行结果列表
*/
public List<Object> executePipelined(RedisCallback<?> callback) {
return redisTemplate.executePipelined(callback);
}
// ============ 发布订阅 ============
/**
* 发布消息到指定频道
* @param channel 频道名称
* @param message 消息内容
*/
public void publish(String channel, Object message) {
redisTemplate.convertAndSend(channel, message);
}
}
3. Redis Repository 使用(类似 JPA)
3.1 实体类定义
java
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;
import org.springframework.data.redis.core.index.Indexed;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 用户实体类
* @RedisHash 注解指定 Redis 中存储的 Hash 名称
*/
@RedisHash(value = "user", timeToLive = 3600) // 设置过期时间(秒)
public class User implements Serializable {
@Id // 标记为主键
private String id;
@Indexed // 创建索引,可以按此字段查询
private String username;
private String email;
private Integer age;
@Indexed
private LocalDateTime createTime;
// 省略构造器、getter、setter、toString等方法
}
3.2 Repository 接口
java
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.List;
/**
* User Repository 接口
* 继承 CrudRepository 获得基本的 CRUD 操作
* 可以通过方法命名约定自动生成查询方法
*/
@Repository
public interface UserRepository extends CrudRepository<User, String> {
/**
* 根据用户名查找用户
* Spring Data Redis 会自动实现这个方法
*/
List<User> findByUsername(String username);
/**
* 根据邮箱查找用户
*/
User findByEmail(String email);
/**
* 根据年龄范围查找用户
*/
List<User> findByAgeBetween(Integer minAge, Integer maxAge);
/**
* 根据创建时间查找用户
*/
List<User> findByCreateTimeAfter(LocalDateTime createTime);
/**
* 根据用户名模糊查询
*/
List<User> findByUsernameContaining(String keyword);
/**
* 分页查询用户
*/
Page<User> findAll(Pageable pageable);
/**
* 统计用户数量
*/
Long countByAgeGreaterThan(Integer age);
/**
* 删除用户
*/
void deleteByUsername(String username);
}
3.3 Repository 使用示例
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* Repository 使用示例服务
*/
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
/**
* 保存用户
*/
public User saveUser(User user) {
user.setCreateTime(LocalDateTime.now());
return userRepository.save(user);
}
/**
* 批量保存用户
*/
public Iterable<User> saveAllUsers(List<User> users) {
users.forEach(user -> user.setCreateTime(LocalDateTime.now()));
return userRepository.saveAll(users);
}
/**
* 根据ID查找用户
*/
public Optional<User> findById(String id) {
return userRepository.findById(id);
}
/**
* 查找所有用户
*/
public Iterable<User> findAllUsers() {
return userRepository.findAll();
}
/**
* 分页查询用户
*/
public Page<User> findUsersByPage(int page, int size) {
PageRequest pageRequest = PageRequest.of(page, size, Sort.by("createTime").descending());
return userRepository.findAll(pageRequest);
}
/**
* 根据用户名查询
*/
public List<User> findUsersByUsername(String username) {
return userRepository.findByUsername(username);
}
/**
* 根据年龄范围查询
*/
public List<User> findUsersByAgeRange(Integer minAge, Integer maxAge) {
return userRepository.findByAgeBetween(minAge, maxAge);
}
/**
* 统计用户数量
*/
public long countUsers() {
return userRepository.count();
}
/**
* 删除用户
*/
public void deleteUser(String id) {
userRepository.deleteById(id);
}
/**
* 删除所有用户
*/
public void deleteAllUsers() {
userRepository.deleteAll();
}
}
4. 高级特性:Redis 缓存
4.1 启用缓存配置
java
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
/**
* Redis 缓存配置
*/
@Configuration
@EnableCaching // 启用缓存注解
public class RedisCacheConfig extends CachingConfigurerSupport {
/**
* 配置缓存管理器
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
// 默认缓存配置
RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1)) // 默认过期时间1小时
.disableCachingNullValues() // 不缓存null值
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(
new Jackson2JsonRedisSerializer<>(Object.class)
)
);
// 为特定缓存名称设置不同的配置
Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
// user 缓存:2小时过期
cacheConfigurations.put("user", defaultCacheConfig.entryTtl(Duration.ofHours(2)));
// product 缓存:30分钟过期
cacheConfigurations.put("product", defaultCacheConfig.entryTtl(Duration.ofMinutes(30)));
// order 缓存:1天过期
cacheConfigurations.put("order", defaultCacheConfig.entryTtl(Duration.ofDays(1)));
return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(defaultCacheConfig)
.withInitialCacheConfigurations(cacheConfigurations)
.transactionAware() // 支持事务
.build();
}
/**
* 自定义缓存key生成器
*/
@Bean
@Override
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(".");
sb.append(method.getName());
if (params.length > 0) {
sb.append("[");
for (Object param : params) {
sb.append(param.toString());
sb.append(",");
}
sb.deleteCharAt(sb.length() - 1);
sb.append("]");
}
return sb.toString();
}
};
}
}
4.2 使用缓存注解
java
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* 缓存注解使用示例
*/
@Service
public class CacheService {
/**
* @Cacheable - 缓存查询结果
* key: 缓存键,支持SpEL表达式
* value/cacheNames: 缓存名称
* unless: 条件表达式,为true时不缓存
*/
@Cacheable(value = "user", key = "#id", unless = "#result == null")
public User getUserById(String id) {
// 模拟数据库查询
System.out.println("查询数据库获取用户: " + id);
return new User(id, "用户" + id);
}
/**
* @CachePut - 更新缓存
* 总是执行方法,并用结果更新缓存
*/
@CachePut(value = "user", key = "#user.id")
public User updateUser(User user) {
System.out.println("更新用户信息: " + user.getId());
// 更新数据库操作
return user;
}
/**
* @CacheEvict - 删除缓存
* allEntries: 是否删除缓存中所有数据
* beforeInvocation: 是否在方法执行前删除缓存
*/
@CacheEvict(value = "user", key = "#id")
public void deleteUser(String id) {
System.out.println("删除用户: " + id);
// 删除数据库操作
}
/**
* @CacheEvict - 清空整个缓存
*/
@CacheEvict(value = "user", allEntries = true)
public void clearAllUserCache() {
System.out.println("清空用户缓存");
}
/**
* @Caching - 组合多个缓存操作
*/
@Caching(
cacheable = {
@Cacheable(value = "user", key = "#username")
},
put = {
@CachePut(value = "user", key = "#result.id")
}
)
public User getUserByUsername(String username) {
System.out.println("根据用户名查询用户: " + username);
// 查询数据库
return new User("123", username);
}
/**
* @CacheConfig - 类级别的缓存配置
*/
@Service
@CacheConfig(cacheNames = "product") // 指定默认缓存名称
public class ProductService {
@Cacheable(key = "#id")
public Product getProduct(String id) {
return new Product(id, "商品" + id);
}
@CacheEvict(key = "#id")
public void deleteProduct(String id) {
// 删除操作
}
}
}
5. 实际应用场景示例
5.1 分布式锁实现
java
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* 基于 Redis 的分布式锁
*/
@Component
public class DistributedLock {
@Autowired
private RedisTemplate<String, String> redisTemplate;
// 锁前缀
private static final String LOCK_PREFIX = "lock:";
// 锁的默认过期时间(秒)
private static final long DEFAULT_EXPIRE_TIME = 30;
// 获取锁的等待时间(毫秒)
private static final long DEFAULT_WAIT_TIME = 3000;
// Lua脚本:释放锁(确保原子性)
private static final String UNLOCK_SCRIPT =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
/**
* 尝试获取锁
* @param lockKey 锁的key
* @param expireTime 过期时间(秒)
* @return 锁的value(用于释放锁)
*/
public String tryLock(String lockKey, long expireTime) {
String lockValue = UUID.randomUUID().toString();
String fullKey = LOCK_PREFIX + lockKey;
Boolean success = redisTemplate.opsForValue().setIfAbsent(
fullKey,
lockValue,
expireTime,
TimeUnit.SECONDS
);
return Boolean.TRUE.equals(success) ? lockValue : null;
}
/**
* 获取锁(阻塞等待)
*/
public String lock(String lockKey) throws InterruptedException {
return lock(lockKey, DEFAULT_EXPIRE_TIME, DEFAULT_WAIT_TIME);
}
/**
* 获取锁(可设置等待时间)
*/
public String lock(String lockKey, long expireTime, long waitTime) throws InterruptedException {
String lockValue;
long startTime = System.currentTimeMillis();
while ((lockValue = tryLock(lockKey, expireTime)) == null) {
// 检查是否超时
if (System.currentTimeMillis() - startTime > waitTime) {
throw new RuntimeException("获取锁超时");
}
// 等待一段时间再重试
Thread.sleep(100);
}
return lockValue;
}
/**
* 释放锁
*/
public boolean unlock(String lockKey, String lockValue) {
String fullKey = LOCK_PREFIX + lockKey;
// 使用Lua脚本确保原子性
RedisScript<Long> script = new DefaultRedisScript<>(UNLOCK_SCRIPT, Long.class);
Long result = redisTemplate.execute(
script,
Collections.singletonList(fullKey),
lockValue
);
return result != null && result > 0;
}
/**
* 使用锁执行任务(模板方法)
*/
public <T> T executeWithLock(String lockKey, long expireTime, LockTask<T> task) {
String lockValue = null;
try {
// 获取锁
lockValue = lock(lockKey, expireTime, 5000);
// 执行任务
return task.execute();
} catch (Exception e) {
throw new RuntimeException("执行任务失败", e);
} finally {
// 释放锁
if (lockValue != null) {
unlock(lockKey, lockValue);
}
}
}
/**
* 锁任务接口
*/
public interface LockTask<T> {
T execute();
}
}
5.2 限流器实现
java
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
/**
* 基于 Redis 的限流器(令牌桶算法)
*/
@Component
public class RateLimiter {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// Lua脚本:令牌桶算法
private static final String RATE_LIMIT_SCRIPT =
"local key = KEYS[1] " +
"local limit = tonumber(ARGV[1]) " +
"local interval = tonumber(ARGV[2]) " +
"local current = tonumber(redis.call('get', key) or '0') " +
"if current + 1 > limit then " +
" return 0 " +
"else " +
" redis.call('incrby', key, 1) " +
" if current == 0 then " +
" redis.call('expire', key, interval) " +
" end " +
" return 1 " +
"end";
/**
* 尝试获取令牌
* @param key 限流key(如:user:1001)
* @param limit 限制数量(如:每分钟100次)
* @param interval 时间间隔(秒)
* @return true-获取成功,false-被限流
*/
public boolean tryAcquire(String key, int limit, int interval) {
List<String> keys = Arrays.asList(key);
DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setScriptText(RATE_LIMIT_SCRIPT);
script.setResultType(Long.class);
Long result = redisTemplate.execute(
script,
keys,
limit,
interval
);
return result != null && result == 1;
}
/**
* 滑动窗口限流
*/
public boolean slidingWindowLimit(String key, int limit, int windowSeconds) {
long currentTime = System.currentTimeMillis();
long windowStartTime = currentTime - windowSeconds * 1000L;
// 使用ZSet存储请求时间戳
redisTemplate.opsForZSet().removeRangeByScore(key, 0, windowStartTime);
// 获取窗口内的请求数量
Long count = redisTemplate.opsForZSet().zCard(key);
if (count != null && count >= limit) {
return false;
}
// 添加当前请求
redisTemplate.opsForZSet().add(key, String.valueOf(currentTime), currentTime);
redisTemplate.expire(key, windowSeconds, TimeUnit.SECONDS);
return true;
}
}
6. 性能优化建议
-
序列化选择
- 优先使用 Jackson2JsonRedisSerializer(可读性好)
- 高并发场景考虑使用 Kryo(性能好)
-
连接池配置
yamlspring: redis: lettuce: pool: max-active: 200 # 根据并发量调整 max-idle: 50 min-idle: 20 max-wait: 1000ms -
批量操作
- 使用 pipeline 减少网络开销
- 批量操作使用 mget、mset
-
监控和调试
java// 开启 Redis 命令监控 @Bean public LettuceConnectionFactory redisConnectionFactory() { LettuceConnectionFactory factory = new LettuceConnectionFactory(); factory.setShareNativeConnection(false); // 关闭连接共享,便于监控 return factory; }
Spring Data Redis 提供了丰富的功能和良好的抽象,大大简化了 Redis 操作。掌握这些核心方法和最佳实践,可以帮助你在项目中高效、安全地使用 Redis。