Java數(shù)據(jù)庫連接池

一锯茄、關(guān)于數(shù)據(jù)庫連接池

一個普通的java程序,要查詢數(shù)據(jù)庫的數(shù)據(jù)茶没,基本流程是這樣的:

查詢數(shù)據(jù)基本流程.jpg

可以看到肌幽,進行一次查詢,要進行很多次網(wǎng)絡(luò)交互抓半,這樣的缺點是:

  1. 網(wǎng)絡(luò)IO多喂急;
  2. 響應(yīng)時間長,導致QPS降低笛求;
  3. 頻繁創(chuàng)建連接和關(guān)閉連接廊移,浪費數(shù)據(jù)庫資源,影響服務(wù)器性能涣易。

什么是數(shù)據(jù)庫連接池画机?
顧名思義,就是一個池子新症,里面放著數(shù)據(jù)庫連接步氏,應(yīng)用服務(wù)需要的時候就去池子里面拿,用完之后歸還給池子徒爹。數(shù)據(jù)庫連接池負責分配荚醒、管理、釋放數(shù)據(jù)庫連接隆嗅,它允許應(yīng)用服務(wù)重復使用數(shù)據(jù)庫連接界阁,而非重新建立。

使用連接池之后胖喳,流程是這樣的:

使用連接池之后泡躯,查詢流程.jpg

數(shù)據(jù)庫的連接創(chuàng)建和關(guān)閉連接均由連接池來實現(xiàn)。這樣做的優(yōu)點有:

  1. 減少網(wǎng)絡(luò)開銷丽焊;
  2. 提升數(shù)據(jù)庫性能较剃;

那數(shù)據(jù)庫連接池是如何管理池子中的數(shù)據(jù)庫連接的呢?

  1. 應(yīng)用啟動時技健,根據(jù)配置的最小連接數(shù)写穴,在連接池將創(chuàng)建此數(shù)目的數(shù)據(jù)庫連接放到池中。
  2. 應(yīng)用訪問時雌贱,首先查看連接池中是否有空閑連接啊送,如果存在空閑連接偿短,則將連接分配給客戶使用;如果沒有空閑連接馋没,則查看當前所開的連接數(shù)是否已經(jīng)達到最大連接數(shù)昔逗,如果沒達到就重新創(chuàng)建一個連接給請求的客戶;如果達到就按設(shè)定的最大等待時間進行等待篷朵,如果超出最大等待時間纤子,則拋出異常給客戶。 當客戶釋放數(shù)據(jù)庫連接時款票,先判斷該連接的引用次數(shù)是否超過了規(guī)定值控硼,如果超過就從連接池中刪除該連接,否則保留等待再次使用艾少。
流程圖.jpg
  1. 應(yīng)用關(guān)閉時卡乾,關(guān)閉池中所有連接,釋放所有資源缚够。
釋放資源.jpg

此過程會涉及的配置:最小連接數(shù)幔妨,最大連接數(shù),最長等待時間谍椅,均配置在應(yīng)用服務(wù)配置文件中误堡。

二、DataSource的實現(xiàn)

現(xiàn)在很多WEB服務(wù)器(Weblogic, WebSphere, Tomcat)都提供了DataSoruce的實現(xiàn)雏吭,即連接池的實現(xiàn)锁施。通常我們把DataSource的實現(xiàn),按其英文含義稱之為數(shù)據(jù)源杖们,數(shù)據(jù)源中都包含了數(shù)據(jù)庫連接池的實現(xiàn)悉抵。也有一些開源組織提供了數(shù)據(jù)源的獨立實現(xiàn):

  1. DBCP 數(shù)據(jù)庫連接池
  2. C3P0 數(shù)據(jù)庫連接池

在使用了數(shù)據(jù)庫連接池之后,在項目的實際開發(fā)中就不需要編寫連接數(shù)據(jù)庫的代碼了摘完,直接從數(shù)據(jù)源獲得數(shù)據(jù)庫的連接姥饰。

DBCP數(shù)據(jù)源

DBCP 是 Apache 軟件基金組織下的開源連接池實現(xiàn),要使用DBCP數(shù)據(jù)源孝治,需要應(yīng)用程序應(yīng)在系統(tǒng)中增加如下兩個 jar 文件:

  • Commons-dbcp.jar:連接池的實現(xiàn)
  • Commons-pool.jar:連接池實現(xiàn)的依賴庫

