数据库快速插入1000万条记录

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");

    }
}

插入时间:

数据库快速插入1000万条记录

相关推荐