使用JDBC是怎么保证数据库客户端和数据库服务端进行连接的?
通过代码:
conn=DriverManager.getConnection(url, username, password);
JDBC通过这条代码方法的调用建立了一条客户端应用程序到后端数据库的物理连接。期间发生了大量的基于TCP的客户端与服务端的交互。
由于跨机器的网络传输是由较大的网络开销的,所以时间花销很大。
传统的多线程JDBC服务中,我们每进行一次服务都需要分配一个线程,每一个线程去建立一个数据库连接,当这条服务结束之后,该线程和该数据库连接都将被销毁。
当我们需要下一次数据库服务时,再次建立新的线程和数据库连接。
这样每次在建立数据库连接上浪费了大量的时间,用户的相应时间变得很慢。
连接池的出现就是为了解决这个问题,连接池实现了数据库连接(connection对象)的复用,当一个线程处理完相应程序服务,随后不紧接着销毁数据库连接,而是把它交给下一个线程使用。这样就避免了多次建立数据库连接而浪费时间的问题出现。实现了从每个线程创建数据库连接到每个线程租用数据库连接的转变。
MySQL数据库内部是怎么分配各种连接的?
在MySQL数据库中也存在着内存限制,当分配的数据库连接过多,会增加数据库内存的占用,加剧各种锁的冲突,占用数据库资源,甚至造成数据库崩溃。所以一般在MySQL数据库中设置最大连接数。
但是,为了保证不出现上述问题,我们可以在连接请求到达数据库之前进行限流,保证数据库资源的优化利用,效率至上。连接池就起到了这个作用:限制连接。
连接池到底是什么?
连接池本质上就是一组java的jar包,介于java应用和JDBC数据库物理连接之间,帮助应用程序来管理数据库连接,通过连接池暴露的接口,应用程序可以获取JDBC连接,使用完后
将JDBC归还给连接池,供下一个线程使用。
在连接池中,数据库连接不足的时候,会自动创建连接;同时当空闲连接过多时也会自动销毁连接。
当多个线程同时访问时,连接池还提供了排队等待的功能。保证了应用程序有序地获取数据库连接。
连接池怎么使用?(相关示例代码贴在后面)
以现在最受欢迎的JDCP连接池为例。
相关jar包下载地址:
http://commons.apache.org/proper/commons-dbcp/download_dbcp.cgi
http://commons.apache.org/proper/commons-pool/download_pool.cgi
http://commons.apache.org/proper/commons-logging/download_logging.cgi
其中,有两种jar版本,windows推荐下载.zip版
关于两版的差别,参考:
http://blog.csdn.net/suyu_yuan/article/details/52733117
https://www.zhihu.com/question/26026741
1)创建一个连接池对象(BasicDataSource):
因为JDCP的底层也是通过JDBC来实现的,所以我们需要告诉它相应的信息。
通过BasicDataSource对象的一系列方法,来对连接池进行相关设置(基础的和高级的)
相关高级配置:
连接数配置:
其中建议:将setMaxIdle()和setMinIdle()设置为相同的值。
DBCP定期检查:
数据库服务端为了释放空闲等待的资源,默认会自动关闭空闲时间等待超过某个阈值的连接。MySQL默认阈值为8个小时。
但是,连接池不知道那个连接失效了,以防止连接池将失效的连接租借给应用程序,所以需要定期检查。
保证连接池和数据库服务端连接对象同步性,保证,连接池租借给应用程序的连接对象都是有效的。
建议:连接池中连接对象的销毁时间阈值要小于数据库服务器的销毁时间阈值。也就是说要在数据库服务器销毁连接对象前,连接池先销毁连接对象。保证连接池中连接对象的有效性。
2)通过BasicDataSource对象获取数据库连接对象(connection),然后跟JDBC中相似进行相关数据库服务的调用。
注意,我们同样需要在一系列操作过后,调用connection对象的close()方法。
但是连接池重写了connection对象的close()方法,使得它的功能不再是将connection对象销毁,而是将connection对象归还给连接池,以供下一个线程调用。
相关示例代码:
package com.java_JDCP; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement; import org.apache.commons.dbcp2.BasicDataSource; public class JDCP_test { private static String driverClassName="com.mysql.jdbc.Driver"; //数据库驱动
//连接数据库的URL地址
private static String url="jdbc:mysql://localhost:3306/hellojdbc?useUnicode=true&characterEncoding=UTF-8";
private static String username="root";//数据库连接用户名
private static String password="123456";//数据库连接密码 private static Connection conn=null;//数据库连接对象
private static Statement stat=null;//语句陈述对象
private static ResultSet rs=null;//结果数据集 private static BasicDataSource ds=null; //BasicDataSource对象的相关初始化
public static void jdcpInit(){
ds=new BasicDataSource();//创建BasicDataSource对象
ds.setUrl(url);
ds.setDriverClassName(driverClassName);
ds.setUsername(username);
ds.setPassword(password);
ds.setMaxIdle(2);//设置连接池中存在的最大连接对象的数量为2个
} public static void main(String[] args) {
jdcpInit();
try {
conn=ds.getConnection();//从BasicDataSource对象中获取数据库连接
//一下的操作就和JDBC相同了
stat=conn.createStatement();
rs=stat.executeQuery("select * from roster");
while(rs.next())
System.out.println("name:"+rs.getString("name"));
} catch (SQLException e) {
e.printStackTrace();
}finally{
try {
if(rs!=null)
rs.close();
if(stat!=null)
stat.close();
if(conn!=null)
conn.close();
//释放数据库连接,这里连接池重写了JDBC中数据库连接(connection对象)的close()方法,原来是销毁,现在是归还给连接池,以供下个线程调用
} catch (SQLException e) {
e.printStackTrace();
} } } }