数据连接池C3P0/DBCP/DRUID/自定义连接池
1.C3P0连接池
快速入门官网 https://www.mchange.com/projects/c3p0/#quickstart
<dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> </dependency>
public class C3P0Pool { public static void main(String[] args) throws PropertyVetoException, SQLException { ComboPooledDataSource cpds = new ComboPooledDataSource(); cpds.setDriverClass("com.mysql.cj.jdbc.Driver"); cpds.setJdbcUrl("jdbc:mysql://localhost:3306/table?useUnicode=" + "true&characterEncoding=utf8&characterSetResults=utf8&useSSL=" + "false&verifyServerCertificate=false&serverTimezone=GMT%2B8 "); cpds.setUser("root"); cpds.setPassword("cgz12345678"); //最大8 cpds.setMaxPoolSize(8); //最小2个 cpds.setMinPoolSize(2); //初始化2 介于MinPoolSize(2)与MaxPoolSize(8)之间 cpds.setInitialPoolSize(2); //当连接数不足时每次补充2个 cpds.setAcquireIncrement(2); //维护statement的总个数 cpds.setMaxStatements(70); //每个连接可以使用的statement的个数 cpds.setMaxStatementsPerConnection(10); //最大保持的空闲时间,否则被回收 与maxConnectionAge的区别是maxConnectionAge表示活着的总时间 cpds.setMaxIdleTime(60000); Connection connection = cpds.getConnection(); System.out.println(connection); } }
<?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <named-config name="c3p0"> <!-- 配置数据库链接地址 --> <property name="jdbcUrl">jdbc:mysql://localhost:3306/user?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false&verifyServerCertificate=false&serverTimezone=GMT%2B8</property> <!-- 配置数据库驱动 --> <property name="driverClass">com.mysql.jdbc.Driver</property> <!-- 当不足时,数据库连接池一次性向数据库要多少个连接对象 ,Default:3--> <property name="acquireIncrement">20</property> <!-- 初始化连接数,Default:3 --> <property name="initialPoolSize">10</property> <!-- 最小连接数 --> <property name="minPoolSize">5</property> <!--最大连接数。Default: 15 --> <property name="maxPoolSize">30</property> <!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements 属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。
如果maxStatements为0,那么maxStatementsPerConnection无效,则缓存被关闭。Default:0 --> <property name="maxStatements">0</property> <!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数如果设置的好能提高性能。Default: 0 --> <property name="maxStatementsPerConnection">0</property> <!--c3p0是异步操作的,缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能 通过多线程实现多个操作同时被执行。Default:3 --> <property name="numHelperThreads">3</property> <!--用户修改系统配置参数执行前最多等待300秒。Default: 300 --> <property name="propertyCycle">300</property> <!-- 获取连接超时设置 默认是一直等待,单位毫秒 --> <property name="checkoutTimeout">1000</property> <!--每多少秒检查所有连接池中的空闲连接。Default: 0 --> <property name="idleConnectionTestPeriod">3</property> <!--最大空闲时间,多少秒内未被使用则连接被丢弃。若为0则永不丢弃。单位秒,Default: 0 --> <property name="maxIdleTime">10</property> <!--配置连接的生存时间,单位秒,超过这个时间的连接将由连接池自动断开丢弃掉。当然正在使用的连接不会马上断开,而是等待它close再断开。配置为0的时候则不会对连接的生存时间进行限制。
不为0则保持到minPoolSize --> <property name="maxIdleTimeExcessConnections">5</property> <!--两次连接中间隔时间,单位毫秒。Default: 1000 --> <property name="acquireRetryDelay">1000</property> <!--c3p0将建一张名为Test的空表,并使用其自带的查询语句进行测试。如果定义了这个参数那么属性preferredTestQuery将被忽略。你不能在这张Test表上进行任何操作,它将只供c3p0测试使用。Default: null --> <property name="automaticTestTable">Test</property> <!-- 获取connnection时测试是否有效 --> <property name="testConnectionOnCheckin">true</property>
<!-- 活着的总时间,单位秒 -->
<property name="maxConnectionAge">12</property> </named-config> </c3p0-config>
备注:可以按上述提供的官网书写相应的xml文件,或者是properties文件,但是名称分别必须为c3p0-config.xml和c3p0.properties,否则找不到,一个xml中可以配置多个c3p0但是<named-config name="c3p0"> 的name不同,假如在maven的 \src\main\resources目录下,那么上述的获取方式为ComboPooledDataSource cpds = new ComboPooledDataSource("c3p0");
2.DBCP
api文档: http://commons.apache.org/proper/commons-dbcp/api-2.5.0/index.html
public class DBCPPoolTest { public static void main(String[] args) throws SQLException { BasicDataSource dataSource = new BasicDataSource(); dataSource.setUsername("root"); dataSource.setUrl("jdbc:mysql://localhost:3306/table?useUnicode=" + "true&characterEncoding=utf8&characterSetResults=utf8&useSSL=" + "false&verifyServerCertificate=false&serverTimezone=GMT%2B8 "); dataSource.setPassword("12345678"); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setInitialSize(1);// 初始化连接数 // 超过8个connection在规定的的等待时间之内得不到连接则会报错 dataSource.setMaxTotal(8);// 最大连接数 // 超过maxIdle值后,刚刚使用完的连接(刚刚空闲下来)会立即被销毁。 dataSource.setMaxIdle(3);// 最大的空闲数,一般设置为MaxTotal的一半 dataSource.setMinIdle(2);// 默认是0 ,一般设置比较小为好,尽量减少连接数的空闲占用,当不足以保证时,就维持现状,当有还给连接池的对象时,又添加来保持自己 dataSource.setMaxWaitMillis(60000);// 连接池分配连接的最大时间,超过会报出异常 // 池中的连接空闲时间,超过被回收默认值就是30分钟 dataSource.setMinEvictableIdleTimeMillis(3000000); Connection con = dataSource.getConnection(); System.out.println(con); con.close();// 并非真正的关闭,而是将其归还到线程池 dataSource.close(); } }
#类似c3p0的driverClass driverClassName=com.mysql.jdbc.Driver #传递给JDBC驱动的用于建立连接的URL #当设置characterEncoding时useUnicode=true必须设置,characterEncoding指按指定编码解码成字节码,autoReconnect=true当数据库连接中断时是否自动重新连接,autoReconnect=true但是这个连接两次访问数据库的时间超出了服务器端wait_timeout的时间限制,还是会CommunicationsException,dontTrackOpenResources要求驱动自动跟踪和关闭资源,如果不能明确的调用作用在语句或者结果集上的close时,会造成内存溢出,设置为true可以提高某些程序的内存效率 默认false url=jdbc:mysql://localhost:3306/testjdbc?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&dontTrackOpenResources=true #传递给JDBC驱动的用于建立连接的用户名 username=root #传递给JDBC驱动的用于建立连接的密码 password=cgz12345678 #初始化连接:初始化连接数量,1.2版本后支持 默认值0 jdbc-pool默认值10 initialSize=5 #最大活动连接:如果设置为非正数则表示不限制 默认值8 jdbc-pool默认值100 maxActive=8 #最大空闲连接:超过的空闲连接将被释放,如果设置为负数表示不限制 默认值8 jdbc-pool默认值 maxIdle=20 #最小空闲连接,低于这个数量将创建新的连接,如果设置为0则不创建 默认值0 jdbc-pool默认值10 minIdle=5 #最大等待时间: (以毫秒计数),超过时间则抛出异常,如果设置为-1表示无限等待 默认值:无限 jdbc-pool默认值30000ms maxWait=28000 #连接池创建的连接的默认的auto-commit状态,默认值true defaultAutoCommit=true #当设置了set-onlyread,则默认值为only-read模式,当没有设置的时候不会被调用,(不支持只读模式) (某些驱动不支持只读模式,比如:Informix) defaultReadOnly #连接池创建的连接的默认的TransactionIsolation状态. Jdbc事物隔离级别中的一个 defaultTransactionIsolation= READ_COMMITTED #连接池创建的连接的默认的catalog defaultCatalog #是否自动回收超时连接,默认值false,这个是由前提条件的,当空闲连接数小于一定数量的时候才会被触发,类似垃圾回收,回收的前提是响应时间超过removeAbandonedTimeout时间 removeAbandoned=true #超过相应的时间没响应就回收, 单位秒 默认300 removeAbandonedTimeout=300 #是否在自动回收超时连接的时候打印连接的超时错误,默认值false jdbc-pool默认值false logAbandoned=true #SQL查询验证语句至少返回一条语句 validationQuery=select 1 #指明是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个. #如果设置为true后如果要生效那么validationQuery参数必须设置为非空字符串 默认值:true,对连接前进行相应的检验,检验失败,则删除连接,尝试下一条连接 testOnBorrow=true #注意: 设置为true后如果要生效,validationQuery参数必须设置为非空字符串 默认值: false jdbc-pool默认值false,指明是否在归还到池中前进行检验 testOnReturn=true #指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败(检测是以timeBetweenEvictionRunsMilli为依据,如果超过这个时间则进行validationQuery检测连接是否有效),则连接将被从池中去除.设置为true后如果要生效,validationQuery参数必须设置为非空字符串 默认值: false testWhileIdle=true #空闲连接回收器线程运行时间间隔,以毫秒为单位. 如果设置为非正数,则不运行空闲连接回收器线程 默认值: -1 jdbc-pool的默认值为5000ms timeBetweenEvictionRunsMilli=-1 #每次空闲连接回收器线程(如果有)运行时检查的连接数量 默认值:1000 * 60 * 30 numTestsPerEvictionRun #保存空闲而不被空闲回收器回收的间隔时间 minEvictableIdleTimeMillis #开启池的prepared statement 池功能 默认值false poolPreparedStatements #statement池能够同时分配的打开的statements的最大数量, 如果设置为0表示不限制 默认值:不限制 maxOpenPreparedStatements=0 #这里可以开启PreparedStatements池. 当开启时, 将为每个连接创建一个statement池,并且被下面方法创建的PreparedStatements将被缓存起来: #PoolGuard是否容许获取底层连接 默认值false #如果容许则可以使用下面的方式来获取底层连接: # Connection conn = ds.getConnection(); # Connection dconn = ((DelegatingConnection) conn).getInnermostDelegate(); # ... # conn.close(); #注意: 不要关闭底层连接, 只能关闭前面的那个. accessToUnderlyingConnectionAllowed 4.2创建方法 方式1 final BasicDataSource dataSource = new BasicDataSource(); dataSource.setUsername("root"); dataSource.setPassword("cgz12345678"); dataSource.setUrl("jdbc:mysql://localhost:3306/testjdbc"); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setInitialSize(5); dataSource.setMaxActive(5); dataSource.setMinIdle(2); dataSource.setMaxWait(1000*5); 方式2 Properties properties = new Properties(); InputStream stream = jdbctestc3p0_dbcp.class.getClassLoader().getResourceAsStream("2.properties"); try { properties.load(stream); DataSource dataSource = BasicDataSourceFactory.createDataSource(properties); BasicDataSource source =(BasicDataSource)dataSource; } catch (Exception e) { e.printStackTrace(); } Properties文件 driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/testjdbc username=root password=cgz12345678 DBCP 是单线程的,为了保证线程安全会锁整个连接池 DBCP 性能不佳 DBCP 太复杂,超过60个类,发展滞后 还有其它的高性能连接池,如C3P0,还有阿里系的druid等
3)DRUID
API官方文档https://github.com/alibaba/druid/wiki/DruidDataSource%E9%85%8D%E7%BD%AE
#驱动类名。 driverClassName=com.mysql.jdbc.Driver #连接数据库的url url=jdbc:mysql://127.0.0.1:3306/day25 #数据库的用户名 username=root #数据库的密码 password=12345678 #初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 initialSize=5 #最大连接池数量 maxActive=10 #获取连接时最大等待时间 maxWait=3000 #已经不再使用,配置了也没效果 maxIdle=6 #最小连接池数量 minIdle=3
// 加载配置文件中的配置参数 InputStream is = Demo03.class.getResourceAsStream("/druid.properties"); Properties pp = new Properties(); pp.load(is); // 创建连接池,使用配置文件中的参数 DataSource ds = DruidDataSourceFactory.createDataSource(pp);
备注:连接池超标报异常
4)自定义线程池
package cn.test.javamail.massage; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.DriverManagerDataSource; import sun.applet.Main; import javax.sql.DataSource; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.LinkedList; import java.util.List; import java.util.Properties; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.logging.Logger; public class MyDataSourcePool implements DataSource { private int initCount ; private int maxCount ; private int curCount ; private List<Connection> list= new LinkedList(); public MyDataSourcePool(int initCount, int maxCount) throws SQLException, IOException, ClassNotFoundException { this.initCount = initCount; this.maxCount = maxCount; for (int i = 0; i < initCount; i++) { Connection connect = getConnect(); list.add(connect); curCount++; } } @Override public Connection getConnection() throws SQLException { Connection connection=null; if(list.size()>0){ connection = list.get(0); list.remove(0); }else if(curCount<maxCount){ try { Connection connect = getConnect();//不足的时候每次创建一个 list.add(connect); curCount++; connection = list.get(0); list.remove(0); } catch (Exception e) { e.printStackTrace(); } }else{ new RuntimeException("已经满了"); } return connection; } public void close(Connection connect){//c3p0的close重写内部的方法 list.add(connect); } @Override public Connection getConnection(String username, String password) throws SQLException { return null; } @Override public <T> T unwrap(Class<T> iface) throws SQLException { return null; } @Override public boolean isWrapperFor(Class<?> iface) throws SQLException { return false; } @Override public PrintWriter getLogWriter() throws SQLException { return null; } @Override public void setLogWriter(PrintWriter out) throws SQLException { } @Override public void setLoginTimeout(int seconds) throws SQLException { } @Override public int getLoginTimeout() throws SQLException { return 0; } @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException { return null; } public Connection getConnect() throws IOException, ClassNotFoundException, SQLException { Properties ps = new Properties(); InputStream is = this.getClass().getClassLoader().getResourceAsStream("config.properties"); ps.load(is); Class.forName(ps.getProperty("driverClass")); Connection connection = DriverManager.getConnection(ps.getProperty("url"), ps); return connection; } }