上週五碰到開發的請求協助解決數據預約程序中對單頭等幾個表檢索數據時檢索條件尾數是9的數據特別慢。第一時間想到的是否以下幾個問題:java
一、 數據庫相應數據最多;sql
二、 數據表進行了分區,而相應數據落在的分區性能存在問題;數據庫
三、 檢索該批記錄的會話不少;服務器
但很快排除了以上幾種可能,由於後續的調查由以下發現:網絡
一、 查詢了相應數據,尾數0-9的數據幾乎平均分佈;app
二、 直接用SQLSERVER查詢分析器查詢速度很快;ide
到此你們基本都認爲數據庫不存在問題,可是以下的調查感受又讓人陷入了迷茫。在相同網段的其餘服務器上創建了相應的用戶、表、數據進行測試,發現程序取數據速度很快,和生產數據庫的區別就是數據量,還有表結構(爲了測試偷懶所有字段定義爲VARCHAR,後來執行程序異常才無可奈何把部分字段改爲了date)。雖然測試過程不嚴謹,但這又彷佛能把問題的天平向數據庫這邊傾斜。sqlserver
就這樣週末測試摸索的一天很快過去了,家裏的無線網絡6塊錢一小時開銷也實在不小,週一一大早就來到公司,迴歸有限寬帶的懷抱,但鬱悶的生產庫咱們只有查詢幾個字段的權限,我動不了。因而聯絡上游數據單位,盤算着經過看執行日誌等手段把問題範圍進一步縮小,而要對方配合卻要工做聯繫單。想一想仍是先花半天時間有把咱們這邊的問題可能性所有掃除了纔開這個聯繫單。性能
如何進一步縮小差距, 接下來來看看網絡是否有問題,巧的是數據庫服務器IP末尾是9,而偏偏是末尾爲9的數據,爲了排除該問題,進行了一系列的探索:測試
一、 更換測試數據庫服務器,發現取數據速度正常;
二、 在測試程序客戶段增長HOST映射,但取數據速度依舊;
3、在同一網段服務器部署測試程序。
以上三種測試均代表網絡異常的可能性愈來愈小,至少在咱們IDC和分中心內網之間的網絡互聯是正常的。
再鎮定想一想數據庫查詢爲何慢,只有三個層面的緣由,數據庫自己的性能問題、網絡問題、客戶端問題,而當今問題最多的無疑就是客戶端程序異常,既然生產數據庫沒法作有效的調試,那麼咱們得把咱們的問題儘可能掃除。
因而請開發部的侯文傑把相關代碼的時間戳進一步細化,文傑幾分鐘內就提供了代碼,當即上傳測試發如今ps = conn.prepareStatement和ps.setString效率上均沒有問題,而rs = ps.executeQuery();就是罪會禍首。
再來仔細審視數據,一個念頭忽然閃現,既然SQLSERVER客戶端查詢正常,會不會是程序組裝的SQL存在問題。因而當即要求文傑給我一個測試任意SQL語句的接口,經過這個接口,我能夠爲所欲爲的測試cout(*),like等語句的性能(悲劇的是很長時間沒有碰代碼,個人開發環境已經沒法運行)。一個小時後我獲得了這個接口,接下來發現了使人興奮的結果:
我本身組裝的一條完整的SQL語句經過程序裝載後執行迅速,而另一條經過JDBC SETSTRING組裝的一樣的SQL語句,居然仍是出奇的慢。
測試案例一:
[jdbctest] 2011-05-09 16:03:41,437 - 得到jdbc鏈接start...
[jdbctest] 2011-05-09 16:03:42,062 - 得到jdbc鏈接end...
[jdbctest] 2011-05-09 16:03:42,078 - 開始執行自定義Statement動態sql=Select TRYhead0_.TRY_ID As TRY1_0_,TRYhead0_.PRE_TRY_ID As Pre2_122_0_ From Ris.risk.TRY_HEAD TRYhead0_ Where TRYhead0_.TRY_ID = '290120040024700579'-----本身寫了完整的SQL語句傳入JAVA程序
[jdbctest] 2011-05-09 16:03:42,187 - com.microsoft.sqlserver.jdbc.SQLServerResultSetMetaData@7c6768
[jdbctest] 2011-05-09 16:03:42,203 - ColumnName is=TRY1_0_,and value is=290120040024700579
[jdbctest] 2011-05-09 16:03:42,203 - ColumnName is=Pre2_122_0_,and value is=000000000002748431
[jdbctest] 2011-05-09 16:03:42,203 - 結束執行自定義Statement動態sql,用時:125毫秒
[jdbctest] 2011-05-09 16:03:42,203 - 得到jdbc鏈接start...
[jdbctest] 2011-05-09 16:03:42,234 - 得到jdbc鏈接end...
[jdbctest] 2011-05-09 16:03:42,265 - conn.prepareStatement(),DYNAMIC_EXECUTE_PREPARESQL is=Select head0.TRY_ID As TRY1_0,head0.PRE_TRY_ID As Pre2_122_0_ From Ris.risk.TRY_HEAD head0 Where head0.TRY_ID = ?,耗時:62毫秒-----採用JDBC的SETSTRING方法設變量
[jdbctest] 2011-05-09 16:05:15,125 - com.microsoft.sqlserver.jdbc.SQLServerResultSetMetaData@b162d5
[jdbctest] 2011-05-09 16:05:15,125 - ColumnName is=TRY1_0,and value is=290120040014700789
[jdbctest] 2011-05-09 16:05:15,125 - ColumnName is=Pre2_122_0_,and value is=000000000002794232
[jdbctest] 2011-05-09 16:05:15,125 - 結束執行自定義prepare動態sql,用時:92922毫秒
這已經很顯然了,JDBC組裝的SQL語句有問題的嫌疑最大。
再查相關資料,在sqlserver jdbc 驅動的文檔發現裏面有這麼一個參數:
SendStringParametersAsUnicode
SendStringParametersAsUnicode={true false}. Determines
whether string parameters are sent to the SQL Server database in
Unicode or in the default character encoding of the database.
True means that string parameters are sent to SQL Server in
Unicode. False means that they are sent in the default encoding,
which can improve performance because the server does not need
to convert Unicode characters to the default encoding. You
should, however, use default encoding only if the parameter
string data that you specify is consistent with the default
encoding of the database.
The default is true
string型的參數傳到數據庫裏面默認是轉換成unicode的。
立刻請文傑進行相關把SendStringParameters屬性(參考附件二)設置成false(詳見附一),設置後查詢的性能獲得了巨大的提升,原來用數分鐘的查詢如今只須要幾時毫秒了。
執行結果以下:
測試案例二:
[jdbctest] 2011-05-09 15:42:59,812 - 開始執行自定義Statement動態sql=Select TRYhead0_.TRY_ID As TRY1_0_,TRYhead0_.PRE_TRY_ID As Pre2_122_0_ From Ris.risk.TRY_HEAD TRYhead0_ Where TRYhead0_.TRY_ID = '290120040024700579'
[jdbctest] 2011-05-09 15:42:59,890 - com.microsoft.sqlserver.jdbc.SQLServerResultSetMetaData@7c6768[jdbctest] 2011-05-09 15:42:59,906 - ColumnName is=TRY1_0_,and value is=290120040024700579
[jdbctest] 2011-05-09 15:42:59,906 - ColumnName is=Pre2_122_0_,and value is=000000000002748431
[jdbctest] 2011-05-09 15:42:59,921 - 結束執行自定義Statement動態sql,用時:109毫秒
[jdbctest] 2011-05-09 15:42:59,921 - 得到jdbc鏈接start...
[jdbctest] 2011-05-09 15:42:59,953 - 得到jdbc鏈接end...
[jdbctest] 2011-05-09 15:42:59,968 - conn.prepareStatement(),DYNAMIC_EXECUTE_PREPARESQL is=Select head0.TRY_ID As TRY1_0,head0.PRE_TRY_ID As Pre2_122_0_ From Ris.risk.TRY_HEAD head0 Where head0.TRY_ID = ?,耗時:47毫秒
[jdbctest] 2011-05-09 15:42:59,984 - com.microsoft.sqlserver.jdbc.SQLServerResultSetMetaData@b162d5
[jdbctest] 2011-05-09 15:42:59,984 - ColumnName is=TRY1_0,and value is=290120040014700789
[jdbctest] 2011-05-09 15:43:00,000 - ColumnName is=Pre2_122_0_,and value is=000000000002794232
[jdbctest] 2011-05-09 15:43:00,000 - 結束執行自定義prepare動態sql,用時:79毫秒-------------對比測試案例一能夠發現速度明顯提升
到此JDBC查詢已經能夠解決問題了,可是咱們的程序都是部署在鏈接池(POOL下的)
查看了下官方文檔,咱們發現有以下內容:
能夠經過多種方式指定鏈接字符串的屬性:
·
當使用 DriverManager 類進行鏈接時,在鏈接 URL 中經過「名稱=值」屬性進行指定。
·
在 DriverManager 類中 Connect 方法的 Properties 參數中經過「名稱=值」屬性進行指定。
·
在驅動程序數據源的適當的 setter 方法中指定值。
咱們能夠很偷懶的改以下URL解決上述問題(增長;sendStringParametersAsUnicode=false),這樣程序不須要作任何更改,但我的認爲測試是否有亂碼仍是必須的:
zjport.datasource.risk.url=jdbc:sqlserver://192.168.1.9:1433;DatabaseName=Ris;sendStringParametersAsUnicode=false
至此問題已經解決了,但最終是什麼緣由只有尾數9的數據有問題,最大的嫌疑對象是JDBC驅動存在某個未知的BUG,咱們仍然須要作後續的探討!
附件一:修改JDBC代碼的方式(文傑提供)
SendStringParameters設置前:
Class.forName(DRIVER_CLASS_NAME);
conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
SendStringParameters設置後:
Class.forName(DRIVER_CLASS_NAME);
Properties info = new Properties();
info.put("user", USERNAME);
info.put("password", PASSWORD);
info.put("sendStringParametersAsUnicode", "false");
conn = DriverManager.getConnection(URL, info);
附件二:參考文檔
http://msdn.microsoft.com/zh-cn/library/ms378988(v=sql.90).aspx
http://school.cnd8.com/java/jiaocheng/11759.htm
http://technet.microsoft.com/zh-cn/library/aa342325(SQL.100).aspx