【轉載原因:親測提升了100倍左右】
【轉載原文:https://blog.csdn.net/tolcf/article/details/52102849#】
這是我轉載的,但是我親測了,好用!我的問題這個方法解決了!!
昨天研究了一下mysql的批處理,最初發現很慢
10000條 每次1000 243秒
50000條 每次500條 1295秒
代碼如下:
package main;
import java.sql.*;
public class JdbcStreaming {
public static void main(String[] args){
try {
Connection conn = null;
Class.forName("com.mysql.jdbc.Driver").newInstance(); // MYSQL驅動
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test_deletable?useUnicode=true&characterEncoding=UTF-8",
"mysql", "mysql"); // 鏈接本地MYSQL
int batchSize = 50000;
int count = 5000000;
conn.setAutoCommit(false);//設置自動提交爲false
PreparedStatement ps = conn.prepareStatement("insert into test (id) values (?)");
Long t1 = System.currentTimeMillis();
System.out.println("====================");
for (long i = 1; i < count; i++) {
ps.setLong(1, i);//設置第一個參數的值爲i
ps.addBatch();//將該條記錄添加到批處理中
if (i% batchSize == 0) {
ps.executeBatch();//執行批處理
conn.commit();//提交
System.out.println(i+":添加"+batchSize+"條");
}
}
if ((count+1) % batchSize != 0) {
ps.executeBatch();
conn.commit();
}
ps.close();
Long t2 = System.currentTimeMillis();
System.out.println(count+"條 每次"+batchSize+"條 "+(t2-t1)/1000+"秒");
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
很明顯,1萬條數據,需要執行243秒。
這個代碼的性能很低,首先排除該表的複雜性(該表只有兩個字段,id爲自增主鍵),二、排除邏輯複雜性(從代碼可以看出,這段代碼只是簡單的把i值賦給id)
後來又調整了很多參數,但都沒有找到根本原因,執行時間會有稍微提高或者沒什麼變化。
最後,添加參數rewriteBatchedStatements=true
即:
[java] view plain copy
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test_deletable?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=UTF-8",
"mysql", "mysql"); // 鏈接本地MYSQL
執行時間提高100倍以上
50000條 每次500條 7秒
後續修改批處理大小,得到執行時間不一樣。
5000000條 每次500條 849秒
5000000條 每次5000條 179秒
5000000條 每次50000條 90秒
5000000條 每次100000條 211秒
可以看出來,批處理大小的設置,也是影響執行時間的一個重要原因,但並不是設置的越大越好,或者越小越好。
下面介紹一下rewriteBatchedStatements
MySql的JDBC連接的url中要加rewriteBatchedStatements參數,並保證5.1.13以上版本的驅動,才能實現高性能的批量插入。
MySQL Jdbc驅動在默認情況下會無視executeBatch()語句,把我們期望批量執行的一組sql語句拆散,一條一條地發給MySQL數據庫,直接造成較低的性能。
只有把rewriteBatchedStatements參數置爲true, 驅動纔會幫你批量執行SQL (jdbc:mysql://ip:port/db?rewriteBatchedStatements=true)。
另外,有人說rewriteBatchedStatements只對INSERT有效,有人說它對UPDATE/DELETE也有效。爲此我做了一些實驗(詳見下文),結論是: 這個選項對INSERT/UPDATE/DELETE都有效,只不過對INSERT它爲會預先重排一下SQL語句。
實驗記錄:未打開rewriteBatchedStatements時
未打開rewriteBatchedStatements時,根據wireshark嗅探出的mysql報文可以看出,
batchDelete(10條記錄) => 發送10次delete 請求
batchUpdate(10條記錄) => 發送10次update 請求
batchInsert(10條記錄) => 發送10次insert 請求
也就是說,batchXXX()的確不起作用
實驗記錄:打開了rewriteBatchedStatements後
打開rewriteBatchedStatements後,根據wireshark嗅探出的mysql報文可以看出
batchDelete(10條記錄) => 發送一次請求,內容爲”delete from t where id = 1; delete from t where id = 2; delete from t where id = 3; ….”
batchUpdate(10條記錄) => 發送一次請求,內容爲”update t set … where id = 1; update t set … where id = 2; update t set … where id = 3 …”
batchInsert(10條記錄) => 發送一次請求,內容爲”insert into t (…) values (…) , (…), (…)”
對delete和update,驅動所做的事就是把多條sql語句累積起來再一次性發出去;而對於insert,驅動則會把多條sql語句重寫成一條風格很酷的sql語句,然後再發出去。 官方文檔說,這種insert寫法可以提高性能(”This is considerably faster (many times faster in some cases) than using separate single-row INSERT statements”)
一個注意事項
需要注意的是,即使rewriteBatchedStatements=true, batchDelete()和batchUpdate()也不一定會走批量: 當batchSize <= 3時,驅動會寧願一條一條地執行SQL。所以,如果你想驗證rewriteBatchedStatements在你的系統裏是否已經生效,記得要使用較大的batch.