使用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(); } } } }