自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

深入理解JDBC的超時(shí)設(shè)置

運(yùn)維 數(shù)據(jù)庫(kù)運(yùn)維
恰當(dāng)?shù)腏DBC超時(shí)設(shè)置能夠有效地減少服務(wù)失效的時(shí)間。本文將對(duì)數(shù)據(jù)庫(kù)的各種超時(shí)設(shè)置及其設(shè)置方法做介紹。

恰當(dāng)?shù)腏DBC超時(shí)設(shè)置能夠有效地減少服務(wù)失效的時(shí)間。本文將對(duì)數(shù)據(jù)庫(kù)的各種超時(shí)設(shè)置及其設(shè)置方法做介紹。

真實(shí)案例:應(yīng)用服務(wù)器在遭到DDos攻擊后無(wú)法響應(yīng)

在遭到DDos攻擊后,整個(gè)服務(wù)都垮掉了。由于第四層交換機(jī)不堪重負(fù),網(wǎng)絡(luò)變得無(wú)法連接,從而導(dǎo)致業(yè)務(wù)系統(tǒng)也無(wú)法正常運(yùn)轉(zhuǎn)。安全組很快屏蔽了所有的DDos攻擊,并恢復(fù)了網(wǎng)絡(luò),但業(yè)務(wù)系統(tǒng)卻還是無(wú)法工作。 通過(guò)分析系統(tǒng)的thread dump發(fā)現(xiàn),業(yè)務(wù)系統(tǒng)停在了JDBC API的調(diào)用上。20分鐘后,系統(tǒng)仍處于WAITING狀態(tài),無(wú)法響應(yīng)。30分鐘后,系統(tǒng)拋出異常,服務(wù)恢復(fù)正常。

為什么我們明明將query timeout設(shè)置成了3秒,系統(tǒng)卻持續(xù)了30分鐘的WAITING狀態(tài)?為什么30分鐘后系統(tǒng)又恢復(fù)正常了? 當(dāng)你對(duì)理解了JDBC的超時(shí)設(shè)置后,就能找到問(wèn)題的答案。

為什么我們要了解JDBC?

當(dāng)遇到性能問(wèn)題或系統(tǒng)出錯(cuò)時(shí),業(yè)務(wù)系統(tǒng)和數(shù)據(jù)庫(kù)通常是我們最關(guān)心的兩個(gè)部分。在公司里,這兩個(gè)部分是交由兩個(gè)不同的部門來(lái)負(fù)責(zé)的,因此各個(gè)部門都會(huì)集中精力地在自身領(lǐng)域內(nèi)尋找問(wèn)題,這樣的話,在業(yè)務(wù)系統(tǒng)和數(shù)據(jù)庫(kù)之間的部分就會(huì)成為一個(gè)盲區(qū)。對(duì)于Java應(yīng)用而言,這個(gè)盲區(qū)就是DBCP數(shù)據(jù)庫(kù)連接池和JDBC,本文將集中介紹JDBC。

什么是JDBC?

JDBC是Java應(yīng)用中用來(lái)連接關(guān)系型數(shù)據(jù)庫(kù)的標(biāo)準(zhǔn)API。Sun公司一共定義了4種類型的JDBC,我們主要使用的是第4種,該類型的Driver完全由Java代碼實(shí)現(xiàn),通過(guò)使用socket與數(shù)據(jù)庫(kù)進(jìn)行通信。 

 

第4種類型的JDBC通過(guò)socket對(duì)字節(jié)流進(jìn)行處理,因此也會(huì)有一些基本網(wǎng)絡(luò)操作,類似于HttpClient這種用于網(wǎng)絡(luò)操作的代碼庫(kù)。當(dāng)在網(wǎng)絡(luò)操作中遇到問(wèn)題的時(shí)候,將會(huì)消耗大量的cpu資源,并且失去響應(yīng)超時(shí)。如果你之前用過(guò)HttpClient,那么你一定遇到過(guò)未設(shè)置timeout造成的錯(cuò)誤。同樣,第4種類型的JDBC,若沒(méi)有合理地設(shè)置socket timeout,也會(huì)有相同的錯(cuò)誤——連接被阻塞。

