在java中,“池”化的设计思想随处可见,池化的最终目的是为了对象复用,降低系统创建、销毁对象的成本,提升资源的可管理性。
尤其是一些大对象,创建销毁比较消耗资源的对象,池化可以极大提高效率,减少系统响应时间,提高系统并发度。
常见的有线程池,实例池(spring容器),连接池等。本节我们介绍连接池里面的数据库连接池。
没有数据库连接池管理的话,每次外部请求过来,请求到我们web层,dao层为每个请求都会创建连接,执行脚本,释放连接,其中dao层和数据库建立连接,底层都是TCP请求,每次都要三次握手,四次挥手。如下如所示:
在交给连接池管理连接后,dao层和数据库交互的时候,直接从连接池获取连接。不再自己手动创建销毁连接,如下图所示:
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.15version>
<scope>runtimescope>
dependency>
@Test
public void test() throws Exception{
StopWatch stopWatch=new StopWatch();
stopWatch.start();
for (int i = 0; i < 10000; i++) {
//步骤
//1 获取数据库连接的URL mysql8 必须要给一个时区 serverTimezone=UTC
String url="jdbc:mysql://xx.xx.xx.xx:3306/openplatform?serverTimezone=UTC";
//2 获取数据库连接的用户名和密码
String username="xxxlatformopr";
String password="xxx";
//3 加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//4 获取数据库连接对象 DriverManager依据数据库的不同,管理JDBC驱动
Connection connection= DriverManager.getConnection(url,username,password);
//5 获取操作数据库的Statement对象
Statement statement=connection.createStatement();
//6向数据库发送sql
String sql="select * from t_cust_info limit 1";
//7 通过statement对象 发送查询请求 拿回结果集
ResultSet resultSet = statement.executeQuery(sql);
//8遍历结果集 做一系列操作
while (resultSet.next()){
Object id = resultSet.getObject("cust_id");
//System.out.println(id);
Object uname = resultSet.getObject("cust_name");
//System.out.println(uname);
Object idNo = resultSet.getObject("id_no");
//System.out.println(idNo);
// System.out.println("==============");
}
//关闭资源
resultSet.close();
statement.close();
connection.close();
}
stopWatch.stop();
double totalTimeSeconds = stopWatch.getTotalTimeSeconds();
System.out.println(totalTimeSeconds);
//5.644
}
在上面的代码中,循环遍历了10000次,每次都会重新创建连接,查询,销毁链接。本地测试100次,平均每次耗时44.09秒,中间甚至偶发的报错,tcp连接超过限制
C3PO是一个开源的JDBC连接池,它能够自动维护和回收数据库连接使用C3PO,可以设置最大连接数、最小连接数、最大空闲时间等参数从而对连接进行有效控制。同时,C3PO还提供了丰富的监控功能,可以帮助我们追踪和排除数据库连接问题
<dependency>
<groupId>com.mchangegroupId>
<artifactId>c3p0artifactId>
<version>0.9.5.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.15version>
<scope>runtimescope>
dependency>
public class C3p0Util {
private static DataSource dataSource = null;
static{
//与配置文件的配置名字 named-config需要保持一致
dataSource = new ComboPooledDataSource("mysqlapp");
}
//从连接池中获取连接
public static Connection getConnection(){
try {
return dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace(); }
return null;
}
//释放连接回连接池
public static void release(Connection conn, Statement stmt, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if (stmt != null) {
try {
stmt.close();
} catch (Exception e) {
e.printStackTrace();
}
stmt = null;
}
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
conn = null;
}
}
}
放到项目resource下面
<c3p0-config>
<named-config name="mysqlapp">
<property name="driverClass">com.mysql.cj.jdbc.Driverproperty>
<property name="jdbcUrl">jdbc:mysql://x.x.x.x:3306/openplatformproperty>
<property name="user">formoprproperty>
<property name="password">xxxxxproperty>
<property name="acquireIncrement">5property>
<property name="initialPoolSize">10property>
<property name="minPoolSize">10property>
<property name="maxPoolSize">200property>
<property name="maxStatements">50property>
<property name="maxStatementsPerConnection">2property>
named-config>
c3p0-config>
@Test
public void testC3p0() throws Exception{
StopWatch stopWatch=new StopWatch();
stopWatch.start();
for (int i = 0; i < 10000; i++) {
//步骤
//4 获取数据库连接对象 DriverManager依据数据库的不同,管理JDBC驱动
Connection connection = C3p0Util.getConnection();
//5 获取操作数据库的Statement对象
Statement statement=connection.createStatement();
//6向数据库发送sql
String sql="select * from t_cust_info limit 1";
//7 通过statement对象 发送查询请求 拿回结果集
ResultSet resultSet = statement.executeQuery(sql);
//8遍历结果集 做一系列操作
while (resultSet.next()){
Object id = resultSet.getObject("cust_id");
//System.out.println(id);
Object uname = resultSet.getObject("cust_name");
//System.out.println(uname);
Object idNo = resultSet.getObject("id_no");
//System.out.println(idNo);
// System.out.println("==============");
}
C3p0Util.release(connection,statement,resultSet);
}
stopWatch.stop();
double totalTimeSeconds = stopWatch.getTotalTimeSeconds();
System.out.println(totalTimeSeconds);
}
上面的代码,本地测试100次,平均每次运行耗时:5.671s,可以看到相对自己手动创建链接,时间大幅减少
<c3p0-config>
<default-config>
<property name="acquireIncrement">3property>
<property name="acquireRetryAttempts">30property>
<property name="acquireRetryDelay">1000property>
<property name="autoCommitOnClose">falseproperty>
<property name="automaticTestTable">Testproperty>
<property name="breakAfterAcquireFailure">falseproperty>
<property name="checkoutTimeout">100property>
<property name="connectionTesterClassName">property>
<property name="factoryClassLocation">nullproperty>
<property name="forceIgnoreUnresolvedTransactions">falseproperty>
<property name="idleConnectionTestPeriod">60property>
<property name="initialPoolSize">3property>
<property name="maxIdleTime">60property>
<property name="maxPoolSize">15property>
<property name="maxStatements">100property>
<property name="maxStatementsPerConnection">property>
<property name="numHelperThreads">3property>
<property name="overrideDefaultUser">rootproperty>
<property name="overrideDefaultPassword">passwordproperty>
<property name="password">property>
<property name="preferredTestQuery">select id from test where id=1property>
<property name="propertyCycle">300property>
<property name="testConnectionOnCheckout">falseproperty>
<property name="testConnectionOnCheckin">trueproperty>
<property name="user">rootproperty>
<property name="usesTraditionalReflectiveProxies">falseproperty>
default-config>
c3p0-config>
DBCP是Apache软件基金会下的一个开源项目,也是一个JDBC连接池。与 C3PO类似,DBCP 也能够自动维护和回收数据库连接。同时,DBCP还支持连接池配置文件的读取,这样就可以通过修改配置文件来改变连接池的参数
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.15version>
<scope>runtimescope>
dependency>
<dependency>
<groupId>commons-dbcpgroupId>
<artifactId>commons-dbcpartifactId>
<version>1.4version>
dependency>
<dependency>
<groupId>commons-poolgroupId>
<artifactId>commons-poolartifactId>
<version>1.6version>
dependency>
public class JDbcConfigReadUtil {
private static DataSource dataSource = null;
static {
Properties props = new Properties();
try {
File file = new File("E:\\weixinData\\WeChat Files\\wxid_gv8xbkloz0wc22\\FileStorage\\File\\2023-03\\test\\src\\main\\resources\\dbcp\\jdbc.properties");//获取文件
InputStream inputStream = new FileInputStream(file);//获取输入流
props.load(inputStream);//props需要通过输入流读取一个.Properties文件
dataSource = BasicDataSourceFactory.createDataSource(props);
} catch (Exception e) {
e.printStackTrace();
}
}
//从连接池中获取连接
public static Connection getConnection() {
try {
return dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
//释放连接回连接池
public static void release(Connection conn, Statement stmt, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if (stmt != null) {
try {
stmt.close();
} catch (Exception e) {
e.printStackTrace();
}
stmt = null;
}
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
conn = null;
}
}
}
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://x.x.x.x:3306/openplatform
username=xx
password=xxx
@Test
public void testDbcp() throws Exception{
StopWatch stopWatch=new StopWatch();
stopWatch.start();
for (int i = 0; i < 10000; i++) {
//4 获取数据库连接对象 DriverManager依据数据库的不同,管理JDBC驱动
Connection connection = JDbcConfigReadUtil.getConnection();
//5 获取操作数据库的Statement对象
Statement statement=connection.createStatement();
//6向数据库发送sql
String sql="select * from t_cust_info limit 1";
//7 通过statement对象 发送查询请求 拿回结果集
ResultSet resultSet = statement.executeQuery(sql);
//8遍历结果集 做一系列操作
while (resultSet.next()){
Object id = resultSet.getObject("cust_id");
//System.out.println(id);
Object uname = resultSet.getObject("cust_name");
//System.out.println(uname);
Object idNo = resultSet.getObject("id_no");
//System.out.println(idNo);
// System.out.println("==============");
}
C3p0Util.release(connection,statement,resultSet);
}
stopWatch.stop();
double totalTimeSeconds = stopWatch.getTotalTimeSeconds();
System.out.println(totalTimeSeconds);
}
上面的代码,本地测试100次,平均每次运行耗时:5.191,可以看到相对自己手动创建链接,时间大幅减少
<!--初始化连接:连接池启动时创建的初始化连接数量-->
initialSize=5
<!--maxActive: 最大连接数量-->
maxActive=30
<!-- 连接在池中保持空闲而不被空闲连接回收器线程-->
minEvictableIdleTimeMillis=1800000
<!--maxIdle: 最大空闲连接-->
maxIdle=5
<!--minIdle: 最小空闲连接-->
minIdle=2
<!--maxWait: 超时等待时间以毫秒为单位 1000等于60秒-->
maxWait=1000
<!--对于事务是否 autoCommit-->
defaultAutoCommit=true
<!-- 在空闲连接回收器线程运行期间休眠的时间值,以毫秒为单位. -->
timeBetweenEvictionRunsMillis=600000
<!-- 在每次空闲连接回收器线程(如果有)运行时检查的连接数量 -->
numTestsPerEvictionRun=3
<!--是否自动回收超时连接-->
removeAbandoned=true
<!--超时时间(以秒数为单位)-->
removeAbandonedTimeout=180
<!-- 连接被泄露时是否打印 -->
logAbandoned=true
<!--连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除.设置为true后如果要生效,validationQuery参数必须设置为非空字符串-->
testWhileIdle=true
validationQuery=select 1
<!--是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个.-->
testOnBorrow=true
<!--是否在归还到池中前进行检验-->
testOnReturn=false
Druid 是阿里巴巴开源的一个数据库连接池。Druid 具有数据源监控SQL监控、容器集成支持、数据源防火墙等特性,可以帮助我们更好地管理数据库连接。同时,Druid 还提供了很多性能优化功能,如预编译语句缓存、分布式ID生成器等
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.16version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.15version>
dependency>
public class DruidUtil {
private static DataSource dataSource = null;
static {
Properties props = new Properties();
try {
File file = new File("E:\\weixinData\\WeChat Files\\wxid_gv8xbkloz0wc22\\FileStorage\\File\\2023-03\\test\\src\\main\\resources\\druid\\druid.properties");//获取文件
InputStream inputStream = new FileInputStream(file);//获取输入流
props.load(inputStream);//props需要通过输入流读取一个.Properties文件
dataSource=DruidDataSourceFactory.createDataSource(props);
} catch (Exception e) {
e.printStackTrace();
}
}
//从连接池中获取连接
public static Connection getConnection() {
try {
return dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
//释放连接回连接池
public static void release(Connection conn, Statement stmt, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if (stmt != null) {
try {
stmt.close();
} catch (Exception e) {
e.printStackTrace();
}
stmt = null;
}
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
conn = null;
}
}
}
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://x.x.x.x:3306/openplatform
username=xx
password=xxx
@Test
public void testDruid() throws Exception{
StopWatch stopWatch=new StopWatch();
stopWatch.start();
for (int i = 0; i < 10000; i++) {
//4 获取数据库连接对象 DriverManager依据数据库的不同,管理JDBC驱动
Connection connection = DruidUtil.getConnection();
//5 获取操作数据库的Statement对象
Statement statement=connection.createStatement();
//6向数据库发送sql
String sql="select * from t_cust_info limit 1";
//7 通过statement对象 发送查询请求 拿回结果集
ResultSet resultSet = statement.executeQuery(sql);
//8遍历结果集 做一系列操作
while (resultSet.next()){
Object id = resultSet.getObject("cust_id");
//System.out.println(id);
Object uname = resultSet.getObject("cust_name");
//System.out.println(uname);
Object idNo = resultSet.getObject("id_no");
//System.out.println(idNo);
// System.out.println("==============");
}
C3p0Util.release(connection,statement,resultSet);
}
stopWatch.stop();
double totalTimeSeconds = stopWatch.getTotalTimeSeconds();
System.out.println(totalTimeSeconds);
}
上面的代码,本地测试100次,平均每次运行耗时:5.58s,可以看到相对自己手动创建链接,时间大幅减少
druid github 属性介绍
参考文献:
C3p0使用详解