標籤:
JDBC使用MySQL處理大資料的時候,自然而然的想到要使用批處理,
普通的執行過程是:每處理一條資料,就訪問一次資料庫;
而批處理是:累積到一定數量,再一次性提交到資料庫,減少了與資料庫的互動次數,所以效率會大大提高
至於事務:事務指邏輯上的一組操作,組成這組操作的各個單元,要不全部成功,要不全部不成功,預設是關閉事務的。
更多事務的資料,請參考這裡:http://blog.csdn.net/caomiao2006/article/details/22412755
1. PreparedStatement使用批處理 executeBatch() 1.1. 不使用executeBatch(),而使用executeUpdate()
代碼如下:
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection(dbUrl, user, password);
PreparedStatement pstmt = conn.prepareStatement("update content set introtext=? where id=?");
for(int i=0; i<10000; i++){
pstmt.setString(1, "abc"+i);
pstmt.setInt(2, id);
pstmt.executeUpdate();
}
這樣,更新10000條資料,就得訪問資料庫10000次
1.2. 而使用executeBatch()
代碼如下:
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection(dbUrl, user, password);
PreparedStatement pstmt = conn.prepareStatement("update content set introtext=? where id=?");
for(int i=0; i<10000; i++){
pstmt.setString(1, "abc"+i);
pstmt.setInt(2, id);
pstmt.addBatch();//添加到同一個批處理中
}
pstmt.executeBatch();//執行批處理
注意:1. 如果使用了 addBatch() -> executeBatch() 還是很慢,那就得使用到這個參數了
rewriteBatchedStatements=true (啟動批處理操作)
在資料庫連接URL後面加上這個參數:
String dbUrl = "jdbc:mysql://localhost:3306/User? rewriteBatchedStatements=true";
2. 在代碼中,pstmt的位置不能亂放,
//必須放在迴圈體外
pstmt = conn.prepareStatement("update content set introtext=? where id=?");
for(int i=0; i<10000; i++){
//放這裡,批處理會執行不了,因為每次迴圈重建了pstmt,不是同一個了
//pstmt = conn.prepareStatement("update content set introtext=? where id=?");
pstmt.setString(1, "abc"+i);
pstmt.setInt(2, id);
pstmt.addBatch();//添加到同一個批處理中
}
pstmt.executeBatch();//執行批處理
2. 啟用交易處理
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection(dbUrl, user, password);
conn.setAutoCommit(false);//將自動認可關閉
PreparedStatement pstmt = conn.prepareStatement("update content set introtext=? where id=?");
pstmt.setString(1, tempintrotext);
pstmt.setInt(2, id);
pstmt.addBatch();
pstmt.executeBatch();
pstmt.close();
conn.commit();//執行完後,手動提交事務
conn.setAutoCommit(true);//在把自動認可開啟
conn.close();
3. 事務和批處理混合使用
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection(dbUrl, user, password);
conn.setAutoCommit(false);//將自動認可關閉
PreparedStatement pstmt = conn.prepareStatement("update content set introtext=? where id=?");
for(int i=0; i<1000000; i++){
pstmt.setString(1, tempintrotext);
pstmt.setInt(2, id);
pstmt.addBatch();
//每500條執行一次,避免記憶體不夠的情況,可參考,Eclipse設定JVM的記憶體參數
if(i>0 && i%500==0){
pstmt.executeBatch();
//如果不想出錯後,完全沒保留資料,則可以沒執行一次提交一次,但得保證資料不會重複
conn.commit();
}
}
pstmt.executeBatch();//執行最後剩下不夠500條的
pstmt.close();
conn.commit();//執行完後,手動提交事務
conn.setAutoCommit(true);//在把自動認可開啟
conn.close();
較完整的代碼:
1 public class ExecuteBatchTest { 2 private Connection conn; 3 private PreparedStatement pstmt; 4 private PreparedStatement pstmt2; 5 private ResultSet rs; 6 private String user = "root"; 7 private String password = "123456"; 8 private String dbUrl = "jdbc:mysql://localhost:3306/user?rewriteBatchedStatements=true"; 9 private int limitNum = 10000;10 11 public void changeData() {12 try {13 Class.forName("com.mysql.jdbc.Driver");14 conn = DriverManager.getConnection(dbUrl, user, password);15 16 //既不用batch,也不用事務17 testBatch(false,false);18 //只用batch, 不用事務19 testBatch(false,true);20 //只用事務,不用batch21 testBatch(true,false);22 //不僅用事務,還用batch23 testBatch(true,true);24 25 pstmt.close();26 conn.close();27 } catch (ClassNotFoundException e) {28 e.printStackTrace();29 } catch (SQLException e) {30 e.printStackTrace();31 }32 }33 34 public void testBatch(Boolean openTransaction, Boolean useBatch) throws SQLException{35 if(openTransaction)36 conn.setAutoCommit(false);37 38 if(pstmt!=null){39 pstmt.clearParameters();40 pstmt.clearBatch();41 }42 43 pstmt = conn.prepareStatement("insert into person (name) values (?)");44 long start = System.currentTimeMillis();45 for(int a = 0;a<limitNum;a++){46 String name = "tommy"+a;47 pstmt.setString(1, name);48 if(useBatch)49 pstmt.addBatch();50 else51 pstmt.executeUpdate();52 }53 54 if(useBatch)55 pstmt.executeBatch();56 57 if(openTransaction){58 conn.commit();59 conn.setAutoCommit(true);60 }61 long end = System.currentTimeMillis();62 System.out.println("use time:"+(end-start)+" ms");63 64 }65 66 //main method67 publi static void main(String[] args){68 ExecuteBatchTest ebt = new ExecuteBatchTest();69 ebt.changeData();70 }71 72 }View Code
運行結果:
分別是: 不用批處理,不用事務;
只用批處理,不用事務;
只用事務,不用批處理;
既用事務,也用批處理;(很明顯,這個最快,所以建議在處理大批量的資料時,同時使用批處理和事務)
JDBC的PreparedStatement啟動事務使用批處理executeBatch()