Redis客户端连接池配置

Redis客户端连接池配置

Redis 的五种数据结构各有特色,用对了才能发挥它的优势。很多人只用到了 String 和 Hash,却不知道 List、Set、ZSet 在特定场景下更合适。本文从应用场景出发,讲什么时候用什么类型。

一、主流Java客户端对比

特性 Jedis Lettuce
连接方式 阻塞IO Netty非阻塞IO
线程安全 连接池 单连接多线程共享
连接池 需要 可选
异步支持 有限 原生支持
响应式 不支持 支持(Reactor)
哨兵/Cluster 支持 支持
性能
维护活跃度 高(Spring默认)

推荐:新项目使用Lettuce,Spring Boot 2.x+ 默认使用Lettuce。

二、Jedis连接池配置

#

2.1 基础配置

@Configuration
public class JedisConfig {

@Bean
public JedisPool jedisPool() {
JedisPoolConfig poolConfig = new JedisPoolConfig();

// 基本配置
poolConfig.setMaxTotal(100); // 最大连接数
poolConfig.setMaxIdle(20); // 最大空闲连接
poolConfig.setMinIdle(5); // 最小空闲连接

// 连接获取配置
poolConfig.setMaxWaitMillis(3000); // 获取连接最大等待时间
poolConfig.setBlockWhenExhausted(true); // 连接耗尽时阻塞等待

// 连接检测配置
poolConfig.setTestOnBorrow(true); // 获取连接时检测
poolConfig.setTestOnReturn(true); // 归还连接时检测
poolConfig.setTestWhileIdle(true); // 空闲时检测

// 空闲连接检测
poolConfig.setTimeBetweenEvictionRunsMillis(60000); // 检测周期60秒
poolConfig.setMinEvictableIdleTimeMillis(300000); // 最小空闲时间5分钟
poolConfig.setNumTestsPerEvictionRun(-1); // 每次检测所有连接

return new JedisPool(poolConfig, "localhost", 6379, 3000, "password");
}
}

#

2.2 使用JedisPool

@Service
public class JedisService {

@Autowired
private JedisPool jedisPool;

public String get(String key) {
try (Jedis jedis = jedisPool.getResource()) {
return jedis.get(key);
}
}

public void set(String key, String value) {
try (Jedis jedis = jedisPool.getResource()) {
jedis.set(key, value);
}
}
}

#

2.3 Jedis Sentinel配置

@Bean
public JedisSentinelPool jedisSentinelPool() {
Set<String> sentinels = new HashSet<>();
sentinels.add("sentinel1:26379");
sentinels.add("sentinel2:26379");
sentinels.add("sentinel3:26379");

JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(100);
poolConfig.setMaxIdle(20);

return new JedisSentinelPool("mymaster", sentinels, poolConfig, "password");
}

#

2.4 Jedis Cluster配置

@Bean
public JedisCluster jedisCluster() {
Set<HostAndPort> nodes = new HashSet<>();
nodes.add(new HostAndPort("node1", 6379));
nodes.add(new HostAndPort("node2", 6379));
nodes.add(new HostAndPort("node3", 6379));

JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(100);

return new JedisCluster(nodes, 5000, 5000, 3, "password", poolConfig);
}

三、Lettuce连接池配置

#

3.1 基础配置

# application.yml
spring:
redis:
host: localhost
port: 6379
password:
database: 0
timeout: 3000ms
lettuce:
pool:
max-active: 50 # 最大连接数(负值表示无限制)
max-idle: 20 # 最大空闲连接
min-idle: 5 # 最小空闲连接
max-wait: 3000ms # 获取连接最大等待时间
time-between-eviction-runs: 60s # 空闲连接检测周期
shutdown-timeout: 200ms # 关闭超时

#

3.2 高级配置

@Configuration
public class LettuceConfig {

@Bean
public LettuceConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName("localhost");
config.setPort(6379);

// 连接池配置
GenericObjectPoolConfig<Object> poolConfig = new GenericObjectPoolConfig<>();
poolConfig.setMaxTotal(50);
poolConfig.setMaxIdle(20);
poolConfig.setMinIdle(5);
poolConfig.setMaxWaitMillis(3000);

LettucePoolingClientConfiguration poolingConfig =
LettucePoolingClientConfiguration.builder()
.poolConfig(poolConfig)
.commandTimeout(Duration.ofSeconds(3))
.shutdownTimeout(Duration.ofMillis(200))
.build();

return new LettuceConnectionFactory(config, poolingConfig);
}

@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);

// 序列化配置
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

template.afterPropertiesSet();
return template;
}
}

#

3.3 Lettuce Cluster配置

@Bean
public LettuceConnectionFactory redisClusterConnectionFactory() {
RedisClusterConfiguration clusterConfig = new RedisClusterConfiguration();
clusterConfig.clusterNode("node1", 6379);
clusterConfig.clusterNode("node2", 6379);
clusterConfig.clusterNode("node3", 6379);
clusterConfig.setMaxRedirects(3);

// 拓扑刷新配置
ClusterTopologyRefreshOptions topologyRefreshOptions =
ClusterTopologyRefreshOptions.builder()
.enablePeriodicRefresh(Duration.ofSeconds(30))
.enableAllAdaptiveRefreshTriggers()
.build();

ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder()
.topologyRefreshOptions(topologyRefreshOptions)
.validateClusterNodeMembership(false)
.build();

LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.clientOptions(clusterClientOptions)
.readFrom(ReadFrom.REPLICA_PREFERRED) // 优先从从节点读取
.build();

return new LettuceConnectionFactory(clusterConfig, clientConfig);
}

