话说:
前面注重简单的CURD功能的实现,今天来总结下连接池。
Why?
我们每次访问页面,页面请求被后端捕获后,后台代码访问数据库,按照我们之前代码的方式,页面每请求一次,后端就连接一次,从数据库取数据,也就是调用我们的getConn()方法。一旦用户多起来,直接会造成数据库服务器的宕机或者页面卡死状态,怎么搞定?假如类似双11那样的访问量呢…….
What?
连接池
就是用来解决以上问题的一种方案。顾名思义,连接池是个数据池子,可以存放多个连接,供用户发起请求的时候直接从池子里面取值,而不是每次都访问数据库服务器。这样的话,大大提高了效率,如果一次访问次数多余连接池初始化数量,那么就有排队机制处理,等其他用户释放资源以后,在获取连接。
常见的数据连接池技术有:c3p0 dbcp druid 等等
既然是对JDBC连接的优化和补充,所以整体连接方式大同小异。
目录
一、c3p0
二、dbcp
三、druid
一、c3p0
1、根据文档配置,连接模式和链接JDBC神似。 http://www.mchange.com/projects/c3p0/ C3P0的API: http://www.mchange.com/projects/c3p0/apidocs/index.html 导入c3p0-0.9.2.1.jar mchange-commons-java-0.2.3.4.jar
新建一个类(TestC3P0Conn)来测试:
package com.hmc.datasource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.beans.PropertyVetoException;
import java.sql.SQLException;
/**
* User:Meice
* 2017/10/6
*/
public class TestC3P0Conn {
public static void main(String[] args) {
ComboPooledDataSource cpds = new ComboPooledDataSource();
try {
cpds.setDriverClass("com.mysql.jdbc.Driver");//loads the jdbc driver
cpds.setJdbcUrl("jdbc:mysql://localhost:3306/news_db");
cpds.setUser("root");
cpds.setPassword("119913");
//设置连接池初始化大小
cpds.setInitialPoolSize(300);
//设置连接池最大大小
cpds.setMaxPoolSize(1000);
long start = System.currentTimeMillis();
for(int i=1;i<=200;i++) {
cpds.getConnection();
}
long end = System.currentTimeMillis();
System.out.println("共耗时:"+(end-start)+"毫秒");
} catch (PropertyVetoException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
// cpds.close(); //close the connection
}
}
同样,我们也用JDBC连接来测试做对比:
package com.hmc.datasource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* User:Meice
* 2017/10/6
*/
public class TestCommonConn {
public static void main(String[] args) {
try {
//开始计时
long start = System.currentTimeMillis();
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//模拟很多用户连接
for(int i=1;i<=300;i++) {
//获取连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/news_db",
"root", "119913");
}
long end = System.currentTimeMillis();
System.out.println("共耗时:"+(end-start)+"毫秒");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
总结:
上面两种连接方式种,我们改变循环次数,查看连接时间(毫秒);在连接C3P0的时候,把连接池初始化大小、连接池最大连接数量轮流作为变量,调整,观察连接时间。
发现,JDBC连接循环300次,直接报错。
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Data source rejected establishment of connection, message from server: “Too many connections”
C3P0也是一样。当然这样初步测试连接时间,不够精确,有专门的测试工具.
实际有运用呢?
1、对于连接池而言,初始化值越大,那么启动速度越慢,访问速度越快;
反之亦然。
2、实践中,我们分为开发环境和生产环境。开发环境时,初始化一般设置小一些,生产环境列?不能想当然设计,要根据业务访问量来灵活设置。
3、同样,连接池还可以用来监测访问量大小。
二、DBCP
官网学习资料:
http://commons.apache.org/proper/commons-dbcp/guide/jndi-howto.html
同样,也是需要包,导入这几个。
commons-dbcp-1.4.jar
commons-logging-1.1.1.jar
commons-pool-1.5.4.jar
代码如下:
package com.hmc.datasource;
import org.apache.commons.dbcp.BasicDataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* User:Meice
* 2017/10/6
*/
public class TestDBCPConn {
public static void main(String[] args) {
BasicDataSource bds= new BasicDataSource();
bds.setDriverClassName("com.mysql.jdbc.Driver");
bds.setUrl("jdbc:mysql://localhost:3306/news_db");
bds.setUsername("root");
bds.setPassword("119913");
bds.setInitialSize(300);
long start = System.currentTimeMillis();
try {
for(int i=1;i<=200;i++) {
Connection conn = bds.getConnection();
conn.close();
}
long end = System.currentTimeMillis();
System.out.println("共耗时:"+(end-start)+"毫秒");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
我们可以反复调整循环次数,来对比运行效率。因为我们这个是单线程,测试不是很有意义。其实最贴切的应该用多线程来实现并发访问,这样测算连接效率最好。
测试了下,当然当做玩玩啦。
网上有专业的对比,大家可以参考,那个有意义,笔者这个只是小打小闹,单线程的。
https://github.com/alibaba/druid/wiki/linux-benchmark
三、druid
参考链接
http://www.iteye.com/magazines/90
API:http://static.druid.io/api/0.10.1/
包:druid-1.0.9.jar
更多详细内容可以参考:
这里温习一下之前的properties
我们在src目录下新建一个druid.properties的文件,做配
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/news_db
user=root
password=119913
写一个类:JavaDruidConn
package com.hmc.datasource;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.alibaba.druid.pool.DruidPooledConnection;
import com.alibaba.druid.util.DruidDataSourceUtils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
/**
* User:Meice
* 2017/10/6
*/
public class TestDruidConn {
DruidDataSource dds = null;
//获取配置数据源,封装为一个方法
public DruidDataSource getDataSource() throws Exception {
//以下代码加载配置文件,不在赘述
Properties pro = new Properties();
InputStream is = this.getClass().getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
String user = pro.getProperty("url");
System.out.println(user);
dds = (DruidDataSource) DruidDataSourceFactory.createDataSource(pro);
return dds;
}
//获取连接方法
public DruidPooledConnection getDruidPooledConnection(DruidDataSource dds ) throws SQLException {
dds.setAccessToUnderlyingConnectionAllowed(true);
DruidPooledConnection dpc = dds.getConnection();
return dpc;
}
public static void main(String[] args){
try {
//测试连接
TestDruidConn tdc = new TestDruidConn();
DruidDataSource dds = tdc.getDataSource();
Connection conn = tdc.getDruidPooledConnection(dds);
} catch (Exception e) {
e.printStackTrace();
}
}
}
不过,测试有问题。暂未找到答案,所以druid测试仅仅作为自己的一个测试记录。