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都不到,而且数据库数量也都是对的: