杰瑞科技汇

Redis Java连接池如何配置与优化?

为什么需要 Redis 连接池?

直接每次操作都创建和销毁 Redis 连接(JedisLettuce)是非常低效的,这会带来以下问题:

Redis Java连接池如何配置与优化?-图1
(图片来源网络,侵删)
  1. 性能开销:创建网络连接(TCP 握手)和认证(Redis 密码)是相对耗时的操作,频繁创建和销毁连接会严重影响应用的响应速度。
  2. 资源浪费:每个连接都会占用服务端和客户端的内存、文件句柄等系统资源,高并发下,可能会耗尽这些资源。
  3. 不稳定:没有连接池管理,连接可能会因为网络抖动、Redis 服务器重启等原因突然失效,导致应用抛出异常。

连接池的作用:预先创建一组并保持一定数量的活跃连接,当应用需要访问 Redis 时,从池中“借用”一个连接,用完后“归还”给池,而不是销毁,这样就避免了上述问题。


主流 Java Redis 客户端

目前主流的 Java Redis 客户端有两个,它们都支持连接池:

  1. Jedis:一个轻量级、高性能的 Redis 客户端,它基于阻塞 I/O (BIO),实现相对简单,是早期非常流行的选择。
  2. Lettuce:一个功能丰富、可扩展性强的 Redis 客户端,它基于异步和非阻塞 I/O (Netty NIO),性能更高,支持同步、异步和响应式编程,是目前更推荐的选择。

我们将分别介绍如何配置这两个客户端的连接池。


Jedis 连接池配置

Jedis 提供了 JedisPool 类来管理连接池。

Redis Java连接池如何配置与优化?-图2
(图片来源网络,侵删)

1 添加依赖

如果你使用 Maven,在 pom.xml 中添加:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>5.1.0</version> <!-- 使用最新稳定版本 -->
</dependency>

2 配置与使用