接下來(lái),就讓我們來(lái)學(xué)習(xí)一下如何正確地設(shè)置socket timeout,以及需要考慮的問(wèn)題。

應(yīng)用與數(shù)據(jù)庫(kù)間的timeout層級(jí)

 

上圖展示了簡(jiǎn)化后應(yīng)用與數(shù)據(jù)庫(kù)間的timeout層級(jí)。(譯者注:WAS/BLOC是作者公司的具體應(yīng)用名稱,無(wú)需深究)

高級(jí)別的timeout依賴于低級(jí)別的timeout,只有當(dāng)?shù)图?jí)別的timeout無(wú)誤時(shí),高級(jí)別的timeout才能確保正常。例如,當(dāng)socket timeout出現(xiàn)問(wèn)題時(shí),高級(jí)別的statement timeout和transaction timeout都將失效。

我們收到的很多評(píng)論中提到:

引用

即使設(shè)置了statement timeout,當(dāng)網(wǎng)絡(luò)出錯(cuò)時(shí),應(yīng)用也無(wú)法從錯(cuò)誤中恢復(fù)。

statement timeout無(wú)法處理網(wǎng)絡(luò)連接失敗時(shí)的超時(shí),它能做的僅僅是限制statement的操作時(shí)間。網(wǎng)絡(luò)連接失敗時(shí)的timeout必須交由JDBC來(lái)處理。

JDBC的socket timeout會(huì)受到操作系統(tǒng)socket timeout設(shè)置的影響,這就解釋了為什么在之前的案例中,JDBC連接會(huì)在網(wǎng)絡(luò)出錯(cuò)后阻塞30分鐘,然后又奇跡般恢復(fù),即使我們并沒(méi)有對(duì)JDBC的socket timeout進(jìn)行設(shè)置。

DBCP連接池位于圖2的左側(cè),你會(huì)發(fā)現(xiàn)timeout層級(jí)與DBCP是相互獨(dú)立的。DBCP負(fù)責(zé)的是數(shù)據(jù)庫(kù)連接的創(chuàng)建和管理,并不干涉timeout的處理。當(dāng)連接在DBCP中創(chuàng)建,或是DBCP發(fā)送校驗(yàn)query檢查連接有效性的時(shí)候,socket timeout將會(huì)影響這些過(guò)程,但并不直接對(duì)應(yīng)用造成影響。

當(dāng)在應(yīng)用中調(diào)用DBCP的getConnection()方法時(shí),你可以設(shè)置獲取數(shù)據(jù)庫(kù)連接的超時(shí)時(shí)間,但是這和JDBC的timeout毫不相關(guān)。

 

什么是Transaction Timeout?

transaction timeout一般存在于框架(Spring, EJB)或應(yīng)用級(jí)。transaction timeout或許是個(gè)相對(duì)陌生的概念,簡(jiǎn)單地說(shuō),transaction timeout就是“statement Timeout * N(需要執(zhí)行的statement數(shù)量) + @(垃圾回收等其他時(shí)間)”。transaction timeout用來(lái)限制執(zhí)行statement的總時(shí)長(zhǎng)。

例如,假設(shè)執(zhí)行一個(gè)statement需要0.1秒,那么執(zhí)行少量statement不會(huì)有什么問(wèn)題,但若是要執(zhí)行100,000個(gè)statement則需要10,000秒(約7個(gè)小時(shí))。這時(shí),transaction timeout就派上用場(chǎng)了。EJB CMT (Container Managed Transaction)就是一種典型的實(shí)現(xiàn),它提供了多種方法供開(kāi)發(fā)者選擇。但我們并不使用EJB,Spring的transaction timeout設(shè)置會(huì)更常用一些。在Spring中,你可以使用下面展示的XML或是在源碼中使用@Transactional注解來(lái)進(jìn)行設(shè)置。

Xml代碼

  1. <tx:attributes> 
  2.  
  3. <tx:method name=“…” timeout=“3″/> 
  4.  
  5. </tx:attributes>  

