Tomcat 的 JDBC 連接池
Tomcat 的 JDBC 連接池簡介
JDBC 連接池 org.apache.tomcat.jdbc.pool 是 Apache Commons DBCP 連接池的一種替換或備選方案。
那究竟為何需要一個(gè)新的連接池?
原因如下:
- Commons DBCP 1.x 是單線程。為了線程安全,在對(duì)象分配或?qū)ο蠓祷氐亩唐趦?nèi),Commons 鎖定了全部池。但注意這并不適用于 Commons DBCP 2.x。
- Commons DBCP 1.x 可能會(huì)變得很慢。當(dāng)邏輯 CPU 數(shù)目增長,或者試圖借出或歸還對(duì)象的并發(fā)線程增加時(shí),性能就會(huì)受到影響。高并發(fā)系統(tǒng)受到的影響會(huì)更為顯著。注意這并不適用于 Commons DBCP 2.x。
- Commons DBCP 擁有 60 多個(gè)類。tomcat-jdbc-pool 核心只有 8 個(gè)類。因此為了未來需求變更著想,肯定需要更少的改動(dòng)。我們真正需要的只是連接池本身,其余的只是附屬。
- Commons DBCP 使用靜態(tài)接口,因此對(duì)于指定版本的 JRE,只能采用正確版本的 DBCP,否則就會(huì)出現(xiàn) NoSuchMethodException 異常。
- 當(dāng)DBCP 可以用其他更簡便的實(shí)現(xiàn)來替代時(shí),實(shí)在不值得重寫那 60 個(gè)類。
- Tomcat JDBC 連接池?zé)o需為庫本身添加額外線程,就能獲取異步獲取連接。
- Tomcat JDBC 連接池是 Tomcat 的一個(gè)模塊,依靠 Tomcat JULI 這個(gè)簡化了的日志架構(gòu)。
- 使用 javax.sql.PooledConnection 接口獲取底層連接。
- 防止饑餓。如果池變空,線程將等待一個(gè)連接。當(dāng)連接返回時(shí),池就將喚醒正確的等待線程。大多數(shù)連接池只會(huì)一直維持饑餓狀態(tài)。
Tomcat JDBC 連接池還具有一些其他連接池實(shí)現(xiàn)所沒有的特點(diǎn):
- 支持高并發(fā)環(huán)境與多核/CPU 系統(tǒng)。
- 接口的動(dòng)態(tài)實(shí)現(xiàn)。支持 java.sql 與 java.sql 接口(只要 JDBC 驅(qū)動(dòng)),甚至在利用低版本的 JDK 來編譯時(shí)。
- 驗(yàn)證間隔時(shí)間。我們不必每次使用單個(gè)連接時(shí)都進(jìn)行驗(yàn)證,可以在借出或歸還連接時(shí)進(jìn)行驗(yàn)證,只要不低于我們所設(shè)定的間隔時(shí)間就行。
- 只執(zhí)行一次查詢。當(dāng)與數(shù)據(jù)庫建立起連接時(shí),只執(zhí)行一次的可配置查詢。這項(xiàng)功能對(duì)會(huì)話設(shè)置非常有用,因?yàn)槟憧赡軙?huì)想在連接建立的整個(gè)時(shí)段內(nèi)都保持會(huì)話。
- 能夠配置自定義攔截器。通過自定義攔截器來增強(qiáng)功能。可以使用攔截器來采集查詢統(tǒng)計(jì),緩存會(huì)話狀態(tài),重新連接之前失敗的連接,重新查詢,緩存查詢結(jié)果,等等。由于可以使用大量的選項(xiàng),所以這種自定義攔截器也是沒有限制的,跟 java.sql/javax.sql 接口的 JDK 版本沒有任何關(guān)系。
- 高性能。后文將舉例展示一些性能差異。
- 極其簡單。它的實(shí)現(xiàn)非常簡單,代碼行數(shù)與源文件都非常少,這都有賴于從一開始研發(fā)它時(shí),就把簡潔當(dāng)做重中之重。對(duì)比一下 c3p0 ,它的源文件超過了 200 個(gè)(最近一次統(tǒng)計(jì)),而 Tomcat JDBC 核心只有 8 個(gè)文件,連接池本身則大約只有這個(gè)數(shù)目的一半,所以能夠輕易地跟蹤和修改可能出現(xiàn)的 Bug。
- 異步連接獲取。可將連接請(qǐng)求隊(duì)列化,系統(tǒng)返回 Future。
- 更好地處理空閑連接。不再簡單粗暴地直接把空閑連接關(guān)閉,而是仍然把連接保留在池中,通過更為巧妙的算法控制空閑連接池的規(guī)模。
- 可以控制連接應(yīng)被廢棄的時(shí)間:當(dāng)池滿了即廢棄,或者指定一個(gè)池使用容差值,發(fā)生超時(shí)就進(jìn)行廢棄處理。
- 通過查詢或語句來重置廢棄連接計(jì)時(shí)器。允許一個(gè)使用了很長時(shí)間的連接不因?yàn)槌瑫r(shí)而被廢棄。這一點(diǎn)是通過使用 ResetAbandonedTimer 來實(shí)現(xiàn)的。
- 經(jīng)過指定時(shí)間后,關(guān)閉連接。與返回池的時(shí)間相類似。
- 當(dāng)連接要被釋放時(shí),獲取 JMX 通知并記錄所有日志。它類似于 removeAbandonedTimeout,但卻不需要采取任何行為,只需要報(bào)告信息即可。通過 suspectTimeout 屬性來實(shí)現(xiàn)。
- 可以通過 java.sql.Driver、javax.sql.DataSource 或 javax.sql.XADataSource 獲取連接。通過 dataSource 與 dataSourceJNDI 屬性實(shí)現(xiàn)這一點(diǎn)。
- 支持 XA 連接。