javaEE(12)_数据库连接池
一、直接获取数据库连接和通过池获取示意图:
二、编写数据库连接池
1、实现DataSource接口,并实现连接池功能的步骤:
2、通过DataSource来取代 DriverManager来获取Connection,像这样内存级别的操作相对于创建连接走公网来说速度快的多,相当于将时间转移到了服务器启动的时候,可以大幅度提高数据库的访问速度(一个请求最大的时间消耗在连接的建立上),通过DataSource获得的Connection都是已经被包裹过的(不是驱动原来的连接),他的close方法已经被修改.我们的程序只和DataSource打交道,不会直接访问连接池.
3、装饰模式实现连接池代码如下:
//自己实现连接池
public class JdbcPool implements DataSource { private static LinkedList<Connection> list = new LinkedList<Connection>();
private static Properties config = new Properties();
static{
try {
config.load(JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("db.properties")); Class.forName(config.getProperty("driver"));
for(int i=0;i<10;i++){
Connection conn = DriverManager.getConnection(config.getProperty("url"),
config.getProperty("username"), config.getProperty("password"));
list.add(conn);
}
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
} //conn.close(),不满足需求,为了保持用户关闭连接的习惯
/* 在实际开发,发现对象的方法满足不了开发需求时,有三种方式对其进行增强
* 1.写一个connecton子类,覆盖close方法,增强close方法
* 2.用包装设计模式,MyConnection也可以叫代理
* 3.用动态代理 aop 面向切面编程
*/
//要加锁,否则多线程情况下不同请求会拿到相同连接
public synchronized Connection getConnection() throws SQLException { if(list.size()<=0){
throw new RuntimeException("数据库忙,请稍会再来!!");
}
Connection conn = list.removeFirst(); //list.get()不行
MyConnection my = new MyConnection(conn);
return my;
} //1.定义一个类,实现与被增强相同的接口
//2.在类中定义一个变量,记住被增强对象
//3.定义一个构造函数,接收被增强对象
//4.覆盖想增强的方法
//5.对于不想增强的方法,直接调用目标对象(被增强对象)的方法
class MyConnection implements Connection{
private Connection conn;
public MyConnection(Connection conn){
this.conn = conn;
}
public void close(){
list.add(this.conn);
}
....
} public static void release(Connection conn,Statement st,ResultSet rs){
if(rs!=null){
try{
rs.close();
}catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if(st!=null){
try{
st.close();
}catch (Exception e) {
e.printStackTrace();
}
st = null;
}
if(conn!=null){
try{
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
4、使用动态代理模式实现连接池代码,将getConnection()改为如下:
public synchronized Connection getConnection() throws SQLException { if(list.size()<=0){
throw new RuntimeException("数据库忙,请稍会再来!!");
}
Connection conn = list.removeFirst(); //list.get()不行
return new MyConnectionHandler(conn,this).getWarpConn();
}
class MyConnectionHandler implements InvocationHandler {
private Connection realConnection;
private Connection warpedConnection;
private MyDataSource dataSource; private int maxUseCount = 5;
private int currentUserCount = 0; MyConnectionHandler(Connection conn,MyDataSource dataSource) {
this.realConnection=conn;
this.dataSource = dataSource;
} Connection getWarpConn() {
warpedConnection = (Connection) Proxy.newProxyInstance(this
.getClass().getClassLoader(), new Class[] { Connection.class },this);
return warpedConnection;
} public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if ("close".equals(method.getName())) {
currentUserCount++;
if (currentUserCount < maxUseCount)
dataSource.connectionsPool.addLast(warpedConnection);
else {
realConnection.close();
dataSource.currentCount--;
}
}
return method.invoke(realConnection, args);
}
}
三、开源数据库连接池
DBCP数据源&C3P0数据源
public class JdbcUtils_DBCP { private static DataSource ds = null;
static{
try{
InputStream in = JdbcUtils_DBCP.class.getClassLoader().
getResourceAsStream("dbcpconfig.properties");
Properties prop = new Properties();
prop.load(in); BasicDataSourceFactory factory = new BasicDataSourceFactory();
ds = factory.createDataSource(prop);
}catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
} public static Connection getConnection() throws SQLException{
return ds.getConnection();
} public static void release(Connection conn,Statement st,ResultSet rs){
if(rs!=null){
try{
rs.close();
}catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if(st!=null){
try{
st.close();
}catch (Exception e) {
e.printStackTrace();
}
st = null;
}
if(conn!=null){
try{
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc
username=root
password= #<!-- 初始化连接 -->
initialSize=10 #最大连接数量
maxActive=50 #<!-- 最大空闲连接 -->
maxIdle=20 #<!-- 最小空闲连接 -->
minIdle=5 #<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000 #JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们.
connectionProperties=useUnicode=true;characterEncoding=gbk #指定由连接池所创建的连接的自动提交(auto-commit)状态.
defaultAutoCommit=true #driver default 指定由连接池所创建的连接的只读(read-only)状态.
#如果没有设置该值,则"setReadOnly"方法将不被调用.(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly= #driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation).
#可用值为下列之一:(详情可见javadoc.)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
3、使用c3p0数据源后,Jdbcutils改为如下:
public class JdbcUtils_C3P0 { private static ComboPooledDataSource ds = null;
static{
try{
ds = new ComboPooledDataSource("mysql");可通过名称获取那个配置
}catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
} public static Connection getConnection() throws SQLException{
return ds.getConnection();
} public static void release(Connection conn,Statement st,ResultSet rs){
if(rs!=null){
try{
rs.close();
}catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if(st!=null){
try{
st.close();
}catch (Exception e) {
e.printStackTrace();
}
st = null;
}
if(conn!=null){
try{
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
c3p0-config.xml配置如下,必须是这个名称,服务器会自动搜索.
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property>
<property name="user">root</property>
<property name="password">root</property> <property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">20</property>
<property name="minPoolSize">5</property>
<property name="maxStatements">200</property>
</default-config> <named-config name="mysql">
<property name="acquireIncrement">50</property>
<property name="initialPoolSize">100</property>
<property name="minPoolSize">50</property>
<property name="maxPoolSize">1000</property>
<property name="maxStatements">0</property>
<property name="maxStatementsPerConnection">5</property>
</named-config> <named-config name="oracle">
<property name="acquireIncrement">50</property>
<property name="initialPoolSize">100</property>
<property name="minPoolSize">50</property>
<property name="maxPoolSize">1000</property>
<property name="maxStatements">0</property>
<property name="maxStatementsPerConnection">5</property>
</named-config>
</c3p0-config>
4、配置Tomcat数据源(tomcat自带有连接池),查看Tomcat文档,示例代码:
<Context>
<Resource name="jdbc/EmployeeDB" auth="Container"
type="javax.sql.DataSource"
username="root"
password="root"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/day16"
initialSize="10"
maxActive="30"
maxIdle="4"/>
</Context>
public class JdbcUtils_Tomcat {
private static DataSource ds;
static {
try {
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
ds = (DataSource) envCtx.lookup("jdbc/EmployeeDB");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static Connection getConnection() throws SQLException{
return ds.getConnection();
}
}
ps:此种配置可以在tomcat server.xml文件中或者是MATA-INF下新建context.xml文件都可以(可以参考apache提供的文档),其实这种用法是将datasource配置(实际就是一个对象)放在了JNDI容器中,这种情况下驱动的jar文件需放置在tomcat的lib下.
四、JNDI技术简介
ps:举个例子,Servlet中要用到request对象,可以将它作为方法参数传递进去,也可以将它放在JNDI容器中.
五、各种连接池性能比较
测试执行申请归还连接1,000,000次总耗时性能对比.
Java6 Environment
OS | OS X 10.8.2 |
CPU | intel i7 2GHz 4 core |
JVM | java version "1.6.0_37" |
Java6 Benchmark Result
Jdbc Connection Pool | 1 thread | 2 threads | 5 threads | 10 threads | 20 threads | 50 threads |
Druid | 1,102 | 1,509 | 1,889 | 1,904 | 2,027 | 1,977 |
tomcat-jdbc | 1,399 | 1,378 | 2,257 | 2,289 | 2,305 | 2,503 |
DBCP | 3,144 | 3,834 | 6,276 | 6,408 | 6,563 | 6,783 |
BoneCP | 4,327 | 3,598 | 3,800 | 5,242 | 9,402 | 19,066 |
jboss-datasource | 4,912 | 3,049 | 6,868 | 6,512 | 40,146 | 43,748 |
C3P0 | 18,570 | 19,467 | 15,270 | 19,294 | 28,195 | 66,677 |
Proxool | 16,221 | 14,455 | 24,688 | 38,905 | 48,087(Exception) | 58,238(Exception) |
Java7 Environment
OS | OS X 10.8.2 |
CPU | intel i7 2GHz 4 core |
JVM | java version "1.7.0_05" |
Java7 Benchmark Result
Jdbc Connection Pool | 1 thread | 2 threads | 5 threads | 10 threads | 20 threads | 50 threads |
Druid | 898 | 1,191 | 1,324 | 1,362 | 1,325 | 1,459 |
tomcat-jdbc | 1,269 | 1,378 | 2,029 | 2,103 | 1,879 | 2,025 |
DBCP | 2,324 | 5,055 | 5,446 | 5,471 | 5,524 | 5,415 |
BoneCP | 3,738 | 3,150 | 3,194 | 5,681 | 11,018 | 23,125 |
jboss-datasource | 4,377 | 2,988 | 3,680 | 3,980 | 32,708 | 37,742 |
C3P0 | 10,841 | 13,637 | 10,682 | 11,055 | 14,497 | 20,351 |
Proxool | 16,337 | 16,187 | 18,310(Exception) | 25,945 | 33,706(Exception) | 39,501 (Exception) |
结论
- Druid是性能最好的数据库连接池,tomcat-jdbc和druid性能接近.
- proxool在激烈并发时会抛异常,完全不靠谱.
- c3p0和proxool都相当慢,慢到影响sql执行效率的地步.
- bonecp性能并不优越,采用LinkedTransferQueue并没有能够获得性能提升.
- 除了bonecp,其他的在JDK 7上跑得比JDK 6上快
- jboss-datasource虽然稳定,但是性能很糟糕
javaEE(12)_数据库连接池的更多相关文章
- javaweb基础(39)_数据库连接池
一.应用程序直接获取数据库连接的缺点 用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长.假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大 ...
- JavaEE JDBC 了解数据库连接池
了解数据库连接池 @author ixenos 数据库连接是有限的资源,如果用户需要离开应用一段时间,那么他占用的连接就不应该保持开放状态: 另一方面,每次查询都获取连接并在随后关闭它的代价也很高. ...
- java web学习总结(十六) -------------------数据库连接池
一.应用程序直接获取数据库连接的缺点 用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长.假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大 ...
- javaweb学习总结(三十九)——数据库连接池
一.应用程序直接获取数据库连接的缺点 用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长.假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大 ...
- JavaWeb学习(三十)———— 数据库连接池
一.应用程序直接获取数据库连接的缺点 用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长.假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大 ...
- javaweb(三十九)——数据库连接池
一.应用程序直接获取数据库连接的缺点 用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长.假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大 ...
- Tomcat 与 数据库连接池 的小坑
连接池的优点众所周知. 我们可以自己实现数据库连接池,也可引入实现数据库连接池的jar包,按要求进行配置后直接使用. 关于这方面的资料,好多dalao博客上记录的都是旧版本Tomcat的配置方式,很可 ...
- JDBC_数据库连接池DRUID
/** * @Description: TODO(这里用一句话描述这个类的作用) * @Author aikang * @Date 2019/8/26 20:12 */ /* 1.数据库连接池: 1. ...
- JDBC_数据库连接池c3p0
/** * @Description: TODO(这里用一句话描述这个类的作用) * @Author aikang * @Date 2019/8/26 20:12 */ /* 1.数据库连接池: 1. ...
随机推荐
- laravel 路由设置
目录 routes\web.php 初始路由,直接渲染视图welcome,即V层 '/'为路径:www.xxx.com/ Route::get('/', function () { return ...
- 如何实现一个无边框Form的移动和改变大小(一)
很多时候我们不希望使用Windows提供的窗体. 我们希望使用一个无边框的窗体,什么border,caption透明就行了. 下面我们来说下一些实现方法. 这个方法要求窗体自定义的border siz ...
- 如何在内网打洞使得能暴露mstsc端口
说明: 1.目标机器Target,有全部控制权,其所处网络无法向外网暴露端口,但是已知Target的外网地址:Target_internet_addr 2.交换机器Exchange,有全部控制权,其所 ...
- P4827 [国家集训队] Crash 的文明世界(第二类斯特林数+树形dp)
传送门 对于点\(u\),所求为\[\sum_{i=1}^ndis(i,u)^k\] 把后面那堆东西化成第二类斯特林数,有\[\sum_{i=1}^n\sum_{j=0}^kS(k,j)\times ...
- [Xcode 实际操作]九、实用进阶-(3)给代码方法添加宏注释
目录:[Swift]Xcode实际操作 本文将演示如何在方法列表中,对方法名称进行注释. 这样可以使程序,按功能分块,使方法清晰.易读并且方便定位. 在项目导航区,打开视图控制器的代码文件[ViewC ...
- bzoj1145[CTSC2008]图腾
传送门 虽然是远古时期的ctsc,但是果然还是ctsc啊 前置芝士:树状数组 这个题最开始的思路很好想,由于之前写过一个类似处理的题,所以这个题我一开始就想到了思路. 首先,我们可以尝试讲图腾表示为x ...
- 黑马方法引用学习 Stream流 函数式接口 Lambda表达式 方法引用
- DRF教程4-视图集和路由类
rest框架包括一个处理viewset的抽象,允许开发人员集中精力处理api交互和建模,url构造都根据常见方式自动处理. ViewSet类 几乎和VIew类一样,不过它提供read,update这样 ...
- Guard Duty (medium) Codeforces - 958E2 || (bzoj 2151||洛谷P1792) 种树 || 编译优化
https://codeforces.com/contest/958/problem/E2 首先求出N个时刻的N-1个间隔长度,问题就相当于在这些间隔中选K个数,相邻两个不能同时选,要求和最小 方法1 ...
- 1-27TreeSet简介
使用TreeSet存储Integer对象 TreeSet的特点是可以对存放进去的元素进行排序. package com.monkey1024.set; import java.util.TreeSet ...