Java 如何将百万数据插入数据库
我正在尝试向DB表中插入100万条记录 我想创建至少3个线程,每个线程触发一个插入,然后,我们可以在每秒钟内得到3个并行请求。我们可以通过让每个线程等待并在同一个中断时一起醒来来控制插入的触发在同一时间发生。然后每个线程进入睡眠状态,直到1秒钟的时间窗过去。然后整个过程将重复。我怎样才能做到呢Java 如何将百万数据插入数据库,java,multithreading,junit,oracle10g,Java,Multithreading,Junit,Oracle10g,我正在尝试向DB表中插入100万条记录 我想创建至少3个线程,每个线程触发一个插入,然后,我们可以在每秒钟内得到3个并行请求。我们可以通过让每个线程等待并在同一个中断时一起醒来来控制插入的触发在同一时间发生。然后每个线程进入睡眠状态,直到1秒钟的时间窗过去。然后整个过程将重复。我怎样才能做到呢 任何建议或提示都会有帮助。插入大量数据被认为是不好的做法。插入大量数据将花费大量时间,而您可以使用sqlloader或类似工具直接将数据加载到表中。这些加载程序速度更快,因为它们不会导致事务管理的开销 当
任何建议或提示都会有帮助。插入大量数据被认为是不好的做法。插入大量数据将花费大量时间,而您可以使用sqlloader或类似工具直接将数据加载到表中。这些加载程序速度更快,因为它们不会导致事务管理的开销 当我有大量数据要加载到数据库中时,我会问自己一些问题
基于上述解释。您可以选择更好地描述您的问题,也可以选择sqlloader。是的,逐个记录插入大量数据不是一种好的做法,它需要很长时间,并且会增加数据库的开销,即使您将插入进程拆分为不同的线程,也会增加数据库的开销和数据库上的其他通信进程的开销减速。 导入的方式是 1) 如果数据来自旧数据库,则使用sql转储,并导入到新数据库中,速度非常快 2) 若您想使用该程序导入,那个么您必须具有批处理,该批处理允许您一次插入多条记录,从而减少事务的开销 3) 如果您使用了一些数据库管理UI工具,它们还允许您通过CSV或excel进行导入,这也很快,如果您的文件中有数据,它们会帮助您
除了以上三种方法中的一种之外,还有许多其他方法适合您。我一直使用sqlloader或sqldeveloper来加载大量数据。这是有意义的,当我们有delta时,插入数据的java应用程序是有意义的。但是第一次安装/迁移数据sql loader/类似选项是最好的选择。这是一个基于mykong代码的批插入的快速示例 这基本上提供了sqlloader的速度,它执行批插入。并且只能使用1个螺纹 我在这里所做的是将插入放入一个循环中,以显示您必须每数千条记录清除一次批处理 您可以删除无限循环,让它插入数据,而不是硬编码的数据
String insertTableSQL = "INSERT INTO DBUSER"
+ "(USER_ID, USERNAME, CREATED_BY, CREATED_DATE) VALUES"
+ "(?,?,?,?)";
PreparedStatement preparedStatement = dbConnection.prepareStatement(insertTableSQL);
try {
dbConnection.setAutoCommit(false);
int batchTotal=0;
for (;;) { // infinate loop? change this to get your data here
preparedStatement.setInt(1, 101);
preparedStatement.setString(2, "mkyong101");
preparedStatement.setString(3, "system");
preparedStatement.setTimestamp(4, getCurrentTimeStamp());
preparedStatement.addBatch();
if (batchTotal++ == 4096) {
int[] result = preparedStatement.executeBatch();
preparedStatement.clearBatch();
batchTotal=0;
}
}
if (batchTotal > 0) {
int[] result = preparedStatement.executeBatch();
}
dbConnection.commit();
} finally {
preparedStatement.close();
}
我们可以利用SQL查询一次插入多行:
INSERT INTO table_name (col1, col2, col3) VALUES (val1, val2, val3),
(val4, val5, val6), ... (valx, valy, valz)
我已经在SQLServer上测试过了。我相信它也应该适用于其他数据库(如果需要的话,在SQL查询中做一些小的修改)
注意:我使用的是Spring数据JPA和Java8+
在本例中,我的实体类如下所示:
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "test")
public class Test {
@Id
private Long id;
private String name;
@Column(name = "age", columnDefinition = "int default 0", nullable = false)
private int age;
//Getters and Setters
}
下面是执行相关任务的类:
import java.util.List;
import java.util.stream.Collectors;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.transaction.annotation.Transactional;
public class MyService {
@PersistenceContext
EntityManager entityManager;
/*
* This method takes a List of POJOs and inserts them as records into the database table.
* Returns the number of records thus inserted.
*/
@Transactional
public int batchInsert(List<Test> list) {
int totalRecordsInserted = 0;
//How many records to insert in a single insert statement
//(SQL Server allows max 1000)
int count = 1000;
//Construct SQL query like this:
//INSERT INTO test (id, age, name)
//VALUES (1, 1, 'Test 1'), (2, 2, 'Test 2'),
//(3, 3, 'Test 3), ... (1000, 1000, 'Test 1000')
String sqlQueryPart1 = "INSERT INTO test (id, age, name) VALUES\r\n";
for (int i = 0; i < list.size(); i += count) {
StringBuilder queryBuilder = new StringBuilder();
//Take in chunks the next 1000 elements from the main list
//and append to the SQL query the corresponding values
list.stream().skip(i).limit(count).forEach(
x -> queryBuilder.append("(" + x.getId() + "," + x.getAge() + "," + parse(x.getName()) + ")\r\n,"));
//Remove the unwanted last comma sign from the batch insert query
String sqlQuery = sqlQueryPart1 + queryBuilder.deleteCharAt(queryBuilder.length() - 1).toString();
//Run the SQL query to insert these 1000 records
int recordsInserted = entityManager.createNativeQuery(sqlQuery).executeUpdate();
totalRecordsInserted += recordsInserted;
}
return totalRecordsInserted;
}
//Added this method to enclose a String value in single quotes
//And also to take care of null or single quotes in the String field
private String parse(String x) {
if(x == null)
return "NULL";
else if(x.contains("'"))
return "'" + x.replaceAll("'", "''") + "'";
else
return "'" + x + "'";
}
}
import java.util.List;
导入java.util.stream.collector;
导入javax.persistence.EntityManager;
导入javax.persistence.PersistenceContext;
导入org.springframework.transaction.annotation.Transactional;
公共类MyService{
@持久上下文
实体管理器实体管理器;
/*
*此方法获取POJO列表,并将其作为记录插入数据库表中。
*返回由此插入的记录数。
*/
@交易的
公共int批插入(列表){
int totalRecordsInserted=0;
//在单个insert语句中插入多少条记录
//(SQL Server允许的最大值为1000)
整数计数=1000;
//按如下方式构造SQL查询:
//插入测试(id、年龄、姓名)
//数值(1,1,‘测试1’,(2,2,‘测试2’),
//(3,3,'测试3),…(1000,1000,'测试1000')
字符串sqlQueryPart1=“插入测试(id、年龄、名称)值\r\n”;
对于(int i=0;iqueryBuilder.append(“(“+x.getId()+”,“+x.getAge()+”,“+parse(x.getName())+”)”\r\n“);
//从批插入查询中删除不需要的最后一个逗号符号
字符串sqlQuery=sqlQueryPart1+queryBuilder.deleteCharAt(queryBuilder.length()-1).toString();
//运行SQL查询以插入这1000条记录
int recordsInserted=entityManager.createNativeQuery(sqlQuery.executeUpdate();
totalRecordsInserted+=recordsInserted;
}
返回已插入的总记录;
}
//添加此方法以将字符串值括在单引号中
//以及处理字符串字段中的空引号或单引号
私有字符串解析(字符串x){
如果(x==null)
返回“NULL”;
else if(x.contains(“'))
返回“'”+x.replaceAll(“'”,“'”)+“”;
其他的
返回“'”+x+“'”;
}
}
是否有任何特殊原因需要生成3个线程?磁盘IO是数据插入数据库速度的瓶颈。创建更多线程无助于提高效率。可能对整个“插入大量数据”位感兴趣我们可以控制插入的触发完全同时发生不,你不能。问题是,你是在执行一系列插入语句还是在使用批插入?我使用批插入(w