前言
Redis 是高性能的键值存储数据库,广泛用于缓存、分布式锁、计数器等场景。Spring Boot 提供了 RedisTemplate 和 Spring Cache 两种 Redis 集成方式。
快速开始
1. 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
2. 配置 Redis
spring:
data:
redis:
host: localhost
port: 6379
password:
database: 0
timeout: 3000ms
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: -1ms
3. 配置序列化
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// JSON 序列化
Jackson2JsonRedisSerializer<Object> serializer =
new Jackson2JsonRedisSerializer<>(Object.class);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
}
RedisTemplate 使用
1. 基本操作
@Service
@RequiredArgsConstructor
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 Boolean.TRUE.equals(redisTemplate.delete(key));
}
/**
* 判断是否存在
*/
public boolean hasKey(String key) {
return Boolean.TRUE.equals(redisTemplate.hasKey(key));
}
/**
* 设置过期时间
*/
public boolean expire(String key, long timeout, TimeUnit unit) {
return Boolean.TRUE.equals(redisTemplate.expire(key, timeout, unit));
}
}
2. Hash 操作
@Service
@RequiredArgsConstructor
public class RedisService {
private final RedisTemplate<String, Object> redisTemplate;
/**
* Hash 设置
*/
public void hSet(String key, String field, Object value) {
redisTemplate.opsForHash().put(key, field, value);
}
/**
* Hash 获取
*/
public Object hGet(String key, String field) {
return redisTemplate.opsForHash().get(key, field);
}
/**
* Hash 获取所有
*/
public Map<Object, Object> hGetAll(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* Hash 删除
*/
public void hDelete(String key, Object... fields) {
redisTemplate.opsForHash().delete(key, fields);
}
}
3. List 操作
@Service
@RequiredArgsConstructor
public class RedisService {
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 List<Object> lRange(String key, long start, long end) {
return redisTemplate.opsForList().range(key, start, end);
}
}
4. Set 操作
@Service
@RequiredArgsConstructor
public class RedisService {
private final RedisTemplate<String, Object> redisTemplate;
/**
* 添加
*/
public long sAdd(String key, Object value) {
return redisTemplate.opsForSet().add(key, value);
}
/**
* 获取所有
*/
public Set<Object> sMembers(String key) {
return redisTemplate.opsForSet().members(key);
}
/**
* 判断是否包含
*/
public boolean sIsMember(String key, Object value) {
return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(key, value));
}
/**
* 删除
*/
public long sRemove(String key, Object... values) {
return redisTemplate.opsForSet().remove(key, values);
}
}
5. ZSet 操作
@Service
@RequiredArgsConstructor
public class RedisService {
private final RedisTemplate<String, Object> redisTemplate;
/**
* 添加
*/
public boolean zAdd(String key, Object value, double score) {
return redisTemplate.opsForZSet().add(key, value, score);
}
/**
* 获取排名
*/
public Long zRank(String key, Object value) {
return redisTemplate.opsForZSet().rank(key, value);
}
/**
* 获取前 N 名
*/
public Set<Object> zTopN(String key, long n) {
return redisTemplate.opsForZSet().reverseRange(key, 0, n - 1);
}
/**
* 获取分数
*/
public Double zScore(String key, Object value) {
return redisTemplate.opsForZSet().score(key, value);
}
}
Spring Cache
1. 启用缓存
@SpringBootApplication
@EnableCaching
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
2. 配置缓存管理器
@Configuration
public class CacheConfig {
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30))
.serializeKeysWith(RedisSerializationContext.SerializationPair
.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()))
.disableCachingNullValues();
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.withInitialCacheConfigurations(singletonMap("users", config.entryTtl(Duration.ofMinutes(60))))
.build();
}
}
3. 使用缓存
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
/**
* 缓存查询结果
*/
@Cacheable(value = "users", key = "#id")
public UserDTO findById(Long id) {
return userRepository.findById(id)
.map(this::convertToDTO)
.orElseThrow(() -> BusinessException.notFound("用户"));
}
/**
* 更新缓存
*/
@CachePut(value = "users", key = "#user.id")
public UserDTO update(UserDTO user) {
return userRepository.save(convertToEntity(user));
}
/**
* 删除缓存
*/
@CacheEvict(value = "users", key = "#id")
public void delete(Long id) {
userRepository.deleteById(id);
}
/**
* 清空缓存
*/
@CacheEvict(value = "users", allEntries = true)
public void clearCache() {
log.info("清空用户缓存");
}
}
4. 条件缓存
@Service
public class UserService {
/**
* 条件缓存
*/
@Cacheable(value = "users", key = "#id", condition = "#id > 10")
public UserDTO findById(Long id) {
return userRepository.findById(id)
.map(this::convertToDTO)
.orElse(null);
}
/**
* 除非条件
*/
@Cacheable(value = "users", key = "#id", unless = "#result == null")
public UserDTO findById(Long id) {
return userRepository.findById(id)
.map(this::convertToDTO)
.orElse(null);
}
}
分布式锁
1. 使用 Redisson
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.24.3</version>
</dependency>
2. 配置 Redisson
@Configuration
public class RedissonConfig {
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer()
.setAddress("redis://localhost:6379")
.setPassword(null)
.setDatabase(0);
return Redisson.create(config);
}
}
3. 使用分布式锁
@Service
@RequiredArgsConstructor
public class OrderService {
private final RedissonClient redissonClient;
public void createOrder(Long userId) {
RLock lock = redissonClient.getLock("order:lock:" + userId);
boolean locked = false;
try {
locked = lock.tryLock(3, 10, TimeUnit.SECONDS);
if (!locked) {
throw new BusinessException("获取锁失败");
}
// 业务逻辑
// ...
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new BusinessException("获取锁中断");
} finally {
if (locked && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
4. 使用 Spring Cache Lock
@Service
public class UserService {
@Cacheable(value = "users", key = "#id", cacheManager = "redisCacheManager")
public UserDTO findById(Long id) {
return userRepository.findById(id)
.map(this::convertToDTO)
.orElse(null);
}
}
Lua 脚本
1. 执行 Lua 脚本
@Service
@RequiredArgsConstructor
public class RedisService {
private final RedisTemplate<String, Object> redisTemplate;
/**
* 限流 Lua 脚本
*/
private static final String LIMIT_SCRIPT = """
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local expire = tonumber(ARGV[2])
local current = redis.call('INCR', key)
if current == 1 then
redis.call('EXPIRE', key, expire)
end
if current > limit then
return 0
else
return 1
end
""";
private final RedisScript<Long> limitScript;
/**
* 限流
*/
public boolean isAllowed(String key, int limit, int expire) {
Long result = redisTemplate.execute(
limitScript,
Collections.singletonList(key),
String.valueOf(limit),
String.valueOf(expire)
);
return result != null && result == 1;
}
}
2. 配置 RedisScript
@Configuration
public class RedisConfig {
@Bean
public RedisScript<Long> limitScript() {
RedisScript<Long> script = new DefaultRedisScript<>();
script.setScriptText("""
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local expire = tonumber(ARGV[2])
local current = redis.call('INCR', key)
if current == 1 then
redis.call('EXPIRE', key, expire)
end
if current > limit then
return 0
else
return 1
end
""");
script.setResultType(Long.class);
return script;
}
}
最佳实践
1. 缓存穿透
@Service
public class UserService {
/**
* 缓存穿透 - 缓存空值
*/
@Cacheable(value = "users", key = "#id")
public UserDTO findById(Long id) {
User user = userRepository.findById(id).orElse(null);
if (user == null) {
// 缓存空值,防止穿透
redisTemplate.opsForValue().set("user:null:" + id, "", 5, TimeUnit.MINUTES);
return null;
}
return convertToDTO(user);
}
}
2. 缓存击穿
@Service
public class UserService {
public UserDTO findById(Long id) {
String key = "user:" + id;
// 尝试从缓存获取
Object cached = redisTemplate.opsForValue().get(key);
if (cached != null) {
return (UserDTO) cached;
}
// 尝试获取分布式锁
RLock lock = redissonClient.getLock("lock:" + key);
try {
if (lock.tryLock(3, 10, TimeUnit.SECONDS)) {
// 双重检查
cached = redisTemplate.opsForValue().get(key);
if (cached != null) {
return (UserDTO) cached;
}
// 从数据库查询
User user = userRepository.findById(id).orElse(null);
if (user != null) {
UserDTO dto = convertToDTO(user);
redisTemplate.opsForValue().set(key, dto, 30, TimeUnit.MINUTES);
return dto;
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
return null;
}
}
3. 缓存雪崩
@Service
public class UserService {
@Cacheable(value = "users", key = "#id")
public UserDTO findById(Long id) {
User user = userRepository.findById(id).orElse(null);
return convertToDTO(user);
}
}
# 配置不同的过期时间,避免同时过期
spring:
cache:
redis:
time-to-live: 1800000 # 30 分钟
random-ttl: true # 随机过期时间
总结
Redis 集成要点:
- ✅ RedisTemplate - 五种数据结构操作
- ✅ Spring Cache - @Cacheable、@CachePut、@CacheEvict
- ✅ 分布式锁 - Redisson
- ✅ Lua 脚本 - 限流、原子操作
- ✅ 最佳实践 - 缓存穿透、击穿、雪崩
Redis 是 Spring Boot 应用中不可或缺的缓存和分布式组件。