什么是连接池

什么是 Connection Pool -- 连接池呢?

我就不解释了。不太清楚的看这篇文章 [生产级别Nodejs开发实践-使用连接池](这篇文章的前半部分讲述了什么是连接池)

Tomcat中的jdbc连接池

它的英文文档在:[The Tomcat JDBC Connection Pool]

Tomcat 不仅是非常受欢迎的 Servlet 容器,也是同时为我们提供了很多非常实用组件。 jdbc pool 就是其中一个非常实用且高效的 jdbc 连接池的实现. Tomcat 官方大概列出了15条tomcat相对于其他连接池的优点,我这里就不在详细解释了。

jdbc pool 组件存在于 tomcat8 release 版本的 lib/tomcat-jdbc.jar 包中。

我们所使用的到所有类都存在于 org.apache.tomcat.jdbc.pool 包名下。

依赖

这里面我们以 mysql 作为数据库来演示
添加 mysql-connector 依赖

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.34</version>
</dependency>

当然也少不了我们的 tomcat-jdbc

<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-jdbc</artifactId>
    <version>8.0.15</version>
</dependency>

要注意的是 tomcat-jdbc 依赖 tomcat-juli 包,这个包是在tomcat中的日志框架。几乎被所有tomcat包所依赖。如果使用maven安装依赖的话,就不用理会,因为是会被自动安装的,如果单独下载jar包的话,还是要下载这个jar包到CLASS_PATH

用代码来描述

废话到这儿,该上点代码了。 怎么创建一个 jdbc 连接池池实例呢?

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;

// main
// 创建连接池属性对象
PoolProperties poolProps = new PoolProperties();
poolProps.setUrl("jdbc:mysql://localhost:3306/test");

poolProps.setDriverClassName("com.mysql.jdbc.Driver");

poolProps.setUsername("root");

poolProps.setPassword("123456");

// 创建连接池, 使用了 tomcat 提供的的实现,它实现了 javax.sql.DataSource 接口
DataSource dataSource = new DataSource();
// 为连接池设置属性
dataSource.setPoolProperties(poolProps);

try (Connection conn = dataSource.getConnection()) {

    PreparedStatement state = conn.prepareStatement("select * from book");

    ResultSet result = state.executeQuery();

    StringBuilder stringBuilder = new StringBuilder();

    while ( result.next() ) {
            stringBuilder.append("[ id: "+ result.getInt("id") + ", ").append("name: " + result.getString("name") + ",\t").append("publisher: " + result.getString("publisher")).append(" ]\n");
    }

    System.out.println(stringBuilder.toString());

} 
catch (SQLException e) 
{
    e.printStackTrace();
}

通过代码可以看出,连接池的创建很简单。PoolProperties 类是管理着连接池属性。我们配置连接池,都是通过这个类。

来看看这个类的属性,以下有 50 个属性,来控制着连接池和池中的连接的工作状态和生命周期。接下来我会对常用的属性进行解释。

    private volatile Properties dbProperties = new Properties();
    private volatile String url = null;
    private volatile String driverClassName = null;
    private volatile Boolean defaultAutoCommit = null;
    private volatile Boolean defaultReadOnly = null;
    private volatile int defaultTransactionIsolation = DataSourceFactory.UNKNOWN_TRANSACTIONISOLATION;
    private volatile String defaultCatalog = null;
    private volatile String connectionProperties;
    private volatile int initialSize = 10;
    private volatile int maxActive = DEFAULT_MAX_ACTIVE;
    private volatile int maxIdle = maxActive;
    private volatile int minIdle = initialSize;
    private volatile int maxWait = 30000;
    private volatile String validationQuery;
    private volatile int validationQueryTimeout = -1;
    private volatile String validatorClassName;
    private volatile Validator validator;
    private volatile boolean testOnBorrow = false;
    private volatile boolean testOnReturn = false;
    private volatile boolean testWhileIdle = false;
    private volatile int timeBetweenEvictionRunsMillis = 5000;
    private volatile int numTestsPerEvictionRun;
    private volatile int minEvictableIdleTimeMillis = 60000;
    private volatile boolean accessToUnderlyingConnectionAllowed = true;
    private volatile boolean removeAbandoned = false;
    private volatile int removeAbandonedTimeout = 60;
    private volatile boolean logAbandoned = false;
    private volatile String name = "Tomcat Connection Pool["+(poolCounter.addAndGet(1))+"-"+System.identityHashCode(PoolProperties.class)+"]";
    private volatile String password;
    private volatile String username;
    private volatile long validationInterval = 30000;
    private volatile boolean jmxEnabled = true;
    private volatile String initSQL;
    private volatile boolean testOnConnect =false;
    private volatile String jdbcInterceptors=null;
    private volatile boolean fairQueue = true;
    private volatile boolean useEquals = true;
    private volatile int abandonWhenPercentageFull = 0;
    private volatile long maxAge = 0;
    private volatile boolean useLock = false;
    private volatile InterceptorDefinition[] interceptors = null;
    private volatile int suspectTimeout = 0;
    private volatile Object dataSource = null;
    private volatile String dataSourceJNDI = null;
    private volatile boolean alternateUsernameAllowed = false;
    private volatile boolean commitOnReturn = false;
    private volatile boolean rollbackOnReturn = false;
    private volatile boolean useDisposableConnectionFacade = true;
    private volatile boolean logValidationErrors = false;
    private volatile boolean propagateInterruptState = false;
    private volatile boolean ignoreExceptionOnPreLoad = false;

