MySql的JDBC驅動不支持批量操作(已結)
MySql連接的url中要加rewriteBatchedStatements參數,例如
String?connectionUrl="jdbc:mysql://192.168.1.100:3306/test?rewriteBatchedStatements=true";
還要保證mysql JDBC驅的版本。MySql的JDBC驅動的批量插入操作性能是很優秀的。
MySql的JDBC驅動,不是真正支持批量操作的,就算你在代碼中調用了批量操作的方法,MySql的JDBC驅動也是按照一般操作來處理的。
直覺告訴我,應該是一些簡單的設置問題,事實上最后得到的結果也是如此的。
帶著解決這個疑惑的想法,依據大家之前得到的一些結果,信息,開始測試
工具:
eclipse-3.6, mysql-5.1.48, mysql-jdbc-driver 5.1.11, mysql workbench前面說過了,我直覺認為代碼不會有問題,所以先著手改善mysql 的服務器配置,innodb的設置。改了幾個參數,都沒有什么效果。加大了日志緩存,只是提高到7000多毫秒。最后甚至很多歪門邪道的設置都大膽用了,一度讓mysql 無法啟動。。。最終都收效甚微,這個步驟大概試了將近一個小時。
這條路看來是走不通了。。得尋找別的方法
冷靜下來想想,其實從代碼中應該是可以發現些端倪
樓主的非batch代碼中,每次調用 execute() 其實是會通過網絡發送一條語句到服務器端的,是不會在客戶端排隊攢著的。
因為這個方法必須返回一個結果。它必然跟服務器發生了一次交互。
而在batch處理的代碼中,其addBatch 就是無返回值,它提供了一個可能就是在客戶端將語句緩存排隊攢著,最后executeBatch時才發送到服務器端。
用代碼可以證明,在batch處理方法的代碼中,在 executeBatch, 及 commit 方法執行前,分別安插兩條打印時間語句:
Java代碼
System.out.println("before?executeBatch.?"+?(System.currentTimeMillis()-a)+"?ms");
prest.executeBatch();
System.out.println("before?commit.?"+?(System.currentTimeMillis()-a)+"?ms");
conn.commit();
System.out.println("before executeBatch. "+ (System.currentTimeMillis()-a)+" ms"); prest.executeBatch(); System.out.println("before commit. "+ (System.currentTimeMillis()-a)+" ms"); conn.commit();
在我機器上的結果是,
Java代碼
before?executeBatch.279ms
before?commit.7922ms
MySql批量插入10萬條記錄用時7923ms
before executeBatch. 279 ms before commit. 7922 ms MySql批量插入10萬條記錄用時7923 ms
說明客戶端在攢語句時,相當的快,279毫秒就完成了,但在 executeBatch 這個方法的調用過程中,花費了 7920? 減 去 279 的毫秒數。大部分都耗在這里了。 最后提交事務非常快,1毫秒而已
想想看,前邊說過,非batch和batch的處理幾乎是一樣的時間。
可不可以先假設 batch 的方式與非batch一樣,每一條insrt語句事實上均是單獨發往服務器的呢?
瀏覽下源代碼吧。
好幾位兄弟都描述了源代碼,直接從那幾個類入手吧,事實上關鍵的類是這個 com.mysql.jdbc.PreparedStatement
先看了其中的 addBatch 方法,沒有任何問題,只是將語句添加進入一個 List 中保存。
那么 executeBatch 呢?
再貼一下吧, 關鍵看其中的這部分,順帶說一下, 這個mysql-jdbcdriver的源代碼是 5.1.13的
Java代碼
try{
clearWarnings();
if(!this.batchHasPlainStatements
&&this.connection.getRewriteBatchedStatements())?{
if(canRewriteAsMultiValueInsertAtSqlLevel())?{
returnexecuteBatchedInserts(batchTimeout);//執行路徑之一
}
if(this.connection.versionMeetsMinimum(4,1,0)
&&?!this.batchHasPlainStatements
&&this.batchedArgs?!=null
&&this.batchedArgs.size()?>3/*?cost?of?option?setting?rt-wise?*/)?{
returnexecutePreparedBatchAsMultiStatement(batchTimeout);//執行路徑之二
}
}
returnexecuteBatchSerially(batchTimeout);//執行路徑之三
}finally{
clearBatch();
}
try { clearWarnings(); if (!this.batchHasPlainStatements && this.connection.getRewriteBatchedStatements()) { if (canRewriteAsMultiValueInsertAtSqlLevel()) { return executeBatchedInserts(batchTimeout); //執行路徑之一 } if (this.connection.versionMeetsMinimum(4, 1, 0) && !this.batchHasPlainStatements && this.batchedArgs != null && this.batchedArgs.size() > 3 /* cost of option setting rt-wise */) { return executePreparedBatchAsMultiStatement(batchTimeout); //執行路徑之二 } } return executeBatchSerially(batchTimeout); //執行路徑之三 } finally { clearBatch(); }
其實最終,executeBatch 的執行路徑有三種可能。代碼中我已標出來
不小心按了提交了,繼續編輯此回復吧。代碼不算太復雜,但是有一個參數能幫助我們更快的確定mysql的batch工作機制,那就是
mysql jdbc driver 的connection url, 其中有一個參數是: rewriteBatchedStatements
完整的參數參考看這里:http://ftp.ntu.edu.tw/ftp/pub/MySQL/doc/refman/5.1/en/connector-j-reference-configuration-properties.html
rewriteBatchedStatements 參數默認為false, 需要手工設置為true,設置方式大概像這樣:
Java代碼
String?connectionUrl="jdbc:mysql://192.168.1.100:3306/test?rewriteBatchedStatements=true";
String connectionUrl="jdbc:mysql://192.168.1.100:3306/test?rewriteBatchedStatements=true";
默認時候,rewriteBatchedStatements=false時,執行路徑會跳到 executeBatchSerially,此方法內部將語句一條條發送,與非batch處理簡直一樣,所以慢,就在這里了。
當設為 true時,會執行executeBatchedInserts方法,事實上mysql支持這樣的插入語句
Sql代碼
insertintot_user(id,uname)values(1,'1'),?(2,'2'),?(3,'3')?....
insert into t_user(id,uname) values(1, '1'), (2,'2'), (3, '3') ....
所以,當rewriteBatchedStatements=true時, 樓主的例子會被編譯為以上形式,當然values里全是?, mysql 客戶端會對這些值添加參數. 這樣的方式當然就快很多了。
其實到現在還不太了解 batch 處理時,執行計劃這個概念,不過我猜 mysql 可能并沒有緩存執行計劃。而只是將這些語句組合起來了。
所以如果是這樣,他的機制與oracle可能是有所不同的,還不是達到最高效的機制,也許這就是開源與商業的區別吧。
我們如果想更深入了解,只能借助于一些服務器端監視工具,sql分析工具了。
寫貼子過程斷斷續續給打擾了,本來還有一些可以寫更詳細的,就留給大家自己去探索了,包括,如果調用addBatch(String sql)后,則仍會按照 executeBatchSerially 方式執行,包括何時執行 executePreparedBatchAsMultiStatement,都可以繼續深入了解。
后記,當使用 update 時,會執行 executePreparedBatchAsMultiStatement,但是如果攢的語句太多,會導致 mysql 崩潰. 我的測試中10000條update不會有事,20000時,mysql 就崩掉了。
分享到:
2011-11-17 15:19
瀏覽 1569
分類:數據庫
評論