1. 場景 : 往數據庫插入10W條記錄
2. 思考方案 : 單純的我們這里不涉及其他任何操作,我們只是想生成一個10W條記錄而已,中間無其他步驟,得到的效果如下圖所示,
而我們又不會mysql腳本啊之類的,那我們不如用java來實現,用jdbc的批次操作來完成 ,博客借鑒自:
https://blog.csdn.net/summeranhx/article/details/81583575?utm_source=blogxgwz0
實現的效果圖為 :
3. 代碼為 :
package root.report.control.dict; import org.apache.ibatis.session.SqlSession; import root.report.db.DbFactory; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Date; /** * @Auther: pccw * @Date: 2018/10/29 17:45 * @Description: */ /* 往自己本地mysql 當中插入10W條記錄 */ public class TestTwo { public static void main(String args[]){ SqlSession sqlSession = DbFactory.Open(DbFactory.FORM); // 本地mysql數據庫 insert(sqlSession); } public static void insert(SqlSession sqlSession){ Connection conn = sqlSession.getConnection(); // 開始時間 Long begin = new Date().getTime(); // sql前綴 String prefix = "INSERT INTO test_dict (code,name) VALUES "; try { // 保存sql后綴 StringBuffer suffix = new StringBuffer(); // 設置事務為非自動提交 conn.setAutoCommit(false); // 比起st,pst會更好些 PreparedStatement pst = (PreparedStatement) conn.prepareStatement("");//准備執行語句 // 外層循環,總提交事務次數 for (int i = 1; i <= 5; i++) { suffix = new StringBuffer(); // 第j次提交步長 if(i == 1) { for (int j = 1; j < 10; j++) { // 構建SQL后綴 suffix.append("(" + j+","+"'0000" +j+"'),"); } }else if(i == 2) { for (int j = 10; j < 100; j++) { // 構建SQL后綴 suffix.append("(" + j+","+"'000" +j+"'),"); } }else if(i == 3) { for (int j = 100; j < 1000; j++) { // 構建SQL后綴 suffix.append("(" + j+","+"'00" +j+"'),"); } }else if(i == 4) { for (int j = 1000; j < 10000; j++) { // 構建SQL后綴 suffix.append("(" + j+","+"'0" +j+"'),"); } }else { for (int j = 10000; j <= 99999; j++) { // 構建SQL后綴 suffix.append("(" + j+","+"'" +j+"'),"); } } // 構建完整SQL String sql = prefix + suffix.substring(0, suffix.length() - 1); // 添加執行SQL pst.addBatch(sql); // 執行操作 pst.executeBatch(); // 提交事務 conn.commit(); // 清空上一次添加的數據 suffix = new StringBuffer(); } // 頭等連接 pst.close(); conn.close(); } catch (SQLException e) { e.printStackTrace(); } // 結束時間 Long end = new Date().getTime(); // 耗時 System.out.println("10萬條數據插入花費時間 : " + (end - begin) / 1000 + " s"); System.out.println("插入完成"); } }
不難看出,代碼沒什么含金量,代碼着重在於下面2處操作:
// 添加執行SQL pst.addBatch(sql); // 執行操作 pst.executeBatch();
4. 我們再改寫成用多線程的方式試試 :
改寫成多線程 方案則要考慮到使用何種線程池 ? 線程池線程都執行完才能繼續執行下一步驟,在此期間怎樣感知線程執行完畢 ?
沒錯,我們這在這里使用了 newFixedThreadPool 這個線程池,這是直接規划出多少個線程,然后我們怎樣讓線程執行完畢再執行其他的?
沒錯,我們直接使用
final CountDownLatch count = new CountDownLatch(5);
CountDownLatch 這個並發包輔助類 -》當我們線程執行完一個任務之后,count.countDown(); -》 同時對於fixedThreadPool 這個變量
要使用 synchronized 來進行鎖定,然后在整個方法之外 對 count 變量進行 await()喚醒操作,繼續執行下列步驟。
好了,廢話少說,下面貼出代碼 :

package root.report.control.dict; import org.apache.ibatis.session.SqlSession; import root.report.db.DbFactory; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Date; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @Auther: pccw * @Date: 2018/10/29 17:45 * @Description: */ /* 往自己本地mysql 當中插入10W條記錄 */ public class TestTwo { public static void main(String args[]) throws SQLException { SqlSession sqlSession = DbFactory.Open(DbFactory.FORM); // insert(sqlSession); insertTwo(sqlSession); } // 多線程案例 使用fix線程 規定為5個 public static void insertTwo(SqlSession sqlSession) throws SQLException { Connection conn = sqlSession.getConnection(); // 開始時間 Long begin = new Date().getTime(); final StringBuffer suffix = new StringBuffer(); // sql前綴 String prefix = "INSERT INTO test_dict (code,name) VALUES "; // 設置事務為非自動提交 conn.setAutoCommit(false); // 比起st,pst會更好些 PreparedStatement pst = (PreparedStatement) conn.prepareStatement("");//准備執行語句 final CountDownLatch count = new CountDownLatch(5); ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5); synchronized (fixedThreadPool) { for (int i = 1; i <= 5; i++) { final int index = i; if (i == 1) { fixedThreadPool.execute(new Runnable() { @Override public void run() { for (int j = 1; j < 10; j++) { // 構建SQL后綴 suffix.append("(" + j + "," + "'0000" + j + "'),"); } count.countDown(); } }); } else if (i == 2) { fixedThreadPool.execute(new Runnable() { @Override public void run() { for (int j = 10; j < 100; j++) { // 構建SQL后綴 suffix.append("(" + j + "," + "'000" + j + "'),"); } count.countDown(); } }); } else if (i == 3) { fixedThreadPool.execute(new Runnable() { @Override public void run() { for (int j = 100; j < 1000; j++) { // 構建SQL后綴 suffix.append("(" + j + "," + "'00" + j + "'),"); } count.countDown(); } }); } else if (i == 4) { fixedThreadPool.execute(new Runnable() { @Override public void run() { for (int j = 1000; j < 10000; j++) { // 構建SQL后綴 suffix.append("(" + j + "," + "'0" + j + "'),"); } count.countDown(); } }); } else { fixedThreadPool.execute(new Runnable() { @Override public void run() { for (int j = 10000; j <= 99999; j++) { // 構建SQL后綴 suffix.append("(" + j + "," + "'" + j + "'),"); } count.countDown(); } }); } } } try { count.await(); // 構建完整SQL String sql = prefix + suffix.substring(0, suffix.length() - 1); // 添加執行SQL pst.addBatch(sql); // 執行操作 pst.executeBatch(); // 提交事務 conn.commit(); // 頭等連接 pst.close(); conn.close(); // 結束時間 Long end = new Date().getTime(); System.out.println("10萬條數據插入花費時間 : " + (end - begin) + " ms"); System.out.println("插入完成"); } catch (InterruptedException e) { e.printStackTrace(); }finally { fixedThreadPool.shutdown(); } } // 測試使用 fetch 按照指定規格讀取數據 }
那么,其實我的機子是8G內存的,在上面非多線程模式執行代碼,花費了4S :
而我們使用了多線程之后,發現連1S都不到,而且數據庫數量也都是對的: