1 引入對(duì)JDBC的理解-1
一般來說,Java應(yīng)用程序訪問數(shù)據(jù)庫的過程是:
- 裝載數(shù)據(jù)庫驅(qū)動(dòng)程序;
- 通過jdbc建立數(shù)據(jù)庫連接;
- 訪問數(shù)據(jù)庫,執(zhí)行sql語句;
- 斷開數(shù)據(jù)庫連接。
Public void FindAllUsers(){
//1、裝載sqlserver驅(qū)動(dòng)對(duì)象
DriverManager.registerDriver(new SQLServerDriver());
//2、通過JDBC建立數(shù)據(jù)庫連接
Connection con =DriverManager.getConnection("jdbc:sqlserver://192.168.2.6:1433;DatabaseName=customer", "sa", "123");
//3、創(chuàng)建狀態(tài)會(huì)話
Statement state =con.createStatement();
//4、查詢數(shù)據(jù)庫并返回結(jié)果
ResultSet result =state.executeQuery("select * from users");
//5、輸出查詢結(jié)果
while(result.next()){
System.out.println(result.getString("email"));
}
//6、斷開數(shù)據(jù)庫連接
result.close();
state.close();
con.close();
}
程序開發(fā)過程中,存在很多問題: 首先,每一次web請(qǐng)求都要建立一次數(shù)據(jù)庫連接。建立連接是一個(gè)費(fèi)時(shí)的活動(dòng),每次都得花費(fèi)0.05s~1s的時(shí)間,而且系統(tǒng)還要分配內(nèi)存資源。 這個(gè)時(shí)間對(duì)于一次或幾次web請(qǐng)求數(shù)據(jù)庫操作,或許感覺不出系統(tǒng)有多大的開銷??墒菍?duì)于現(xiàn)在的web應(yīng)用,尤其是大型電子商務(wù)網(wǎng)站,同時(shí)有幾百人甚至幾千人在線是很正常的事。
在這種情況下,頻繁的進(jìn)行數(shù)據(jù)庫連接操作勢(shì)必占用很多的系統(tǒng)資源,網(wǎng)站的響應(yīng)速度必定下降,嚴(yán)重的甚至?xí)斐煞?wù)器的崩潰。 不是危言聳聽,這就是制約某些電子商務(wù)網(wǎng)站發(fā)展的技術(shù)瓶頸問題。其次,對(duì)于每一次數(shù)據(jù)庫連接,使用完后都得斷開。 但是,如果程序出現(xiàn)異常而未能關(guān)閉,將會(huì)導(dǎo)致數(shù)據(jù)庫系統(tǒng)中的內(nèi)存泄漏,最終將不得不重啟數(shù)據(jù)庫。 還有,這種開發(fā)不能控制被創(chuàng)建的連接對(duì)象數(shù),系統(tǒng)資源會(huì)被毫無顧及的分配出去,如連接過多,也可能導(dǎo)致內(nèi)存泄漏,服務(wù)器崩潰。
上述的用戶查詢案例,如果同時(shí)有1000人訪問,就會(huì)不斷的有數(shù)據(jù)庫連接、斷開操作:
通過上面的分析,我們可以看出來,“數(shù)據(jù)庫連接”是一種稀缺的資源,為了保障網(wǎng)站的正常使用,應(yīng)該對(duì)其進(jìn)行妥善管理。其實(shí),我們查詢完數(shù)據(jù)庫后,如果不關(guān)閉連接,而是暫時(shí)存放起來,當(dāng)別人使用時(shí),把這個(gè)連接給他們使用,就避免了一次建立數(shù)據(jù)庫連接和斷開的操作時(shí)間消耗。原理如下:
由上面的分析可以看出,問題的根源就在于對(duì)數(shù)據(jù)庫連接資源的低效管理。我們知道,對(duì)于共享資源,有一個(gè)很著名的設(shè)計(jì)模式:資源池(resource pool)。該模式正是為了解決資源的頻繁分配、釋放所造成的問題。 為解決上述問題,可以采用數(shù)據(jù)庫連接池技術(shù)。
數(shù)據(jù)庫連接池的基本思想就是為數(shù)據(jù)庫連接建立一個(gè)“緩沖池”。預(yù)先在緩沖池中放入一定數(shù)量的連接,當(dāng)需要建立數(shù)據(jù)庫連接時(shí),只需從“緩沖池”中取出一個(gè),使用完畢之后再放回去。
我們可以通過設(shè)定連接池最大連接數(shù)來防止系統(tǒng)無盡的與數(shù)據(jù)庫連接。更為重要的是我們可以通過連接池的管理機(jī)制——監(jiān)視數(shù)據(jù)庫的連接的數(shù)量、使用情況,為系統(tǒng)開發(fā)、測(cè)試及性能調(diào)整提供依據(jù)。
我們自己嘗試開發(fā)一個(gè)連接池,來為上面的查詢業(yè)務(wù)提供數(shù)據(jù)庫連接服務(wù):
- 編寫class 實(shí)現(xiàn)DataSource 接口
- 在class構(gòu)造器一次性創(chuàng)建10個(gè)連接,將連接保存LinkedList中
- 實(shí)現(xiàn)getConnection 從 LinkedList中返回一個(gè)連接
- 提供將連接放回連接池中方法
public class MyDataSource implements DataSource {
//鏈表 --- 實(shí)現(xiàn)棧結(jié)構(gòu)
privateLinkedList<Connection> dataSources = new LinkedList<Connection>();
//初始化連接數(shù)量
publicMyDataSource() {
//一次性創(chuàng)建10個(gè)連接
for(int i = 0; i < 10; i++) {
try {
//1、裝載sqlserver驅(qū)動(dòng)對(duì)象
DriverManager.registerDriver(new SQLServerDriver());
//2、通過JDBC建立數(shù)據(jù)庫連接
Connection con =DriverManager.getConnection(
"jdbc:sqlserver://192.168.2.6:1433;DatabaseName=customer", "sa", "123");
//3、將連接加入連接池中
dataSources.add(con);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
publicConnection getConnection() throws SQLException {
//取出連接池中一個(gè)連接
finalConnection conn = dataSources.removeFirst(); // 刪除第一個(gè)連接返回
return conn;
}
//將連接放回連接池
publicvoid releaseConnection(Connection conn) {
dataSources.add(conn);
}
}
//查詢所有用戶
Public void FindAllUsers(){
//1、使用連接池建立數(shù)據(jù)庫連接
MyDataSource dataSource = new MyDataSource();
Connection conn =dataSource.getConnection();
//2、創(chuàng)建狀態(tài)
Statement state =con.createStatement();
//3、查詢數(shù)據(jù)庫并返回結(jié)果
ResultSet result =state.executeQuery("select * from users");
//4、輸出查詢結(jié)果
while(result.next()){
System.out.println(result.getString("email"));
}
//5、斷開數(shù)據(jù)庫連接
result.close();
state.close();
//6、歸還數(shù)據(jù)庫連接給連接池
dataSource.releaseConnection(conn);
}
這就是數(shù)據(jù)庫連接池的原理,它大大提供了數(shù)據(jù)庫連接的利用率,減小了內(nèi)存吞吐的開銷。我們?cè)陂_發(fā)過程中,就不需要再關(guān)心數(shù)據(jù)庫連接的問題,自然有數(shù)據(jù)庫連接池幫助我們處理,這回放心了吧。但連接池需要考慮的問題不僅僅如此,下面我們就看看還有哪些問題需要考慮。
1、并發(fā)問題
為了使連接管理服務(wù)具有最大的通用性,必須考慮多線程環(huán)境,即并發(fā)問題。這個(gè)問題相對(duì)比較好解決,因?yàn)閖ava語言自身提供了對(duì)并發(fā)管理的支持,使用synchronized關(guān)鍵字即可確保線程是同步的。使用方法為直接在類方法前面加上synchronized關(guān)鍵字,如:
public synchronized connection getconnection()
2、多數(shù)據(jù)庫服務(wù)器和多用戶
對(duì)于大型的企業(yè)級(jí)應(yīng)用,常常需要同時(shí)連接不同的數(shù)據(jù)庫(如連接oracle和sybase)。如何連接不同的數(shù)據(jù)庫呢?我們采用的策略是:
設(shè)計(jì)一個(gè)符合單例模式的連接池管理類,在連接池管理類的唯一實(shí)例被創(chuàng)建時(shí)讀取一個(gè)資源文件,其中資源文件中存放著多個(gè)數(shù)據(jù)庫的url地址等信息。根據(jù)資源文件提供的信息,創(chuàng)建多個(gè)連接池類的實(shí)例,每一個(gè)實(shí)例都是一個(gè)特定數(shù)據(jù)庫的連接池。連接池管理類實(shí)例為每個(gè)連接池實(shí)例取一個(gè)名字,通過不同的名字來管理不同的連接池。
對(duì)于同一個(gè)數(shù)據(jù)庫有多個(gè)用戶使用不同的名稱和密碼訪問的情況,也可以通過資源文件處理,即在資源文件中設(shè)置多個(gè)具有相同url地址,但具有不同用戶名和密碼的數(shù)據(jù)庫連接信息。
3、事務(wù)處理
我們知道,事務(wù)具有原子性,此時(shí)要求對(duì)數(shù)據(jù)庫的操作符合“all-all-nothing”原則即對(duì)于一組sql語句要么全做,要么全不做。在java語言中,connection類本身提供了對(duì)事務(wù)的支持,可以通過設(shè)置connection的autocommit屬性為false
然后顯式的調(diào)用commit或rollback方法來實(shí)現(xiàn)。
但要高效的進(jìn)行connection復(fù)用,就必須提供相應(yīng)的事務(wù)支持機(jī)制。可采用每一個(gè)事務(wù)獨(dú)占一個(gè)連接來實(shí)現(xiàn),這種方法可以大大降低事務(wù)管理的復(fù)雜性。
4、連接池的分配與釋放
連接池的分配與釋放,對(duì)系統(tǒng)的性能有很大的影響。合理的分配與釋放,可以提高連接的復(fù)用度,從而降低建立新連接的開銷,同時(shí)還可以加快用戶的訪問速度。
對(duì)于連接的管理可使用空閑池。即把已經(jīng)創(chuàng)建但尚未分配出去的連接按創(chuàng)建時(shí)間
存放到一個(gè)空閑池中。
每當(dāng)用戶請(qǐng)求一個(gè)連接時(shí),系統(tǒng)首先檢查空閑池內(nèi)有沒有空閑連接。如果有就把建立時(shí)間最長(通過容器的順序存放實(shí)現(xiàn))的那個(gè)連接分配給他(實(shí)際是先做連接是否有效的判斷,如果可用就分配給用戶,如不可用就把這個(gè)連接從空閑池刪掉,重新檢測(cè)空閑池是否還有連接);
如果沒有則檢查當(dāng)前所開連接池是否達(dá)到連接池所允許的最大連接數(shù)(maxconn)如果沒有達(dá)到,就新建一個(gè)連接,如果已經(jīng)達(dá)到,就等待一定的時(shí)間(timeout)。
如果在等待的時(shí)間內(nèi)有連接被釋放出來就可以把這個(gè)連接分配給等待的用戶,如果等待時(shí)間超過預(yù)定時(shí)間timeout 則返回空值(null)。
系統(tǒng)對(duì)已經(jīng)分配出去正在使用的連接只做計(jì)數(shù),當(dāng)使用完后再返還給空閑池。對(duì)于空閑連接的狀態(tài),可開辟專門的線程定時(shí)檢測(cè),這樣會(huì)花費(fèi)一定的系統(tǒng)開銷,但可以保證較快的響應(yīng)速度。也可采取不開辟專門線程,只是在分配前檢測(cè)的方法。
5、連接池的配置與維護(hù)
連接池中到底應(yīng)該放置多少連接,才能使系統(tǒng)的性能最佳?系統(tǒng)可采取設(shè)置最小連接數(shù)(minconn)和最大連接數(shù)(maxconn)來控制連接池中的連接。
最小連接數(shù)是系統(tǒng)啟動(dòng)時(shí)連接池所創(chuàng)建的連接數(shù)。如果創(chuàng)建過多,則系統(tǒng)啟動(dòng)就慢,但創(chuàng)建后系統(tǒng)的響應(yīng)速度會(huì)很快;如果創(chuàng)建過少,則系統(tǒng)啟動(dòng)的很快,響應(yīng)起來卻慢。
最大連接數(shù)是連接池中允許連接的最大數(shù)目,具體設(shè)置多少,要看系統(tǒng)的訪問量,可通過反復(fù)測(cè)試,找到最佳點(diǎn)。如何確保連接池中的最小連接數(shù)呢?有動(dòng)態(tài)和靜態(tài)兩種策略。
動(dòng)態(tài)即每隔一定時(shí)間就對(duì)連接池進(jìn)行檢測(cè),如果發(fā)現(xiàn)連接數(shù)量小于最小連接數(shù),則補(bǔ)充相應(yīng)數(shù)量的新連接以保證連接池的正常運(yùn)轉(zhuǎn)。靜態(tài)是發(fā)現(xiàn)空閑連接不夠時(shí)再去檢查。
實(shí)際開發(fā)中有成熟的開源連接池供我們使用:
理解了連接池的原理就可以了,沒有必要什么都從頭寫一遍,那樣會(huì)花費(fèi)很多時(shí)間,并且性能及穩(wěn)定性也不一定滿足要求。事實(shí)上,已經(jīng)存在很多流行的性能優(yōu)良的第三方數(shù)據(jù)庫連接池jar包供我們使用。如:
Apache commons-dbcp
c3p0
Druid
HikariCP
其中c3p0已經(jīng)很久沒有更新了,DBCP更新速度很慢,基本處于不活躍狀態(tài)。而Druid和HikariCP處于活躍狀態(tài)的更新中。
2 引入對(duì)JDBC的理解-2
數(shù)據(jù)庫應(yīng)用,在許多軟件系統(tǒng)中經(jīng)常用到,是開發(fā)中大型系統(tǒng)不可缺少的輔助。 但如果對(duì)數(shù)據(jù)庫資源沒有很好地管理(如:沒有及時(shí)回收數(shù)據(jù)庫的游標(biāo)(ResultSet)、Statement、連接 (Connection)等資源),往往會(huì)直接導(dǎo)致系統(tǒng)的穩(wěn)定。 這類不穩(wěn)定因素,不單單由數(shù)據(jù)庫或者系統(tǒng)本身一方引起,只有系統(tǒng)正式使用后,隨著流量、用戶的增加,才會(huì)逐步顯露。
對(duì)于一個(gè)簡單的數(shù)據(jù)庫應(yīng)用,由于對(duì)于數(shù)據(jù)庫的訪問不是很頻繁。這時(shí)可以簡單地在需要訪問數(shù)據(jù)庫時(shí),就新創(chuàng)建一個(gè)連接,用完后就關(guān)閉它,這樣做也不會(huì)帶來什么明顯的性能上的開銷。 但是對(duì)于一個(gè)復(fù)雜的數(shù)據(jù)庫應(yīng)用,情況就完全不同了。頻繁的建立、關(guān)閉連接,會(huì)極大的減低系統(tǒng)的性能,因?yàn)閷?duì)于連接的使用成了系統(tǒng)性能的瓶頸。
連接復(fù)用: 通過建立一個(gè)數(shù)據(jù)庫連接池以及一套連接使用管理策略,使得一個(gè)數(shù)據(jù)庫連接可以得到高效、安全的復(fù)用,避免了數(shù)據(jù)庫連接頻繁建立、關(guān)閉的開銷。 對(duì)于共享資源,有一個(gè)很著名的設(shè)計(jì)模式:資源池。該模式正是為了解決資源頻繁分配、釋放所造成的問題的。 把該模式應(yīng)用到數(shù)據(jù)庫連接管理領(lǐng)域,就是建立一個(gè)數(shù)據(jù)庫連接池,提供一套高效的連接分配、使用策略,最終目標(biāo)是實(shí)現(xiàn)連接的高效、安全的復(fù)用。
數(shù)據(jù)庫連接池的基本原理: 在內(nèi)部對(duì)象池中維護(hù)一定數(shù)量的數(shù)據(jù)庫連接,并對(duì)外暴露數(shù)據(jù)庫連接獲取和返回方法。 如:外部使用者可通過 getConnection 方法獲取連接,使用完畢后再通過 releaseConnection 方法將連接返回,注意此時(shí)連接并沒有關(guān)閉,而是由連接池管理器回收,并為下一次使用做好準(zhǔn)備。
數(shù)據(jù)庫連接池技術(shù)帶來的優(yōu)勢(shì):
1 資源重用
由于數(shù)據(jù)庫連接得到重用,避免了頻繁創(chuàng)建、釋放連接引起的大量性能開銷。在減少系統(tǒng)消耗的基礎(chǔ)上,另一方面也增進(jìn)了系統(tǒng)運(yùn)行環(huán)境的平穩(wěn)性(減少內(nèi)存碎片以及數(shù)據(jù)庫臨時(shí)進(jìn)程/線程的數(shù)量)。
2 更快的系統(tǒng)響應(yīng)速度
數(shù)據(jù)庫連接池在初始化過程中,往往已經(jīng)創(chuàng)建了若干數(shù)據(jù)庫連接置于池中備用。此時(shí)連接的初始化工作均已完成。 對(duì)于業(yè)務(wù)請(qǐng)求處理而言,直接利用現(xiàn)有可用連接,避免了數(shù)據(jù)庫連接初始化和釋放過程的時(shí)間開銷,從而縮減了系統(tǒng)整體響應(yīng)時(shí)間。
3 新的資源分配手段
對(duì)于多應(yīng)用共享同一數(shù)據(jù)庫的系統(tǒng)而言,可在應(yīng)用層通過數(shù)據(jù)庫連接的配置,實(shí)現(xiàn)數(shù)據(jù)庫連接池技術(shù),如果設(shè)計(jì)中還沒有考慮到連接池的應(yīng)用, 那么…….快在設(shè)計(jì)文檔中加上這部分的內(nèi)容吧。某一應(yīng)用最大可用數(shù)據(jù)庫連接數(shù)的限制,避免某一應(yīng)用獨(dú)占所有數(shù)據(jù)庫資源。
4 統(tǒng)一的連接管理,避免數(shù)據(jù)庫連接泄漏
在較為完備的數(shù)據(jù)庫連接池實(shí)現(xiàn)中,可根據(jù)預(yù)先的連接占用超時(shí)設(shè)定,強(qiáng)制收回被占用連接。從而避免了常規(guī)數(shù)據(jù)庫連接操作中可能出現(xiàn)的資源泄漏。
一個(gè)最小化的數(shù)據(jù)庫連接池實(shí)現(xiàn): 在基于Java開發(fā)的系統(tǒng)中,JDBC是程序員和數(shù)據(jù)庫打交道的主要途徑,提供了完備的數(shù)據(jù)庫操作方法接口。 但考慮到規(guī)范的適用性,JDBC只提供了最直接的數(shù)據(jù)庫操作規(guī)范,對(duì)數(shù)據(jù)庫資源管理,如:對(duì)物理連接的管理及緩沖,期望第三方應(yīng)用服務(wù)器(Application Server)的提供。
應(yīng)用在獲取數(shù)據(jù)庫連接connection時(shí),需要指定使用哪種類型的Driver,在獲得特定的連接后,可按照固定的接口操作不同類型的數(shù)據(jù)庫, 如: 分別獲取Statement、執(zhí)行SQL獲得ResultSet等,如下面的例子 :
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection(url, username, password);
Statement stmt = conn.createStatement();
String sql1 = "update user set age = 100 where id = 1";
int rows1 = stmt.executeUpdate(sql1);
stmt.close();
conn.close();
在完成數(shù)據(jù)操作后,一定要關(guān)閉所有涉及到的數(shù)據(jù)庫資源。這雖然對(duì)應(yīng)用程序的邏輯沒有任何影響,但是關(guān)鍵的操作。 上面是個(gè)簡單的例子,如果攙和眾多的if-else、exception,資源的管理也難免百密一疏,Java系統(tǒng)也同樣會(huì)面臨崩潰的惡運(yùn)。 所以數(shù)據(jù)庫資源的管理依賴于應(yīng)用系統(tǒng)本身,是不安全、不穩(wěn)定的一種隱患。
JDBC連接池: 在標(biāo)準(zhǔn)JDBC對(duì)應(yīng)用的接口中,并沒有提供資源的管理方法。所以,缺省的資源管理由應(yīng)用自己負(fù)責(zé)。雖然在JDBC規(guī)范中,多次提及資源的關(guān)閉/回收及其他的合理運(yùn)用。 但最穩(wěn)妥的方式,還是為應(yīng)用提供有效的管理手段。 所以,JDBC為第三方應(yīng)用服務(wù)器(Application Server)提供了一個(gè)由數(shù)據(jù)庫廠家實(shí)現(xiàn)的管理標(biāo)準(zhǔn)接口:連接緩沖(connection pooling)。 引入了連接池( Connection Pool )的概念 ,也就是以緩沖池的機(jī)制管理數(shù)據(jù)庫的資源。
JDBC最常用的資源有三類: Connection數(shù)據(jù)庫連接。 Statement會(huì)話。 ResultSet結(jié)果集游標(biāo)。
數(shù)據(jù)庫連接池的實(shí)現(xiàn)及原理:
這是一種“爺—父—子”的關(guān)系,對(duì)Connection的管理,就是對(duì)數(shù)據(jù)庫資源的管理。舉個(gè)例子: 如果想確定某個(gè)數(shù)據(jù)庫連接(Connection)是否超時(shí),則需要確定其(所有的)子Statement是否超時(shí),同樣,需要確定所有相關(guān)的 ResultSet是否超時(shí);在關(guān)閉Connection前,需要關(guān)閉所有相關(guān)的Statement和ResultSet。因此,連接池(Connection Pool)所起到的作用,不僅僅簡單地管理Connection,還涉及到 Statement和ResultSet。
連接池(ConnectionPool)與資源管理:
ConnectionPool以緩沖池的機(jī)制,在一定數(shù)量上限范圍內(nèi),控制管理Connection,Statement和ResultSet。任何數(shù)據(jù)庫的資源是有限的,如果被耗盡,則無法獲得更多的數(shù)據(jù)服務(wù)。
在大多數(shù)情況下,資源的耗盡不是由于應(yīng)用的正常負(fù)載過高,而是程序的原因。在實(shí)際工作中,數(shù)據(jù)資源往往是瓶頸資源,不同的應(yīng)用都會(huì)訪問同一數(shù)據(jù)源。其中某個(gè)應(yīng)用耗盡了數(shù)據(jù)庫資源后,意味其他的應(yīng)用也無法正常運(yùn)行。
因此,ConnectionPool的第一個(gè)任務(wù)是限制:每個(gè)應(yīng)用或系統(tǒng)可以擁有的最大資源,也就是確定連接池的大小(PoolSize)。
ConnectionPool的第二個(gè)任務(wù):在連接池的大小(PoolSize)范圍內(nèi),最大限度地使用資源,縮短數(shù)據(jù)庫訪問的使用周期。
許多數(shù)據(jù)庫中,連接(Connection)并不是資源的最小單元,控制Statement資源比Connection更重要。以O(shè)racle為例: 每申請(qǐng)一個(gè)數(shù)據(jù)庫連接(Connection)會(huì)在物理網(wǎng)絡(luò)(如 TCP/IP網(wǎng)絡(luò))上建立一個(gè)用于通訊的連接,在此連接上還可以申請(qǐng)一定數(shù)量的Statement。同一連接可提供的活躍Statement數(shù)量可以達(dá)到幾百。 在節(jié)約網(wǎng)絡(luò)資源的同時(shí),縮短了每次會(huì)話周期(物理連接的建立是個(gè)費(fèi)時(shí)的操作)。
但在一般的應(yīng)用中,多數(shù)按照上例代碼操作,這樣有10個(gè)程序調(diào)用,則會(huì)產(chǎn)生10次物理連接,每個(gè)Statement單獨(dú)占用一個(gè)物理連接,這是極大的資源浪費(fèi)。
ConnectionPool可以解決這個(gè)問題,讓幾十、幾百個(gè)Statement只占用同一個(gè)物理連接, 發(fā)揮數(shù)據(jù)庫原有的優(yōu)點(diǎn)。通過ConnectionPool對(duì)資源的有效管理,應(yīng)用可以獲得的Statement總數(shù)到達(dá) : (并發(fā)物理連接數(shù))×(每個(gè)連接可提供的Statement數(shù)量)
例如,某種數(shù)據(jù)庫可同時(shí)建立的物理連接數(shù)為 200個(gè),每個(gè)連接可同時(shí)提供250個(gè)Statement,那么ConnectionPool最終為應(yīng)用提供的并發(fā)Statement總數(shù)為: 200 × 250 = 50,000個(gè)。這是個(gè)并發(fā)數(shù)字,很少有系統(tǒng)會(huì)突破這個(gè)量級(jí)。所以指出資源的耗盡與應(yīng)用程序直接管理有關(guān)。
簡單JDBC連接池的實(shí)現(xiàn):
在JDBC規(guī)范中,應(yīng)用通過驅(qū)動(dòng)接口(Driver Interface)直接獲取數(shù)據(jù)庫的資源。為了有效、合理地管理資源,在應(yīng)用與JDBC Driver之間,增加了連接池: Snap-ConnectionPool。
并且通過面向?qū)ο蟮臋C(jī)制,使連接池的大部分操作是透明的。
參見下圖,Snap-ConnectionPool的體系:
圖中所示,通過實(shí)現(xiàn)JDBC的部分資源對(duì)象接口( Connection, Statement, ResultSet ),
在 Snap-ConnectionPool內(nèi)部分別產(chǎn)生三種邏輯資源對(duì)象: PooledConnection, PooledStatement和 PooledResultSet。它們也是連接池主要的管理操作對(duì)象,并且繼承了JDBC中相應(yīng)的從屬關(guān)系。這樣的體系有以下幾個(gè)特點(diǎn):
透明性:
在不改變應(yīng)用原有的使用JDBC驅(qū)動(dòng)接口的前提下,提供資源管理的服務(wù)。
資源封裝:
復(fù)雜的資源管理被封裝在 Snap-ConnectionPool內(nèi)部,不需要應(yīng)用系統(tǒng)過多的干涉。管理操作的可靠性、安全性由連接池保證。應(yīng)用的干涉(如:主動(dòng)關(guān)閉資源),只起到優(yōu)化系統(tǒng)性能的作用,遺漏操作不會(huì)帶來負(fù)面影響。
資源合理應(yīng)用:
按照J(rèn)DBC中資源的從屬關(guān)系,Snap-ConnectionPool不僅對(duì)Connection進(jìn)行緩沖處理,對(duì)Statement也有相應(yīng)的機(jī)制處理。合理運(yùn)用Connection和Statement之間的關(guān)系,可以更大限度地使用資源。所以,Snap-ConnectionPool封裝了Connection資源, 通過內(nèi)部管理PooledConnection,為應(yīng)用系統(tǒng)提供更多的Statement 資源。
資源連鎖管理:
Snap-ConnectionPool包含的三種邏輯對(duì)象,繼承了JDBC中相應(yīng)對(duì)象之間的從屬關(guān)系。在內(nèi)部管理中,也依照從屬關(guān)系進(jìn)行連鎖管理。例如:判斷一個(gè)Connection是否超時(shí),需要根據(jù)所包含的Statement是否活躍;判斷Statement也要根據(jù) ResultSet的活躍程度。
連接池集中管理ConnectionManager
ConnectionPool是Snap-ConnectionPool的連接池對(duì)象。在Snap-ConnectionPool內(nèi)部,可以指定多個(gè)不同的連接池(ConnectionPool)為應(yīng)用服務(wù)。ConnectionManager管理所有的連接池,每個(gè)連接池以不同的名稱區(qū)別。通過配置文件適應(yīng)不同的數(shù)據(jù)庫種類。如下圖所示:
通過ConnectionManager,可以同時(shí)管理多個(gè)不同的連接池,提供通一的管理界面。在應(yīng)用系統(tǒng)中通過 ConnectionManager和相關(guān)的配置文件,可以將凌亂散落在各自應(yīng)用程序中的數(shù)據(jù)庫配置信息(包括:數(shù)據(jù)庫名、用戶、密碼等信息),集中在一個(gè)文件中。便于系統(tǒng)的維護(hù)工作。
連接池使用范例:
對(duì)上栗代碼的標(biāo)準(zhǔn)JDBC的使用范例,改為使用連接池,結(jié)果如下:
import java.sql.*;
import net.snapbug.util.dbtool.*;
…
ConnectionPool dbConn = ConnectionManager .getConnectionPool("testOracle" );
Statement st = dbConn.createStatement();
ResultSet rs = st.executeQuery(“select * from demo_table” );
…
some data source operation in here
rs.close();
st.close();
在例子中,Snap-ConnectionPool封裝了應(yīng)用對(duì)Connection的管理。只要改變JDBC獲取Connection的方法——獲取連接池(ConnectionPool)(粗體部分),其他的數(shù)據(jù)操作都可以不做修改。
按照這樣的方式,Snap-ConnectionPool可幫助應(yīng)用有效地管理數(shù)據(jù)庫資源。
如果應(yīng)用忽視了最后資源的釋放: rs.close() 和 st.close(),連接池會(huì)通過超時(shí)(time-out)機(jī)制,自動(dòng)回收。
小結(jié):
無論是Snap-ConnectionPool還是其他的數(shù)據(jù)庫連接池,都應(yīng)當(dāng)具備一下基本功能:
- 對(duì)源數(shù)據(jù)庫資源的保護(hù)
- 充分利用發(fā)揮數(shù)據(jù)庫的有效資源
- 簡化應(yīng)用的數(shù)據(jù)庫接口,封閉資源管理。
- 對(duì)應(yīng)用遺留資源的自動(dòng)回收和整理,提高資源的再次利用率。
- 在這個(gè)前提下,應(yīng)用程序才能投入更多的精力于各自的業(yè)務(wù)邏輯中,數(shù)據(jù)庫資源也不再成為系統(tǒng)的瓶頸。
3 技術(shù)經(jīng)理:求求你,別再亂改數(shù)據(jù)庫連接池的大小了
基本上來說,大部分項(xiàng)目都需要跟數(shù)據(jù)庫做交互,那么,數(shù)據(jù)庫連接池的大小設(shè)置成多大合適呢? 一些開發(fā)老鳥可能還會(huì)告訴你:沒關(guān)系,盡量設(shè)置的大些,比如設(shè)置成 200,這樣數(shù)據(jù)庫性能會(huì)高些,吞吐量也會(huì)大些! 你也許會(huì)點(diǎn)頭稱是,真的是這樣嗎?看完這篇文章,也許會(huì)顛覆你的認(rèn)知哦!
可以很直接的說,關(guān)于數(shù)據(jù)庫連接池大小的設(shè)置,每個(gè)開發(fā)者都可能在一環(huán)節(jié)掉進(jìn)坑里,事實(shí)上呢,大部分程序員可能都會(huì)依靠自己的直覺去設(shè)置它的大小,設(shè)置成 100 ? 思量許久后,自顧自想,應(yīng)該差不多吧?
不妨意淫一下,你手里有個(gè)網(wǎng)站,并發(fā)壓力雖然還沒到 Facebook 那個(gè)級(jí)別,但是呢?也有個(gè)1萬上下的并發(fā)量! 也就是說差不多2萬左右的 TPS[TPS:Transactions Per Second(每秒傳輸?shù)氖挛锾幚韨€(gè)數(shù)),TPS包括一條消息入和一條消息出,加上一次用戶數(shù)據(jù)庫訪問,那么問題來了!這個(gè)網(wǎng)站的數(shù)據(jù)庫連接池應(yīng)該設(shè)置成多大合適呢? 其實(shí)這個(gè)問法本身就是有問題的,我們需要反過來問,正確問法應(yīng)該是: “這個(gè)網(wǎng)站的數(shù)據(jù)庫連接池應(yīng)該設(shè)置成多小合適呢?”
對(duì) Oracle 數(shù)據(jù)庫進(jìn)行了壓力測(cè)試,模擬 9600 個(gè)并發(fā)線程來操作數(shù)據(jù)庫,每兩次數(shù)據(jù)庫操作之間 sleep 550ms,注意,開始設(shè)置的線程池大小為 2048。
讓我們來看看數(shù)據(jù)庫連接池的大小為 2048 性能測(cè)試結(jié)果的鬼樣子: 每個(gè)請(qǐng)求要在連接池隊(duì)列里等待 33ms,獲得連接之后,執(zhí)行SQL需要耗時(shí)77ms, CPU 消耗維持在 95% 左右;
接下來,我們將連接池的大小改小點(diǎn),設(shè)置成 1024,其他測(cè)試參數(shù)不變,結(jié)果咋樣? "這里,獲取連接等待時(shí)長基本不變,但是 SQL 的執(zhí)行耗時(shí)降低了!"
哎呦,有長進(jìn)哦!
接下來,我們?cè)僭O(shè)置小些,連接池的大小降低到 96,并發(fā)數(shù)等其他參數(shù)不變,看看結(jié)果如何: 每個(gè)請(qǐng)求在連接池隊(duì)列中的平均等待時(shí)間為 1ms, SQL 執(zhí)行耗時(shí)為 2ms。
我去!什么鬼?
我們沒調(diào)整任何東西,僅僅只是將數(shù)據(jù)庫連接池的大小降低了,這樣,就能把之前平均 100ms 響應(yīng)時(shí)間縮短到了 3ms。吞吐量指數(shù)級(jí)上升??! 你這也太溜了!
為啥有這種效果? 我們不妨想一下,為啥 Nginx 內(nèi)部僅僅使用了 4 個(gè)線程,其性能就大大超越了 100 個(gè)進(jìn)程的 Apache HTTPD 呢? 追究其原因的話,回想一下計(jì)算機(jī)科學(xué)的基礎(chǔ)知識(shí),答案其實(shí)非常明顯。 要知道,即使是單核 CPU 的計(jì)算機(jī)也能“同時(shí)”運(yùn)行著數(shù)百個(gè)線程。但我們其實(shí)都知道,這只不過是操作系統(tǒng)快速切換時(shí)間片,跟我們玩的一個(gè)小把戲罷了。 一核 CPU同一時(shí)刻只能執(zhí)行一個(gè)線程,然后操作系統(tǒng)切換上下文,CPU 核心快速調(diào)度,執(zhí)行另一個(gè)線程的代碼,不停反復(fù),給我們?cè)斐闪怂羞M(jìn)程同時(shí)運(yùn)行假象。 其實(shí),在一核 CPU 的機(jī)器上,順序執(zhí)行A和B永遠(yuǎn)比通過時(shí)間分片切換“同時(shí)”執(zhí)行A和B要快,其中原因,學(xué)過操作系統(tǒng)這門課程的童鞋應(yīng)該很清楚。 一旦線程的數(shù)量超過了 CPU 核心的數(shù)量,再增加線程數(shù)系統(tǒng)就只會(huì)更慢,而不是更快,因?yàn)檫@里涉及到上下文切換耗費(fèi)的額外的性能。 說到這里,你應(yīng)該恍然大悟了 ……
當(dāng)我們?cè)趯ふ覕?shù)據(jù)庫的性能瓶頸時(shí),大致可歸為三類: CPU、 磁盤 IO 、網(wǎng)絡(luò) IO ,也許你會(huì)說,還有內(nèi)存這一因素?內(nèi)存的確是需要考慮的,但是比起磁盤IO和網(wǎng)絡(luò)IO,稍顯微不足道,這里就不加了。
假設(shè)我們不考慮磁盤 IO 和網(wǎng)絡(luò) IO,就很好定論了,在一個(gè) 8 核的服務(wù)器上,數(shù)據(jù)庫連接數(shù)/線程數(shù)設(shè)置為 8 能夠提供最優(yōu)的性能,如果再增加連接數(shù),反而會(huì)因?yàn)樯舷挛那袚Q導(dǎo)致性能下降。
大家都知道,數(shù)據(jù)庫通常把數(shù)據(jù)存儲(chǔ)在磁盤上,而磁盤呢,通常是由一些旋轉(zhuǎn)著的金屬碟片和一個(gè)裝在步進(jìn)馬達(dá)上的讀寫頭組成的。 讀/寫頭同一時(shí)刻只能出現(xiàn)在一個(gè)位置,當(dāng)它需要再次執(zhí)行讀寫操作時(shí),它必須“尋址”到另外一個(gè)位置才能完成任務(wù)。 所以呢?這里就有了尋址耗時(shí),此外還有旋轉(zhuǎn)耗時(shí),讀寫頭需要等待磁盤碟片上的目標(biāo)數(shù)據(jù)“旋轉(zhuǎn)到位”才能進(jìn)行讀寫操作。使用緩存當(dāng)然是能夠提升性能的,但上述原理仍然適用。
在這段(“I/O等待”)時(shí)間內(nèi),線程是處于“阻塞”等待狀態(tài),也就是說沒干啥正事!此時(shí)操作系統(tǒng)可以將這個(gè)空閑的CPU 核心用于服務(wù)其他線程。 這里我們可以總結(jié)一下,當(dāng)你的線程處理的是 I/O 密集型業(yè)務(wù)時(shí),便可以讓線程/連接數(shù)設(shè)置的比 CPU核心大一些,這樣就能夠在同樣的時(shí)間內(nèi),完成更多的工作,提升吞吐量。
那么問題又來了? 大小設(shè)置成多少合適呢?
這要取決于磁盤,如果你使用的是 SSD 固態(tài)硬盤,它不需要尋址,也不需要旋轉(zhuǎn)碟片。打住打住?。?!你千萬可別理所當(dāng)然的認(rèn)為:“既然SSD速度更快,我們把線程數(shù)的大小設(shè)置的大些吧!!” 結(jié)論正好相反!無需尋址和沒有旋回耗時(shí)的確意味著更少的阻塞,所以更少的線程(更接近于CPU核心數(shù))會(huì)發(fā)揮出更高的性能。只有當(dāng)阻塞密集時(shí),更多的線程數(shù)才能發(fā)揮出更好的性能。
上面我們已經(jīng)說過了磁盤 IO, 接下來我們談?wù)劸W(wǎng)絡(luò) IO!
網(wǎng)絡(luò) IO 其實(shí)也是非常相似的。通過以太網(wǎng)接口讀寫數(shù)據(jù)時(shí)也會(huì)造成阻塞,10G帶寬會(huì)比1G帶寬的阻塞耗時(shí)少一些,而 1G 帶寬又會(huì)比 100M 帶寬的阻塞少一些。 通常情況下,我們把網(wǎng)絡(luò) IO 放在第三順位來考慮,然而有些人會(huì)在性能計(jì)算中忽略網(wǎng)絡(luò) IO 帶來的影響。
上圖是 PostgreSQL 的基準(zhǔn)性能測(cè)試數(shù)據(jù),從圖中我們可以看到,TPS 在連接數(shù)達(dá)到 50 時(shí)開始變緩。 回過頭來想下,Oracle 的性能測(cè)試中,測(cè)試人員們將連接數(shù)從 2048 降到了 96,實(shí)際上 96 還是太高了,除非你的服務(wù)器 CPU 核心數(shù)有 16 或 32。
下面公式由 PostgreSQL 提供,不過底層原理是不變的,它適用于市面上絕大部分?jǐn)?shù)據(jù)庫產(chǎn)品。 還有,你應(yīng)該模擬預(yù)期的訪問量,并通過下面的公式先設(shè)置一個(gè)偏合理的值,然后在實(shí)際的測(cè)試中,通過微調(diào),來尋找最合適的連接數(shù)大小。
連接數(shù) = ((核心數(shù) * 2) + 有效磁盤數(shù))
核心數(shù)不應(yīng)包含超線程(hyper thread),即使打開了超線程也是如此,如果熱點(diǎn)數(shù)據(jù)全被緩存了,那么有效磁盤數(shù)實(shí)際是0,隨著緩存命中率的下降,有效磁盤數(shù)也逐漸趨近于實(shí)際的磁盤數(shù)。 另外需要注意,這一公式作用于SSD 的效果如何,尚未明了。
好了,按照這個(gè)公式,如果說你的服務(wù)器 CPU 是 4核 i7 的,連接池大小應(yīng)該為 ((4*2)+1)=9
。 取個(gè)整, 我們就設(shè)置為 10 吧。你這個(gè)行不行?。?0 也太小了吧!
你要是覺得不太行的話,可以跑個(gè)性能測(cè)試看看,我們可以保證,它能輕松支撐 3000 用戶以 6000 TPS 的速率并發(fā)執(zhí)行簡單查詢的場(chǎng)景。 你還可以將連接池大小超過 10,那時(shí),你會(huì)看到響應(yīng)時(shí)長開始增加,TPS 開始下降。
**結(jié)論:**你需要的是一個(gè)小連接池,和一個(gè)等待連接的線程隊(duì)列 ,假設(shè)說你有 10000 個(gè)并發(fā)訪問,而你設(shè)置了連接池大小為 10000,你怕是石樂志哦。 改成 1000,太高?改成 100?還是太多了。你僅僅需要一個(gè)大小為 10 數(shù)據(jù)庫連接池,然后讓剩下的業(yè)務(wù)線程都在隊(duì)列里等待就可以了。
**連接池中的連接數(shù)量大小應(yīng)該設(shè)置成:**數(shù)據(jù)庫能夠有效同時(shí)進(jìn)行的查詢?nèi)蝿?wù)數(shù)(通常情況下來說不會(huì)高于 2*CPU核心數(shù))。 你應(yīng)該經(jīng)常會(huì)看到一些用戶量不是很大的 web 應(yīng)用中,為應(yīng)付大約十來個(gè)的并發(fā),卻將數(shù)據(jù)庫連接池設(shè)置成 100, 200 的情況。請(qǐng)不要過度配置您的數(shù)據(jù)庫連接池的大小。
實(shí)際上,連接池的大小的設(shè)置還是要結(jié)合實(shí)際的業(yè)務(wù)場(chǎng)景來說事。 比如說,你的系統(tǒng)同時(shí)混合了長事務(wù)和短事務(wù),這時(shí),根據(jù)上面的公式來計(jì)算就很難辦了。正確的做法應(yīng)該是創(chuàng)建兩個(gè)連接池,一個(gè)服務(wù)于長事務(wù),一個(gè)服務(wù)于"實(shí)時(shí)"查詢,也就是短事務(wù)。 還有一種情況,比方說一個(gè)系統(tǒng)執(zhí)行一個(gè)任務(wù)隊(duì)列,業(yè)務(wù)上要求同一時(shí)間內(nèi)只允許執(zhí)行一定數(shù)量的任務(wù),這時(shí),我們就應(yīng)該讓并發(fā)任務(wù)數(shù)去適配連接池連接數(shù),而不是連接數(shù)大小去適配并發(fā)任務(wù)數(shù)。
4 數(shù)據(jù)庫連接池druid參數(shù)詳解
spring:
datasource:
druid:
# 指定數(shù)據(jù)源類型為DruidDataSource
type: com.alibaba.druid.pool.DruidDataSource
# 數(shù)據(jù)庫連接URL
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false
# 數(shù)據(jù)庫用戶名
username: root
# 數(shù)據(jù)庫密碼
password: "xx123!@#"
# 連接池初始化時(shí)創(chuàng)建的連接數(shù)
initialSize: 5
# 連接池中最大連接數(shù)
maxActive: 50
# 連接池中最小空閑連接數(shù)
minIdle: 5
# 連接池中最大空閑連接數(shù)
maxIdle: 10
# 獲取連接時(shí)的最大等待時(shí)間(毫秒)
maxWait: 60000
# 間隔多久檢測(cè)一次空閑連接(毫秒)
timeBetweenEvictionRunsMillis: 60000
# 連接池中連接最小空閑時(shí)間(毫秒)
minEvictableIdleTimeMillis: 300000
# 用于檢測(cè)連接是否有效的SQL語句
validationQuery: SELECT 1
# 是否開啟空閑連接的檢測(cè)
testWhileIdle: true
# 是否開啟連接的檢測(cè)功能,在獲取連接時(shí)檢測(cè)連接是否有效
testOnBorrow: false
# 是否開啟連接的檢測(cè)功能,在歸還連接時(shí)檢測(cè)連接是否有效
testOnReturn: false
# 是否緩存PreparedStatement對(duì)象
poolPreparedStatements: true
# 緩存PreparedStatement對(duì)象的最大數(shù)量
maxPoolPreparedStatementPerConnectionSize: 20
# 配置監(jiān)控統(tǒng)計(jì)用的filter,允許監(jiān)控統(tǒng)計(jì)
filters: stat
# 配置擴(kuò)展屬性,用于監(jiān)控統(tǒng)計(jì)分析SQL性能等
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
1、type: 數(shù)據(jù)源類型,這里使用了Druid連接池的類型。 注意事項(xiàng):使用Druid連接池的時(shí)候需要添加Druid連接池的依賴
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
2、url: 數(shù)據(jù)庫連接的URL。 注意事項(xiàng):需要根據(jù)實(shí)際情況修改URL。
3、username: 數(shù)據(jù)庫用戶名。
4、password: 數(shù)據(jù)庫密碼 ;包含一些特殊字符的時(shí)候需要加引號(hào)。
5、initialSize: 連接池初始化時(shí)創(chuàng)建的連接數(shù)。 需要根據(jù)實(shí)際情況設(shè)置連接數(shù)。連接池創(chuàng)建連接時(shí),會(huì)創(chuàng)建initialSize個(gè)連接,以確保應(yīng)用程序在啟動(dòng)時(shí)可以立即獲取到數(shù)據(jù)庫連接。
6、maxActive: 連接池中最大連接數(shù)。 需要根據(jù)實(shí)際情況設(shè)置最大連接數(shù)。如果連接池中連接數(shù)達(dá)到maxActive,則應(yīng)用程序獲取連接的請(qǐng)求將被阻塞,直到有連接被釋放。
7、minIdle: 連接池中最小空閑連接數(shù)。 需要根據(jù)實(shí)際情況設(shè)置最小空閑連接數(shù)。如果連接池中空閑連接數(shù)少于minIdle,連接池會(huì)創(chuàng)建新的連接以達(dá)到minIdle。
8、maxIdle: 連接池中最大空閑連接數(shù)。 需要根據(jù)實(shí)際情況設(shè)置最大空閑連接數(shù)。如果連接池中空閑連接數(shù)超過maxIdle,連接池會(huì)關(guān)閉多余的連接以避免占用過多的資源。
9、maxWait: 獲取連接時(shí)的最大等待時(shí)間。 需要根據(jù)實(shí)際情況設(shè)置最大等待時(shí)間。如果連接池中沒有可用連接且已經(jīng)達(dá)到最大連接數(shù),則應(yīng)用程序獲取連接的請(qǐng)求將被阻塞,最多等待maxWait毫秒。
10、timeBetweenEvictionRunsMillis: 間隔多久檢測(cè)一次空閑連接(毫秒)。 需要根據(jù)實(shí)際情況設(shè)置檢測(cè)間隔時(shí)間。連接池會(huì)定期檢查空閑連接的狀態(tài),如果空閑時(shí)間超過minEvictableIdleTimeMillis,則將連接關(guān)閉。
11、minEvictableIdleTimeMillis: 連接池中連接最小空閑時(shí)間(毫秒)。 需要根據(jù)實(shí)際情況設(shè)置最小空閑時(shí)間。連接池會(huì)定期檢查空閑連接的狀態(tài),如果空閑時(shí)間超過minEvictableIdleTimeMillis,則將連接關(guān)閉。
12、validationQuery: 用于檢測(cè)連接是否有效的SQL語句。 需要根據(jù)實(shí)際情況設(shè)置SQL語句。連接池會(huì)使用這個(gè)SQL語句來檢測(cè)連接是否有效。
13、testWhileIdle: 是否開啟空閑連接的檢測(cè)。 需要根據(jù)實(shí)際情況設(shè)置是否開啟空閑連接的檢測(cè)。如果開啟,連接池會(huì)定期檢查空閑連接的狀態(tài)。
14、testOnBorrow: 是否開啟連接的檢測(cè)功能, 在獲取連接時(shí)檢測(cè)連接是否有效。 需要根據(jù)實(shí)際情況設(shè)置是否開啟連接的檢測(cè)功能。如果開啟,連接池在獲取連接時(shí)會(huì)檢查連接是否有效。
15、testOnReturn: 是否開啟連接的檢測(cè)功能,在歸還連接時(shí)檢測(cè)連接是否有效。 需要根據(jù)實(shí)際情況設(shè)置是否開啟連接的檢測(cè)功能。如果開啟,連接池在歸還連接時(shí)會(huì)檢查連接是否有效。
16、poolPreparedStatements: 是否緩存PreparedStatement對(duì)象。 需要根據(jù)實(shí)際情況設(shè)置是否緩存PreparedStatement對(duì)象。如果開啟,連接池會(huì)緩存PreparedStatement對(duì)象以提高性能。
17、maxPoolPreparedStatementPerConnectionSize: 緩存PreparedStatement對(duì)象的最大數(shù)量。 需要根據(jù)實(shí)際情況設(shè)置緩存的最大數(shù)量。如果開啟了緩存PreparedStatement對(duì)象,連接池會(huì)限制每個(gè)連接緩存的最大數(shù)量。
maxPoolPreparedStatementPerConnectionSize是Druid連接池的一個(gè)配置項(xiàng),用于配置連接池中緩存的PreparedStatement的最大數(shù)量。 PreparedStatement是預(yù)編譯的SQL語句,可以提高SQL執(zhí)行的效率和安全性,避免SQL注入等安全問題。 在使用Druid連接池時(shí),當(dāng)開啟了緩存PreparedStatement功能時(shí),每個(gè)連接都會(huì)緩存一定數(shù)量的PreparedStatement對(duì)象,以便在需要執(zhí)行SQL語句時(shí)能夠快速獲取。
而maxPoolPreparedStatementPerConnectionSize就是用于配置每個(gè)連接中緩存的PreparedStatement的最大數(shù)量。 需要注意的是,緩存PreparedStatement雖然可以提高SQL執(zhí)行的效率,但同時(shí)也會(huì)占用一定的內(nèi)存資源。因此,需要根據(jù)實(shí)際情況進(jìn)行合理配置。 默認(rèn)情況下,該屬性的值為10。
在上面的配置中,maxPoolPreparedStatementPerConnectionSize: 20 ;表示將每個(gè)連接中緩存的PreparedStatement的最大數(shù)量設(shè)置為20個(gè)。
18、filters: 配置監(jiān)控統(tǒng)計(jì)用的filter,允許監(jiān)控統(tǒng)計(jì)。 如果要使用Druid的監(jiān)控功能,需要配置此項(xiàng)。stat表示使用Druid的監(jiān)控功能。
19、connectionProperties: 配置擴(kuò)展屬性,用于監(jiān)控統(tǒng)計(jì)分析SQL性能等。 druid.stat.mergeSql和druid.stat.slowSqlMillis是兩個(gè)與SQL監(jiān)控有關(guān)的屬性。
druid.stat.mergeSql用于配置是否合并SQL。當(dāng)該屬性設(shè)置為true時(shí),Druid會(huì)將相同的SQL語句合并為一條,以節(jié)省SQL統(tǒng)計(jì)的開銷和提高統(tǒng)計(jì)精度。 默認(rèn)情況下,該屬性的值為false。文章來源:http://www.zghlxwxcb.cn/news/detail-631053.html
druid.stat.slowSqlMillis用于配置SQL執(zhí)行的時(shí)間閾值,單位為毫秒。當(dāng)一條SQL執(zhí)行的時(shí)間超過該閾值時(shí),Druid會(huì)將該SQL記錄到慢SQL列表中,以便進(jìn)行分析和優(yōu)化。默認(rèn)情況下,該屬性的值為3000毫秒。 在上面的配置中,druid.stat.mergeSql=true表示啟用SQL合并功能, druid.stat.slowSqlMillis=5000表示將SQL執(zhí)行的時(shí)間閾值設(shè)置為5000毫秒。 這些屬性的具體含義和配置方法,可以參考Druid的官方文檔。文章來源地址http://www.zghlxwxcb.cn/news/detail-631053.html
到了這里,關(guān)于Mybatis引出的一系列問題-JDBC 的探究的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!