Java Spring Boot JPA存储时的存储性能()
我的spring boot应用程序在插入数据时性能非常慢 我正在从一个数据库中提取一大部分数据,并将数据插入另一个数据库 以下是我的实体Java Spring Boot JPA存储时的存储性能(),java,spring,hibernate,spring-boot,spring-data-jpa,Java,Spring,Hibernate,Spring Boot,Spring Data Jpa,我的spring boot应用程序在插入数据时性能非常慢 我正在从一个数据库中提取一大部分数据,并将数据插入另一个数据库 以下是我的实体 @Entity @Table(name = "element") public class VXMLElementHistorical { @Id @Column(name = "elementid") private long elementid; @Column(name = "elementname") private String eleme
@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> {
}
public interface ElementRepository扩展了JpaRepository{
}
并使用我的对象调用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...
}
@Transactional
public void processData(列出宿主元素)
抛出DataAccessException{
列表元素=新的ArrayList();
对于(int i=0;i
发生的情况是,对于每个项目,执行插入需要6到12秒。我已打开hibernate跟踪日志记录和统计,当我调用save函数时,hibernate执行两个查询,一个选择和一个插入。选择查询占用了99%的总时间
我直接在数据库上运行了select查询,结果以纳秒的速度返回。这让我相信这不是索引问题,但我不是DBA
我已经在我的dev环境中创建了一个负载测试,并且使用类似的负载大小,整个过程的时间没有prod环境中的时间长
有什么建议吗?正如@M.Deinum在评论中所说的那样,您可以通过在一定数量的插入后调用
flush()
和clear()
进行改进,如下所示
int i = 0;
for(Element element: elements) {
dao.save(element);
if(++i % 20 == 0) {
dao.flushAndClear();
}
}
不要创建元素列表并保存这些元素,而是保存单个元素。每隔一段时间进行
刷新和清除,以防止脏检查成为瓶颈
@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.
如果您的JDBC驱动程序支持,这将把50个单insert语句转换为1个大批量insert,即50个insert转换为1个insert
另请参见,因为加载实体似乎是一个瓶颈,您只需要执行插入操作,即您知道数据库中不存在实体,因此可能不应该使用Spring Data JPA的标准保存方法
原因是它执行了一个合并
,这会触发Hibernate来加载数据库中可能已经存在的实体
相反,请向存储库中添加一个,该存储库在实体管理器上执行持久化
。由于您事先设置了Id
,请确保您有一个版本属性,以便Hibernate可以确定这确实是一个新实体
这将使选择消失
其他答案中给出的其他建议值得作为第二步考虑:
- 启用批处理
- 尝试中间冲洗和清除会话
- 一次保存一个实例,而不将它们收集到集合中,因为调用
merge
或persist
实际上不会触发对数据库的写入,但只有刷新才会触发(这是一种简化,但应适用于此上下文)
我们在项目中使用spring数据jpa,插入元素从未花过这么长时间。可能根本原因与spring数据jpa不同,首先你不应该转换,将其放在列表中,然后保存所有元素。你已经有效地复制了所有元素(这将增加内存)。此外,您在一个大型事务中执行所有操作,这也会导致问题。相反,请直接保存创建的元素,并对每个x元素(例如50个)执行刷新
和清除
。最好您还将刷新模式设置为手动(以防止在其间进行脏检查和刷新)。由于大部分时间用于加载数据,因此清除缓存实际上可能会降低性能。
spring.jpa.properties.hibernate.jdbc.batch_size=50