连接池是一种创建和管理可供任何需要它们的线程使用的连接池的技术 。连接池可以大大提高Java应用程序的性能,同时减少总体资源使用量。

连接池如何工作

大多数应用程序在主动处理事务时,只需要一个线程即可访问JDBC连接 ,通常只需几毫秒即可完成。当不处理事务时,连接处于空闲状态。连接池使空闲连接可以被其他一些线程用来完成有用的工作。

实际上,当线程需要使用JDBC针对MySQL或其他数据库进行工作时,它会请求池中的连接。使用连接完成线程后,它将返回到池,以便任何其他线程都可以使用它。

从池中借出连接后,请求该线程的线程将独占使用该连接。从编程的角度来看,这就像线程在DriverManager.getConnection()每次需要JDBC连接时都调用 它一样。使用连接池,线程可能最终会使用新连接或已经存在的连接。

连连接池的好处连

连接池的主要好处是:

减少连接创建时间。

尽管与其他数据库相比,MySQL提供的快速连接设置通常不成问题,但是创建新的JDBC连接仍然会引起网络和JDBC驱动程序开销,如果回收连接可以避免这些开销。

简化的编程模型。

使用连接池时,每个单独的线程都可以像创建自己的JDBC连接一样工作,从而使您可以使用直接的JDBC编程技术。

受控的资源使用。

如果每次线程需要一个连接而不是使用连接池时都创建一个新连接,则应用程序的资源使用可能会很浪费,并且在负载很重的情况下,可能会导致应用程序发生无法预测的行为。

连使用连接池连

JDBC中的连接池的概念已经通过JDBC 2.0 Optional接口进行了标准化,并且所有主要的应用服务器都具有与MySQL Connector / J一起使用的这些API的实现。

通常,您在应用程序服务器配置文件中配置连接池,然后通过Java命名和目录接口(JNDI)访问它。以下代码显示了如何使用J2EE应用程序服务器中部署的应用程序中的连接池:

例8.1连接器/ J:将连接池与J2EE应用程序服务器一起使用

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import javax.naming.InitialContext;
import javax.sql.DataSource;
public class MyServletJspOrEjb{
public void doSomething() throws Exception{
/*
* Create a JNDI Initial context to be able to
* lookup the DataSource
*
* In production-level code, this should be cached as
* an instance or static variable, as it can
* be quite expensive to create a JNDI context.
*
* Note: This code only works when you are using servlets
* or EJBs in a J2EE application server. If you are
* using connection pooling in standalone Java code, you
* will have to create/configure datasources using whatever
* mechanisms your particular connection pooling library
* provides.
*/
InitialContext ctx = new InitialContext();
/*
* Lookup the DataSource, which will be backed by a pool
* that the application server provides. DataSource instances
* are also a good candidate for caching as an instance
* variable, as JNDI lookups can be expensive as well.
*/
DataSource ds =
(DataSource)ctx.lookup("java:comp/env/jdbc/MySQLDB");/*
* The following code is what would actually be in your
* Servlet, JSP or EJB 'service' method...where you need
* to work with a JDBC connection.
*/
Connection conn = null;
Statement stmt = null;
try {
conn = ds.getConnection();
/*
* Now, use normal JDBC programming to work with
* MySQL, making sure to close each resource when you're
* finished with it, which permits the connection pool
* resources to be recovered as quickly as possible
*/
stmt = conn.createStatement();
stmt.execute("SOME SQL QUERY");
stmt.close();
stmt = null;
conn.close();
conn = null;
} finally {
/*
* close any jdbc instances here that weren't
* explicitly closed during normal code path, so
* that we don't 'leak' resources...
*/
if (stmt != null) {
try {
stmt.close();
} catch (sqlexception sqlex) {
// ignore, as we can't do anything about it here
}
stmt = null;
}
if (conn != null) {
try {
conn.close();
} catch (sqlexception sqlex) {
// ignore, as we can't do anything about it here
}
conn = null;
}
}
}
}

