SpringBoot 集成 Redis

摘要

  • 本文介绍 SpringBoot 集成 Redis 的方法
  • 本文基于redis-7.4.7springboot-3.5.8
  • Redis官网:https://redis.io/

引入依赖

  • pom.xml中引入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.5.8</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- redis 连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
</dependencies>

配置 application.yml

单机模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
spring:
data:
redis:
host: 127.0.0.1
port: 6379
database: 0
username: admin
password: redis123
timeout: 5s
connectTimeout: 3s
clientName: demo-service
lettuce:
shutdown-timeout: 100ms
pool:
max-active: 64
max-idle: 32
min-idle: 16
max-wait: 2s
time-between-eviction-runs: 30s
  • 基础连接配置项

配置项 示例值 含义 默认值 生产建议
spring.data.redis.host localhost Redis 服务地址(IP / 域名) localhost 生产使用内网 IP / 域名
spring.data.redis.port 6379 Redis 服务端口 6379 一般无需修改
spring.data.redis.username admin Redis ACL 用户名(Redis 6+) (空) 未启用 ACL 可不配置
spring.data.redis.password 123456 Redis 访问密码 (空) 生产必须配置
spring.data.redis.database 0 逻辑数据库索引(0–15) 0 Cluster 模式无效
spring.data.redis.timeout 3s Redis 命令执行超时 60s(依版本) 建议 2–5s
spring.data.redis.connect-timeout 3s TCP 建连超时 OS 默认 建议 1–5s
spring.data.redis.client-name my-redis-client 客户端标识,用于运维定位 (空) 强烈建议配置
  • 连接池配置(Lettuce Pool)

配置项 示例值 含义 默认值 生产建议
spring.data.redis.lettuce.pool.max-active 8 连接池最大连接数(使用中 + 空闲) 8 CPU × 2~4 或压测评估
spring.data.redis.lettuce.pool.max-wait 2s 连接耗尽时等待时间 -1(无限等待) 必须设置,1–3s
spring.data.redis.lettuce.pool.max-idle 8 最大空闲连接数 8 max-active × 30%~50%
spring.data.redis.lettuce.pool.min-idle 0 最小空闲连接数 0 ≥ max-active × 25%
spring.data.redis.lettuce.pool.time-between-eviction-runs 60s 空闲连接检测周期 -1(不启用) 30–60s
  • Lettuce 客户端运行参数

配置项 示例值 含义 默认值 生产建议
spring.data.redis.lettuce.shutdown-timeout 100ms 应用关闭时等待连接释放时间 100ms 一般无需修改

sentinel 模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
spring:
data:
redis:
database: 0
username: admin
password: redis123
timeout: 5s
connectTimeout: 3s
clientName: demo-service
sentinel:
master: mymaster
nodes:
- 10.0.0.10:26379
- 10.0.0.11:26379
- 10.0.0.12:26379
lettuce:
shutdown-timeout: 100ms
pool:
max-active: 64
max-idle: 32
min-idle: 16
max-wait: 2s
time-between-eviction-runs: 30s
  • 哨兵模式配置项

配置项 示例值 含义 是否必填 说明
spring.data.redis.sentinel.master mymaster Sentinel 监控的 Master 名称 必须与 Sentinel monitor 名称完全一致
spring.data.redis.sentinel.nodes 10.0.0.10:26379,10.0.0.11:26379,10.0.0.12:26379 Sentinel 节点列表 至少配置 2–3 个 Sentinel,提升可用性
spring.data.redis.sentinel.username (空) Sentinel 的认证用户名 如果启用 ACL,则必填
spring.data.redis.sentinel.password (空) Sentinel 的认证密码 如果启用 ACL,则必填

cluster 模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
spring:
data:
redis:
username: admin
password: redis123
timeout: 5s
connectTimeout: 3s
clientName: order-service
cluster:
nodes:
- 192.168.1.10:6379
- 192.168.1.11:6379
- 192.168.1.12:6379
lettuce:
shutdown-timeout: 100ms
cluster:
refresh:
adaptive: true
period: 10s
pool:
max-active: 64
max-idle: 32
min-idle: 16
max-wait: 2s
time-between-eviction-runs: 30s
  • 注意

1
集群模式下,不能配置 `spring.data.redis.database`,因为集群只能使用默认数据库索引 0
  • 集群模式配置项

配置项 示例值 含义 是否必填 默认值 生产建议
spring.data.redis.cluster.nodes 192.168.1.10:6379,... Redis Cluster 节点地址列表 (空) 至少配置 3 个节点
  • Cluster 拓扑自动刷新配置,用于解决集群拓扑变化时 Client 无法自动感知

