我在我的项目中使用spring和hibernate,前几天我发现Dev环境因Java堆空间异常而崩溃.在使用一些堆分析工具和visual vm进行一些初步分析之后,我发现问题出在一个选择SQL查询上.我以不同的方式重写了SQL,解决了内存问题...
我在我的项目中使用spring和hibernate,前几天我发现Dev环境因Java堆空间异常而崩溃.在使用一些堆分析工具和visual vm进行一些初步分析之后,我发现问题出在一个选择SQL查询上.我以不同的方式重写了SQL,解决了内存问题.但现在我不确定为什么以前的SQL导致了内存问题.
注意:该方法位于DAO中,并在批量大小为800的while循环中调用,直到拉出所有数据.表大小约为2000万行.
对于每个调用,都会创建并销毁新的hibernate会话.
以前的SQL:
@Override
public List<Book> getbookByJournalId(UnitOfWork uow,
List<Journal> batch) {
StringBuilder sb = new StringBuilder();
sb.append("select i from Book i where ( ");
if (batch == null || batch.size() <= 0)
sb.append("1=0 )");
else {
for (int i = 0; i < batch.size(); i++) {
if (i > 0)
sb.append(" OR ");
sb.append("( i.journalId='" + batch.get(i).journalId() + "')");
}
sb.append(")");
sb.append(
" and i.isDummy=:isNotDummy and i.statusId !=:BookStatus and i.BookNumber like :book ");
}
Query query = uow.getSession().createQuery(sb.toString());
query.setParameter("isNotDummy", Definitions.BooleanIdentifiers_Char.No);
query.setParameter("Book", "%" + Definitions.NOBook);
query.setParameter("BookStatus", Definitions.BookStatusID.CLOSED.getValue());
List<Book> bookList = (List<Book>) query.getResultList();
return bookList;
}
重写的SQL:
@Override
public List<Book> getbookByJournalId(UnitOfWork uow,
List<Journal> batch) {
List<String> bookIds = new ArrayList<>();
for(Journal J : batch){
bookIds.add(J.getJournalId());
}
StringBuilder sb = new StringBuilder();
sb.append("select i from Book i where i.journalId in (:bookIds) and i.isDummy=:isNotDummy and i.statusId !=:BookStatus and i.BookNumber like :Book");
Query query = uow.getSession().createQuery(sb.toString());
query.setParameter("isNotDummy", Definitions.BooleanIdentifiers_Char.No);
query.setParameter("Book", "%" + Definitions.NOBook);
query.setParameter("BookStatus", Definitions.BookStatusID.CLOSED.getValue());
query.setParameter("specimenNums",specimenNums);
query.setParameter("bookIds", bookIds);
List<Book> bookList = (List<Book>) query.getResultList();
return bookList;
}
解决方法:
在创建动态SQL语句时,错过了数据库缓存语句,索引甚至整个表以优化数据检索的能力.也就是说,动态SQL仍然是一个实用的解决方案.
但是,您需要成为应用程序和数据库服务器上的好公民,因为您的内存使用效率非常高.对于需要扩展到2000万行的解决方案,我建议使用更多基于磁盘的方法,尽可能少地使用RAM(即避免使用数组).
我从第一个陈述中可以看到的问题如下:
每批次的第一个语句最多可添加800个OR条件.这使得SQL语句非常长(不好).我相信[如果我错了请纠正我]需要在JVM堆中缓存然后传递给数据库.
Java可能不会立即从堆中释放此语句,并且垃圾收集可能太慢而无法跟上代码,从而增加了RAM的使用.在代码运行时,您不应该依赖它来清理.
如果您并行运行此代码,那么许多hibernate会话也可能会在数据库上有很多会话.我相信你应该只使用一个会话,除非有特殊原因.创建和销毁不需要的会话只会在服务器和网络上创建不必要的流量.
如果您以串行方式运行此代码,那么为什么要删除会话,何时可以将其重新用于下一批?您可能有正当理由,但必须提出问题.
在第二个语句中,创建bookIds数组会再次占用JVM堆中的RAM,并且SQL中的(:bookIds)部分中的i.journalId仍然很长.没有以前那么糟糕,但我认为还是太长了.
做以下事情你会好得多:
使用batchNumber,bookId和一些元数据(如标志或时间戳)在数据库上创建一个表.使用静态语句将Book表连接到新表,并将batchNumber作为新参数传递.
create table Batch
(
id integer primary key,
batchNumber integer not null,
bookId integer not null,
processed_datetime timestamp
);
create unique index Batch_Idx on Batch (batchNumber, bookId);
-- Put this statement into a loop, or use INSERT/SELECT if the data is available in the database
insert into Batch batchNumber values (:batchNumber, :bookId);
-- Updated SQL statement. This is now static. Note that batchNumber needs to be provided as a parameter.
select i
from Book i
inner join Batch b on b.bookId = i.journalId
where b.batchNumber = :batchNumber
and i.isDummy=:isNotDummy and i.statusId !=:BookStatus and i.BookNumber like :Book;
本文标题为:如果我在sql而不是’in’中使用’或’,那么Java会出现堆空间错误
基础教程推荐
- 高并发下如何避免重复数据产生技巧 2023-03-07
- 关于protected修饰符详解-源于Cloneable接口 2023-08-10
- java实现自定义时钟并实现走时功能 2023-01-23
- 在Idea中新建Java项目 2023-10-08
- Spring入门到精通之注解开发详解 2023-02-19
- Java实现Excel文件加密解密的示例代码 2022-11-29
- JAVA实现Base64编码的三种方式 2023-02-05
- 基于javaweb+jsp实现学生宿舍管理系统 2023-07-30
- SpringBoot与Spring中数据缓存Cache超详细讲解 2023-06-30
- Spring Boot MQTT Too many publishes in progress错误的解决方案 2023-02-11