一.关于数据库连接池
查询数据库数据的典型java程序的基本流程如下。
查询数据的基本过程. jpg
如您所见,要进行一次查询,需要进行多次网络交互。 这样的缺点如下。
网络IO多;
响应时间长,导致QPS降低;
频繁创建和关闭连接会浪费数据库资源,并影响服务性能。
什么是数据库连接池?
超级帅气的钢铁侠是有数据库连接的ssddc,需要APP应用服务时将其拿到ssddc中,使用完毕后返还给ssddc。 连接池负责分配、管理和释放数据库连接,并允许APP应用服务重用数据库连接而不是重新建立。
使用连接池后,过程如下。
使用连接池后,查询进程. jpg
创建和关闭与数据库的连接是通过连接池完成的。 这样做的优点如下。
减少网络开销
提高数据库性能
数据库连接池是如何管理ssddc中的数据库连接的呢?
应用开始后,将根据设置的最小连接数将在连接池中创建的此数量的数据库连接放置在池中。
在应用访问时,首先检查连接池中是否存在空闲连接,如果存在空闲连接,则将连接分配给客户使用; 如果没有空闲连接,请检查当前打开的连接数是否达到最大连接数,否则在请求的客户上重新创建连接。 达到后在设定的最大等待时间等待,超过最大等待时间后,抛出异常并交给客户。 在客户释放数据库连接时,首先确定该连接的引用次数是否超过了规定值,如果超过,则从连接池中删除该连接,否则等待重用。
流程图. jpg
应用完成后,关闭池中的所有连接并释放所有资源。
释放资源. jpg
此过程涉及的配置:最小连接数、最大连接数和最大等待时间在APP应用程序服务配置文件中配置。
二.实现数据来源
目前,许多WEB服务器(Weblogic、WebSphere、Tomcat )提供DataSoruce实现或连接池实现。 DataSource的实现通常在英语意义上称为数据源,数据源包含数据库连接池的实现。 一些开源组织提供数据源的独立实现。
bcp数据库连接池
C3P0数据库连接池
通过使用数据库连接池,项目的实际开发可以直接获取从数据源到数据库的连接,而无需编写连接数据库的代码。
DBCP数据源
DBCP在Apache软件基金下作为开放源代码连接池实现,要使用DBCP数据源,APP应用程序必须在系统中添加以下两个jar文件:
Commons-dbcp.jar :连接池实现
Commons-pool.jar :连接池实现依赖库
Tomcat连接池是使用此连接池实现的。 此数据库连接池可以与APP应用程序服务器集成使用,也可以仅在APP应用程序中使用。
将dbcp连接池添加到APP应用程序
导入关联的jar包commons-dbcp-1.2.2.jar和commons-pool.jar
将dbcp配置文件添加到类别目录中。 dbcpconfig.properties
dbcpconfig.properties的配置信息如下:
#连接设置
driver class name=com.MySQL.JDBC.driver
URL=JDBC : MySQL ://localhost :3306/JDBC study
username=root
password=XDP
#
初始大小=10
#最大连接数
最大活动=50
#
最大值=20
#
minIdle=5
#
最大等待=60000
#JDBC驱动程序建立连接时附带的连接属性的格式为[属性名称=property; ]
#注意:“用户”和“密码”这两个属性是明确传递的,因此不需要在此处包含他们。
connection properties=use unicode=true; mndxmg=UTF8
#指定连接池创建的连接的自动提交(auto-commit )状态。
defaultAutoCommit=true
#driver default指定连接池创建的连接的只读(read-only )状态。
#如果未设置此值,则不调用" setReadOnly "方法。 (某些驱动程序不支持只读模式,如Informix。)
defaultReadOnly=
#指定由驱动程序默认连接池创建的连接的事务级别(TransactionIsolati )
on)。#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
在获取数据库连接的工具类的静态代码块中创建池
//数据库连接工具类
public class JdbcDBCP {
/**
* 在java中,编写数据库连接池需实现java.sql.DataSource接口,每一种数据库连接池都是DataSource接口的实现
* DBCP连接池就是java.sql.DataSource接口的一个具体实现
*/
private static DataSource ds = null;
//在静态代码块中创建数据库连接池
static{
try{
//加载dbcpconfig.properties配置文件
InputStream in = JdbcDBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
Properties prop = new Properties();
prop.load(in);
//创建数据源
ds = BasicDataSourceFactory.createDataSource(prop);
}catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
//从数据源中获取数据库连接
public static Connection getConnection() throws SQLException{
//从数据源中获取数据库连接
return ds.getConnection();
}
/**
* 释放资源,释放的资源包括Connection数据库连接对象,负责执行SQL命
*令的Statement对象,存储查询结果的ResultSet对象
*/
public static void release(Connection conn,Statement st,ResultSet rs){
if(rs!=null){
try{
//关闭存储查询结果的ResultSet对象
rs.close();
}catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if(st!=null){
try{
//关闭负责执行SQL命令的Statement对象
st.close();
}catch (Exception e) {
e.printStackTrace();
}
}
if(conn!=null){
try{
//将Connection连接对象还给数据库连接池
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
三、配置Tomcat数据源
在实际开发中,我们有时候还会使用服务器提供给我们的数据库连接池,比如我们希望Tomcat服务器在启动的时候可以帮我们创建一个数据库连接池,那么在应用程序中就不需要手动去创建数据库连接池,直接使用Tomcat服务器创建好的数据库连接池即可。要想让Tomcat服务器在启动的时候创建一个数据库连接池,那么需要简单配置一下Tomcat服务器。
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文档,我们知道使用如下的方式配置tomcat服务器的数据源:
type="javax.sql.DataSource" username="root" password="XDP"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/jdbcstudy"
maxActive="8" maxIdle="4"/>
服务器创建好数据源之后,我们的应用程序又该怎么样得到这个数据源呢?Tomcat服务器创建好数据源之后是以JNDI的形式绑定到一个JNDI容器中的,可以把JNDI想象成一个大大的容器,我们可以往这个容器中存放一些对象、一些资源,JNDI容器中存放的对象和资源都会有一个独一无二的名称。应用程序想从JNDI容器中获取资源时,只需要告诉JNDI容器要获取的资源的名称,JNDI根据名称去找到对应的资源后返回给应用程序。我们平时做javaEE开发时,服务器会为我们的应用程序创建很多资源,比如request对象,response对象,服务器创建的这些资源有两种方式提供给我们的应用程序使用:
第一种是通过方法参数的形式传递进来,比如我们在Servlet中写的doPost和doGet方法中使用到的request对象和response对象就是服务器以参数的形式传递给我们的。
第二种就是JNDI的方式,服务器把创建好的资源绑定到JNDI容器中去,应用程序想要使用资源时,就直接从JNDI容器中获取相应的资源即可。
对于上面的name="jdbc/datasource"数据源资源,在应用程序中可以用如下的代码去获取:
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
dataSource = (DataSource)envCtx.lookup("jdbc/datasource");
此种配置下,数据库的驱动jar文件需放置在tomcat的lib下。
配置Tomcat数据源
在Web项目的WebRoot目录下的META-INF目录创建一个context.xml文件
在context.xml文件配置tomcat服务器的数据源
name="jdbc/datasource"
auth="Container"
type="javax.sql.DataSource"
username="root"
password="XDP"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/jdbcstudy"
maxActive="8"
maxIdle="4"/>
将数据库的驱动jar文件需放置在tomcat的lib下
在获取数据库连接的工具类的静态代码块中获取JNDI容器中的数据源
//数据库连接工具类
public class JdbcJNDI {
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/datasource");
}catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
//从数据源中获取数据库连接
public static Connection getConnection() throws SQLException{
//从数据源中获取数据库连接
return ds.getConnection();
}
/**
* 释放资源,
* 释放的资源包括Connection数据库连接对象,负责执行SQL命令的Statement对象,存储查询结果的ResultSet对象
*/
public static void release(Connection conn,Statement st,ResultSet rs){
if(rs!=null){
try{
//关闭存储查询结果的ResultSet对象
rs.close();
}catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if(st!=null){
try{
//关闭负责执行SQL命令的Statement对象
st.close();
}catch (Exception e) {
e.printStackTrace();
}
}
if(conn!=null){
try{
//将Connection连接对象还给数据库连接池
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
四、Java常见数据库连接池性能比较
目前,流行的Java数据库连接池有HikariCP,druid,tomcat-jdbc,dbcp,c3p0。性能从高到低顺序排列(单从性能角度看)。下图是HikariCP官网给出的性能对比:
性能对比.jpg
HikariCP是目前最快的Java数据库连接池,spring boot 2.x已经使用HikariCP作为默认的数据库连接池,足见其优秀。
五、HikariCP的优越性
HikariCP为什么这么快呢?
优化并精简字节码:使用Java字节码修改类库Javassist来生成委托实现动态代理,JDK Proxy生成的字节码更少。
使用更好的并发集合类ConcurrentBag。
使用FastList替代ArrayList。