Spring提供的transaction timeout配置非常簡(jiǎn)單,它會(huì)記錄每個(gè)事務(wù)的開(kāi)始時(shí)間和消耗時(shí)間,當(dāng)特定的事件發(fā)生時(shí)就會(huì)對(duì)消耗時(shí)間做校驗(yàn),當(dāng)超出timeout值時(shí)將拋出異常。

Spring中,數(shù)據(jù)庫(kù)連接被保存在ThreadLocal里,這被稱為事務(wù)同步(Transaction Synchronization),與此同時(shí),事務(wù)的開(kāi)始時(shí)間和消耗時(shí)間也被保存下來(lái)。當(dāng)使用這種代理連接創(chuàng)建statement時(shí),就會(huì)校驗(yàn)事務(wù)的消耗時(shí)間。EJB CMT的實(shí)現(xiàn)方式與之類似,其結(jié)構(gòu)本身也十分簡(jiǎn)單。

當(dāng)你選用的容器或框架并不支持transaction timeout這一特性,你可以考慮自己來(lái)實(shí)現(xiàn)。transaction timeout并沒(méi)有標(biāo)準(zhǔn)的API。Lucy框架的1.5和1.6版本都不支持transaction timeout,但是你可以通過(guò)使用Spring的Transaction Manager來(lái)達(dá)到與之同樣的效果。

假設(shè)某個(gè)事務(wù)中包含5個(gè)statement,每個(gè)statement的執(zhí)行時(shí)間是200ms,其他業(yè)務(wù)邏輯的執(zhí)行時(shí)間是100ms,那么transaction timeout至少應(yīng)該設(shè)置為1,100ms(200 * 5 + 100)。

什么是Statement Timeout?

statement timeout用來(lái)限制statement的執(zhí)行時(shí)長(zhǎng),timeout的值通過(guò)調(diào)用JDBC的java.sql.Statement.setQueryTimeout(int timeout) API進(jìn)行設(shè)置。不過(guò)現(xiàn)在開(kāi)發(fā)者已經(jīng)很少直接在代碼中設(shè)置,而多是通過(guò)框架來(lái)進(jìn)行設(shè)置。

以iBatis為例,statement timeout的默認(rèn)值可以通過(guò)sql-map-config.xml中的defaultStatementTimeout 屬性進(jìn)行設(shè)置。同時(shí),你還可以設(shè)置sqlmap中select,insert,update標(biāo)簽的timeout屬性,從而對(duì)不同sql語(yǔ)句的超時(shí)時(shí)間進(jìn)行獨(dú)立的配置。

如果你使用的是Lucy1.5或1.6版本,通過(guò)設(shè)置queryTimeout屬性可以在datasource層面對(duì)statement timeout進(jìn)行設(shè)置。

statement timeout的具體值需要依據(jù)應(yīng)用本身的特性而定,并沒(méi)有可供推薦的配置。

JDBC的statement timeout處理過(guò)程

不同的關(guān)系型數(shù)據(jù)庫(kù),以及不同的JDBC驅(qū)動(dòng),其statement timeout處理過(guò)程會(huì)有所不同。其中,Oracle和MS SQLServer的處理相類似,MySQL和CUBRID類似。

Oracle JDBC Statement的QueryTimeout處理過(guò)程

1. 通過(guò)調(diào)用Connection的createStatement()方法創(chuàng)建statement

2. 調(diào)用Statement的executeQuery()方法

3. statement通過(guò)自身connection將query發(fā)送給Oracle數(shù)據(jù)庫(kù)

4. statement在OracleTimeoutPollingThread(每個(gè)classloader一個(gè))上進(jìn)行注冊(cè)

5. 達(dá)到超時(shí)時(shí)間

6. OracleTimeoutPollingThread調(diào)用OracleStatement的cancel()方法

7. 通過(guò)connection向正在執(zhí)行的query發(fā)送cancel消息

 

JTDS (MS SQLServer) Statement的QueryTimeout處理過(guò)程

1. 通過(guò)調(diào)用Connection的createStatement()方法創(chuàng)建statement

2. 調(diào)用Statement的executeQuery()方法

3. statement通過(guò)自身connection將query發(fā)送給MS SqlServer數(shù)據(jù)庫(kù)

