Java 如何使用Hibernate尽可能快地插入数据
我读取文件并从中创建一个对象,然后将其存储到postgresql数据库。我的文件有100000个文档,我从一个文件中读取这些文档并将其拆分,最后存储到数据库中。 我无法创建Java 如何使用Hibernate尽可能快地插入数据,java,database,performance,hibernate,jpa,Java,Database,Performance,Hibernate,Jpa,我读取文件并从中创建一个对象,然后将其存储到postgresql数据库。我的文件有100000个文档,我从一个文件中读取这些文档并将其拆分,最后存储到数据库中。 我无法创建列表并将所有文档存储在列表中,因为我的RAM很少。我对数据库的读写代码如下。但我的JVM堆已满,无法继续存储更多文档。如何高效地读取文件并存储到数据库 public void readFile() { StringBuilder wholeDocument = new StringBuilder(); try
列表
并将所有文档存储在列表
中,因为我的RAM很少。我对数据库的读写代码如下。但我的JVM堆已满,无法继续存储更多文档。如何高效地读取文件并存储到数据库
public void readFile() {
StringBuilder wholeDocument = new StringBuilder();
try {
bufferedReader = new BufferedReader(new FileReader(files));
String line;
int count = 0;
while ((line = bufferedReader.readLine()) != null) {
if (line.contains("<page>")) {
wholeDocument.append(line);
while ((line = bufferedReader.readLine()) != null) {
wholeDocument = wholeDocument.append("\n" + line);
if (line.contains("</page>")) {
System.out.println(count++);
addBodyToDatabase(wholeDocument.toString());
wholeDocument.setLength(0);
break;
}
}
}
}
wikiParser.commit();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void addBodyToDatabase(String wholeContent) {
Page page = new Page(new Timestamp(System.currentTimeMillis()),
wholeContent);
database.addPageToDatabase(page);
}
public static int counter = 1;
public void addPageToDatabase(Page page) {
session.save(page);
if (counter % 3000 == 0) {
commit();
}
counter++;
}
public void readFile(){
StringBuilder wholeDocument=新建StringBuilder();
试一试{
bufferedReader=新bufferedReader(新文件读取器(文件));
弦线;
整数计数=0;
而((line=bufferedReader.readLine())!=null){
如果(第行包含(“”)){
wholeDocument.append(行);
而((line=bufferedReader.readLine())!=null){
wholeDocument=wholeDocument.append(“\n”+行);
如果(第行包含(“”)){
System.out.println(count++);
addBodyToDatabase(wholeDocument.toString());
wholeDocument.setLength(0);
打破
}
}
}
}
commit();
}catch(filenotfounde异常){
e、 printStackTrace();
}捕获(IOE异常){
e、 printStackTrace();
}最后{
试一试{
bufferedReader.close();
}捕获(IOE异常){
e、 printStackTrace();
}
}
}
public void addbody数据库(字符串wholeContent){
Page Page=新页面(新时间戳(System.currentTimeMillis()),
整体内容);
数据库。addPageToDatabase(第页);
}
公共静态整数计数器=1;
公共无效addPageToDatabase(第页){
会话保存(第页);
如果(计数器%3000==0){
提交();
}
计数器++;
}
我对您的数据文件的结构不是很确定。如果您能提供一个文件示例,这将很容易理解
内存消耗的根本原因是读取/迭代文件的方式。一旦有东西被读取,就会留在记忆中。您应该使用java.io.FileInputStream
或org.apache.commons.io.FileUtils
下面是一个使用java.io.FileInputStream进行迭代的示例代码
try (
FileInputStream inputStream = new FileInputStream("/tmp/sample.txt");
Scanner sc = new Scanner(inputStream, "UTF-8")
) {
while (sc.hasNextLine()) {
String line = sc.nextLine();
addBodyToDatabase(line);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
下面是一个使用org.apache.commons.io.FileUtils进行迭代的示例代码
File file = new File("/tmp/sample.txt");
LineIterator it = FileUtils.lineIterator(file, "UTF-8");
try {
while (it.hasNext()) {
String line = it.nextLine();
addBodyToDatabase(line);
}
} finally {
LineIterator.closeQuietly(it);
}
您应该开始一个事务,执行保存操作并提交一个事务。(保存后不要开始事务!)。您可以尝试使用排除缓存的内存消耗
并在代码中使用更多更小的值,例如20
if (counter % 20 == 0)
您可以尝试尽可能将StringBuilder
作为方法的参数传递。我使用@RookieGuy answer。
我用
session.flush();
session.clear();
最后读取所有文档并存储到数据库中
tx.commit();
session.close();
改变
wholeDocument = wholeDocument.append("\n" + line);
到
首先,您应该在这里应用一种方法
主任务解析该文件,并将最多100个项目的批发送到。ExecutorService
应具有与可用数据库连接数相等的工作线程数。如果您有4个CPU核,那么假设数据库可以进行8个并发连接,而无需进行太多的上下文切换
然后,您应该配置一个连接池DataSource
,并将minSize设置为maxSize和8。尝试HikariCP或ViburDBCP进行连接池
然后需要配置JDBC批处理。如果您使用的是MySQL,则标识生成器将禁用沐浴功能。如果您使用的是支持序列的数据库,请确保还使用增强的标识符生成器(它们是Hibernate 5.x中的默认选项)
通过这种方式,实体插入过程被并行化,并与主解析线程解耦。主线程应该等待ExecutorService在关闭之前完成所有任务。实际上,如果不进行真正的分析,就很难向您提出建议,并找出是什么原因导致代码运行缓慢或效率低下
但是,我们可以从您的代码中看到一些东西
您正在低效地使用StringBuilder
wholeDocument.append(“\n”+行)代码>应写为wholeDocument.append(“\n”).append(行)代码>取而代之
因为你原来写的东西会被编译器翻译成
whileDocument.append(新的StringBuilder(“\n”).append(line.toString())
。您可以看到您创建了多少不必要的StringBuilder
s:)
关于使用Hibernate的思考
我不确定您是如何管理您的会话的
,还是如何实现您的提交()
,我假设您做对了,还有更多事情需要考虑:
- 您是否在Hibernate中正确设置了批大小?(
hibernate.jdbc.batch_size
)默认情况下,jdbc批大小大约为5。您可能希望确保将其设置为更大的大小(以便内部Hibernate将以更大的批发送插入)
- 鉴于您不需要一级缓存中的实体供以后使用,您可能需要执行间歇会话
flush()
+clear()
,以
- 触发上一点中提到的批插入
- 清除一级缓存
为此功能从休眠状态切换
冬眠很酷,但它不是万能的。鉴于此功能,您只需根据文本文件内容将记录保存到数据库中。您不需要任何实体行为,也不需要使用一级缓存进行后续处理,考虑到额外的处理和空间开销,这里没有太多的理由使用Hibernate。简单地执行JDBCWI
wholeDocument.append("\n" + line);