【轉】深入理解JDBC的超時設置

原文:http://www.cubrid.org/blog/dev-platform/understanding-jdbc-internals-and-timeout-configuration/

恰當?shù)腏DBC超時設置能夠有效地減少服務失效的時間嘹屯。本文將對數(shù)據庫的各種超時設置及其設置方法做介紹械哟。

真實案例:應用服務器在遭到DDos攻擊后無法響應

在遭到DDos攻擊后,整個服務都垮掉了怜森。由于第四層交換機不堪重負速挑,網絡變得無法連接,從而導致業(yè)務系統(tǒng)也無法正常運轉副硅。安全組很快屏蔽了所有的DDos攻擊姥宝,并恢復了網絡,但業(yè)務系統(tǒng)卻還是無法工作恐疲。 通過分析系統(tǒng)的thread dump發(fā)現(xiàn)腊满,業(yè)務系統(tǒng)停在了JDBC API的調用上套么。20分鐘后,系統(tǒng)仍處于WAITING狀態(tài)碳蛋,無法響應胚泌。30分鐘后,系統(tǒng)拋出異常疮蹦,服務恢復正常诸迟。

為什么我們明明將query timeout設置成了3秒,系統(tǒng)卻持續(xù)了30分鐘的WAITING狀態(tài)愕乎?為什么30分鐘后系統(tǒng)又恢復正常了阵苇?

當你對理解了JDBC的超時設置后,就能找到問題的答案感论。

為什么我們要了解JDBC

當遇到性能問題或系統(tǒng)出錯時绅项,業(yè)務系統(tǒng)和數(shù)據庫通常是我們最關心的兩個部分。在公司里比肄,這兩個部分是交由兩個不同的部門來負責的快耿,因此各個部門都會集中精力地在自身領域內尋找問題,這樣的話芳绩,在業(yè)務系統(tǒng)和數(shù)據庫之間的部分就會成為一個盲區(qū)掀亥。對于Java應用而言,這個盲區(qū)就是DBCP數(shù)據庫連接池和JDBC妥色,本文將集中介紹JDBC搪花。

什么是JDBC

JDBC是Java應用中用來連接關系型數(shù)據庫的標準API。Sun公司一共定義了4種類型的JDBC嘹害,我們主要使用的是第4種撮竿,該類型的Driver完全由Java代碼實現(xiàn),通過使用socket與數(shù)據庫進行通信笔呀。

圖1 JDBC Type 4.

第4種類型的JDBC通過socket對字節(jié)流進行處理幢踏,因此也會有一些基本網絡操作,類似于HttpClient這種用于網絡操作的代碼庫许师。當在網絡操作中遇到問題的時候房蝉,將會消耗大量的cpu資源,并且失去響應超時枯跑。如果你之前用過HttpClient惨驶,那么你一定遇到過未設置timeout造成的錯誤。同樣敛助,第4種類型的JDBC,若沒有合理地設置socket timeout屋确,也會有相同的錯誤——連接被阻塞纳击。

接下來续扔,就讓我們來學習一下如何正確地設置socket timeout,以及需要考慮的問題焕数。

**應用與數(shù)據庫間的timeout層級 **


圖2 Timeout Class.

上圖展示了簡化后應用與數(shù)據庫間的timeout層級纱昧。(譯者注:WAS/BLOC是作者公司的具體應用名稱,無需深究) 高級別的timeout依賴于低級別的timeout堡赔,只有當?shù)图墑e的timeout無誤時识脆,高級別的timeout才能確保正常。例如善已,當socket timeout出現(xiàn)問題時灼捂,高級別的statement timeout和transaction timeout都將失效。

我們收到的很多評論中提到:

即使設置了statement timeout换团,當網絡出錯時悉稠,應用也無法從錯誤中恢復。

statement timeout無法處理網絡連接失敗時的超時艘包,它能做的僅僅是限制statement的操作時間的猛。網絡連接失敗時的timeout必須交由JDBC來處理。

JDBC的socket timeout會受到操作系統(tǒng)socket timeout設置的影響想虎,這就解釋了為什么在之前的案例中卦尊,JDBC連接會在網絡出錯后阻塞30分鐘,然后又奇跡般恢復舌厨,即使我們并沒有對JDBC的socket timeout進行設置岂却。

