JDBC實現往MySQL插入百萬級數據

想往某個表中插入幾百萬條數據作下測試,java

原先的想法,直接寫個循環10W次隨便插入點數據試試吧,好吧,我真的很天真....mysql

 

DROP PROCEDURE IF EXISTS proc_initData;--若是存在此存儲過程則刪掉
DELIMITER $
CREATE PROCEDURE proc_initData()
BEGIN
    DECLARE i INT DEFAULT 1;
    WHILE i<=100000 DO
        INSERT INTO text VALUES(i,CONCAT('姓名',i),'XXXXXXXXX');
        SET i = i+1;
    END WHILE;
END $
CALL proc_initData();

 

執行CALL proc_initData()後,原本想一想,再慢10W條數據頂多30分鐘能搞定吧,結果我打了2把LOL後,回頭一看,還在執行,此時內心是完全懵逼的....待我打完第三把結束後,終於執行完了,這種方法如果讓我等上幾百萬條數據,是否是早上去上班,下午下班回來還沒結束呢?10W條數據,有圖有真相sql

JDBC往數據庫中普通插入方式

後面查了一下,使用JDBC批量操做往數據庫插入100W+的數據貌似也挺快的,數據庫

先來講說JDBC往數據庫中普通插入方式,簡單的代碼大體以下,循環了1000條,中間加點隨機的數值,畢竟本身要拿數據測試,數據全都同樣也很差區分dom

 1    private String url = "jdbc:mysql://localhost:3306/test01";
 2     private String user = "root";
 3     private String password = "123456";
 4     @Test
 5     public void Test(){
 6         Connection conn = null;
 7         PreparedStatement pstm =null;
 8         ResultSet rt = null;
 9         try {
10             Class.forName("com.mysql.jdbc.Driver");
11             conn = DriverManager.getConnection(url, user, password);        
12             String sql = "INSERT INTO userinfo(uid,uname,uphone,uaddress) VALUES(?,CONCAT('姓名',?),?,?)";
13             pstm = conn.prepareStatement(sql);
14             Long startTime = System.currentTimeMillis();
15             Random rand = new Random();
16             int a,b,c,d;
17             for (int i = 1; i <= 1000; i++) {
18                     pstm.setInt(1, i);
19                     pstm.setInt(2, i);
20                     a = rand.nextInt(10);
21                     b = rand.nextInt(10);
22                     c = rand.nextInt(10);
23                     d = rand.nextInt(10);
24                     pstm.setString(3, "188"+a+"88"+b+c+"66"+d);
25                     pstm.setString(4, "xxxxxxxxxx_"+"188"+a+"88"+b+c+"66"+d);27                     pstm.executeUpdate();
28             }
29             Long endTime = System.currentTimeMillis();
30             System.out.println("OK,用時:" + (endTime - startTime)); 
31         } catch (Exception e) {
32             e.printStackTrace();
33             throw new RuntimeException(e);
34         }finally{
35             if(pstm!=null){
36                 try {
37                     pstm.close();
38                 } catch (SQLException e) {
39                     e.printStackTrace();
40                     throw new RuntimeException(e);
41                 }
42             }
43             if(conn!=null){
44                 try {
45                     conn.close();
46                 } catch (SQLException e) {
47                     e.printStackTrace();
48                     throw new RuntimeException(e);
49                 }
50             }
51         }
52     }

 

輸出結果:OK,用時:738199,單位毫秒,也就是說這種方式與直接數據庫中循環是差很少的。測試

在討論批量處理以前,先說說遇到的坑,首先,JDBC鏈接的url中要加rewriteBatchedStatements參數設爲true是批量操做的前提,其次就是檢查mysql驅動包時候是5.1.13以上版本(低於該版本不支持),因網上隨便下載了5.1.7版本的,而後執行批量操做(100W條插入),結果由於驅動器版本過低緣故並不支持,致使中止掉java程序後,mysql還在不斷的往數據庫中插入數據,最後不得不中止掉數據庫服務才停下來...ui

