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); poolConfig.setMinEvictableIdleTimeMillis(300000); 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 基础配置
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); }
|
四、连接池参数详解
#
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() { } }
|
#
5.3 告警规则
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 |
不推荐 |
推荐(默认) |
| 高并发 |
一般 |
优秀 |
| 异步需求 |
不支持 |
原生支持 |
| 响应式 |
不支持 |
支持 |
| 配置复杂度 |
简单 |
中等 |
八、总结
连接池配置核心要点:
- 使用Lettuce:Spring Boot 2.x+ 默认,性能更好
- 合理设置连接数:根据并发量调整
- 启用连接检测:避免使用失效连接
- 监控连接池状态:及时发现异常
- 注意连接泄漏:确保正确释放连接
核心要点
String:简单的键值对,适合缓存、计数器
Hash:存储对象属性,适合用户信息、配置
List:有序列表,适合消息队列、最新列表
Set:无序去重,适合共同好友、抽奖
ZSet:有序集合,适合排行榜、积分系统
总结
选择合适的数据结构是使用 Redis 的关键。在实际项目中,根据业务需求选择合适的类型,可以提升性能和开发效率。