我遇到一个问题,即插入数据时我的Spring Boot应用程序性能非常慢。
我正在从一个数据库中提取大量数据并将其插入到另一个数据库中。
以下是我的实体。
@Entity
@Table(name = "element")
public class VXMLElementHistorical {
@Id
@Column(name = "elementid")
private long elementid;
@Column(name = "elementname")
private String elementname;
Getter/Setter methods...
我已经配置了一个JPA存储库
public interface ElementRepository extends JpaRepository<Element, Long> {
}
并使用我的对象调用save()方法
@Transactional
public void processData(List<sElement> hostElements)
throws DataAccessException {
List<Element> elements = new ArrayList<Element>();
for (int i = 0; i < hostElements.size(); i++) {
Element element = new Element();
element.setElementid(hostElements.get(i).getElementid());
element.setElementname(hostElements.get(i).getElementname());
elements.add(element);
}
try{
elementRepository.save(elements);{
//catch etc...
}
发生的事情是,对于每个项目,执行插入操作都需要6到12秒。我已经打开了休眠跟踪记录和统计信息,当我调用保存功能时,发生的事情是休眠执行了两个查询,一个选择和一个插入。选择查询占用了总时间的99%。
我直接在数据库上运行了选择查询,结果以纳秒为单位返回。这使我相信这不是索引问题,但是我不是DBA。
我已经在我的开发环境中创建了一个负载测试,并且具有相似的负载大小,总的处理时间远没有在产品环境中那么长。
有什么建议吗?
答案 0 :(得分:0)
为@M。 Deinum在评论中说,您可以通过在一定数量的插入之后调用flush()
和clear()
来进行改进,如下所示。
int i = 0;
for(Element element: elements) {
dao.save(element);
if(++i % 20 == 0) {
dao.flushAndClear();
}
}
答案 1 :(得分:0)
保存单个元素,而不是创建元素列表并保存它们。然后每执行一次flush
和clear
,以防止脏检查成为瓶颈。
@PersistenceContext
private EntityManager entityManager;
@Transactional
public void processData(List<sElement> hostElements)
throws DataAccessException {
for (int i = 0; i < hostElements.size(); i++) {
Element element = new Element();
element.setElementid(hostElements.get(i).getElementid());
element.setElementname(hostElements.get(i).getElementname());
elementRepository.save(element)
if ( (i % 50) == 0) {
entityManager.flush();
entityManager.clear();
}
}
entityManager.flush(); // flush the last records.
您要刷新+清除每个x元素(此处为50,但是您可能希望找到自己的最佳数字。
现在,当您使用Spring Boot时,您可能还想添加一些其他属性。就像配置批处理大小一样。
spring.jpa.properties.hibernate.jdbc.batch_size=50
如果您的JDBC驱动程序支持,它将把50个单插入语句转换为1个大批量插入。即50次插入到1次插入。
另请参见https://vladmihalcea.com/how-to-batch-insert-and-update-statements-with-hibernate/
答案 2 :(得分:0)
由于加载实体似乎是瓶颈,而您实际上只是想进行插入,即您知道实体在数据库中不存在,因此您可能不应该使用Spring Data的标准save
方法JPA。
原因是它执行merge
,触发Hibernate加载数据库中可能已经存在的实体。
相反,将custom method添加到您的存储库中,该存储库在实体管理器上执行persist
。由于您是预先设置的Id
,因此请确保具有版本属性,以便Hibernate可以确定这确实是一个新实体。
这应该会使选择消失。
其他答案中给出的其他建议值得考虑作为第二步:
merge
或persist
的调用实际上不会触发写入数据库,但是只有刷新会触发(这是一个简化,但在这种情况下应如此)