4. statement在TimerThread上進(jìn)行注冊(cè)

5. 達(dá)到超時(shí)時(shí)間

6. TimerThread調(diào)用JtdsStatement實(shí)例中的TsdCore.cancel()方法

7. 通過(guò)ConnectionJDBC向正在執(zhí)行的query發(fā)送cancel消息

 

MySQL JDBC Statement的QueryTimeout處理過(guò)程

1. 通過(guò)調(diào)用Connection的createStatement()方法創(chuàng)建statement

2. 調(diào)用Statement的executeQuery()方法

3. statement通過(guò)自身connection將query發(fā)送給MySQL數(shù)據(jù)庫(kù)

4. statement創(chuàng)建一個(gè)新的timeout-execution線程用于超時(shí)處理

5. 5.1版本后改為每個(gè)connection分配一個(gè)timeout-execution線程

6. 向timeout-execution線程進(jìn)行注冊(cè)

7. 達(dá)到超時(shí)時(shí)間

6. TimerThread調(diào)用JtdsStatement實(shí)例中的TsdCore.cancel()方法

7. timeout-execution線程創(chuàng)建一個(gè)和statement配置相同的connection

8. 使用新創(chuàng)建的connection向超時(shí)query發(fā)送cancel query(KILL QUERY “connectionId”)

 

CUBRID JDBC Statement的QueryTimeout處理過(guò)程

1. 通過(guò)調(diào)用Connection的createStatement()方法創(chuàng)建statement

2. 調(diào)用Statement的executeQuery()方法

3. statement通過(guò)自身connection將query發(fā)送給CUBRID數(shù)據(jù)庫(kù)

4. statement創(chuàng)建一個(gè)新的timeout-execution線程用于超時(shí)處理

5. 5.1版本后改為每個(gè)connection分配一個(gè)timeout-execution線程

6. 向timeout-execution線程進(jìn)行注冊(cè)

7. 達(dá)到超時(shí)時(shí)間

6. TimerThread調(diào)用JtdsStatement實(shí)例中的TsdCore.cancel()方法

7. timeout-execution線程創(chuàng)建一個(gè)和statement配置相同的connection

8. 使用新創(chuàng)建的connection向超時(shí)query發(fā)送cancel消息

 

什么是JDBC的socket timeout?

第4種類型的JDBC使用socket與數(shù)據(jù)庫(kù)連接,數(shù)據(jù)庫(kù)并不對(duì)應(yīng)用與數(shù)據(jù)庫(kù)間的連接超時(shí)進(jìn)行處理。

JDBC的socket timeout在數(shù)據(jù)庫(kù)被突然停掉或是發(fā)生網(wǎng)絡(luò)錯(cuò)誤(由于設(shè)備故障等原因)時(shí)十分重要。由于TCP/IP的結(jié)構(gòu)原因,socket沒(méi)有辦法探測(cè)到網(wǎng)絡(luò)錯(cuò)誤,因此應(yīng)用也無(wú)法主動(dòng)發(fā)現(xiàn)數(shù)據(jù)庫(kù)連接斷開(kāi)。如果沒(méi)有設(shè)置socket timeout的話,應(yīng)用在數(shù)據(jù)庫(kù)返回結(jié)果前會(huì)無(wú)期限地等下去,這種連接被稱為dead connection。

為了避免dead connections,socket必須要有超時(shí)配置。socket timeout可以通過(guò)JDBC設(shè)置,socket timeout能夠避免應(yīng)用在發(fā)生網(wǎng)絡(luò)錯(cuò)誤時(shí)產(chǎn)生無(wú)休止等待的情況,縮短服務(wù)失效的時(shí)間。

不推薦使用socket timeout來(lái)限制statement的執(zhí)行時(shí)長(zhǎng),因此socket timeout的值必須要高于statement timeout,否則,socket timeout將會(huì)先生效,這樣statement timeout就變得毫無(wú)意義,也無(wú)法生效。