那麼低版本的驅動包是否對100W+數據插入就無力了呢?實際還有另一種方式,效率相比來講仍是能夠接受的。url

使用事務提交方式

先將命令的提交方式設爲false,即手動提交conn.setAutoCommit(false);最後在全部命令執行完以後再提交事務conn.commit();spa

 1     private String url = "jdbc:mysql://localhost:3306/test01";
 2     private String user = "root";
 3     private String password = "123456";
 4     @Test
 5     public void Test(){
 6         Connection conn = null;
 7         PreparedStatement pstm =null;
 8         ResultSet rt = null;
 9         try {
10             Class.forName("com.mysql.jdbc.Driver");
11             conn = DriverManager.getConnection(url, user, password);        
12             String sql = "INSERT INTO userinfo(uid,uname,uphone,uaddress) VALUES(?,CONCAT('姓名',?),?,?)";
13             pstm = conn.prepareStatement(sql);
14             conn.setAutoCommit(false);
15             Long startTime = System.currentTimeMillis();
16             Random rand = new Random();
17             int a,b,c,d;
18             for (int i = 1; i <= 100000; i++) {
19                     pstm.setInt(1, i);
20                     pstm.setInt(2, i);
21                     a = rand.nextInt(10);
22                     b = rand.nextInt(10);
23                     c = rand.nextInt(10);
24                     d = rand.nextInt(10);
25                     pstm.setString(3, "188"+a+"88"+b+c+"66"+d);
26                     pstm.setString(4, "xxxxxxxxxx_"+"188"+a+"88"+b+c+"66"+d);
27                     pstm.executeUpdate();
28             }
29             conn.commit();
30             Long endTime = System.currentTimeMillis();
31             System.out.println("OK,用時:" + (endTime - startTime)); 
32         } catch (Exception e) {
33             e.printStackTrace();
34             throw new RuntimeException(e);
35         }finally{
36             if(pstm!=null){
37                 try {
38                     pstm.close();
39                 } catch (SQLException e) {
40                     e.printStackTrace();
41                     throw new RuntimeException(e);
42                 }
43             }
44             if(conn!=null){
45                 try {
46                     conn.close();
47                 } catch (SQLException e) {
48                     e.printStackTrace();
49                     throw new RuntimeException(e);
50                 }
51             }
52         }
53     }

 

以上代碼插入10W條數據,輸出結果:OK,用時:18086,也就十八秒左右的時間,理論上100W也就是3分鐘這樣,勉強還能夠接受。code

批量處理

接下來就是批量處理了,注意,必定要5.1.13以上版本的驅動包。

 1 private String url = "jdbc:mysql://localhost:3306/test01?rewriteBatchedStatements=true";
 2     private String user = "root";
 3     private String password = "123456";
 4     @Test
 5     public void Test(){
 6         Connection conn = null;
 7         PreparedStatement pstm =null;
 8         ResultSet rt = null;
 9         try {
10             Class.forName("com.mysql.jdbc.Driver");
11             conn = DriverManager.getConnection(url, user, password);        
12             String sql = "INSERT INTO userinfo(uid,uname,uphone,uaddress) VALUES(?,CONCAT('姓名',?),?,?)";
13             pstm = conn.prepareStatement(sql);
14             Long startTime = System.currentTimeMillis();
15             Random rand = new Random();
16             int a,b,c,d;
17             for (int i = 1; i <= 100000; i++) {
18                     pstm.setInt(1, i);
19                     pstm.setInt(2, i);
20                     a = rand.nextInt(10);
21                     b = rand.nextInt(10);
22                     c = rand.nextInt(10);
23                     d = rand.nextInt(10);
24                     pstm.setString(3, "188"+a+"88"+b+c+"66"+d);
25                     pstm.setString(4, "xxxxxxxxxx_"+"188"+a+"88"+b+c+"66"+d);
26                     pstm.addBatch();
27             }
28             pstm.executeBatch();
29             Long endTime = System.currentTimeMillis();
30             System.out.println("OK,用時:" + (endTime - startTime)); 
31         } catch (Exception e) {
32             e.printStackTrace();
33             throw new RuntimeException(e);
34         }finally{
35             if(pstm!=null){
36                 try {
37                     pstm.close();
38                 } catch (SQLException e) {
39                     e.printStackTrace();
40                     throw new RuntimeException(e);
41                 }
42             }
43             if(conn!=null){
44                 try {
45                     conn.close();
46                 } catch (SQLException e) {
47                     e.printStackTrace();
48                     throw new RuntimeException(e);
49                 }
50             }
51         }
52     }

 