最佳实践是使用 JedisPoolConfig 来配置连接池参数,然后将 JedisPoolConfig 和 Redis 服务器信息一起传给 JedisPool

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class JedisPoolExample {
    // 1. 创建连接池配置对象
    private static JedisPoolConfig poolConfig = new JedisPoolConfig();
    static {
        // 设置最大连接数(根据你的应用负载和Redis服务器性能来调整)
        poolConfig.setMaxTotal(200);
        // 设置最大空闲连接数
        poolConfig.setMaxIdle(20);
        // 设置最小空闲连接数
        poolConfig.setMinIdle(5);
        // 设置当连接池耗尽时,是否阻塞等待,true表示等待,false表示抛出异常,建议设置为true。
        poolConfig.setBlockWhenExhausted(true);
        // 设置等待超时时间(毫秒)
        poolConfig.setMaxWaitMillis(3000);
        // 在从池中取出连接前,进行有效性检查
        poolConfig.setTestOnBorrow(true);
        // 在将连接放回池中前,进行有效性检查
        poolConfig.setTestOnReturn(true);
        // 在连接空闲时,进行有效性检查
        poolConfig.setTestWhileIdle(true);
        // 设置连接池在空闲时,每隔多久检查一次连接的有效性(毫秒)
        poolConfig.setTimeBetweenEvictionRunsMillis(30000);
    }
    // 2. 创建连接池对象
    private static JedisPool jedisPool = new JedisPool(
            poolConfig, 
            "127.0.0.1", // Redis服务器IP
            6379,         // Redis服务器端口
            2000,         // 连接超时时间(毫秒)
            "yourpassword" // Redis密码,如果没有密码则为null
    );
    public static void main(String[] args) {
        // 从连接池中获取一个Jedis连接
        try (Jedis jedis = jedisPool.getResource()) {
            // 使用jedis对象操作Redis
            jedis.set("foo", "bar");
            String value = jedis.get("foo");
            System.out.println("Value from Redis: " + value);
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 注意:在应用程序关闭时,必须关闭连接池,释放资源
        // Runtime.getRuntime().addShutdownHook(new Thread(jedisPool::close));
    }
}

3 关键配置参数说明

参数 说明 推荐值
maxTotal 连接池中最大连接数。 根据应用峰值 QPS 和 Redis 服务器性能设定,通常设置为 QPS * 平均请求耗时
maxIdle 连接池中最大空闲连接数。 一般小于或等于 maxTotal,建议设置为 maxTotal 的 1/10 到 1/4。
minIdle 连接池中最小空闲连接数。 保持一定的空闲连接可以快速响应新的请求。
blockWhenExhausted 连接池耗尽时是否阻塞。 true,避免因无连接导致应用崩溃。
maxWaitMillis blockWhenExhausted 为 true 时,等待获取连接的最大毫秒数。 3000 (3秒)
testOnBorrow 在从池中获取连接前,验证其有效性(执行 PING)。 true,可以防止获取到已失效的连接。
testOnReturn 在将连接返回池中前,验证其有效性。 true/false
testWhileIdle 对空闲连接进行有效性检查。 true,防止连接长时间空闲后失效。
timeBetweenEvictionRunsMillis 连接池空闲时,执行空闲连接检查的间隔时间。 30000 (30秒)

Lettuce 连接池配置

Lettuce 的连接池是 Spring Data Redis 提供的 GenericObjectPool,因此需要额外引入 commons-pool2 依赖。

1 添加依赖

<dependency>
    <groupId>io.lettuce</groupId>
    <artifactId>lettuce-core</artifactId>
    <version>6.3.2.RELEASE</version> <!-- 使用最新稳定版本 -->
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.12.0</version> <!-- 必须依赖 -->
</dependency>

2 配置与使用

Lettuce 的配置对象是 GenericObjectPoolConfig,它与 Jedis 的配置非常相似。

import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.support.ConnectionPoolSupport;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
public class LettucePoolExample {
    public static void main(String[] args) {
        // 1. 创建RedisURI,配置连接信息
        RedisURI redisURI = RedisURI.create("redis://:yourpassword@127.0.0.1:6379/0");
        // 2. 创建RedisClient
        RedisClient redisClient = RedisClient.create(redisURI);
        // 3. 创建连接池配置对象
        GenericObjectPoolConfig<StatefulRedisConnection<String, String>> poolConfig = new GenericObjectPoolConfig<>();
        poolConfig.setMaxTotal(200);
        poolConfig.setMaxIdle(20);
        poolConfig.setMinIdle(5);
        poolConfig.setBlockWhenExhausted(true);
        poolConfig.setMaxWaitMillis(3000);
        // Lettuce有自己的连接验证机制,但也可以配置
        poolConfig.setTestOnBorrow(true);
        poolConfig.setTestOnReturn(true);
        poolConfig.setTestWhileIdle(true);
        poolConfig.setTimeBetweenEvictionRunsMillis(30000);
        // 4. 创建连接池
        // Lettuce的连接池是GenericObjectPool,需要提供PooledObjectFactory
        // ConnectionPoolSupport提供了便捷的工厂方法
        GenericObjectPool<StatefulRedisConnection<String, String>> connectionPool =
                ConnectionPoolSupport.createGenericObjectPool(() -> redisClient.connect(), poolConfig);
        // 5. 从连接池中获取连接
        try (StatefulRedisConnection<String, String> connection = connectionPool.getResource()) {
            // 使用connection操作Redis
            connection.sync().set("foo", "bar");
            String value = connection.sync().get("foo");
            System.out.println("Value from Redis: " + value);
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 6. 应用关闭时,关闭连接池和RedisClient
        // connectionPool.close();
        // redisClient.shutdown();
    }
}

Spring Boot 集成(推荐方式)

在实际项目中,我们通常使用 Spring Boot,它会自动为我们配置好连接池,我们只需要在 application.propertiesapplication.yml 中进行配置即可。

1 添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

Spring Boot 3.x 默认使用 Lettuce,Spring Boot 2.x 默认使用 Lettuce,如果你想用 Jedis,需要排除默认依赖并手动添加:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <exclusions>
        <exclusion>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

2 在 application.yml 中配置

下面是使用 Lettuce 的配置示例:

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: yourpassword # 如果没有密码,可以注释掉或删除这行
    database: 0 # 使用哪个数据库,默认0
    lettuce:
      pool:
        # 连接池最大连接数(使用负值表示没有限制)
        max-active: 200
        # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1ms
        # 连接池中的最大空闲连接
        max-idle: 20
        # 连接池中的最小空闲连接
        min-idle: 5
        # 连接空闲超时时间,超时后将被释放
        time-between-eviction-runs: 30s

3 在代码中使用

Spring Boot 会自动配置一个 RedisTemplate bean,我们直接注入即可使用。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RedisController {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @GetMapping("/set")
    public String set() {
        redisTemplate.opsForValue().set("spring-boot-key", "Hello, Spring Boot Redis!");
        return "Set value successfully!";
    }
    @GetMapping("/get")
    public String get() {
        Object value = redisTemplate.opsForValue().get("spring-boot-key");
        return "Value: " + value;
    }
}

Spring Boot 的 RedisTemplate 内部已经集成了连接池管理,我们无需关心连接的获取和释放。


总结与最佳实践

特性 Jedis Lettuce Spring Boot 集成
I/O 模型 阻塞 I/O (BIO) 异步/非阻塞 I/O (NIO) 默认使用 Lettuce,可配置 Jedis
性能 较高 更高,尤其在高并发下
易用性 简单直接 API 更丰富,支持异步/响应式 非常简单,自动配置
连接池 JedisPool GenericObjectPool (来自 commons-pool2) 自动配置,无需手动管理
推荐场景 简单项目,对依赖大小敏感 生产环境首选,高并发、高性能应用 所有 Spring Boot 项目

最佳实践建议:

  1. 优先使用 Spring Boot 集成:如果你在使用 Spring Boot,这是最简单、最可靠的方式,无需手动创建和管理连接池。
  2. 不使用 Spring Boot 时,优先选择 Lettuce:Lettuce 的 NIO 模型在高并发下表现更好,且 API 设计更现代。
  3. 合理配置连接池参数maxTotal, maxIdle, minIdle 是最关键的参数,需要根据你的应用负载和服务器资源进行调整。
  4. 始终关闭资源:在应用关闭时,务必调用 jedisPool.close()connectionPool.close() 来释放连接池占用的资源。
  5. 处理异常:网络问题可能导致连接失效,代码中应有适当的异常处理机制。
分享:
扫描分享到社交APP
上一篇
下一篇