配置项 示例值 含义 默认值 是否推荐
adaptive true 开启 事件驱动拓扑刷新 false ✅ 必须
period 10s 开启 定时拓扑刷新周期 关闭 ✅ 必须

SpringBoot 配置类

封装 RedisTemplate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package com.example.config;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
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 ObjectMapper redisObjectMapper() {
return JsonMapper.builder()
.addModule(new JavaTimeModule())
.serializationInclusion(JsonInclude.Include.NON_NULL)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.build();
}

@Bean
public RedisTemplate<String, Object> redisTemplate(
RedisConnectionFactory connectionFactory,
ObjectMapper objectMapper) {

RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);

// --- Key 序列化 ---
StringRedisSerializer stringSerializer = new StringRedisSerializer();

// --- Value 序列化 ---
// 使用 Jackson JSON,避免 JDK 序列化性能与安全问题
GenericJackson2JsonRedisSerializer jsonSerializer =
new GenericJackson2JsonRedisSerializer(objectMapper);

// Key
template.setKeySerializer(stringSerializer);
template.setHashKeySerializer(stringSerializer);

// Value
template.setValueSerializer(jsonSerializer);
template.setHashValueSerializer(jsonSerializer);

template.afterPropertiesSet();
return template;
}
}

开启注解式缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package com.example.config;


import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cache.CacheManager;
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.cache.RedisCacheWriter;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

@Configuration
@AutoConfigureAfter(value = RedisConfig.class)
//注入redis分组配置属性:ttlmap
@ConfigurationProperties(prefix = "caching")
public class RedisCachingConfig {

/**
* 分组配置项
*/
@Getter
@Setter
private Map<String, Long> ttlmap;

@Bean
public CacheManager cacheManager(RedisTemplate<String, Object> redisTemplate) {
return RedisCacheManager
.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisTemplate.getConnectionFactory()))
//缺省配置
.cacheDefaults(redisCacheConfiguration(redisTemplate, 3600L))
//分组配置,不需要分组配置可以去掉,不同的组配置不同的缓存过期时间,可以防止"缓存雪崩"
.withInitialCacheConfigurations(initialRedisCacheConfiguration(redisTemplate))
.build();
}

/**
* 缺省缓存配置
*/
private RedisCacheConfiguration redisCacheConfiguration(RedisTemplate<String, Object> redisTemplate, Long ttl) {

return RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(ttl)) //设置过期,单位秒
//.disableCachingNullValues() //不允许存储null值,默认可以存储null,缓存null可以防止"缓存穿透"
//.disableKeyPrefix() //设置key前面不带前缀,最好不要去掉前缀,否则执行删除缓存时会清空全部缓存
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getStringSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()));
}

/**
* 针对不同的缓存组配置不同的设置
*/
private Map<String, RedisCacheConfiguration> initialRedisCacheConfiguration(RedisTemplate<String, Object> redisTemplate) {
Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
for (Map.Entry<String, Long> entry : ttlmap.entrySet()) {
redisCacheConfigurationMap.put(entry.getKey(), redisCacheConfiguration(redisTemplate, entry.getValue()));
}
return redisCacheConfigurationMap;
}

}
  • ttlmap 配置项

1
2
3
4
caching:
ttlmap:
commonCache: 3600
loginCache: 7200
  • 启动类上要加 @EnableCaching

1
2
3
4
5
6
7
8
@EnableCaching
@SpringBootApplication
public class DemoApplication {

public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
  • 缓存注解使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Service
// 缓存分组
@CacheConfig(cacheNames = "commonCache")
public class SystemUserServiceImpl implements ISystemUserService {
@Autowired
SystemUserJpaRepository systemUserJpaRepository;

//向组内添加缓存
@Cacheable(key = "'SystemUserServiceImpl.findAll'")
public List<SystemUser> findAll() {
List<SystemUser> systemUserList = systemUserJpaRepository.findAll();
return systemUserList;
}
//向组内添加缓存
@Cacheable(key = "'SystemUserServiceImpl.findById_'+ #userId")
public SystemUser findById(String userId) {
return systemUserJpaRepository.findById(userId);
}
// 删除组内指定缓存
@CacheEvict(key = "'SystemUserServiceImpl.findById_'+ #userId")
public void deleteById(String userId) {
return systemUserJpaRepository.deleteById(userId);
}
// 删除本组全部缓存
@CacheEvict(allEntries = true, beforeInvocation = true)
public SystemUser add(SystemUser user) {
systemUserJpaRepository.save(user);
return user;
}
}