如上面的示例所示,在获取JNDI InitialContext并查找之后 DataSource,其余代码遵循熟悉的JDBC约定。

使用连接池时,请始终确保关闭连接及其创建的所有内容(例如语句或结果集)。无论代码中发生了什么(异常,控制流等),此规则都适用。当这些对象关闭时,它们可以重新使用;否则,它们将被搁浅,这意味着它们所代表的MySQL服务器资源(例如缓冲区,锁或套接字)被占用了一段时间,或者在最坏的情况下可以被永久占用。

连调整连接池的大小连

每个与MySQL的连接在客户端和服务器端都有开销(内存,CPU,上下文切换等)。每个连接都会限制应用程序和MySQL服务器可用的资源数量。无论连接实际上是否在做任何有用的工作,都将使用其中的许多资源!可以调整连接池以最大程度地提高性能,同时将资源利用率保持在应用程序开始失败而不是运行缓慢的程度以下。

连接池的最佳大小取决于预期的负载和平均数据库事务时间。实际上,最佳连接池大小可能比您预期的要小。如果以Oracle的Java Petstore蓝图应用程序为例,则使用MySQL和Tomcat以可接受的响应时间为15-20个连接的连接池提供相对适中的负载(600个并发用户)。

要为应用程序正确确定连接池的大小,请使用Apache JMeter或The Grinder之类的工具创建负载测试脚本,然后对应用程序进行负载测试。

确定起点的一种简单方法是配置连接池的最大无限制连接数,运行负载测试,并测量并发使用的最大连接数。然后,您可以从那里向后进行工作,以确定最小和最大池连接的值为您的特定应用程序提供最佳性能。

连验证连接连

MySQL Connector / J可以通过对服务器执行轻量级ping来验证连接。对于负载平衡的连接,将对保留的所有活动的池内部连接执行此操作。这对于使用连接池的Java应用程序是有益的,因为该池可以使用此功能来验证连接。根据您的连接池和配置,可以在不同时间执行此验证:

在池返回到应用程序的连接之前。

当应用程序返回到池的连接时。

在定期检查空闲连接期间。

要使用此功能,请在连接池中以开头指定一个验证查询/ping/。请注意,语法必须完全指定。这将导致驱动程序向服务器发送ping并返回虚拟的轻量级结果集。使用ReplicationConnection或时 LoadBalancedConnection,将在所有活动连接之间发送ping。

正确指定语法至关重要。出于效率考虑,语法必须准确,因为此测试是针对每个执行的语句执行的:

protected static final String PING_MARKER = "/* ping */";
...
if (sql.charAt(0) == '/') {
if (sql.startsWith(PING_MARKER)) {
doPingInstead();
...
由于ping语法对空格,大小写和位置均敏感,因此以下代码段均无效:
sql = "/* PING */ SELECT 1";
sql = "SELECT 1 /* ping*/";
sql = "/*ping*/ SELECT 1";
sql = " /* ping */ SELECT 1";
sql = "/*to ping or not to ping*/ SELECT 1";

前面的所有语句都将发出普通 SELECT语句,并且 不会转换为轻量级ping。此外,对于负载平衡的连接,将针对内部池中的一个连接执行该语句,而不是验证每个基础物理连接。这会导致处于非活动状态的物理连接处于陈旧状态,并且它们可能会死亡。如果然后Connector / J重新平衡,则它可能会选择一个死连接,从而导致异常被传递到应用程序。为了防止这种情况,您可以 loadBalanceValidateConnectionOnSwapServer在使用前先验证连接。

如果你的连接器/ J部署使用连接池,允许你指定一个验证查询,利用它,但要确保查询的开始正好与 /ping/。如果您使用Connector / J的负载平衡或复制感知功能,那么这尤其重要,因为它将帮助保持活动连接,否则连接将会陈旧并终止,并在以后引起问题。