DBCP連接池位于圖2的左側,你會發(fā)現(xiàn)timeout層級與DBCP是相互獨立的邓线。DBCP負責的是數(shù)據庫連接的創(chuàng)建和管理淌友,并不干涉timeout的處理。當連接在DBCP中創(chuàng)建骇陈,或是DBCP發(fā)送校驗query檢查連接有效性的時候震庭,socket timeout將會影響這些過程,但并不直接對應用造成影響你雌。

當在應用中調用DBCP的getConnection()方法時器联,你可以設置獲取數(shù)據庫連接的超時時間,但是這和JDBC的timeout毫不相關婿崭。

圖3 Timeout for Each Levels.

什么是Transaction Timeout

transaction timeout一般存在于框架(Spring, EJB)或應用級拨拓。

transaction timeout或許是個相對陌生的概念,簡單地說氓栈,transaction timeout就是“Statement Timeout * N(需要執(zhí)行的statement數(shù)量) + @(垃圾回收等其他時間)”渣磷。transaction timeout用來限制執(zhí)行statement的總時長。

例如授瘦,假設執(zhí)行一個statement需要0.1秒醋界,那么執(zhí)行少量statement不會有什么問題竟宋,但若是要執(zhí)行100,000個statement則需要10,000秒(約7個小時)。這時形纺,transaction timeout就派上用場了丘侠。EJB CMT (Container Managed Transaction)就是一種典型的實現(xiàn),它提供了多種方法供開發(fā)者選擇逐样。但我們并不使用EJB蜗字,Spring的transaction timeout設置會更常用一些。在Spring中脂新,你可以使用下面展示的XML或是在源碼中使用@Transactional注解來進行設置挪捕。

<tx:attributes>  
        <tx:method name=“…” timeout=“3″/>  
</tx:attributes>  

Spring提供的transaction timeout配置非常簡單,它會記錄每個事務的開始時間和消耗時間戏羽,當特定的事件發(fā)生時就會對消耗時間做校驗担神,當超出timeout值時將拋出異常。

Spring中始花,數(shù)據庫連接被保存在ThreadLocal里妄讯,這被稱為事務同步(Transaction Synchronization),與此同時酷宵,事務的開始時間和消耗時間也被保存下來亥贸。當使用這種代理連接創(chuàng)建statement時,就會校驗事務的消耗時間浇垦。

EJB CMT的實現(xiàn)方式與之類似炕置,其結構本身也十分簡單。 當你選用的容器或框架并不支持transaction timeout這一特性男韧,你可以考慮自己來實現(xiàn)朴摊。transaction timeout并沒有標準的API。Lucy框架的1.5和1.6版本都不支持transaction timeout此虑,但是你可以通過使用Spring的Transaction Manager來達到與之同樣的效果甚纲。 假設某個事務中包含5個statement,每個statement的執(zhí)行時間是200ms朦前,其他業(yè)務邏輯的執(zhí)行時間是100ms介杆,那么transaction timeout至少應該設置為1,100ms(200 * 5 + 100)。

什么是Statement Timeout韭寸?

statement timeout用來限制statement的執(zhí)行時長春哨,timeout的值通過調用JDBC的java.sql.Statement.setQueryTimeout(int timeout) API進行設置。不過現(xiàn)在開發(fā)者已經很少直接在代碼中設置恩伺,而多是通過框架來進行設置赴背。 以iBatis為例,statement timeout的默認值可以通過sql-map-config.xml中的defaultStatementTimeout 屬性進行設置。同時癞尚,你還可以設置sqlmap中select耸三,insert乱陡,update標簽的timeout屬性浇揩,從而對不同sql語句的超時時間進行獨立的配置。 如果你使用的是Lucy1.5或1.6版本憨颠,通過設置queryTimeout屬性可以在datasource層面對statement timeout進行設置胳徽。 statement timeout的具體值需要依據應用本身的特性而定,并沒有可供推薦的配置爽彤。

JDBC的statement timeout處理過程
不同的關系型數(shù)據庫养盗,以及不同的JDBC驅動,其statement timeout處理過程會有所不同适篙。其中往核,Oracle和MS SQLServer的處理相類似,MySQL和CUBRID類似嚷节。

Oracle JDBC Statement的QueryTimeout處理過程

  1. 通過調用Connection的createStatement()方法創(chuàng)建statement
  1. 調用Statement的executeQuery()方法
  2. statement通過自身connection將query發(fā)送給Oracle數(shù)據庫
  3. statement在OracleTimeoutPollingThread(每個classloader一個)上進行注冊
  4. 達到超時時間
  5. OracleTimeoutPollingThread調用OracleStatement的cancel()方法
  6. 通過connection向正在執(zhí)行的query發(fā)送cancel消息
