相信使用MySQL的同學都配置過它的JDBC驅動,多數人會直接從哪里貼一段URL過來,然后稍作修改就上去了,對應的連接池配置也是一樣的,很少有人會去細想這每一個參數都是什么含義。今天我們就來聊兩個比較常見的配置——是否要開啟autoReconnect和是否緩存PreparedStatement。
一、autoReconnect=true真的好用么?
筆者看到過很多MySQL的URL里都是這樣寫的,復制過來改改IP、端口和庫名就能用了:
jdbc:mysql://xxx.xxx.xxx.xxx:3306/xxx?autoReconnect=true&...從字面上看挺好的,在連接斷開后還會自動重連,加之MySQL有8小時自動斷開連接的特性,在斷開后連接會重連,多好的功能呀。但是如果你去閱讀一下MySQL Connect/J開發手冊的相關章節,就會看到官方是這么說明的:
The use of this feature is not recommended, because it has side effects related to session state and data consistency when applications don't handle SQLExceptions properly, and is only designed to be used when you are unable to configure your application to handle SQLExceptions resulting from dead and stale connections properly.
簡單來說,不推薦開啟這個特性,因為有副作用,在沒有正確處理SQLException時容易造成會話狀態和數據一致性的問題。
一般的應用都會使用數據庫連接池,那我們的連接池是否正確地處理了拋出的SQLException呢?抱著這個疑問,我們來看看阿里的Druid連接池是怎么處理的。
首先,通過設置合理的健康檢查及連接存活時間能解決大部分問題;其次,它有針對特定異常的處理邏輯,在MySqlExceptionSorter中會對特定返回碼、異常類(比如com.mysql.jdbc.CommunicationsException和com.mysql.jdbc.exceptions.jdbc4.CommunicationsException)以及錯誤消息進行處理,如果是致命錯誤就把連接拋棄。也就是說,如果用了Druid,不管是否設置了autoReconnect,都能保證后續請求的正確處理。JBoss的連接池實現也有類似的特性。
二、MySQL是否真的不用打開PSCache?
一般在設置連接池時,都會有類似下面的設置:
很多文章上都說PSCache對使用游標的數據庫有巨大的性能提升,但MySQL不建議開啟,因為它不支持游標。所以很多人在用MySQL時,都會將poolPreparedStatements設置為false,就連Druid的文檔上也是這么寫的。
但事實真的是這樣么,MySQL使用PSCache真的對性能沒有提升么?
先來看看關于游標的問題,其實大部分文章的表述不太準確,現在的MySQL在存儲過程里是支持游標的,但其他地方的確不支持,具體詳見官方手冊(MySQL supports cursors inside stored programs.)。但這并不是我們要討論的關鍵。
3.1.0版本后的JDBC驅動里有一個參數是useServerPrepStmts,如果服務器支持的話,會開啟服務端PreparedStatement,默認是false。官方手冊中有如下說明:
Server-side Prepared Statements - Connector/J 3.1 will automatically detect and use server-side prepared statements when they are available (MySQL server version 4.1.0 and newer).
也就是說在MySQL 4.1.0版本后,3.1.0以上的驅動會檢測到支持服務端PreparedStatement,并且啟用該特性。根據MySQLTUTORIAL上的說明,整個過程分為PREPARE、EXECUTE和DEALLOCATE PREPARE三步。MySQL JDBC驅動的Contributor Jess Balint在StackOverflow上做了一個詳細的說明,《High-Performance Java Persistence》的作者也專門撰寫文章分析了兩者的區別。
ps=conn.prepareStatement("select ?")ps.setInt(1, 42)ps.executeQuery()ps.setInt(1, 43)ps.executeQuery()上述代碼在使用客戶端PreparedStatement時,MySQL日志里看到的是:
255 Query select 42255 Query select 43如果用的是服務端PreparedStatement,看到的則是(實際每次執行只會傳占位符的值,語句是不傳的):
254 Prepare select ?254 Execute select 42254 Execute select 43在整個使用過程中,Prepare只會做一次,在這時服務端會對語句進行解析,后續收到具體值時會優化執行計劃。如果同一條語句每次都新建PreparedStatement,那么每次都會多一回網絡交互和語句解析,這顯然是可以優化的。
綜上所述,現在在使用MySQL時(如果版本比較新的話),出于性能考慮,應該在數據庫連接池上開啟針對PreparedStatement的緩存。如果沒有使用連接池,或者所用的連接池不支持PSCache,也可以在JDBC連接上設置cachePrepStmts=true。
事實上,MySQL的JDBC驅動還有不少針對性能的優化,比如設置useConfigs=maxPerformance(請酌情使用),相當于同時做了如下設置:
cachePrepStmts=truecacheCallableStmts=truecacheServerConfiguration=trueuseLocalSessionState=trueelideSetAutoCommits=truealwaysSendSetIsolation=falseenableQueryTimeouts=false各位同學,是時候檢視一下自己的系統是如何連接MySQL的了,時代在發展,有些以前適用的配置也許就不再合適了。