Tomcat 的連接池正是采用該連接池來實現(xiàn)的列粪。該數(shù)據(jù)庫連接池既可以與應(yīng)用服務(wù)器整合使用,也可由應(yīng)用程序獨立使用谈飒。

在應(yīng)用程序中加入dbcp連接池

  1. 導入相關(guān)jar包commons-dbcp-1.2.2.jar岂座、commons-pool.jar
  2. 在類目錄下加入dbcp的配置文件:dbcpconfig.properties

dbcpconfig.properties的配置信息如下:

#連接設(shè)置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy
username=root
password=XDP

#<!-- 初始化連接 -->
initialSize=10

#最大連接數(shù)量
maxActive=50

#<!-- 最大空閑連接 -->
maxIdle=20

#<!-- 最小空閑連接 -->
minIdle=5

#<!-- 超時等待時間以毫秒為單位 6000毫秒/1000等于60秒 -->
maxWait=60000

#JDBC驅(qū)動建立連接時附帶的連接屬性屬性的格式必須為這樣:[屬性名=property;] 
#注意:"user" 與 "password" 兩個屬性會被明確地傳遞,因此這里不需要包含他們步绸。
connectionProperties=useUnicode=true;characterEncoding=UTF8

#指定由連接池所創(chuàng)建的連接的自動提交(auto-commit)狀態(tài)掺逼。
defaultAutoCommit=true

#driver default 指定由連接池所創(chuàng)建的連接的只讀(read-only)狀態(tài)吃媒。
#如果沒有設(shè)置該值瓤介,則“setReadOnly”方法將不被調(diào)用吕喘。(某些驅(qū)動并不支持只讀模式,如:Informix)
defaultReadOnly=

#driver default 指定由連接池所創(chuàng)建的連接的事務(wù)級別(TransactionIsolation)刑桑。
#可用值為下列之一:(詳情可見javadoc氯质。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED

在獲取數(shù)據(jù)庫連接的工具類的靜態(tài)代碼塊中創(chuàng)建池

//數(shù)據(jù)庫連接工具類
public class JdbcDBCP {
    /**
     * 在java中,編寫數(shù)據(jù)庫連接池需實現(xiàn)java.sql.DataSource接口祠斧,每一種數(shù)據(jù)庫連接池都是DataSource接口的實現(xiàn)
     * DBCP連接池就是java.sql.DataSource接口的一個具體實現(xiàn)
     */
    private static DataSource ds = null;
    //在靜態(tài)代碼塊中創(chuàng)建數(shù)據(jù)庫連接池
    static{
        try{
            //加載dbcpconfig.properties配置文件
            InputStream in = JdbcDBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
            Properties prop = new Properties();
            prop.load(in);
            //創(chuàng)建數(shù)據(jù)源
            ds = BasicDataSourceFactory.createDataSource(prop);
        }catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }
    
   //從數(shù)據(jù)源中獲取數(shù)據(jù)庫連接
    public static Connection getConnection() throws SQLException{
        //從數(shù)據(jù)源中獲取數(shù)據(jù)庫連接
        return ds.getConnection();
    }
    