#

3.4 Lettuce读写分离

@Bean
public LettuceConnectionFactory redisConnectionFactory() {
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.readFrom(ReadFrom.REPLICA_PREFERRED) // 优先从从节点读取
.build();

RedisStandaloneConfiguration config = new RedisStandaloneConfiguration("master", 6379);

return new LettuceConnectionFactory(config, clientConfig);
}

// ReadFrom选项:
// MASTER: 只从主节点读取
// MASTER_PREFERRED: 优先主节点
// REPLICA: 只从从节点读取
// REPLICA_PREFERRED: 优先从节点(推荐)
// ANY: 任意节点
// ANY_REPLICA: 任意从节点

四、连接池参数详解

#

4.1 核心参数

参数 说明 建议值
maxTotal/max-active 最大连接数 50-100
maxIdle 最大空闲连接 20-50
minIdle 最小空闲连接 5-10
maxWait/max-wait 获取连接等待时间 3000ms
testOnBorrow 获取时检测 true(生产环境)
testOnReturn 归还时检测 true(生产环境)
testWhileIdle 空闲时检测 true
timeBetweenEvictionRuns 检测周期 60s
minEvictableIdleTime 最小空闲时间 300s

#

4.2 连接数计算

最大连接数建议:

max-active = (核心数 / (1 - 阻塞系数)) + 冗余

示例:
- 应用部署4台机器
- 每台机器连接数50
- 总共200连接

Redis服务端:
maxclients = 应用总连接数 + 预留(如监控、管理)
= 200 + 50 = 250

五、监控和调优

#

5.1 Lettuce监控

@Component
public class LettuceMetrics {

@Autowired
private MeterRegistry meterRegistry;

@Autowired
private LettuceConnectionFactory connectionFactory;

@Scheduled(fixedRate = 60000)
public void collectMetrics() {
ClientResources clientResources = connectionFactory.getClientResources();

if (clientResources != null) {
EventBus eventBus = clientResources.eventBus();

// 监控连接创建/关闭事件
eventBus.get().subscribe(event -> {
if (event instanceof ConnectionCreatedEvent) {
meterRegistry.counter("redis.connection.created").increment();
} else if (event instanceof ConnectionClosedEvent) {
meterRegistry.counter("redis.connection.closed").increment();
}
});
}
}
}

#

5.2 连接池状态监控

@Component
public class ConnectionPoolMetrics {

@Autowired
private GenericObjectPoolConfig<?> poolConfig;

@Scheduled(fixedRate = 60000)
public void reportPoolStats() {
// 通过JMX获取连接池状态
// 或使用自定义指标收集
}
}

#

5.3 告警规则

# Prometheus告警
groups:
- name: redis-client
rules:
- alert: RedisConnectionPoolExhausted
expr: redis_client_connections_active / redis_client_connections_max > 0.9
for: 5m
labels:
severity: critical
annotations:
summary: "Redis连接池耗尽"

- alert: RedisConnectionTimeout
expr: rate(redis_client_timeout_total[5m]) > 0
for: 1m
labels:
severity: warning
annotations:
summary: "Redis连接超时"

六、常见问题

#

6.1 连接池耗尽

现象:获取连接超时
原因:
1. 连接数设置过小
2. 连接泄漏(未正确关闭)
3. 连接被防火墙断开但池不知道

解决:
1. 增加max-active
2. 检查连接泄漏
3. 启用testWhileIdle检测

#

6.2 连接超时

原因:
1. 网络问题
2. Redis服务端繁忙
3. 命令执行时间过长

解决:
1. 增加timeout配置
2. 优化慢查询
3. 检查网络延迟

#

6.3 大量TIME_WAIT

原因:客户端频繁创建/关闭连接

解决:
1. 使用连接池
2. 调整系统TCP参数
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 0

七、配置对比

场景 Jedis Lettuce
Spring Boot 不推荐 推荐(默认)
高并发 一般 优秀
异步需求 不支持 原生支持
响应式 不支持 支持
配置复杂度 简单 中等

八、总结

连接池配置核心要点:

  1. 使用Lettuce:Spring Boot 2.x+ 默认,性能更好
  2. 合理设置连接数:根据并发量调整
  3. 启用连接检测:避免使用失效连接
  4. 监控连接池状态:及时发现异常
  5. 注意连接泄漏:确保正确释放连接

核心要点

  1. String:简单的键值对,适合缓存、计数器

  2. Hash:存储对象属性,适合用户信息、配置

  3. List:有序列表,适合消息队列、最新列表

  4. Set:无序去重,适合共同好友、抽奖

  5. ZSet:有序集合,适合排行榜、积分系统

总结

选择合适的数据结构是使用 Redis 的关键。在实际项目中,根据业务需求选择合适的类型,可以提升性能和开发效率。


   转载规则


《Redis客户端连接池配置》 小乐 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录