下面展示了socket timeout的兩個(gè)設(shè)置項(xiàng),不同的JDBC驅(qū)動(dòng)其配置方式會(huì)有所不同。

  • socket連接時(shí)的timeout:通過(guò)Socket.connect(SocketAddress endpoint, int timeout)設(shè)置
  • socket讀寫時(shí)的timeout:通過(guò)Socket.setSoTimeout(int timeout)設(shè)置

通過(guò)查看CUBRID,MySQL,MS SQL Server (JTDS)和Oracle的JDBC驅(qū)動(dòng)源碼,我們發(fā)現(xiàn)所有的驅(qū)動(dòng)內(nèi)部都是使用上面的2個(gè)API來(lái)設(shè)置socket timeout的。

下面是不同驅(qū)動(dòng)的socket timeout配置方式。

 

  • connectTimeout和socketTimeout的默認(rèn)值為0時(shí),timeout不生效。
  • 除了調(diào)用DBCP的API以外,還可以通過(guò)properties屬性進(jìn)行配置。

通過(guò)properties屬性進(jìn)行配置時(shí),需要傳入key為“connectionProperties”的鍵值對(duì),value的格式為“[propertyName=property;]*”。下面是iBatis中的properties配置。

Xml代碼

  1. <transactionManager type=“JDBC”>   
  2.  
  3.   <dataSource type=“com.nhncorp.lucy.db.DbcpDSFactory”>   
  4.  
  5.      ….   
  6.  
  7.      <property name=“connectionProperties” value=“oracle.net.CONNECT_TIMEOUT=6000;oracle.jdbc.ReadTimeout=6000″/>    
  8.  
  9.   </dataSource>   
  10.  
  11. </transactionManager>  

操作系統(tǒng)的socket timeout配置

如果不設(shè)置socket timeout或connect timeout,應(yīng)用多數(shù)情況下是無(wú)法發(fā)現(xiàn)網(wǎng)絡(luò)錯(cuò)誤的。因此,當(dāng)網(wǎng)絡(luò)錯(cuò)誤發(fā)生后,在連接重新連接成功或成功接收到數(shù)據(jù)之前,應(yīng)用會(huì)無(wú)限制地等下去。但是,通過(guò)本文開(kāi)篇處的實(shí)際案例我們發(fā)現(xiàn),30分鐘后應(yīng)用的連接問(wèn)題奇跡般的解決了,這是因?yàn)椴僮飨到y(tǒng)同樣能夠?qū)ocket timeout進(jìn)行配置。公司的Linux服務(wù)器將socket timeout設(shè)置為了30分鐘,從而會(huì)在操作系統(tǒng)的層面對(duì)網(wǎng)絡(luò)連接做校驗(yàn),因此即使JDBC的socket timeout設(shè)置為0,由網(wǎng)絡(luò)錯(cuò)誤造成的數(shù)據(jù)庫(kù)連接問(wèn)題的持續(xù)時(shí)間也不會(huì)超過(guò)30分鐘。

通常,應(yīng)用會(huì)在調(diào)用Socket.read()時(shí)由于網(wǎng)絡(luò)問(wèn)題被阻塞住,而很少在調(diào)用Socket.write()時(shí)進(jìn)入waiting狀態(tài),這取決于網(wǎng)絡(luò)構(gòu)成和錯(cuò)誤類型。當(dāng)Socket.write()被調(diào)用時(shí),數(shù)據(jù)被寫入到操作系統(tǒng)內(nèi)核的緩沖區(qū),控制權(quán)立即回到應(yīng)用手上。因此,一旦數(shù)據(jù)被寫入內(nèi)核緩沖區(qū),Socket.write()調(diào)用就必然會(huì)成功。但是,如果系統(tǒng)內(nèi)核緩沖區(qū)由于某種網(wǎng)絡(luò)錯(cuò)誤而滿了的話,Socket.write()也會(huì)進(jìn)入waiting狀態(tài)。這種情況下,操作系統(tǒng)會(huì)嘗試重新發(fā)包,當(dāng)達(dá)到重試的時(shí)間限制時(shí),將產(chǎn)生系統(tǒng)錯(cuò)誤。在我們公司,重新發(fā)包的超時(shí)時(shí)間被設(shè)置為15分鐘。