url, driverClassName, username, password 这些属性知道jdbc的同学,我就不解释了。

  • name 连接池的名称

  • defaultAutoCommit 指定由连接池所创建的连接的自动提交(auto-commit)状态。

  • DefaultReadOnly 由连接池所创建的连接对数据库的只读属性

  • DefaultTransactionIsolation
    指定由连接池所创建的连接的事务级别(TransactionIsolation)。可用值为下列之一:(详情可见javadoc)
    NONE, READ_COMMITTED, READ_UNCOMMITTED, REPEATABLE_READ, SERIALIZABLE

  • MinEvictableIdleTimeMillis

  • JmxEnabled 开启jmx的管理功能

  • TestWhileIdle 会使用测试线程,测试池中连接是否能够正常使用。

  • ValidationQuery
    指定连接进入空闲状态时是否经过空闲对象驱逐进程的校验(如果存在空闲对象驱逐进程)。如果校验未通过,则该连接被连接池断掉。
    注意:要想值为true时该设置生效,则validationQuery参数必须为一个非空字串。

  • TestOnBorrow
    指定连接被调用时是否经过校验。如果校验未通过,则该连接被连接池断掉,并由连接池尝试调用另一个连接。
    指定连接返回到池中时是否经过校验。
    注意:要想值为true时该设置生效,则validationQuery参数必须为一个非空字串。

  • TestOnReturn
    指定连接返回到池中时是否经过校验。
    注意:要想值为true时该设置生效,则validationQuery参数必须为一个非空字串。

  • ValidationInterval 以毫秒为单位验证时间间隔。

  • TimeBetweenEvictionRunsMillis 以毫秒表示的空闲对象驱逐进程由运行状态进入休眠状态的数值。值为非正数时表示不运行任何空闲对象驱逐进程。

  • MaxActive 池中 工作连接的最大个数,此值为非正数是表述不限制

  • InitialSize 以毫秒表示的当连接池中没有可用连接时等待可用连接返回的时间,超时则抛出异常,值为-1时无限期等待。

  • MaxWait 以毫秒表示的当连接池中没有可用连接时等待可用连接返回的时间,超时则抛出异常,值为-1时无限期等待。

  • MinIdle 池中最小空闲连接数,当连接数少于此值时,池会创建连接来补充到该值的数量

  • MaxIdle 池中允许的最大连接数,值为非正数时表示不限制

  • NumTestsPerEvictionRun 连接池检查每个空闲对象驱逐进程的对象数量(如果存在空闲对象驱逐进程)

  • MinEvictableIdleTimeMillis 以毫秒表示的连接被空闲对象驱逐进程驱逐前在池中保持空闲状态的最小时间(如果存在空闲对象驱逐进程)。

  • LogAbandoned
    当清除无效连接时是否在日志中记录清除信息的标志。 记录无效的语句和连接,并附加每个连接开启或新建一个语句的系统开销。如果你启用了“removeAbandoned”,可能会导致被设为无效的连接被连接池回收。
    这个机制将会在满足下列两个条件时启动:(getNumIdle() < 2) 和 (getNumActive() > getMaxActive() - 3)
    例如:假设maxActive=20,而当前已经拥有18个活动连接,1个空闲连接,“removeAbandoned”机制将会启动。 但是只有在活动连接没有使用的时长超过“removeAbandonedTimeout”(默认为300秒)的连接被清除。在遍历结果集时,所使用的连接不会被标为活动连接。

  • RemoveAbandoned
    是否清除已经超过“removeAbandonedTimout”设置的无效连接。
    如果值为“true”则超过“removeAbandonedTimout”设置的无效连接将会被清除。设置此属性可以从那些没有合适关闭连接的程序中恢复数据库的连接。
    记录无效的语句和连接,并附加每个连接开启或新建一个语句的系统开销。

  • RemoveAbandonedTimeout 以秒表示的清除无效连接的时限。

  • JdbcInterceptors
    设置 tomcat jdbc 连接池的拦截器。
    内置的拦截器: org.apache.tomcat.jdbc.pool.interceptor.ConnectionState
    追踪自动提交、只读状态、catalog和事务隔离等级等状态

org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer 追踪打开的statement,当连接被归还时关闭它们.

多个拦截器用;分割,例如:

poolProps.setJdbcInterceptors("org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer");
  • dataSourceJNDI
    指定一个 jndi 作为数据源
  • accessToUnderlyingConnectionAllowed

以上属性当你我无法理解的时候,最好保持默认值。

以下待续。。。