圖4 Query Timeout Execution Process for Oracle JDBC Statement.

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

  1. 通過調用Connection的createStatement()方法創(chuàng)建statement
  1. 調用Statement的executeQuery()方法
  2. statement通過自身connection將query發(fā)送給MS SqlServer數(shù)據庫
  3. statement在TimerThread上進行注冊
  4. 達到超時時間
  5. TimerThread調用JtdsStatement實例中的TsdCore.cancel()方法
  6. 通過ConnectionJDBC向正在執(zhí)行的query發(fā)送cancel消息
圖5 QueryTimeout Execution Process for JTDS (MS SQLServer) Statement.

MySQL JDBC Statement的QueryTimeout處理過程(5.0.8)

  1. 通過調用Connection.createStatement()方法創(chuàng)建statement
  1. 調用Statement.executeQuery()方法
  2. statement通過自身connection將query發(fā)送給MySQL數(shù)據庫
  3. statement創(chuàng)建一個新的timeout-execution線程用于超時處理
  4. 5.1版本后改為每個connection分配一個timeout-execution線程
  5. 向timeout-execution線程進行注冊
  6. 達到超時時間
  7. timeout-execution線程創(chuàng)建一個和statement配置相同的connection
  8. 使用新創(chuàng)建的connection向超時query發(fā)送cancel query(KILL QUERY “connectionId”)
圖6 QueryTimeout Execution Process for MySQL JDBC Statement (5.0.8).

CUBRID JDBC Statement的QueryTimeout處理過程

  1. 通過調用Connection的createStatement()方法創(chuàng)建statement
  1. 調用Statement的executeQuery()方法
  2. statement通過自身connection將query發(fā)送給CUBRID數(shù)據庫
  3. statement創(chuàng)建一個新的timeout-execution線程用于超時處理
  4. 5.1版本后改為每個connection分配一個timeout-execution線程 6. 向timeout-execution線程進行注冊
  5. 達到超時時間
  6. TimerThread調用JtdsStatement實例中的TsdCore.cancel()方法
  7. timeout-execution線程創(chuàng)建一個和statement配置相同的connection
  8. 使用新創(chuàng)建的connection向超時query發(fā)送cancel消息
圖7 QueryTimeout Execution Process for CUBRID JDBC Statement.

什么是JDBC的socket timeout聂儒?

第4種類型的JDBC使用socket與數(shù)據庫連接,數(shù)據庫并不對應用與數(shù)據庫間的連接超時進行處理硫痰。 JDBC的socket timeout在數(shù)據庫被突然停掉或是發(fā)生網絡錯誤(由于設備故障等原因)時十分重要衩婚。由于TCP/IP的結構原因,socket沒有辦法探測到網絡錯誤效斑,因此應用也無法主動發(fā)現(xiàn)數(shù)據庫連接斷開非春。如果沒有設置socket timeout的話,應用在數(shù)據庫返回結果前會無期限地等下去缓屠,這種連接被稱為dead connection奇昙。 為了避免dead connections,socket必須要有超時配置敌完。socket timeout可以通過JDBC設置储耐,socket timeout能夠避免應用在發(fā)生網絡錯誤時產生無休止等待的情況,縮短服務失效的時間蠢挡。

不推薦使用socket timeout來限制statement的執(zhí)行時長弧岳,因此socket timeout的值必須要高于statement timeout,否則业踏,socket timeout將會先生效禽炬,這樣statement timeout就變得毫無意義,也無法生效勤家。

下面展示了socket timeout的兩個設置項腹尖,不同的JDBC驅動其配置方式會有所不同。

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

通過查看CUBRID伐脖,MySQL热幔,MS SQL Server (JTDS)和Oracle的JDBC驅動源碼乐设,我們發(fā)現(xiàn)所有的驅動內部都是使用上面的2個API來設置socket timeout的。

下面是不同驅動的socket timeout配置方式绎巨。

