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");
}
}插入时间:
