Redis
Spring Boot 集成 Redis 完整指南
Redis 是一个开源的内存数据结构存储系统,常用作数据库、缓存和消息代理。在 Spring Boot 中集成 Redis 可以显著提升应用性能。以下是完整的集成指南。
- 添加依赖
在 pom.xml
中添加 Redis 相关依赖:
<!-- Spring Data Redis 核心依赖 -->
<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>
<!-- 如果需要使用JSON序列化 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
- 基本配置
在 application.properties
或 application.yml
中配置 Redis:
# Redis基本配置
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=yourpassword
spring.redis.database=0
# 连接池配置
spring.redis.lettuce.pool.enabled=true
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
spring.redis.lettuce.shutdown-timeout=100ms
YAML 格式:
spring:
redis:
host: localhost
port: 6379
password: yourpassword
database: 0
lettuce:
pool:
enabled: true
max-active: 8
max-idle: 8
min-idle: 0
max-wait: -1ms
shutdown-timeout: 100ms
- Redis 配置类
创建自定义配置类来配置 RedisTemplate 和序列化方式:
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.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
// 使用GenericJackson2JsonRedisSerializer来序列化和反序列化redis的value值
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
template.afterPropertiesSet();
return template;
}
}
- 基本操作示例
4.1 注入 RedisTemplate
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class RedisService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 设置缓存
public void set(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
// 设置缓存并设置过期时间
public void setWithExpire(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);
}
// 判断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, TimeUnit unit) {
return redisTemplate.getExpire(key, unit);
}
}
4.2 使用示例
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/redis")
public class RedisController {
@Autowired
private RedisService redisService;
@PostMapping("/set")
public String set(@RequestParam String key, @RequestParam String value) {
redisService.set(key, value);
return "设置成功";
}
@GetMapping("/get")
public Object get(@RequestParam String key) {
return redisService.get(key);
}
@PostMapping("/setWithExpire")
public String setWithExpire(@RequestParam String key,
@RequestParam String value,
@RequestParam long timeout) {
redisService.setWithExpire(key, value, timeout, TimeUnit.SECONDS);
return "设置成功,过期时间:" + timeout + "秒";
}
@DeleteMapping("/delete")
public Boolean delete(@RequestParam String key) {
return redisService.delete(key);
}
}
- 高级功能
5.1 使用 Hash 结构
// 在RedisService中添加方法
// 设置Hash结构
public void hSet(String key, String hashKey, Object value) {
redisTemplate.opsForHash().put(key, hashKey, value);
}
// 获取Hash结构
public Object hGet(String key, String hashKey) {
return redisTemplate.opsForHash().get(key, hashKey);
}
// 获取整个Hash
public Map<Object, Object> hGetAll(String key) {
return redisTemplate.opsForHash().entries(key);
}
// 删除Hash中的某个键
public Long hDelete(String key, Object... hashKeys) {
return redisTemplate.opsForHash().delete(key, hashKeys);
}
5.2 使用 List 结构
// 在RedisService中添加方法
// 左推入List
public Long lLeftPush(String key, Object value) {
return redisTemplate.opsForList().leftPush(key, value);
}
// 右推入List
public Long lRightPush(String key, Object value) {
return redisTemplate.opsForList().rightPush(key, value);
}
// 获取List范围
public List<Object> lRange(String key, long start, long end) {
return redisTemplate.opsForList().range(key, start, end);
}
// 获取List长度
public Long lSize(String key) {
return redisTemplate.opsForList().size(key);
}
5.3 使用 Set 结构
// 在RedisService中添加方法
// 添加Set元素
public Long sAdd(String key, Object... values) {
return redisTemplate.opsForSet().add(key, values);
}
// 获取Set所有成员
public Set<Object> sMembers(String key) {
return redisTemplate.opsForSet().members(key);
}
// 判断是否是Set成员
public Boolean sIsMember(String key, Object value) {
return redisTemplate.opsForSet().isMember(key, value);
}
// 获取Set大小
public Long sSize(String key) {
return redisTemplate.opsForSet().size(key);
}
5.4 使用 ZSet (有序集合)
// 在RedisService中添加方法
// 添加ZSet元素
public Boolean zAdd(String key, Object value, double score) {
return redisTemplate.opsForZSet().add(key, value, score);
}
// 获取ZSet范围
public Set<Object> zRange(String key, long start, long end) {
return redisTemplate.opsForZSet().range(key, start, end);
}
// 获取ZSet范围(带分数)
public Set<ZSetOperations.TypedTuple<Object>> zRangeWithScores(String key, long start, long end) {
return redisTemplate.opsForZSet().rangeWithScores(key, start, end);
}
// 获取ZSet中元素的排名
public Long zRank(String key, Object value) {
return redisTemplate.opsForZSet().rank(key, value);
}
// 获取ZSet中元素的分数
public Double zScore(String key, Object value) {
return redisTemplate.opsForZSet().score(key, value);
}
- 发布/订阅模式
6.1 配置消息监听器
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
@Configuration
public class RedisPubSubConfig {
@Bean
public RedisMessageListenerContainer redisMessageListenerContainer(
RedisConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(listenerAdapter, new ChannelTopic("news"));
return container;
}
@Bean
public MessageListenerAdapter listenerAdapter(RedisMessageReceiver receiver) {
return new MessageListenerAdapter(receiver, "receiveMessage");
}
}
6.2 创建消息接收器
import org.springframework.stereotype.Component;
@Component
public class RedisMessageReceiver {
public void receiveMessage(String message) {
System.out.println("收到消息: " + message);
}
}
6.3 发布消息
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
@Service
public class RedisPubService {
@Autowired
private StringRedisTemplate redisTemplate;
public void sendMessage(String channel, String message) {
redisTemplate.convertAndSend(channel, message);
}
}
- 分布式锁实现
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class RedisDistributedLock {
@Autowired
private StringRedisTemplate redisTemplate;
private static final String LOCK_PREFIX = "lock:";
private static final long DEFAULT_EXPIRE_TIME = 30; // 默认锁过期时间30秒
/**
* 获取分布式锁
* @param lockKey 锁的key
* @param requestId 请求标识(可使用UUID)
* @param expireTime 锁的过期时间(秒)
* @return 是否获取成功
*/
public boolean tryGetLock(String lockKey, String requestId, long expireTime) {
Boolean result = redisTemplate.opsForValue().setIfAbsent(
LOCK_PREFIX + lockKey,
requestId,
expireTime,
TimeUnit.SECONDS);
return Boolean.TRUE.equals(result);
}
/**
* 释放分布式锁
* @param lockKey 锁的key
* @param requestId 请求标识
* @return 是否释放成功
*/
public boolean releaseLock(String lockKey, String requestId) {
String currentValue = redisTemplate.opsForValue().get(LOCK_PREFIX + lockKey);
if (currentValue != null && currentValue.equals(requestId)) {
redisTemplate.delete(LOCK_PREFIX + lockKey);
return true;
}
return false;
}
/**
* 尝试获取锁,带重试机制
* @param lockKey 锁的key
* @param requestId 请求标识
* @param expireTime 锁的过期时间(秒)
* @param retryTimes 重试次数
* @param sleepMillis 每次重试间隔(毫秒)
* @return 是否获取成功
* @throws InterruptedException
*/
public boolean tryGetLockWithRetry(String lockKey, String requestId,
long expireTime, int retryTimes,
long sleepMillis) throws InterruptedException {
for (int i = 0; i < retryTimes; i++) {
if (tryGetLock(lockKey, requestId, expireTime)) {
return true;
}
Thread.sleep(sleepMillis);
}
return false;
}
}
- 缓存注解使用
Spring Boot 提供了方便的缓存注解:
8.1 启用缓存
在主类上添加 @EnableCaching
注解:
@SpringBootApplication
@EnableCaching
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
8.2 常用缓存注解
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;
@Service
public class UserService {
// 使用缓存,方法执行前先检查缓存
@Cacheable(value = "user", key = "#id")
public User getUserById(Long id) {
// 模拟数据库查询
return userRepository.findById(id).orElse(null);
}
// 更新缓存
@CachePut(value = "user", key = "#user.id")
public User updateUser(User user) {
// 更新数据库
return userRepository.save(user);
}
// 清除缓存
@CacheEvict(value = "user", key = "#id")
public void deleteUser(Long id) {
// 删除数据库记录
userRepository.deleteById(id);
}
// 复杂缓存配置
@Caching(
cacheable = {
@Cacheable(value = "user", key = "#name")
},
put = {
@CachePut(value = "user", key = "#result.id", condition = "#result != null")
}
)
public User getUserByName(String name) {
// 根据名称查询用户
return userRepository.findByName(name);
}
}
性能优化建议
合理使用连接池:配置适当的连接池大小
批量操作:使用
multi
或pipeline
减少网络往返序列化优化:选择合适的序列化方式
键设计:使用合理的键命名规则,避免大键
过期时间:为缓存设置合理的过期时间
避免大查询:使用
SCAN
替代KEYS
命令Lua脚本:复杂操作用Lua脚本减少网络开销
常见问题解决
10.1 连接超时
检查网络连接和Redis服务器状态,调整超时配置:
spring.redis.timeout=3000ms
10.2 序列化错误
确保存储和读取使用相同的序列化方式,或使用JSON序列化。
10.3 内存不足
监控Redis内存使用,设置合理的淘汰策略:
# 在Redis服务器配置中设置
maxmemory-policy allkeys-lru
10.4 缓存穿透
使用空值缓存或布隆过滤器:
@Cacheable(value = "user", key = "#id", unless = "#result == null")
public User getUserById(Long id) {
User user = userRepository.findById(id).orElse(null);
if (user == null) {
// 缓存空值,设置较短过期时间
redisTemplate.opsForValue().set("user:null:" + id, "", 5, TimeUnit.MINUTES);
}
return user;
}
- 监控与维护
11.1 健康检查
Spring Boot Actuator 提供Redis健康检查:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
配置 application.properties
:
management.endpoints.web.exposure.include=health,info
management.endpoint.health.show-details=always
访问 /actuator/health
查看Redis健康状态。
11.2 监控指标
Spring Boot Micrometer 提供Redis监控指标:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
配置 application.properties
:
management.endpoints.web.exposure.include=health,info,metrics,prometheus
访问 /actuator/metrics/redis
查看Redis相关指标。
- 集群与哨兵模式配置
12.1 Redis集群配置
spring.redis.cluster.nodes=192.168.1.1:6379,192.168.1.2:6379,192.168.1.3:6379
spring.redis.cluster.max-redirects=3
12.2 Redis哨兵配置
spring.redis.sentinel.master=mymaster
spring.redis.sentinel.nodes=192.168.1.1:26379,192.168.1.2:26379,192.168.1.3:26379
spring.redis.sentinel.password=sentinelpassword
- 测试
13.1 单元测试
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
public class RedisTest {
@Autowired
private RedisService redisService;
@Test
public void testSetAndGet() {
String key = "test:key";
String value = "test value";
redisService.set(key, value);
String result = (String) redisService.get(key);
assertEquals(value, result);
}
@Test
public void testExpire() throws InterruptedException {
String key = "test:expire";
String value = "will expire";
long timeout = 2; // 2秒
redisService.setWithExpire(key, value, timeout, TimeUnit.SECONDS);
assertTrue(redisService.hasKey(key));
Thread.sleep(timeout * 1000 + 500); // 等待过期
assertFalse(redisService.hasKey(key));
}
}
最佳实践
键命名规范:使用冒号分隔,如
user
profile
避免大键:单个键值不宜过大
合理设置TTL:所有缓存都应设置过期时间
读写分离:读多写少的数据适合缓存
缓存雪崩防护:分散过期时间
本地缓存配合:高频访问数据可配合Caffeine等本地缓存
监控告警:设置内存和性能监控
通过以上完整指南,您可以在Spring Boot应用中高效地集成和使用Redis,提升应用性能和可扩展性。