JDBC Driver connectTimeout配置項 socketTimeout配置項 url格式 示例
MySQL Driver connectTimeout(默認值:0近尚,單位:ms) socketTimeout(默認值:0,單位:ms) jdbc:mysql://[host:port],[host:port]…/[database][?propertyName1][=propertyValue1][&propertyName2][=propertyValue2]… jdbc:mysql://xxx.xx.xxx.xxx:3306/database?connectTimeout=60000&socketTimeout=60000
MS-SQL DriverjTDS Driver loginTimeout(默認值:0场勤,單位:s) socketTimeout(默認值:0戈锻,單位:s) jdbc:jtds:<server_type>://<server>[:<port>][/<database>][;<property>=<value>[;...]] jdbc:jtds:sqlserver://server:port/database;loginTimeout=60;socketTimeout=60
Oracle Thin Driver oracle.net.CONNECT_TIMEOUT (默認值:0,單位:ms) oracle.jdbc.ReadTimeout(默認值:0和媳,單位:ms) 不支持通過url配置格遭,只能通過OracleDatasource.setConnectionProperties() API設置,使用DBCP時可以調用BasicDatasource.setConnectionProperties()或BasicDatasource.addConnectionProperties()進行設置 -
CUBRID Thin Driver 無獨立配置項(默認值:5,000留瞳,單位:ms) 無獨立配置項(默認值:5,000拒迅,單位:ms) - -
  • connectTimeoutsocketTimeout的默認值為0時,timeout不生效她倘。
  • 除了調用DBCP的API以外璧微,還可以通過properties屬性進行配置。

通過properties屬性進行配置時帝牡,需要傳入key為“connectionProperties”的鍵值對往毡,value的格式為“[propertyName=property;]*”。下面是iBatis中的properties配置靶溜。
Xml代碼

<transactionManager type=“JDBC”>  
  <dataSource type=“com.nhncorp.lucy.db.DbcpDSFactory”>  
     ….  
     <property name=“connectionProperties” value=“oracle.net.CONNECT_TIMEOUT=6000;oracle.jdbc.ReadTimeout=6000″/>   
  </dataSource>  
</transactionManager>  

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

如果不設置socket timeout或connect timeout开瞭,應用多數(shù)情況下是無法發(fā)現(xiàn)網絡錯誤的。因此罩息,當網絡錯誤發(fā)生后嗤详,在連接重新連接成功或成功接收到數(shù)據之前,應用會無限制地等下去瓷炮。但是葱色,通過本文開篇處的實際案例我們發(fā)現(xiàn),30分鐘后應用的連接問題奇跡般的解決了娘香,這是因為操作系統(tǒng)同樣能夠對socket timeout進行配置苍狰。公司的Linux服務器將socket timeout設置為了30分鐘,從而會在操作系統(tǒng)的層面對網絡連接做校驗烘绽,因此即使JDBC的socket timeout設置為0淋昭,由網絡錯誤造成的數(shù)據庫連接問題的持續(xù)時間也不會超過30分鐘。

通常安接,應用會在調用Socket.read()時由于網絡問題被阻塞住翔忽,而很少在調用Socket.write()時進入waiting狀態(tài),這取決于網絡構成和錯誤類型。當Socket.write()被調用時歇式,數(shù)據被寫入到操作系統(tǒng)內核的緩沖區(qū)驶悟,控制權立即回到應用手上。因此材失,一旦數(shù)據被寫入內核緩沖區(qū)痕鳍,Socket.write()調用就必然會成功。但是豺憔,如果系統(tǒng)內核緩沖區(qū)由于某種網絡錯誤而滿了的話额获,Socket.write()也會進入waiting狀態(tài)。這種情況下恭应,操作系統(tǒng)會嘗試重新發(fā)包,當達到重試的時間限制時耘眨,將產生系統(tǒng)錯誤昼榛。在我們公司,重新發(fā)包的超時時間被設置為15分鐘剔难。

至此胆屿,我已經對JDBC的內部操作做了講解,希望能夠讓大家學會如何正確的配置超時時間偶宫,從而減少錯誤的發(fā)生非迹。
最后,我將列出一些常見的問題纯趋。

FAQ

Q1. **我已經使用Statement.setQueryTimeout()方法設置了查詢超時憎兽,但在網絡出錯時并沒有產生作用。 **
? 查詢超時僅在socket timeout生效的前提下才有效吵冒,它并不能用來解決外部的網絡錯誤纯命,要解決這種問題,必須設置JDBC的socket timeout痹栖。

Q2. ** transaction timeout亿汞,statement timeout和socket timeout和DBCP的配置有什么關系? **
? 當通過DBCP獲取數(shù)據庫連接時揪阿,除了DBCP獲取連接時的waitTimeout配置以外疗我,其他配置對JDBC沒有什么影響。

Q3. 如果設置了JDBC的socket timeout南捂,那DBCP連接池中處于IDLE狀態(tài)的連接是否也會在達到超時時間后被關閉吴裤?
? 不會。socket的設置只會在產生數(shù)據讀寫時生效黑毅,而不會對DBCP中的IDLE連接產生影響嚼摩。當DBCP中發(fā)生新連接創(chuàng)建,老的IDLE連接被移除,或是連接有效性校驗的時候枕面,socket設置會對其產生一定的影響愿卒,但除非發(fā)生網絡問題,否則影響很小潮秘。

Q4. **socket timeout應該設置為多少琼开? **
? 就像我在正文中提的那樣,socket timeout必須高于statement timeout枕荞,但并沒有什么推薦值柜候。在發(fā)生網絡錯誤的時候,socket timeout將會生效躏精,但是再小心的配置也無法避免網絡錯誤的發(fā)生渣刷,只是在網絡錯誤發(fā)生后縮短服務失效的時間(如果網絡恢復正常的話)。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末矗烛,一起剝皮案震驚了整個濱河市辅柴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌瞭吃,老刑警劉巖碌嘀,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異歪架,居然都是意外死亡股冗,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進店門和蚪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來止状,“玉大人,你說我怎么就攤上這事惠呼〉挤” “怎么了甘磨?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵强法,是天一觀的道長添坊。 經常有香客問我绍昂,道長荆陆,這世上最難降的妖魔是什么崇摄? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任饼问,我火速辦了婚禮稚照,結果婚禮上矫付,老公的妹妹穿的比我還像新娘凯沪。我一直安慰自己,他們只是感情好买优,可當我...
    茶點故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布妨马。 她就那樣靜靜地躺著挺举,像睡著了一般。 火紅的嫁衣襯著肌膚如雪烘跺。 梳的紋絲不亂的頭發(fā)上湘纵,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天,我揣著相機與錄音滤淳,去河邊找鬼梧喷。 笑死,一個胖子當著我的面吹牛脖咐,可吹牛的內容都是我干的铺敌。 我是一名探鬼主播,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼屁擅,長吁一口氣:“原來是場噩夢啊……” “哼偿凭!你這毒婦竟也來了?” 一聲冷哼從身側響起煤蹭,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤笔喉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后硝皂,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡作谭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年稽物,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片折欠。...
    茶點故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡贝或,死狀恐怖,靈堂內的尸體忽然破棺而出锐秦,到底是詐尸還是另有隱情咪奖,我是刑警寧澤,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布酱床,位于F島的核電站羊赵,受9級特大地震影響,放射性物質發(fā)生泄漏扇谣。R本人自食惡果不足惜昧捷,卻給世界環(huán)境...
    茶點故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望罐寨。 院中可真熱鬧靡挥,春花似錦、人聲如沸鸯绿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至毒返,卻和暖如春租幕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背饿悬。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工令蛉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人狡恬。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓珠叔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親弟劲。 傳聞我的和親對象是個殘疾皇子祷安,可洞房花燭夜當晚...
    茶點故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內容

  • 這是最近讀到的講關于 JDBC 的超時問題最透徹的文章,原文是http://www.cubrid.org/blog...
    預流閱讀 11,514評論 4 19
  • 背景 項目中應用服務直接通過jdbc連接impala做數(shù)據查詢兔乞,其他遇到一個問題汇鞭,查詢impala時因為沒有設置查...
    胖imp閱讀 9,602評論 0 4
  • 數(shù)據庫連接池在J2EE領域是一個不可缺失的組件霍骄,盡管DRUID越來越流行,但是DBCP作為一個老牌的數(shù)據庫連接池一...
    新棟BOOK閱讀 1,710評論 0 7
  • JDBC概述 在Java中淡溯,數(shù)據庫存取技術可分為如下幾類:JDBC直接訪問數(shù)據庫读整、JDO技術、第三方O/R工具咱娶,如...
    usopp閱讀 3,533評論 3 75
  • 最近備戰(zhàn)京東雙11米间,在配置MySQL的超時配置發(fā)現(xiàn)有很多地方可以設置。這么多超時的配置有什么影響膘侮,以及配置會有什么...
    LinkedKeeper閱讀 19,921評論 0 0