精华内容
下载资源
问答
  • 几种常用数据库连接池的使用

    万次阅读 多人点赞 2019-02-14 12:38:38
    一、应用程序直接获取数据库连接的缺点  用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要...二、使用数据库连接池优化程序性能 2.1、数据库连接池的基本概念  数据库连接是一种关键的有限的昂...

     

    一、应用程序直接获取数据库连接的缺点

      用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。如下图所示:

      

    二、使用数据库连接池优化程序性能

    2.1、数据库连接池的基本概念

      数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现的尤为突出.对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标.数据库连接池正式针对这个问题提出来的.数据库连接池负责分配,管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。如下图所示:

      

           数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中, 这些数据库连接的数量是由最小数据库连接数来设定的.无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量.连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中.

           数据库连接池的最小连接数和最大连接数的设置要考虑到以下几个因素:

    1. 最小连接数:是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费.
    2. 最大连接数:是连接池能申请的最大连接数,如果数据库连接请求超过次数,后面的数据库连接请求将被加入到等待队列中,这会影响以后的数据库操作
    3. 如果最小连接数与最大连接数相差很大:那么最先连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接.不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,他将被放到连接池中等待重复使用或是空间超时后被释放.

    2.2、编写数据库连接池

      编写连接池需实现java.sql.DataSource接口。DataSource接口中定义了两个重载的getConnection方法:

    • Connection getConnection()
    • Connection getConnection(String username, String password)

      实现DataSource接口,并实现连接池功能的步骤:

    1. 在DataSource构造函数中批量创建与数据库的连接,并把创建的连接加入LinkedList对象中。
    2. 实现getConnection方法,让getConnection方法每次调用时,从LinkedList中取一个Connection返回给用户。
    3. 当用户使用完Connection,调用Connection.close()方法时,Collection对象应保证将自己返回到LinkedList中,而不要把conn还给数据库。Collection保证将自己返回到LinkedList中是此处编程的难点

     数据库连接池核心代码

      使用动态代理技术构建连接池中的connection

    包结构:

     JdbcPool.java:

    大致思路:1、读取配置文件,将属性值取出

                       2、注册jdbc驱动

                       3、通过数据库连接数和驱动管理器获得相应连接(.getConnection()),因为DataSource是接口,所以这个方法需要                           我们手动实现

                       4、重点:实现getConnection()方法

                              思考:当外部有连接需求时,直接从connectList拿出一个connect,就实现了这个方法,但是我们再想想,它用完了怎么回收呢,确实,有close()方法,但是这个方法作用是将这个连接还给数据库,而不是数据库连接池,这样会导致数据库连接池中 的连接越来越少,这样可不行,但是我们也不能影响它的正常使用吧,在这种情况下,我们想要监测这个连接对象的动态,在它调用close()方法时,我们将其再添加进connectList,这样连接池中的连接不就没少了吗,在java中对于监听一个对象的动态,最常用也最实用的便是动态代理,对于动态代理,其实我们可以把它想象成就是一个大盒子,里面装着一个真实对象小盒子,这就在大盒子和小盒子间形成了一个横切空隙,而真实做事的还是这个对象,代理只是个接活的,接到活就给内部的小盒子干,自然这个空隙可以用来监测真实对象的事务,也就是监听对象的方法。具体可以看看这个,动态代理:https://www.cnblogs.com/xdp-gacl/p/3971367.html

    package jdbcPoolTest;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.PrintWriter;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.sql.SQLFeatureNotSupportedException;
    import java.util.LinkedList;
    import java.util.Properties;
    import java.util.logging.Logger;
    
    import javax.sql.DataSource;
    
    import org.omg.PortableServer.POA;
    
    public class JdbcPool implements DataSource{
    	
    	
    	private static LinkedList<Connection> listConnections = new LinkedList<>();
    	
    	static {
    		//静态代码块中加载db.properties配置文件
    		InputStream in = JdbcPool.class.getClassLoader().getResourceAsStream("db.properties");
    		
    		Properties prop = new Properties();
    		try {
    			//读取文件内容
    			prop.load(in);
    			String driver = prop.getProperty("driver");
    			String url = prop.getProperty("url");
    			String username = prop.getProperty("username");
    			String password = prop.getProperty("password");
    			
    			//数据库连接池的初始化连接大小
    			int jdbcPoolInitSize = Integer.parseInt(prop.getProperty("jdbcPoolInitSize"));
    			//加载数据库驱动		
    			Class.forName(driver);	
    			for(int i=0; i<jdbcPoolInitSize; i++) {
    			//获取连接	
    			Connection conn = DriverManager.getConnection(url, username, password);
    			System.out.println("获取连接:"+conn);
    			//将conn连接加入listConnections集合中,此时的listConnections就是一个连接池
    			listConnections.add(conn);
    			
    				
    			}
    			
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}catch (ClassNotFoundException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}catch (SQLException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		
    		
    		
    	}
    
    	@Override
    	public Connection getConnection() throws SQLException {
    		// TODO Auto-generated method stub
    		//如果数据库连接池的连接数大于0
    		System.out.println("进来了");
    		if (listConnections.size()>0) {
    			//从listConnections中获取一个连接
    			final Connection conn = listConnections.removeFirst();
    			System.out.println("数据库连接池的大小为:"+listConnections.size());
    			//返回Connection的代理,利用代理可以处理一些横切事件
    			System.out.println("取出的连接为:"+conn);
    			return (Connection) Proxy.newProxyInstance(JdbcPool.class.getClassLoader(), conn.getClass().getInterfaces(), new InvocationHandler() {
    				
    				@Override
    				public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    					// TODO Auto-generated method stub
    					//如果不是执行关闭操作,则通过反射执行相应方法
    					if (!method.getName().equals("close")) {
    												
    						return method.invoke(conn, args);
    						
    					}else {
    						//否则,将conn归还给连接池
    						listConnections.add(conn);
    						System.out.println("归还连接:"+conn);
    						System.out.println("连接池大小为:"+listConnections.size());
    						return null;
    					}
    					
    				}
    			});
    			
    		}else {
    			throw new RuntimeException("对不起,数据库正忙");
    		}
    		
    	}
    	@Override
    	public PrintWriter getLogWriter() throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public int getLoginTimeout() throws SQLException {
    		// TODO Auto-generated method stub
    		return 0;
    	}
    
    	@Override
    	public Logger getParentLogger() throws SQLFeatureNotSupportedException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public void setLogWriter(PrintWriter arg0) throws SQLException {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void setLoginTimeout(int arg0) throws SQLException {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public boolean isWrapperFor(Class<?> arg0) throws SQLException {
    		// TODO Auto-generated method stub
    		return false;
    	}
    
    	@Override
    	public <T> T unwrap(Class<T> arg0) throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	
    
    	@Override
    	public Connection getConnection(String username, String password) throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    }
    

    写一个JdbcUtil测试数据库连接池

    package jdbcPoolTest;
    
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    public class JdbcUtil {
    
    	
    	private static JdbcPool jdbcPool = new JdbcPool();
    	
    	public static Connection getConnection() throws SQLException {
    		
    		return jdbcPool.getConnection();
    		
    	}
    	//释放的资源包括Connection数据库连接对象,负责执行SQL命令的Statement对象,存储查询结果的ResultSet对象
    	public static void release(Connection conn,Statement st ,ResultSet rs) {
    		if (conn!=null) {
    			try {
    				conn.close();
    			} catch (SQLException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    		if (st!=null) {
    			try {
    				st.close();
    			} catch (SQLException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    		if (rs!=null) {
    			try {
    				rs.close();
    			} catch (SQLException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			rs = null;
    		}
    	}
    }
    

     db.properties配置文件如下:

    driver=com.mysql.jdbc.Driver
     url=jdbc:mysql://localhost:3306/student
     username=root
     password=123456
     jdbcPoolInitSize=10

     

    三、开源数据库连接池

      现在很多WEB服务器(Weblogic, WebSphere, Tomcat)都提供了DataSoruce的实现,即连接池的实现。通常我们把DataSource的实现,按其英文含义称之为数据源,数据源中都包含了数据库连接池的实现。
      也有一些开源组织提供了数据源的独立实现:

    • DBCP 数据库连接池
    • C3P0 数据库连接池

      在使用了数据库连接池之后,在项目的实际开发中就不需要编写连接数据库的代码了,直接从数据源获得数据库的连接。

    3.1、DBCP数据源

      DBCP 是 Apache 软件基金组织下的开源连接池实现,要使用DBCP数据源,需要应用程序应在系统中增加如下两个 jar 文件:

    • Commons-dbcp.jar:连接池的实现
    • Commons-pool.jar:连接池实现的依赖库

      Tomcat 的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。

    3.2、在应用程序中加入dbcp连接池

      1.导入相关jar包
            commons-dbcp-1.2.2.jar、commons-pool.jar、common-logging.jar三个包
      2、在类目录下加入dbcp的配置文件:dbcpconfig.properties

    用了官方的连接池,自然配置文件内容也就多了

    dbcpconfig.properties的配置信息如下:

     注意这段:

    #driver default 指定由连接池所创建的连接的只读(read-only)状态。
     #如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
     defaultReadOnly=

    如果设置为ture,那就只能进行查询操作

    #连接设置   报错com.mysql.jdbc.Connection.isValid(I)Z,更新mysql包
    driverClassName=com.mysql.jdbc.Driver
     url=jdbc:mysql://localhost:3306/student
     username=root
     password=123456
     #初始化连接池大小
     jdbcPoolInitSize=10
     #最大连接数量
     maxActive=50
     
     #<!-- 最大空闲连接 -->
     maxIdle=20
     
     #<!-- 最小空闲连接 -->
     minIdle=5
     
     #<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
     maxWait=60000
     
     
     #JDBC驱动建立连接时附带的连接属性的格式必须为这样:[属性名=property;] 
     #注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
     connectionProperties=useUnicode=true;characterEncoding=UTF8
     
     #指定由连接池所创建的连接的自动提交(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

    包结构: 

    jdbcUtils_DBCP:

    大致思路:

    1、从配置文件中获取输入流,进行加载

    2、有了BasicDataSoureFactory,那我们也不需要将配置文件中的属性值一个一个读取出来了,它有个方法createDataSource()能自动获取到数据源 

    3、接下来只需要拿着这个数据源对象ds去获取连接了,具体如何连接,就不需要我们关心,大致思路与我们自行编写连接池类似,只不过多了些其他属性的出来方法。从而简化了我们的开发

    package zdb.jdbc.util;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.Properties;
    
    import javax.sql.DataSource;
    
    import org.apache.commons.dbcp2.BasicDataSourceFactory;
    
    
    public class jdbcUtils_DBCP {
    
    	/**
    	      * 在java中,编写数据库连接池需实现java.sql.DataSource接口,每一种数据库连接池都是DataSource接口的实现
    	      * DBCP连接池就是java.sql.DataSource接口的一个具体实现
    	      */
    	private  static DataSource ds = null;
    	
    	
    	static {
    	InputStream in = jdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
    	Properties prop = new Properties();
    	try {
    		prop.load(in);
    		//创建数据源
    		ds = BasicDataSourceFactory.createDataSource(prop);	 
    		
    	} catch (IOException e) {
    		// TODO Auto-generated catch block
    		e.printStackTrace();
    	}
    	catch (Exception e) {
    		// TODO Auto-generated catch block
    		e.printStackTrace();
    	}
    	}
    	public static Connection getConnection() throws SQLException{
    		return ds.getConnection();
    		
    	}
    	//释放的资源包括Connection数据库连接对象,负责执行SQL命令的Statement对象,存储查询结果的ResultSet对象
    	public static void release(Connection conn,PreparedStatement ps ,ResultSet rs) {
    		if (conn!=null) {
    			try {
    				conn.close();
    			} catch (SQLException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    		if (ps!=null) {
    			try {
    				ps.close();
    			} catch (SQLException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    		if (rs!=null) {
    			try {
    				rs.close();
    			} catch (SQLException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			rs = null;
    		}
    	}
    	
    }
    

    接下来我们通过在表中插入属性值来测试--DataSourceTest: 

    注意这段:

    //由于包版本问题:必须加上PreparedStatement.RETURN_GENERATED_KEYS,不然将会报主键错误
     ps = conn.prepareStatement(sql,PreparedStatement.RETURN_GENERATED_KEYS);

    package zdb.jdbc.util;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    
    import javafx.css.PseudoClass;
    
    public class DataSourceTest {
    	 
    	public static void jdbcDataSource() {
    		
    		PreparedStatement ps = null;
    		ResultSet rs = null;
    		Connection conn = null;
    		try {
    			//conn = jdbcUtils_DBCP.getConnection();
    			conn = jdbcUtils_DBCP.getConnection();
    			String sql = "insert into user(uname) values(?)";
    			//由于版本兼容问题:必须加上PreparedStatement.RETURN_GENERATED_KEYS
    			ps = conn.prepareStatement(sql,PreparedStatement.RETURN_GENERATED_KEYS);
    			ps.setString(1, "lonewolf");
    			ps.executeUpdate();
    			//获得主键
    			rs = ps.getGeneratedKeys();
    			if (rs.next()) {
    				System.out.println(rs.getInt(1));
    			}
    		} catch (SQLException e) {
    			// TODO Auto-generated catch blockrs
    			e.printStackTrace();
    		}
    			finally {
    			jdbcUtils_DBCP.release(conn, ps, rs);
    			//jdbcUtils_C3P0.release(conn, ps, rs);
    		}
    		
    		
    		
    	}
    
    	public static void main(String[] args) {
    		new DataSourceTest().jdbcDataSource();
    	}
    	
    	
    }
    

     

    3.3、C3P0数据源(重点

      C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。C3P0数据源在项目开发中使用得比较多。

      c3p0与dbcp区别

    1. dbcp默认不自动回收空闲连接,需要手动开启
    2. c3p0默认自动回收空闲连接功能

    3.4、在应用程序中加入C3P0连接池

      1.导入相关jar包
           c3p0-0.9.5.2.jar、mchange-commons-java-0.2.12jar,如果操作的是Oracle数据库,那么还需要导入c3p0-oracle-thin-extras-0.9.2-pre1.jar

    注意:导入包时,两个包的版本也是需要匹配的

    不然会报错:Exception in thread "main" java.lang.NoClassDefFoundError: com/mchange/v2/ser/Indirector

    但是这两个包版本是匹配的,百度网盘下载链接:https://pan.baidu.com/s/1o9cBkMVb_kZmAksZjjoZYg 密码:c7pr 

           2、包结构:


    3、在类目录下加入C3P0的配置文件:c3p0-config.xml,即在项目根目录下读取文件

    c3p0-config.xml的配置信息如下:

    注意:配置文件中有两种内容相似的配置,一种缺省配置,意思是在没有指定配置文件名时,调用该配置,一种命名配置,也就是在使用配置文件时,加上配置名即可引用,如接下来会说的:new ComboPooledDataSource("MySQL");

    <c3p0-config>
         <!--
         C3P0的缺省(默认)配置,
         如果在代码中“ComboPooledDataSource ds = new ComboPooledDataSource();”这样写就表示使用的是C3P0的缺省(默认)配置信息来创建数据源
         -->
         <default-config>
             <property name="driverClass">com.mysql.jdbc.Driver</property>
             <property name="jdbcUrl">jdbc:mysql://localhost:3306/student</property>
             <property name="user">root</property>
             <property name="password">123456</property>
             
             <property name="acquireIncrement">5</property>
             <property name="initialPoolSize">10</property>
             <property name="minPoolSize">5</property>
             <property name="maxPoolSize">20</property>
         </default-config>
     
         <!--
         C3P0的命名配置,
         如果在代码中“ComboPooledDataSource ds = new ComboPooledDataSource("MySQL");”这样写就表示使用的是name是MySQL的配置信息来创建数据源
         -->
         <named-config name="MySQL">
             <property name="driverClass">com.mysql.jdbc.Driver</property>
             <property name="jdbcUrl">jdbc:mysql://localhost:3306/student</property>
             <property name="user">root</property>
             <property name="password">123456</property>
             
             <property name="acquireIncrement">5</property>
             <property name="initialPoolSize">10</property>
             <property name="minPoolSize">5</property>
             <property name="maxPoolSize">20</property>
         </named-config>
     
     </c3p0-config>

    jdbcUtils_C3P0:

    大致思路:

     我们可以通过代码看到,c3p0将我们的代码量又缩减了许多

    ComboPooledDataSource ds = new ComboPooledDataSource("MySQL");我们直接new一个ComboPooledDataSource()对象就可以获得数据源,所以这就是为什么之前需要将xml配置文件放在根目录下,“MySQL”就是我们的配置的名字

    package zdb.jdbc.util;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    public class jdbcUtils_C3P0 {
    	
    	private static ComboPooledDataSource ds = null;
    	static {
    		ds = new ComboPooledDataSource("MySQL");
    		
    	}
    	public static Connection getConnection() throws SQLException{
    	    //从数据源中获取数据库连接
    	    return ds.getConnection();
    }
    	//释放的资源包括Connection数据库连接对象,负责执行SQL命令的Statement对象,存储查询结果的ResultSet对象
    		public static void release(Connection conn,PreparedStatement ps ,ResultSet rs) {
    			if (conn!=null) {
    				try {
    					conn.close();
    				} catch (SQLException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			}
    			if (ps!=null) {
    				try {
    					ps.close();
    				} catch (SQLException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			}
    			if (rs!=null) {
    				try {
    					rs.close();
    				} catch (SQLException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    				rs = null;
    			}
    		}
    
    }
    

     测试C3P0数据源-DataSourceTest

    package zdb.jdbc.util;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    
    import javafx.css.PseudoClass;
    
    public class DataSourceTest {
    	 
    	public static void jdbcDataSource() {
    		
    		PreparedStatement ps = null;
    		ResultSet rs = null;
    		Connection conn = null;
    		try {
    			//conn = jdbcUtils_DBCP.getConnection();
    			conn = jdbcUtils_C3P0.getConnection();
    			String sql = "insert into user(uname) values(?)";
    			//由于版本兼容问题:必须加上PreparedStatement.RETURN_GENERATED_KEYS
    			ps = conn.prepareStatement(sql,PreparedStatement.RETURN_GENERATED_KEYS);
    			ps.setString(1, "lonewolf");
    			ps.executeUpdate();
    			//获得主键
    			rs = ps.getGeneratedKeys();
    			if (rs.next()) {
    				System.out.println(rs.getInt(1));
    			}
    		} catch (SQLException e) {
    			// TODO Auto-generated catch blockrs
    			e.printStackTrace();
    		}
    			finally {
    			jdbcUtils_DBCP.release(conn, ps, rs);
    			//jdbcUtils_C3P0.release(conn, ps, rs);
    		}
    		
    		
    		
    	}
    
    	public static void main(String[] args) {
    		new DataSourceTest().jdbcDataSource();
    	}
    	
    	
    }
    

    这些便是数据库连接池的大致内容。

    接下来说说

    四、Tomcat中配置数据库源

    在实际开发中,我们有时候还会使用服务器提供给我们的数据库连接池,比如我们希望Tomcat服务器在启动的时候可以帮我们创建一个数据库连接池,那么我们在应用程序中就不需要手动去创建数据库连接池,直接使用Tomcat服务器创建好的数据库连接池即可。要想让Tomcat服务器在启动的时候帮我们创建一个数据库连接池,那么需要简单配置一下Tomcat服务器。

    4.1、JNDI技术简介

      JNDI(Java Naming and Directory Interface),Java命名和目录接口,它对应于J2SE中的javax.naming包,
      这 套API的主要作用在于:它可以把Java对象放在一个容器中(JNDI容器),并为容器中的java对象取一个名称,以后程序想获得Java对象,只需 通过名称检索即可。其核心API为Context,它代表JNDI容器,其lookup方法为检索容器中对应名称的对象。

      Tomcat服务器创建的数据源是以JNDI资源的形式发布的,所以说在Tomat服务器中配置一个数据源实际上就是在配置一个JNDI资源

    服务器创建好数据源之后,我们的应用程序又该怎么样得到这个数据源呢,Tomcat服务器创建好数据源之后是以JNDI的形式绑定到一个JNDI容器中的,我们可以把JNDI想象成一个大大的容器,我们可以往这个容器中存放一些对象,一些资源,JNDI容器中存放的对象和资源都会有一个独一无二的名称,应用程序想从JNDI容器中获取资源时,只需要告诉JNDI容器要获取的资源的名称,JNDI根据名称去找到对应的资源后返回给应用程序。我们平时做javaEE开发时,服务器会为我们的应用程序创建很多资源,比如request对象,response对象,服务器创建的这些资源有两种方式提供给我们的应用程序使用:第一种是通过方法参数的形式传递进来,比如我们在Servlet中写的doPost和doGet方法中使用到的request对象和response对象就是服务器以参数的形式传递给我们的。第二种就是JNDI的方式,服务器把创建好的资源绑定到JNDI容器中去,应用程序想要使用资源时,就直接从JNDI容器中获取相应的资源即可。

      对于上面的name="jdbc/datasource"数据源资源,在应用程序中可以用如下的代码去获取

    1 Context initCtx = new InitialContext();
    2 Context envCtx = (Context) initCtx.lookup("java:comp/env");
    3 dataSource = (DataSource)envCtx.lookup("jdbc/datasource");

    此种配置下,数据库的驱动jar文件需放置在tomcat的lib下

    4.2、配置Tomcat数据源

    包结构:

     如果报错com.mysql.jdbc.Connection.isValid(I)Z,说明包太旧了,更新mysql包即可解决

    配置数据源JNDI的方式有很多,详细请看:https://blog.csdn.net/dyllove98/article/details/7706218

    注意了:有个问题坑了我一天

    先看我的这篇文章---解决Tomcat中修改server.xml和content.xml后自动还原问题:https://blog.csdn.net/qq_36528311/article/details/87214000

    之前我是直接在外部的tomcat文件下的conf文件夹下去修改这两个文件,所以落坑

    看完这篇文章应该知道了server.xml、context.xml的大概配置,接下来,我们来具体配置

    我使用---配置全局JNDI数据源,应用到所有Tomcat下部署的应用

    第一步、找到Tomcat的server.xml中GlobalNamingResources节点,在节点下加一个全局数据源

    注意:name="jdbc/mysql"中“jdbc/mysql”为数据源名,在文件中多处引用  

    <Resource  
        name="jdbc/mysql"   
        scope="Shareable"   
        type="javax.sql.DataSource"  
        factory="org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory"  
        url="jdbc:mysql://localhost:3306/test"  
        driverClassName ="com.mysql.jdbc.Driver"  
        username="root"  
        password="root"  
    />  
    

    第二步,找到Tomcat的context.xml,在Context节点下加一个ResourceLink节点对第一步配置的数据源进行引用

    这个XML配置文件的根节点就是<Context>

     插入代码:

    <ResourceLink global="jdbc/mysql" name="jdbc/mysql" type="javax.sql.DataSource"/>

     第三步,配置web.xml文件,添加资源映射(此步骤可要可不要

     <resource-ref>
       <description>DB Connection</description>
       <res-ref-name>jdbc/mysql</res-ref-name>
       <res-type>javax.sql.DataSource</res-type>
       <res-auth>Container</res-auth>
     </resource-ref> 

    这种配置方式 

    优点:重用性,一次性到位

    缺点:没有可控性(tomcat原始文件遭到修改)

     配置文件配置好了,现在就来写代码

    JdbcUtils_JNDI:

    package zdb.util.JNDI_tomcat;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    import javax.naming.Context;
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
    import javax.sql.DataSource;
    
    public class JdbcUtils_JNDI {
    	private static DataSource ds = null;
    	static {
    		
    		try {
    			//初始化JNDI
    			Context initCtx = new InitialContext();
    			//得到JNDI容器
    			Context envCtx = (Context) initCtx.lookup("java:comp/env");
    			//从JNDI容器中检索name为jdbc/datasource的数据源
    			ds = (DataSource) envCtx.lookup("jdbc/mysql");
    		} catch (NamingException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		
    		
    	}
    	public static Connection getConnection() throws SQLException{
    	    //从数据源中获取数据库连接
    		
    	    return ds.getConnection();
    	   
    }
    	//释放的资源包括Connection数据库连接对象,负责执行SQL命令的Statement对象,存储查询结果的ResultSet对象
    		public static void release(Connection conn,PreparedStatement ps ,ResultSet rs) {
    			if (conn!=null) {
    				try {
    					conn.close();
    				} catch (SQLException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			}
    			if (ps!=null) {
    				try {
    					ps.close();
    				} catch (SQLException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			}
    			if (rs!=null) {
    				try {
    					rs.close();
    				} catch (SQLException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    				rs = null;
    			}
    
    }
    }
    

     测试下

    JNDI_test:

    package zdb.util.JNDI_tomcat;
    
    import java.io.IOException;
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    @WebServlet("/JNDI_test")
    public class JNDI_test extends HttpServlet{
    	
    	public void doGet(HttpServletRequest request, HttpServletResponse response)
    	             throws ServletException, IOException {
    	         Connection conn = null;
    	         PreparedStatement st = null;
    	         ResultSet rs = null;
    	         try{
    	             //获取数据库连接
    	             conn = JdbcUtils_JNDI.getConnection();
    	             System.out.println("mysql Connection pool connected !!");
    	             String sql = "insert into user(uname) values(?)";
    	             st = conn.prepareStatement(sql,PreparedStatement.RETURN_GENERATED_KEYS);
    	             st.setString(1, "gacl");
    	             st.executeUpdate();
    	             //获取数据库自动生成的主键
    	             rs = st.getGeneratedKeys();
    	             if(rs.next()){
    	                 System.out.println(rs.getInt(1));
    	             }
    	         }catch (Exception e) {
    	             e.printStackTrace();
    	         }finally{
    	             //释放资源
    	             JdbcUtils_JNDI.release(conn, st, rs);
    	         }
    	     }
    	 public void doPost(HttpServletRequest request, HttpServletResponse response)
    	              throws ServletException, IOException {
    	          doGet(request, response);
    	     }
    
    }
    

     我再通过写一个jsp页面来映射到该servlet测试下

    test.jsp:

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    <style>
    	body{
    	text-align:center;
    	}	
    </style>
    </head>
    <body>
    	<a href="${pageContext.servletContext.contextPath }/JNDI_test">测试</a>
    	
    </body>
    

    测试结果成功:

    拓展:由于此种配置模式的缺点,我们还可以通过增加一个新的xml文件来增加节点,而不用动原始文件

    具体请看:tomcat虚拟路径的配置

    几篇文章推荐给大家看下:

    tomcat下面web应用发布路径配置( 即虚拟目录配置 ):http://www.voidcn.com/article/p-zctzsjte-rs.html

    Tomcat服务器原理详解https://www.cnblogs.com/crazylqy/p/4706223.html

    Tomcat中主目录配置与虚拟目录配置问题自我总结:http://blog.51cto.com/longx/1357666

    文章借鉴参照:-孤傲苍狼-原文:https://www.cnblogs.com/xdp-gacl/p/4002804.html

     

     

    展开全文
  • C# 数据库连接池 C# 数据库连接池

    热门讨论 2010-10-27 17:48:32
    C# 数据库连接池 C# 数据库连接池 C# 数据库连接池 C# 数据库连接池
  • 常用数据库连接池的配置

    千次阅读 2017-03-20 16:00:01
    Druid是阿里巴巴开源平台上的一个项目,整个项目由数据库连接池、插件框架和SQL解析器组成。该项目主要是为了扩展JDBC的一些限制,可以让程序员实现一些特殊的需求,比如向密钥服务请求凭证、统计SQL信息、SQL性能...

    1.阿里巴巴-德鲁伊druid连接池配置

    1.1、简介:

    Druid是阿里巴巴开源平台上的一个项目,整个项目由数据库连接池、插件框架和SQL解析器组成。该项目主要是为了扩展JDBC的一些限制,可以让程序员实现一些特殊的需求,比如向密钥服务请求凭证、统计SQL信息、SQL性能收集、SQL注入检查、SQL翻译等,程序员可以通过定制来实现自己需要的功能。

    1.2、Druid支持哪些数据库?

    Druid支持所有JDBC兼容的数据库,包括Oracle、MySql、Derby、Postgresql、SQL Server、H2等等。 

    Druid针对Oracle和MySql做了特别优化,比如Oracle的PS Cache内存占用优化,MySql的ping检测优化。

    1.3、基本配置

    <!-- 数据库连接池 -->
    	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
    		<!-- 数据库基本信息配置 -->
    		 <property name="url" value="${jdbc_url}" />  
        	<property name="username" value="${jdbc_user}" />  
        	<property name="password" value="${jdbc_password}" />  	
    		<!-- 初始化连接数量 -->
    		<property name="initialSize" value="${druid.initialSize}" />
    		<!-- 最大并发连接数 -->
    		<property name="maxActive" value="${druid.maxActive}" />
    		<!-- 最大空闲连接数 :已经不再使用,配置了也没效果-->
    		<!-- <property name="maxIdle" value="${druid.maxIdle}" /> -->
    		<!-- 最小空闲连接数 -->
    		<property name="minIdle" value="${druid.minIdle}" />
    		<!-- 配置获取连接等待超时的时间 -->		
    		<property name="maxWait" value="${druid.maxWait}" />
    		<!-- 超过时间限制是否回收 -->
    		<property name="removeAbandoned" value="${druid.removeAbandoned}" />
    		<!-- 超过时间限制多长; -->
    		<property name="removeAbandonedTimeout" value="${druid.removeAbandonedTimeout}" />
    		<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
    		<property name="timeBetweenEvictionRunsMillis" value="${druid.timeBetweenEvictionRunsMillis}" />
    		<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
    		<property name="minEvictableIdleTimeMillis" value="${druid.minEvictableIdleTimeMillis}" />
    		<!-- 用来检测连接是否有效的sql,要求是一个查询语句-->	
    		<property name="validationQuery" value="${druid.validationQuery}" />
    		<!-- 申请连接的时候检测 -->
    		<property name="testWhileIdle" value="${druid.testWhileIdle}" />
    		<!-- 申请连接时执行validationQuery检测连接是否有效,配置为true会降低性能 -->
    		<property name="testOnBorrow" value="${druid.testOnBorrow}" />
    		<!-- 归还连接时执行validationQuery检测连接是否有效,配置为true会降低性能  -->
    		<property name="testOnReturn" value="${druid.testOnReturn}" />
    		<!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
    		<property name="poolPreparedStatements" value="${druid.poolPreparedStatements}" />		
    		<property name="maxPoolPreparedStatementPerConnectionSize" value="${druid.maxPoolPreparedStatementPerConnectionSize}" />
    		<!--属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:  				
    				监控统计用的filter:stat
    				日志用的filter:log4j
     				防御SQL注入的filter:wall -->
    		<property name="filters" value="${druid.filters}" />		
    	</bean>
    	<!-- 配置sqlsessionFactory -->
    	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    		<property name="dataSource" ref="dataSource" />
    		<property name="configLocation" value="classpath:mybatis/sqlMapConfiger.xml"></property>
    		<property name="mapperLocations" value="classpath*:/com/sun/blog/sqlMap/**/*Mapper.xml"></property> 
    	</bean>
    属性配置文件:
    driverClassName  = com.mysql.jdbc.Driver
    jdbc_url=jdbc:mysql://127.0.0.1:3306/blog?Unicode=true&characterEncoding=utf8#
    jdbc_user=root
    jdbc_password=1127
    druid.initialSize = 5
    druid.maxActive = 10
    druid.minIdle = 3
    druid.maxWait = 60000
    druid.removeAbandoned = true
    druid.removeAbandonedTimeout = 180
    druid.timeBetweenEvictionRunsMillis = 60000
    druid.minEvictableIdleTimeMillis = 300000
    druid.validationQuery = SELECT 1 FROM DUAL
    druid.testWhileIdle = true 
    druid.testOnBorrow = false
    druid.testOnReturn = false
    #mysql一般配置为false
    druid.poolPreparedStatements = false
    druid.maxPoolPreparedStatementPerConnectionSize = 50
    druid.filters = stat


    
    

    2、HikariCP数据库连接池


    2.1、简介

    HikariCP 是一个高性能的 JDBC 连接池组件。下图是性能的比较测试结果:


    2.2、配置

    <!-- 数据库连接池 -->
    <bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
            <property name="poolName" value="SpringHikariCP"/>
            <!-- 连接池中允许的最大连接数。缺省值:10;推荐的公式:((core_count * 2) + effective_spindle_count) -->
            <property name="maximumPoolSize" value="${jdbc.maximumPoolSize}"/>
            <!-- 一个连接idle状态的最大时长(毫秒),超时则被释放(retired),缺省:10分钟 -->
            <property name="idleTimeout" value="${jdbc.idleTimeout}"/>
            <!-- 一个连接的生命时长(毫秒),超时而且没被使用则被释放(retired),缺省:30分钟,建议设置比数据库超时时长少30秒,参考MySQL wait_timeout参数(show variables like '%timeout%';) -->
            <property name="maxLifetime" value="${jdbc.maxLifetime}" />
            <property name="connectionTestQuery" value="${jdbc.connectionTestQuery}"/>
     
            <property name="dataSourceClassName" value="${jdbc.dataSourceClassName}"/>
            <property name="dataSourceProperties">
                <props>
                    <prop key="url">${jdbc.url}</prop>
                    <prop key="user">${jdbc.username}</prop>
                    <prop key="password">${jdbc.password}</prop>
                </props>
            </property>
        </bean>
     
        <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
            <constructor-arg ref="hikariConfig"/>
        </bean>
    <!-- 配置sqlsessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="configLocation" value="classpath:mybatis/sqlMapConfiger.xml"></property>
    <property name="mapperLocations" value="classpath*:/com/sun/blog/sqlMap/**/*Mapper.xml"></property> 
    </bean>
    
    属性配置文件:

    #数据库连接池的配置
    #jdbc.dataSourceClassName=oracle.jdbc.pool.OracleDataSource
    #jdbc.url=jdbc:oracle:thin:@10.101.205.3:1521:orcl4
    #属性驱动自动识别
    jdbc.dataSourceClassName=com.mysql.jdbc.jdbc2.optional.MysqlDataSource
    jdbc.url=jdbc:mysql://127.0.0.1:3306/blog?Unicode=true&characterEncoding=utf8
    jdbc.username=root
    jdbc.password=1127
    
    jdbc.maximumPoolSize=10
    jdbc.idleTimeout=30000
    jdbc.maxLifetime=1800000
    jdbc.connectionTestQuery=SELECT 1 from dual











     

    展开全文
  • 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。 为什么要使用连接池  数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用...

    什么是连接池

    数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。

    为什么要使用连接池

     数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。  一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,这样造成系统的 性能低下。 数据库连接池的解决方案是在应用程序启动时建立足够的数据库连接,并讲这些连接组成一个连接池(简单说:在一个“池”里放了好多半成品的数据库联接对象),由应用程序动态地对池中的连接进行申请、使用和释放。对于多于连接池中连接数的并发请求,应该在请求队列中排队等待。并且应用程序可以根据池中连接的使用率,动态增加或减少池中的连接数。 连接池技术尽可能多地重用了消耗内存地资源,大大节省了内存,提高了服务器地服务效率,能够支持更多的客户服务。通过使用连接池,将大大提高程序运行效率,同时,我们可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。 

    传统的连接机制与数据库连接池的运行机制区别

    不使用连接池流程

    下面以访问MySQL为例,执行一个SQL命令,如果不使用连接池,需要经过哪些流程。

    不使用数据库连接池的步骤:

    1. TCP建立连接的三次握手
    2. MySQL认证的三次握手
    3. 真正的SQL执行
    4. MySQL的关闭
    5. TCP的四次握手关闭

    可以看到,为了执行一条SQL,却多了非常多我们不关心的网络交互。

    优点:

    1. 实现简单

    缺点:

    1. 网络IO较多
    2. 数据库的负载较高
    3. 响应时间较长及QPS较低
    4. 应用频繁的创建连接和关闭连接,导致临时对象较多,GC频繁
    5. 在关闭连接后,会出现大量TIME_WAIT 的TCP状态(在2个MSL之后关闭)

    使用连接池流程

    使用数据库连接池的步骤:

    第一次访问的时候,需要建立连接。 但是之后的访问,均会复用之前创建的连接,直接执行SQL语句。

    优点:

    1. 较少了网络开销
    2. 系统的性能会有一个实质的提升
    3. 没了麻烦的TIME_WAIT状态

    数据库连接池的工作原理

    连接池的工作原理主要由三部分组成,分别为

    1. 连接池的建立
    2. 连接池中连接的使用管理
    3. 连接池的关闭

            第一、连接池的建立。一般在系统初始化时,连接池会根据系统配置建立,并在池中创建了几个连接对象,以便使用时能从连接池中获取。连接池中的连接不能随意创建和关闭,这样避免了连接随意建立和关闭造成的系统开销。Java中提供了很多容器类可以方便的构建连接池,例如Vector、Stack等。

            第二、连接池的管理。连接池管理策略是连接池机制的核心,连接池内连接的分配和释放对系统的性能有很大的影响。其管理策略是:

            当客户请求数据库连接时,首先查看连接池中是否有空闲连接,如果存在空闲连接,则将连接分配给客户使用;如果没有空闲连接,则查看当前所开的连接数是否已经达到最大连接数,如果没达到就重新创建一个连接给请求的客户;如果达到就按设定的最大等待时间进行等待,如果超出最大等待时间,则抛出异常给客户。

            当客户释放数据库连接时,先判断该连接的引用次数是否超过了规定值,如果超过就从连接池中删除该连接,否则保留为其他客户服务。

            该策略保证了数据库连接的有效复用,避免频繁的建立、释放连接所带来的系统资源开销。

            第三、连接池的关闭。当应用程序退出时,关闭连接池中所有的连接,释放连接池相关的资源,该过程正好与创建相反。

    连接池主要参数

    使用连接池时,要配置一下参数

    1. 最小连接数:是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费.
    2. 最大连接数:是连接池能申请的最大连接数,如果数据库连接请求超过次数,后面的数据库连接请求将被加入到等待队列中,这会影响以后的数据库操作
    3. 最大空闲时间
    4. 获取连接超时时间
    5. 超时重试连接次数

    连接池需要注意的点

    1、并发问题

      为了使连接管理服务具有最大的通用性,必须考虑多线程环境,即并发问题。这个问题相对比较好解决,因为各个语言自身提供了对并发管理的支持像java,c#等等,使用synchronized(java)lock(C#)关键字即可确保线程是同步的。使用方法可以参考,相关文献。

    2、事务处理

      我们知道,事务具有原子性,此时要求对数据库的操作符合“ALL-OR-NOTHING”原则,即对于一组SQL语句要么全做,要么全不做。 
      我们知道当2个线程共用一个连接Connection对象,而且各自都有自己的事务要处理时候,对于连接池是一个很头疼的问题,因为即使Connection类提供了相应的事务支持,可是我们仍然不能确定那个数据库操作是对应那个事务的,这是由于我们有2个线程都在进行事务操作而引起的。为此我们可以使用每一个事务独占一个连接来实现,虽然这种方法有点浪费连接池资源但是可以大大降低事务管理的复杂性。 

    3、连接池的分配与释放

      连接池的分配与释放,对系统的性能有很大的影响。合理的分配与释放,可以提高连接的复用度,从而降低建立新连接的开销,同时还可以加快用户的访问速度。 
      对于连接的管理可使用一个List。即把已经创建的连接都放入List中去统一管理。每当用户请求一个连接时,系统检查这个List中有没有可以分配的连接。如果有就把那个最合适的连接分配给他(如何能找到最合适的连接文章将在关键议题中指出);如果没有就抛出一个异常给用户,List中连接是否可以被分配由一个线程来专门管理捎后我会介绍这个线程的具体实现。

    4、连接池的配置与维护

      连接池中到底应该放置多少连接,才能使系统的性能最佳?系统可采取设置最小连接数(minConnection)和最大连接数(maxConnection)等参数来控制连接池中的连接。比方说,最小连接数是系统启动时连接池所创建的连接数。如果创建过多,则系统启动就慢,但创建后系统的响应速度会很快;如果创建过少,则系统启动的很快,响应起来却慢。这样,可以在开发时,设置较小的最小连接数,开发起来会快,而在系统实际使用时设置较大的,因为这样对访问客户来说速度会快些。最大连接数是连接池中允许连接的最大数目,具体设置多少,要看系统的访问量,可通过软件需求上得到。 
      如何确保连接池中的最小连接数呢?有动态和静态两种策略。动态即每隔一定时间就对连接池进行检测,如果发现连接数量小于最小连接数,则补充相应数量的新连接,以保证连接池的正常运转。静态是发现空闲连接不够时再去检查。

    数据库对比

    第一、二代连接池

    区分一个数据库连接池是属于第一代产品还是代二代产品有一个最重要的特征就是看它在架构和设计时采用的线程模型,因为这直接影响的是并发环境下存取数据库连接的性能。

    一般来讲采用单线程同步的架构设计都属于第一代连接池,二采用多线程异步架构的则属于第二代。比较有代表性的就是Apache Commons DBCP,在1.x版本中,一直延续着单线程设计模式,到2.x才采用多线程模型。

    用版本发布时间来辨别区分两代产品,则一个偷懒的好方法。以下是这些常见数据库连接池最新版本的发布时间:

    数据库连接池

    最新版本

    发布时间

    C3P0

    c3p0-0.9.5.2

    on 9 Dec 2015

    DBCP

    2.2.0

    27 December 2017

    Druid

    0.11.0

    Dec 4 2017

    HikariCP

    2.7.6

    2018-01-14

    从表中可以看出,C3P0已经很久没有更新了。DBCP更新速度很慢,基本处于不活跃状态,而Druid和HikariCP处于活跃状态的更新中,这就是我们说的二代产品了。

    二代产品对一代产品的超越是颠覆性的,除了一些“历史原因”,你很难再找到第二条理由说服自己不选择二代产品,但任何成功都不是偶然的,二代产品的成功很大程度上得益于前代产品们打下的基础,站在巨人的肩膀上,新一代的连接池的设计师们将这一项“工具化”的产品,推向了极致。其中,最具代表性的两款产品是:

    • HikariCP
    • Druid

    彻底死掉的C3P0

    C3P0是我使用的第一款数据库连接池,在很长一段时间内,它一直是Java领域内数据库连接池的代名词,当年盛极一时的Hibernate都将其作为内置的数据库连接池,可以业内对它的稳定性还是认可的。C3P0功能简单易用,稳定性好这是它的优点,但是性能上的缺点却让它彻底被打入冷宫。C3P0的性能很差,差到即便是同时代的产品相比它也是垫底的,更不用和Druid、HikariCP等相比了。正常来讲,有问题很正常,改就是了,但c3p0最致命的问题就是架构设计过于复杂,让重构变成了一项不可能完成的任务。随着国内互联网大潮的涌起,性能有硬伤的c3p0彻底的退出了历史舞台。

    咸鱼翻身的DBCP

    DBCP(DataBase Connection Pool)属于Apache顶级项目Commons中的核心子项目(最早在Jakarta Commons里就有),在Apache的生态圈中的影响里十分广泛,比如最为大家所熟知的Tomcat就在内部集成了DBCP,实现JPA规范的OpenJPA,也是默认集成DBCP的。但DBCP并不是独立实现连接池功能的,它内部依赖于Commons中的另一个子项目Pool,连接池最核心的“池”,就是由Pool组件提供的,因此,DBCP的性能实际上就是Pool的性能,DBCP和Pool的依赖关系如下表:

    Apache Commons DBCP

    Apache Commons Pool

    v1.2.2

    v1.3

    v1.3

    v1.5.4

    v1.4

    v1.5.4

    v2.0.x

    v2.2

    v2.1.x

    v2.4.2

    v2.2.x

    v2.5.0

    可以看到,因为核心功能依赖于Pool,所以DBCP本身只能做小版本的更新,真正大版本的更迭则完全依托于pool。有很长一段时间,pool都还是停留在1.x版本,这直接导致DBCP也更新乏力。很多依赖DBCP的应用在遇到性能瓶颈之后,别无选择,只能将其替换掉,DBCP忠实的拥趸tomcat就在其tomcat 7.0版本中,自己重新设计开发出了一套连接池(Tomcat JDBC Pool)。好在,在2013年事情终于迎来转机,13年9月Commons-Pool 2.0版本发布,14年2月份,DBCP也终于迎来了自己的2.0版本,基于新的线程模型全新设计的“池”让DBCP重焕青春,虽然和新一代的连接池相比仍有一定差距,但差距并不大,DBCP2.x版本已经稳稳达到了和新一代产品同级别的性能指标(见下图)。

    DBCP终于靠Pool咸鱼翻身,打了一个漂亮的翻身仗,但长时间的等待已经完全消磨了用户的耐心,与新一代的产品项目相比,DBCP没有任何优势,试问,谁会在有选择的前提下,去选择那个并不优秀的呢?也许,现在还选择DBCP2的唯一理由,就是情怀吧。

    性能无敌的HikariCP

    HikariCP号称“性能杀手”(It’s Faster),它的表现究竟如何呢,先来看下官网提供的数据:

    不光性能强劲,稳定性也不差:

    那它是怎么做到如此强劲的呢?官网给出的说明如下:

    • 字节码精简:优化代码,直到编译后的字节码最少,这样,CPU缓存可以加载更多的程序代码;
    • 优化代理和拦截器:减少代码,例如HikariCP的Statement proxy只有100行代码;
    • 自定义数组类型(FastStatementList)代替ArrayList:避免每次get()调用都要进行range check,避免调用remove()时的从头到尾的扫描;
    • 自定义集合类型(ConcurrentBag):提高并发读写的效率;
    • 其他缺陷的优化,比如对于耗时超过一个CPU时间片的方法调用的研究(但没说具体怎么优化)。

    可以看到,上述这几点优化,和现在能找到的资料来看,HakariCP在性能上的优势应该是得到共识的,再加上它自身小巧的身形,在当前的“云时代、微服务”的背景下,HakariCP一定会得到更多人的青睐。

    功能全面的Druid

    近几年,阿里在开源项目上动作频频,除了有像fastJson、dubbo这类项目,更有像AliSQL这类的大型软件,今天说的Druid,就是阿里众多优秀开源项目中的一个。它除了提供性能卓越的连接池功能外,还集成了SQL监控,黑名单拦截等功能,用它自己的话说,Druid是“为监控而生”。借助于阿里这个平台的号召力,产品一经发布就赢得了大批用户的拥趸,从用户使用的反馈来看,Druid也确实没让用户失望。

    相较于其他产品,Druid另一个比较大的优势,就是中文文档比较全面(毕竟是国人的项目么),在github的wiki页面,列举了日常使用中可能遇到的问题,对一个新用户来讲,上面提供的内容已经足够指导它完成产品的配置和使用了。

    下图为Druid自己提供的性能测试数据:

    现在项目开发中,我还是比较倾向于使用Durid,它不仅仅是一个数据库连接池,它还包含一个ProxyDriver,一系列内置的JDBC组件库,一个SQL Parser。

    Druid 相对于其他数据库连接池的优点

    1. 强大的监控特性,通过Druid提供的监控功能,可以清楚知道连接池和SQL的工作情况。

    a. 监控SQL的执行时间、ResultSet持有时间、返回行数、更新行数、错误次数、错误堆栈信息;

    b. SQL执行的耗时区间分布。什么是耗时区间分布呢?比如说,某个SQL执行了1000次,其中0~1毫秒区间50次,1~10毫秒800次,10~100毫秒100次,100~1000毫秒30次,1~10秒15次,10秒以上5次。通过耗时区间分布,能够非常清楚知道SQL的执行耗时情况;

    c. 监控连接池的物理连接创建和销毁次数、逻辑连接的申请和关闭次数、非空等待次数、PSCache命中率等。

    1. 方便扩展。Druid提供了Filter-Chain模式的扩展API,可以自己编写Filter拦截JDBC中的任何方法,可以在上面做任何事情,比如说性能监控、SQL审计、用户名密码加密、日志等等。
    2. Druid集合了开源和商业数据库连接池的优秀特性,并结合阿里巴巴大规模苛刻生产环境的使用经验进行优化。

    总结

    时至今日,虽然每个应用(需要RDBMS的)都离不开连接池,但在实际使用的时候,连接池已经可以做到“隐形”了。也就是说在通常情况下,连接池完成项目初始化配置之后,就再不需要再做任何改动了。不论你是选择Druid或是HikariCP,甚至是DBCP,它们都足够稳定且高效!之前讨论了很多关于连接池的性能的问题,但这些性能上的差异,是相较于其他连接池而言的,对整个系统应用来说,第二代连接池在使用过程中体会到的差别是微乎其微的,基本上不存在因为连接池的自身的配饰和使用导致系统性能下降的情况,除非是在单点应用的数据库负载足够高的时候(压力测试的时候),但即便是如此,通用的优化的方式也是单点改集群,而不是在单点的连接池上死扣。

     

    参考:

    http://rainbowhorse.site/2018/02/06/%E5%A4%A7%E8%AF%9D%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5%E6%B1%A0/

    https://blog.csdn.net/frightingforambition/article/details/25464129

    https://juejin.im/entry/58fb03220ce4630061233c98

    展开全文
  • 3种常用数据库连接池的xml配置

    千次阅读 2018-03-24 16:50:19
    主体:这里主要说明3种常用数据库连接池(c3p0,dbcp,BoneCP)的xml配置方式配置环境: Maven+SSM1.1 连接池一:c3p01.2 导包,pom.xml导包&lt;dependency&gt; &lt;groupId&gt;c3p0&lt;/groupId&...

    背景:连接池负责分配、管理和释放数据库连接,能明显提高对数据库操作的性能。

    主体:这里主要说明3种常用的数据库连接池(c3p0,dbcp,BoneCP)的xml配置方式

    配置环境: Maven+SSM

    1.1 连接池一:c3p0

    1.2 导包,pom.xml导包

    <dependency>
    	<groupId>c3p0</groupId>
    	<artifactId>c3p0</artifactId>
       <version>0.9.1.2</version>
    </dependency>
    
    1.3 配置spring-mybatis.xml中dataSource的连接池
    <bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">  
            <property name="driverClass" value="com.mysql.jdbc.Driver" />  
            <property name="jdbcUrl" value="jdbc:mysql://106.15.200.190:3306/test?useUnicode=true&characterEncoding=UTF-8" />  
            <property name="username" value="root" />  
            <property name="password" value="root" />   
        </bean>  
    

    2.1 连接池二:dbcp

    2.2 导包

    <dependency>
    	<groupId>commons-dbcp</groupId>
    	<artifactId>commons-dbcp</artifactId>
    	<version>1.2.2</version>
    </dependency>
    

    2.3 配置spring-mybatis.xml中dataSource的连接池

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"  
            destroy-method="close">  
            <property name="driverClassName" value="com.mysql.jdbc.Driver" />  
            <property name="url" value="jdbc:mysql://106.15.200.190:3306/test?useUnicode=true&characterEncoding=UTF-8"/>  
            <property name="username" value="root" />  
            <property name="password" value="root"/>  
        </bean> 
    

    3.1 连接池二:BoneCP

    <dependency>
    	<groupId>com.jolbox</groupId>
    	<artifactId>bonecp</artifactId>
    	<version>0.8.0.RELEASE</version>
    </dependency>
    

    3.2 配置spring-mybatis.xml中dataSource的连接池

    <bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">  
            <property name="driverClass" value="com.mysql.jdbc.Driver" />  
            <property name="jdbcUrl" value="jdbc:mysql://106.15.200.190:3306/test?useUnicode=true&characterEncoding=UTF-8" />  
            <property name="username" value="root" />  
            <property name="password" value="root" />   
    </bean>  
    
    c3p0和dbcp效率方面相差不大,BoneCP效率较前两个效率会快上许多,这个持保留意见,因为我使用BoneCP做一个简单的登录,项目发布第一次点击登录返回的json数据很明显比前两个要慢,第二次点击的话个人感觉速度相差不大。
    以上配置都没有对连接池进行一些详细的配置,如最大连接数,最小连接数等等,项目需要的话可以进一步配置,不配置采用的是默认配置。另外注意这三种数据库连接四大件的别名问题。
    展开全文
  • 数据库连接池负责分配、管理和释放数据库连接,其基本思想就是为数据库建立一个“缓冲池”,预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕后再放回去。...
  • 常用Java数据库连接池性能测试

    千次阅读 2017-03-27 13:01:51
    这个测试的目的是验证当前常用数据库连接池的性能。testcase Connection conn = dataSource.getConnection(); PreparedStatement stmt = conn.preparedStatement("select 1"); ResultSet rs = stmt.executeQuery()...
  • 数据库连接池|c3p0数据库连接池

    千次阅读 2016-03-17 17:05:17
    水池中水,需要时直接去取,电池中有电,需要用电时直接去取,那么数据库连接池很多数据库连接,当我们需要数据库连接时,不必自己创建,直接来取即可。 创建和销毁数据库连接都很消耗性能,消耗性能的操作都...
  • 数据库连接池

    千次阅读 2019-10-22 19:54:01
    1、什么是数据库连接池数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个; 释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放...
  • 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个; 释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接...
  • java常用数据库连接池jar包 java常用数据库连接池jar包
  • 数据库连接池原理详解与自定义连接池实现

    万次阅读 多人点赞 2017-05-17 18:18:54
    实现原理数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数制约。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的...
  • 数据库连接池 1、什么是数据库连接池数据库连接池(Database Connection Pooling)在程序初始化时创建一定数量的数据库连接对象并将其保存在一块内存区中,它允许应用程序重复使用一个现有的数据库连接,而...
  • Java常用数据库连接池【整理】

    千次阅读 2015-12-08 19:43:17
    应用程序直接获取数据库连接的缺点 用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费...
  • 数据库连接池面试

    千次阅读 2019-08-31 15:38:42
    数据库连接池了解吗, 什么好处? 了解数据库连接池吗? 17.自己如何设计数据库连接...数据库连接池了解哪些 那单例我们一般用来做什么? 只需要一个对象的场景,比如数据库连接池、文件系统这种 会用线程池吧。...
  • 谈谈数据库连接池的原理

    万次阅读 多人点赞 2013-11-06 10:14:07
    这次我们采取技术演进的方式来谈谈数据库连接池的技术出现过程及其原理,以及当下最流行的开源数据库连接池jar包。
  • 本篇内容综合广大网友提供内容,笔者经过整理,对数据库连接池原理和实现过程做个很系统的并且通俗易懂的分析讲解,以及手写一个连接池实现过程作为演示。 一、早期通过JDBC方式操作数据库 我们先来看早期使用JDBC...
  • 上一个章节,我们讲了xml文件的解析框架XML...这个章节主要实现自己的数据库连接池,封装自己的BasicDataSource类。实现自己业务的数据池。下面开始我们的项目构建。 1.1.1. maven依赖。 org.apache.commons commons
  • 常用jdbc数据库连接jar包,数据库连接池jar包

    千次下载 热门讨论 2007-12-18 16:49:16
    收集了常见的数据库连接jar包,包括oracle、mysql、sql server、db2、opta、dbcp连接池、c3p0连接池等等常见的数据库jar包,不断更新中。
  • 数据库连接池配置

    2012-12-10 20:38:38
    这个文档详细讲述了mysql数据库连接池的配置以及数据库连接池的工作原理。
  • SpringBoot切换数据库连接池

    千次阅读 2017-08-12 03:28:10
    数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库...数据库连接池有哪些? DBCP(DBCP(DataBase Connection Pool)数据库连接池,是java数据库连接池的一种,由Apache开发,通过
  • 一、数据库连接池 1. 什么是连接池 传统的开发模式下,Servlet处理用户的请求,找Dao查询数据,dao会创建与数据库之间的连接,完成数据查询后会关闭数据库的链接。 这样的方式会导致用户每次请求都要向数据库建立...
  • 数据库连接池DataSource

    千次阅读 2017-09-17 18:40:28
    一:数据库连接池介绍 什么是数据库连接池 数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的...
  • 1.SpringBoot的数据库连接池的相关默认 springBoot之前的版本默认使用的是Tomcat的数据库连接池 较新的版本2点多(具体多少我看看)默认使用的是Hikari(我使用的是2.2.1) 默认支持数据库连接池: Tomcat ...
  • 常用的三种数据库连接池

    千次阅读 2017-12-28 16:11:55
    1.数据库连接池的概念 数据库连接池概述:  数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。对数据库连接的管理能显著影响到整个 应用程序的伸缩性和健壮性,...
  • 本节内容紧接着上篇的数据库连接池原理分析,继续讲解连接池需要考虑的其他重要问题 1、并发问题  为了使连接管理服务具有最大的通用性,必须考虑多线程环境,即并发问题。这个问题相对比较好解决,因为java语言...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 332,125
精华内容 132,850
关键字:

常用的数据库连接池有哪些