 /**
   * 釋放資源闻察,釋放的資源包括Connection數(shù)據(jù)庫連接對象,負責執(zhí)行SQL命 
   *令的Statement對象琢锋,存儲查詢結(jié)果的ResultSet對象
   */
    public static void release(Connection conn,Statement st,ResultSet rs){
        if(rs!=null){
            try{
                //關(guān)閉存儲查詢結(jié)果的ResultSet對象
                rs.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
            rs = null;
        }
        if(st!=null){
            try{
                //關(guān)閉負責執(zhí)行SQL命令的Statement對象
                st.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        if(conn!=null){
            try{
                //將Connection連接對象還給數(shù)據(jù)庫連接池
                conn.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

三辕漂、配置Tomcat數(shù)據(jù)源

在實際開發(fā)中,我們有時候還會使用服務(wù)器提供給我們的數(shù)據(jù)庫連接池吴超,比如我們希望Tomcat服務(wù)器在啟動的時候可以幫我們創(chuàng)建一個數(shù)據(jù)庫連接池钉嘹,那么在應(yīng)用程序中就不需要手動去創(chuàng)建數(shù)據(jù)庫連接池,直接使用Tomcat服務(wù)器創(chuàng)建好的數(shù)據(jù)庫連接池即可鲸阻。要想讓Tomcat服務(wù)器在啟動的時候創(chuàng)建一個數(shù)據(jù)庫連接池跋涣,那么需要簡單配置一下Tomcat服務(wù)器。

JNDI技術(shù)簡介

JNDI(Java Naming and Directory Interface)鸟悴,Java命名和目錄接口陈辱,它對應(yīng)于J2SE中的javax.naming包。這套API的主要作用在于:它可以把Java對象放在一個容器中(JNDI容器)细诸,并為容器中的java對象取一個名稱沛贪,以后程序想獲得Java對象,只需通過名稱檢索即可震贵。其核心API為Context鹏浅,它代表JNDI容器,其lookup方法為檢索容器中對應(yīng)名稱的對象屏歹。

Tomcat服務(wù)器創(chuàng)建的數(shù)據(jù)源是以JNDI資源的形式發(fā)布的隐砸,所以說在Tomat服務(wù)器中配置一個數(shù)據(jù)源實際上就是在配置一個JNDI資源,通過查看Tomcat文檔蝙眶,我們知道使用如下的方式配置tomcat服務(wù)器的數(shù)據(jù)源:

<Context>
  <Resource 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"/>
</Context>

服務(wù)器創(chuàng)建好數(shù)據(jù)源之后季希,我們的應(yīng)用程序又該怎么樣得到這個數(shù)據(jù)源呢?Tomcat服務(wù)器創(chuàng)建好數(shù)據(jù)源之后是以JNDI的形式綁定到一個JNDI容器中的幽纷,可以把JNDI想象成一個大大的容器式塌,我們可以往這個容器中存放一些對象、一些資源友浸,JNDI容器中存放的對象和資源都會有一個獨一無二的名稱峰尝。應(yīng)用程序想從JNDI容器中獲取資源時,只需要告訴JNDI容器要獲取的資源的名稱收恢,JNDI根據(jù)名稱去找到對應(yīng)的資源后返回給應(yīng)用程序武学。我們平時做javaEE開發(fā)時祭往,服務(wù)器會為我們的應(yīng)用程序創(chuàng)建很多資源,比如request對象火窒,response對象硼补,服務(wù)器創(chuàng)建的這些資源有兩種方式提供給我們的應(yīng)用程序使用:

  1. 第一種是通過方法參數(shù)的形式傳遞進來,比如我們在Servlet中寫的doPost和doGet方法中使用到的request對象和response對象就是服務(wù)器以參數(shù)的形式傳遞給我們的熏矿。
  2. 第二種就是JNDI的方式已骇,服務(wù)器把創(chuàng)建好的資源綁定到JNDI容器中去,應(yīng)用程序想要使用資源時票编,就直接從JNDI容器中獲取相應(yīng)的資源即可褪储。

對于上面的name="jdbc/datasource"數(shù)據(jù)源資源,在應(yīng)用程序中可以用如下的代碼去獲然塾颉:

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

此種配置下乱豆,數(shù)據(jù)庫的驅(qū)動jar文件需放置在tomcat的lib下。

配置Tomcat數(shù)據(jù)源

  1. 在Web項目的WebRoot目錄下的META-INF目錄創(chuàng)建一個context.xml文件
  2. 在context.xml文件配置tomcat服務(wù)器的數(shù)據(jù)源
<Context>
   <Resource 
       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"/>
</Context>
  1. 將數(shù)據(jù)庫的驅(qū)動jar文件需放置在tomcat的lib下
  2. 在獲取數(shù)據(jù)庫連接的工具類的靜態(tài)代碼塊中獲取JNDI容器中的數(shù)據(jù)源
//數(shù)據(jù)庫連接工具類
public class JdbcJNDI {
    
    private static DataSource ds = null;
    //在靜態(tài)代碼塊中創(chuàng)建數(shù)據(jù)庫連接池
    static{
        try{
             //初始化JNDI
            Context initCtx = new InitialContext();
             //得到JNDI容器
            Context envCtx = (Context) initCtx.lookup("java:comp/env");
             //從JNDI容器中檢索name為jdbc/datasource的數(shù)據(jù)源
            ds = (DataSource)envCtx.lookup("jdbc/datasource");
        }catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }
    
    //從數(shù)據(jù)源中獲取數(shù)據(jù)庫連接
    public static Connection getConnection() throws SQLException{
        //從數(shù)據(jù)源中獲取數(shù)據(jù)庫連接
        return ds.getConnection();
    }
    
    /**
    *  釋放資源吊趾,
    * 釋放的資源包括Connection數(shù)據(jù)庫連接對象宛裕,負責執(zhí)行SQL命令的Statement對象,存儲查詢結(jié)果的ResultSet對象
    */ 
    public static void release(Connection conn,Statement st,ResultSet rs){
        if(rs!=null){
            try{
                //關(guān)閉存儲查詢結(jié)果的ResultSet對象
                rs.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
            rs = null;
        }
        if(st!=null){
            try{
                //關(guān)閉負責執(zhí)行SQL命令的Statement對象
                st.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        if(conn!=null){
            try{
                //將Connection連接對象還給數(shù)據(jù)庫連接池
                conn.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

四论泛、Java常見數(shù)據(jù)庫連接池性能比較

目前揩尸,流行的Java數(shù)據(jù)庫連接池有HikariCP,druid屁奏,tomcat-jdbc岩榆,dbcp,c3p0坟瓢。性能從高到低順序排列(單從性能角度看)勇边。下圖是HikariCP官網(wǎng)給出的性能對比:

性能對比.jpg

HikariCP是目前最快的Java數(shù)據(jù)庫連接池,spring boot 2.x已經(jīng)使用HikariCP作為默認的數(shù)據(jù)庫連接池折联,足見其優(yōu)秀粒褒。

五、HikariCP的優(yōu)越性

HikariCP為什么這么快呢?

  1. 優(yōu)化并精簡字節(jié)碼:使用Java字節(jié)碼修改類庫Javassist來生成委托實現(xiàn)動態(tài)代理诚镰,JDK Proxy生成的字節(jié)碼更少奕坟。
  2. 使用更好的并發(fā)集合類ConcurrentBag。
  3. 使用FastList替代ArrayList清笨。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末月杉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子抠艾,更是在濱河造成了極大的恐慌苛萎,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異腌歉,居然都是意外死亡蛙酪,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進店門究履,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人脸狸,你說我怎么就攤上這事最仑。” “怎么了炊甲?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵泥彤,是天一觀的道長。 經(jīng)常有香客問我卿啡,道長吟吝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任颈娜,我火速辦了婚禮剑逃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘官辽。我一直安慰自己蛹磺,他們只是感情好,可當我...
    茶點故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布同仆。 她就那樣靜靜地躺著萤捆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪俗批。 梳的紋絲不亂的頭發(fā)上俗或,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天,我揣著相機與錄音岁忘,去河邊找鬼辛慰。 笑死,一個胖子當著我的面吹牛干像,可吹牛的內(nèi)容都是我干的昆雀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼蝠筑,長吁一口氣:“原來是場噩夢啊……” “哼狞膘!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起什乙,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤挽封,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后臣镣,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辅愿,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡智亮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了点待。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片阔蛉。...
    茶點故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖癞埠,靈堂內(nèi)的尸體忽然破棺而出状原,到底是詐尸還是另有隱情,我是刑警寧澤苗踪,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布颠区,位于F島的核電站,受9級特大地震影響通铲,放射性物質(zhì)發(fā)生泄漏毕莱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一颅夺、第九天 我趴在偏房一處隱蔽的房頂上張望朋截。 院中可真熱鬧,春花似錦吧黄、人聲如沸质和。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饲宿。三九已至,卻和暖如春胆描,著一層夾襖步出監(jiān)牢的瞬間瘫想,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工昌讲, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留国夜,地道東北人。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓短绸,卻偏偏與公主長得像车吹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子醋闭,可洞房花燭夜當晚...
    茶點故事閱讀 45,440評論 2 359