(轉)MySQL批量插入處理之提高速度

【轉載原因:親測提升了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.  

 

 

很明顯,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.

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章