至此,我已經(jīng)對(duì)JDBC的內(nèi)部操作做了講解,希望能夠讓大家學(xué)會(huì)如何正確的配置超時(shí)時(shí)間,從而減少錯(cuò)誤的發(fā)生。

最后,我將列出一些常見(jiàn)的問(wèn)題。

FAQ

Q1. 我已經(jīng)使用Statement.setQueryTimeout()方法設(shè)置了查詢超時(shí),但在網(wǎng)絡(luò)出錯(cuò)時(shí)并沒(méi)有產(chǎn)生作用。

➔ 查詢超時(shí)僅在socket timeout生效的前提下才有效,它并不能用來(lái)解決外部的網(wǎng)絡(luò)錯(cuò)誤,要解決這種問(wèn)題,必須設(shè)置JDBC的socket timeout。

Q2. transaction timeout,statement timeout和socket timeout和DBCP的配置有什么關(guān)系?

➔ 當(dāng)通過(guò)DBCP獲取數(shù)據(jù)庫(kù)連接時(shí),除了DBCP獲取連接時(shí)的waitTimeout配置以外,其他配置對(duì)JDBC沒(méi)有什么影響。

Q3. 如果設(shè)置了JDBC的socket timeout,那DBCP連接池中處于IDLE狀態(tài)的連接是否也會(huì)在達(dá)到超時(shí)時(shí)間后被關(guān)閉?

➔ 不會(huì)。socket的設(shè)置只會(huì)在產(chǎn)生數(shù)據(jù)讀寫時(shí)生效,而不會(huì)對(duì)DBCP中的IDLE連接產(chǎn)生影響。當(dāng)DBCP中發(fā)生新連接創(chuàng)建,老的IDLE連接被移除,或是連接有效性校驗(yàn)的時(shí)候,socket設(shè)置會(huì)對(duì)其產(chǎn)生一定的影響,但除非發(fā)生網(wǎng)絡(luò)問(wèn)題,否則影響很小。

Q4. socket timeout應(yīng)該設(shè)置為多少?

➔ 就像我在正文中提的那樣,socket timeout必須高于statement timeout,但并沒(méi)有什么推薦值。在發(fā)生網(wǎng)絡(luò)錯(cuò)誤的時(shí)候,socket timeout將會(huì)生效,但是再小心的配置也無(wú)法避免網(wǎng)絡(luò)錯(cuò)誤的發(fā)生,只是在網(wǎng)絡(luò)錯(cuò)誤發(fā)生后縮短服務(wù)失效的時(shí)間(如果網(wǎng)絡(luò)恢復(fù)正常的話)。 

責(zé)任編輯:龐桂玉 來(lái)源: 程序源
相關(guān)推薦

2010-06-01 15:25:27

JavaCLASSPATH

2016-12-08 15:36:59

HashMap數(shù)據(jù)結(jié)構(gòu)hash函數(shù)

2020-07-21 08:26:08

SpringSecurity過(guò)濾器

2014-12-03 13:10:10

openstacknetworkneutron

2012-11-22 10:11:16

LispLisp教程

2013-09-22 14:57:19

AtWood

2023-10-19 11:12:15

Netty代碼

2021-02-17 11:25:33

前端JavaScriptthis

2009-09-25 09:14:35

Hibernate日志

2020-09-23 10:00:26

Redis數(shù)據(jù)庫(kù)命令

2017-01-10 08:48:21

2017-08-15 13:05:58

Serverless架構(gòu)開(kāi)發(fā)運(yùn)維

2024-02-21 21:14:20

編程語(yǔ)言開(kāi)發(fā)Golang

2019-06-25 10:32:19

UDP編程通信

2024-07-18 10:12:04

2022-07-04 08:01:01

鎖優(yōu)化Java虛擬機(jī)

2017-05-03 17:00:16

Android渲染機(jī)制

2018-04-16 11:04:23

HBaseRegion Serv數(shù)據(jù)庫(kù)

2021-08-31 10:32:11

LinuxPage Cache命令

2022-01-14 12:28:18

架構(gòu)OpenFeign遠(yuǎn)程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)