Java EclipseLink批量插入非常非常慢
我正在尝试使用EclipseLink优化我的JPA实现。 我给它添加了批处理操作。但是要做50000个插入仍然需要很多时间。使用原始SQL和JDBC进行完全相同的插入所需的时间是使用原始SQL所需时间的10倍多 为了确保批处理操作确实有效,我使用Wireshark检查我的数据包,但它没有使用批插入 以下是其中一个插入数据包: 它没有做到:Java EclipseLink批量插入非常非常慢,java,mysql,jpa,eclipselink,Java,Mysql,Jpa,Eclipselink,我正在尝试使用EclipseLink优化我的JPA实现。 我给它添加了批处理操作。但是要做50000个插入仍然需要很多时间。使用原始SQL和JDBC进行完全相同的插入所需的时间是使用原始SQL所需时间的10倍多 为了确保批处理操作确实有效,我使用Wireshark检查我的数据包,但它没有使用批插入 以下是其中一个插入数据包: 它没有做到: INSERT INTO ENTITYCLASSTEST (LASTNAME, NAME) VALUES ('sfirosijfhgdoi 0', 'dsuf
INSERT INTO ENTITYCLASSTEST (LASTNAME, NAME) VALUES ('sfirosijfhgdoi 0', 'dsufius0'), ('sfirosijfhgdoi 0', 'dsufius0'), ('sfirosijfhgdoi 0', 'dsufius0'), ('sfirosijfhgdoi 0', 'dsufius0')... and so on
我希望它能像上面那样做,但它是每个包插入一行,而不是每个包插入多行
这是我的实体类:
@Entity
public class EntityClassTest implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String lastname;
public EntityClassTest() {
}
public EntityClassTest(Long id, String name, String lastname) {
this.id = id;
this.name = name;
this.lastname = lastname;
}
public EntityClassTest(String name, String lastname) {
this.name = name;
this.lastname = lastname;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public String getLastName() {
return lastname;
}
public void setId(Long id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setLastName(String lastname) {
this.lastname = lastname;
}
@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof EntityClassTest)) {
return false;
}
EntityClassTest other = (EntityClassTest) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
public String toString() {
return "database.EntityClassTest [id=" + id + " ]";
}
}
这里是我的persist方法,它接收一个列表
,并保存其中的所有对象
public void insertListToTable(final String persistenceUnit, final List list) throws SQLException {
final EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(persistenceUnit);
final EntityManager entityManager = entityManagerFactory.createEntityManager();
final EntityTransaction transaction = entityManager.getTransaction();
try {
final int listSize = list.size();
transaction.begin();
for (int i = 0; i<listSize; i++) { //Object object : list) {
final Object object = list.get(i);
entityManager.persist(object);
if ( i % 500 == 0 ) { //500, same as the JDBC batch size defined in the persistence.xml
//flush a batch of inserts and release memory:
entityManager.flush();
entityManager.clear();
}
}
transaction.commit();
}
catch(Exception e) {
if (transaction != null) {
transaction.rollback();
}
throw new SQLException(e.getMessage());
}
finally {
entityManager.close();
}
}
所以我的问题是,为什么不是批量插入?我相信我在EclipseLink网站上和这里所读到的内容中,已经对EclipseLink进行了很好的配置
/////////EDIT///////////strong>
根据Chris的回答,我将
EntityClassTest
中的该值@GeneratedValue(strategy=GenerationType.IDENTITY)
更改为@GeneratedValue(strategy=GenerationType.SEQUENCE)
,然后重新运行测试,数据包将像以前一样发送(如我上面发布的图像)。恐怕它没能解决我的问题
/////EDIT 2/////strong>
我已将persistence.xml
文件中的日志记录级别更改为FINEST
,如下所示
<property name="eclipselink.logging.level" value="FINEST"/>
//编辑4//////strong>
根据CuriousMind的回答,我将我的EntityClassTest
id注释编辑为:
@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator="id-seq-gen")
@SequenceGenerator( name="id-seq-gen", sequenceName="ID_SEQ_GEN", allocationSize=500 )
private Long id;
但这并没有解决我的问题,我仍然得到每个数据包一次插入(如上图所述),并且在EclipseLink
日志中我得到:
[EL Fine]: sql: 2014-10-19 06:44:02.608--ClientSession(824177287)--Connection(1674390738)--Thread(Thread[main,5,main])--SELECT LAST_INSERT_ID()
[EL Finest]: sequencing: 2014-10-19 06:44:02.608--UnitOfWork(1985011414)--Thread(Thread[main,5,main])--assign sequence to the object (1.251 -> database.EntityClassTest [id=null ])
[EL Finest]: query: 2014-10-19 06:44:02.608--UnitOfWork(1985011414)--Thread(Thread[main,5,main])--Execute query InsertObjectQuery(database.EntityClassTest [id=null ])
[EL Finest]: query: 2014-10-19 06:44:02.608--ClientSession(824177287)--Thread(Thread[main,5,main])--Execute query ValueReadQuery(name="ID_SEQ_GEN" sql="SELECT LAST_INSERT_ID()")
[EL Fine]: sql: 2014-10-19 06:44:02.608--ClientSession(824177287)--Connection(1674390738)--Thread(Thread[main,5,main])--INSERT INTO ENTITYCLASSTEST (LASTNAME, NAME) VALUES (?, ?)
bind => [sfirosijfhgdoi 2068, dsufius1034]
[EL Fine]: sql: 2014-10-19 06:44:02.608--ClientSession(824177287)--Connection(1674390738)--Thread(Thread[main,5,main])--SELECT LAST_INSERT_ID()
[EL Finest]: sequencing: 2014-10-19 06:44:02.608--UnitOfWork(1985011414)--Thread(Thread[main,5,main])--assign sequence to the object (1.252 -> database.EntityClassTest [id=null ])
[EL Finest]: query: 2014-10-19 06:44:02.608--UnitOfWork(1985011414)--Thread(Thread[main,5,main])--Execute query InsertObjectQuery(database.EntityClassTest [id=null ])
[EL Finest]: query: 2014-10-19 06:44:02.608--ClientSession(824177287)--Thread(Thread[main,5,main])--Execute query ValueReadQuery(name="ID_SEQ_GEN" sql="SELECT LAST_INSERT_ID()")
[EL Fine]: sql: 2014-10-19 06:44:02.608--ClientSession(824177287)--Connection(1674390738)--Thread(Thread[main,5,main])--INSERT INTO ENTITYCLASSTEST (LASTNAME, NAME) VALUES (?, ?)
bind => [sfirosijfhgdoi 2244, dsufius1122]
等等…您正在使用GenerationType.IDENTITY进行排序,这需要从每个insert语句中逐个检索ID。尝试一种允许批量预分配500份的排序方案,您将看到改进:
似乎是序列生成导致了问题,请参考此。它提供了预分配序列生成的方法。已经一年了,可能已经来不及回答了。在我的例子中,我发现flush()导致了这个问题。我在persist()之后为每个记录调用flush()。这会阻止批量写入以进行优化,并导致插入性能差。删除flush()后,一切都进行得很顺利。这可能是因为序列/标识(需要使用预分配),但对于mysql,我认为您需要一个连接字符串属性来允许批量插入: rewriteBatchedStatements=true
jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
我将我的
EntityClassTest
中的该值@GeneratedValue(strategy=GenerationType.IDENTITY)
更改为@GeneratedValue(strategy=GenerationType.SEQUENCE)
,然后重新运行测试,数据包将像以前一样发送(就像我在问题中发布的图像一样)。因此,恐怕它并没有解决我的问题。您需要能够在批处理中获得与批处理写入大小匹配的序列,否则JPA必须中断批处理以获得额外的序列值。我不相信MySQL有序列,所以它默认为Identity;您需要改用表排序。看到了吗?请发布您正在使用的每个软件的版本,如MySQL、Eclipse链接、MySQL驱动程序等。您是否尝试将rewriteBatchedStatements=true添加到连接属性中?@Márciosuzajúnior您是对的。我忘了在persistence.xml
文件中向连接添加rewriteBatchedStatements=true
,我把它放在了我的JDBC中,错误地认为我在persistence.xml
中也有它。这部分解决了我的问题。克里斯的回答确实解决了我的问题。@keerate你太棒了!解决了我的案子。
[EL Fine]: sql: 2014-10-19 06:44:02.608--ClientSession(824177287)--Connection(1674390738)--Thread(Thread[main,5,main])--SELECT LAST_INSERT_ID()
[EL Finest]: sequencing: 2014-10-19 06:44:02.608--UnitOfWork(1985011414)--Thread(Thread[main,5,main])--assign sequence to the object (1.251 -> database.EntityClassTest [id=null ])
[EL Finest]: query: 2014-10-19 06:44:02.608--UnitOfWork(1985011414)--Thread(Thread[main,5,main])--Execute query InsertObjectQuery(database.EntityClassTest [id=null ])
[EL Finest]: query: 2014-10-19 06:44:02.608--ClientSession(824177287)--Thread(Thread[main,5,main])--Execute query ValueReadQuery(name="ID_SEQ_GEN" sql="SELECT LAST_INSERT_ID()")
[EL Fine]: sql: 2014-10-19 06:44:02.608--ClientSession(824177287)--Connection(1674390738)--Thread(Thread[main,5,main])--INSERT INTO ENTITYCLASSTEST (LASTNAME, NAME) VALUES (?, ?)
bind => [sfirosijfhgdoi 2068, dsufius1034]
[EL Fine]: sql: 2014-10-19 06:44:02.608--ClientSession(824177287)--Connection(1674390738)--Thread(Thread[main,5,main])--SELECT LAST_INSERT_ID()
[EL Finest]: sequencing: 2014-10-19 06:44:02.608--UnitOfWork(1985011414)--Thread(Thread[main,5,main])--assign sequence to the object (1.252 -> database.EntityClassTest [id=null ])
[EL Finest]: query: 2014-10-19 06:44:02.608--UnitOfWork(1985011414)--Thread(Thread[main,5,main])--Execute query InsertObjectQuery(database.EntityClassTest [id=null ])
[EL Finest]: query: 2014-10-19 06:44:02.608--ClientSession(824177287)--Thread(Thread[main,5,main])--Execute query ValueReadQuery(name="ID_SEQ_GEN" sql="SELECT LAST_INSERT_ID()")
[EL Fine]: sql: 2014-10-19 06:44:02.608--ClientSession(824177287)--Connection(1674390738)--Thread(Thread[main,5,main])--INSERT INTO ENTITYCLASSTEST (LASTNAME, NAME) VALUES (?, ?)
bind => [sfirosijfhgdoi 2244, dsufius1122]
jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true