10W輸出結果:OK,用時:3386,才3秒鐘.

批量操做+事務

而後我就想,要是批量操做+事務提交呢?會不會有神器的效果?

 1 private String url = "jdbc:mysql://localhost:3306/test01?rewriteBatchedStatements=true";
 2     private String user = "root";
 3     private String password = "123456";
 4     @Test
 5     public void Test(){
 6         Connection conn = null;
 7         PreparedStatement pstm =null;
 8         ResultSet rt = null;
 9         try {
10             Class.forName("com.mysql.jdbc.Driver");
11             conn = DriverManager.getConnection(url, user, password);        
12             String sql = "INSERT INTO userinfo(uid,uname,uphone,uaddress) VALUES(?,CONCAT('姓名',?),?,?)";
13             pstm = conn.prepareStatement(sql);
14             conn.setAutoCommit(false);
15             Long startTime = System.currentTimeMillis();
16             Random rand = new Random();
17             int a,b,c,d;
18             for (int i = 1; i <= 100000; i++) {
19                     pstm.setInt(1, i);
20                     pstm.setInt(2, i);
21                     a = rand.nextInt(10);
22                     b = rand.nextInt(10);
23                     c = rand.nextInt(10);
24                     d = rand.nextInt(10);
25                     pstm.setString(3, "188"+a+"88"+b+c+"66"+d);
26                     pstm.setString(4, "xxxxxxxxxx_"+"188"+a+"88"+b+c+"66"+d);
27                     pstm.addBatch();
28             }
29             pstm.executeBatch();
30             conn.commit();
31             Long endTime = System.currentTimeMillis();
32             System.out.println("OK,用時:" + (endTime - startTime)); 
33         } catch (Exception e) {
34             e.printStackTrace();
35             throw new RuntimeException(e);
36         }finally{
37             if(pstm!=null){
38                 try {
39                     pstm.close();
40                 } catch (SQLException e) {
41                     e.printStackTrace();
42                     throw new RuntimeException(e);
43                 }
44             }
45             if(conn!=null){
46                 try {
47                     conn.close();
48                 } catch (SQLException e) {
49                     e.printStackTrace();
50                     throw new RuntimeException(e);
51                 }
52             }
53         }
54     }

 

如下是100W數據輸出對比:(5.1.17版本MySql驅動包下測試,交替兩種方式下的數據測試結果對比)

批量操做(10W) 批量操做+事務提交(10W) 批量操做(100W) 批量錯做+事務提交(100W)

OK,用時:3901

OK,用時:3343

OK,用時:44242

OK,用時:39798

OK,用時:4142

OK,用時:2949

OK,用時:44248

OK,用時:39959

OK,用時:3664

OK,用時:2689

OK,用時:44389

OK,用時:39367

可見有必定的效率提高,可是並非太明顯,固然由於數據差不算太大,也有可能存在偶然因數,畢竟每項只測3次。

預編譯+批量操做

網上還有人說使用預編譯+批量操做的方式可以提升效率更明顯,可是本人親測,效率不高反降,可能跟測試的數據有關吧。

預編譯的寫法,只需在JDBC的鏈接url中將寫入useServerPrepStmts=true便可,

如:

private String url = "jdbc:mysql://localhost:3306/test01?useServerPrepStmts=true&rewriteBatchedStatements=true"

 

好了,先到這裏...

相關文章
相關標籤/搜索