cyydjt 2020-05-31
在实际的开发中,有些业务场景需要我们插入大量的测试数据,这时就考虑如何处理能够快速的插入。
现在去面试,偶尔也会遇到问:你会如何快速向数据插入1000万记录,这个问题我也被问到过,当时是没有回到上。其实很多东西没有用到,脑子被问到确实也没有idea。
最近刚好要使用到,就记录下。
1、关闭数据自动提交,避免每次更新数据自动提交带来的时间损耗,改为手动提交。
2、批量插入处理,减少数据库连接带来的时间损耗次数。
1、编写存储过程进行处理。
2、编写Java程序进行处理。(身为Java开发人员,这种方式更友好,同时设置字段值也更加灵活方便)
这里使用的是MySQL,大致就是把语句拼接成如下形式,然后提交到数据库服务器;
insert into table_name (field_1, field_2, ..., field_n) values (value_11,value_12,..., value_1n), (value_21,value_22,..., value_2n), ... (value_n1,value_n2,..., value_nn)
注意:这里提交的数据量是有限制的,通过 show variables like ‘%max_allowed_packet%‘; 默认大小是4M,提交的数量过大,就会报错。
我每次提交的数据量是10万条记录(需要提交100次),10万条记录对应的大小是12多M,报错了,然后把max_allowed_packet的大小设置成了20M。(通过my.ini进行改动,需要重启mysql)
DB工具类:
public class DBUtil { private static final String DB_DRIVER = "com.mysql.cj.jdbc.Driver"; private static final String DB_URL = "jdbc:mysql://localhost:3306/demos?serverTimezone=GMT%2B8&characterEncoding=utf8&useSSL=false&useUnicode=true"; private static final String DB_USER = "root"; private static final String DB_PASSWORD = "root"; static { try { // 载入JDBC驱动程序 : 驱动器仅需要加载一次就可以了 Class.forName(DB_DRIVER); } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 获取数据库连接 * @return */ public static Connection getConnection() { Connection connection = null; try { connection = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); } catch (SQLException e) { e.printStackTrace(); } return connection; }; /** * 关闭数据库连接 * @param connection */ public static void close(Connection connection) { if (connection!=null) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
插入逻辑类:
public class DBInsertTest { public static void main(String[] args) { /* user_sex [1-2] user_province [1-34] user_area [1-10] visit_module[1-10] visit_function[60] visit_day[1-7] */ // 记录开始时间 Instant begin = Instant.now(); // 获取数据库连接 Connection connection = DBUtil.getConnection(); String sqlPrefix = "insert into user_log(visit_name, visit_ip, user_sex, user_province, " + "user_area, visit_path, visit_module, visit_function, visit_day, visit_count, visit_time)" + "values"; StringBuffer sqlSuffix = null; Random random = new Random(); int day = 20200601; String[] hours = {"00", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "23"}; try { // 设置事务为非自动提交 connection.setAutoCommit(false); Statement statement = connection.createStatement(); // 20*1000*50 10天 10000人 100条记录 = 1千万记录 for(int i=0; i<100; i++) { // 100条记录 外层提交事务,10万记录提交一次,总共提交100次 sqlSuffix = new StringBuffer(); for(int j=0; j<10000; j++) { // 10000人 for(int k=0; k<10; k++) { // 10天 String visit_name = "name" + k + j; //姓名 long visit_ip = 4294850663L; // IP int user_sex = random.nextInt(2) + 1; // 性别 int user_province = random.nextInt(34) + 1; // 用户户籍区 int user_area = random.nextInt(10) + 1; // 用户注册区 String visit_path = "https://blog.csdn.net/qq_31725371/article/details/83019856/xxxxxx"; int visit_module = random.nextInt(10) + 1; // 模块号 String visit_function = "function" + (random.nextInt(60) + 1); // 功能 int visit_day = random.nextInt(7) + 1; // 工作日 int visit_count = 5; // 同一路劲访问次数 long visit_time = Integer.parseInt(day+random.nextInt(20) + "" + hours[random.nextInt(24)]); // 访问时间 sqlSuffix.append("(") .append("\‘" + visit_name + "\‘").append(",") .append(visit_ip).append(",") .append(user_sex).append(",") .append(user_province).append(",") .append(user_area).append(",") .append("\‘" + visit_path + "\‘").append(",") .append(visit_module).append(",") .append("\‘" + visit_function + "\‘").append(",") .append(visit_day).append(",") .append(visit_count).append(",") .append(visit_time).append("),"); } } // 构建完整SQL String sql = sqlPrefix + sqlSuffix.substring(0, sqlSuffix.length() - 1); //System.out.println("sql = " + sql); // 添加执行SQL statement.addBatch(sql); // 执行操作 statement.executeBatch(); // 提交事务 connection.commit(); } // 关闭连接 statement.close(); DBUtil.close(connection); } catch (Exception e1) { e1.printStackTrace(); } finally { try { connection.close(); } catch (Exception e2) { e2.printStackTrace(); } } Instant end = Instant.now(); System.out.println("1000万记录插入时间为:" + Duration.between(begin, end).getSeconds() + "s"); } }
插入时间: