背景
項目中應用服務直接通過jdbc連接impala做數據查詢,其他遇到一個問題,查詢impala時因為沒有設置查詢超時,有些大sql一直占用連接,同時這個sql在impala集群中執行著,也占用了impala集群的資源,這樣擠壓了其他sql的響應。所以這時候設置查詢超時,讓連接斷開,空閑出集群資源能夠很大程度上提升服務的穩定性。
解決過程
連接impala的jdbc主要有cloudera jdbc和hive jdbc。因為需要使用kerberos認證來連接impala,最開始同事為了簡單話選擇了hive jdbc,配置kerberos也方便(官方推薦 Cloudera JDBC Connector)。開始用的很爽,但后面發現并不能設置查詢超時(Statement.setQueryTime()無效,但是如果通過hive jdbc連接查詢hive的話是能夠生效的---HIVE-4924,查詢impala卻不行)。最后想查詢下hive jdbc是否還有其他參數能夠設置,通過幾天的尋找,最終卻無果。
后來把目光放在了cloudera jdbc上,通過文檔中的參數,發現一個SocketTimeout參數,并在本地嘗試了cloudera jdbc配置上SocketTimeout這個參數,在自測過程上都出現了大的查詢沒有執行完,因為SocketTimeout時間到了而斷開并拋出socket timeout exception,當時很開心,以為問題解決了,當把這個拿給同事時,同事試了幾次,有時候會timeout斷開,有時候卻根本不斷開,直到sql執行成功返回,基本宣布這個參數失敗。
再一次失敗后,都快覺得這個問題搞不定了,我覺得不太可能是官方定義了一個不靠譜的參數,更可能是自己理解上面的錯誤,后來又反復查看了這個參數的解釋(The number of seconds after which Impala closes the connection with the client application if the connection is idle),一旦連接空閑超過這個時長,impala就會關閉應該客戶端的連接。什么叫connection is idle,以及它為何叫socketTimeout卻不是查詢超時的英文呢?socket是網絡層,而且在參數定義中,說連接空閑,而不是連接占用的時間。有了這些疑問后,又搜索查詢了一番,最終在一篇文章中,很詳細的解釋了jdbc中的各種timeout。
The higher level timeout is dependent on the lower level timeout. The higher level timeout will operate normally only if the lower level timeout operates normally as well. If the JDBC driver socket timeout does not work properly, then higher level timeouts such as statement timeout and transaction timeout will not work properly either.
Even after the statement timeout was configured, the application still did not recover from the error because the statement timeout did not work at the time of network failure.
什么是JDBC的socket timeout?
第4種類型的JDBC使用socket與數據庫連接,數據庫并不對應用與數據庫間的連接超時進行處理。
JDBC的socket timeout在數據庫被突然停掉或是發生網絡錯誤(由于設備故障等原因)時十分重要。由于TCP/IP的結構原因,socket沒有辦法探測到網絡錯誤,因此應用也無法主動發現數據庫連接斷開。如果沒有設置socket timeout的話,應用在數據庫返回結果前會無期限地等下去,這種連接被稱為dead connection。
為了避免dead connections,socket必須要有超時配置。socket timeout可以通過JDBC設置,socket timeout能夠避免應用在發生網絡錯誤時產生無休止等待的情況,縮短服務失效的時間。
不推薦使用socket timeout來限制statement的執行時長,因此socket timeout的值必須要高于statement timeout,否則,socket timeout將會先生效,這樣statement timeout就變得毫無意義,也無法生效。什么是Statement Timeout?
statement timeout用來限制statement的執行時長,timeout的值通過調用JDBC的java.sql.Statement.setQueryTimeout(int timeout) API進行設置。不過現在開發者已經很少直接在代碼中設置,而多是通過框架來進行設置。
不同的關系型數據庫,以及不同的JDBC驅動,其statement timeout處理過程會有所不同。
通過這篇文章的講解,我知道我之前在hive jdbc配置的Statement.setQueryTime()是statement timeout,它是依賴于更低基本的socket timeout, impala沒有生效的原因很大可能是對于impala的查詢statement并沒有去處理超時(Statement Timeout Execution Process for JDBC Driver),而對hive查詢卻做了處理。
而cloudera jdbc提供了socket timeout參數,也在statement timeout提供了超時的處理,即statement查詢超時后會中斷查詢并拋出java.sql.SQLTimeoutException。所以配置上Statement.setQueryTime(),就能夠在查詢超過timeout值后拋出異常,關閉連接。
最后講queryTimeOut配置入DataSource,與orm結合,查詢超時問題算告一段落。
參考
cloudera jdbc: https://www.cloudera.com/documentation/enterprise/5-11-x/topics/impala_jdbc.html#jdbc_driver_choice
query timeout: https://www.cubrid.org/blog/understanding-jdbc-internals-and-timeout-configuration
帶有kerberos驗證連接impala: http://blog.csdn.net/tlqfreedom/article/details/75220058