[英]Hibernate performance issue while inserting massive data
我們將從Amazon的DynamoDB遷移大量數據(單一類型的實體)到MySQL數據庫。 我們使用Hibernate將這個類映射到一個mysql實體。 大約有300萬個實體(不包括列屬性行) 。 這是我們的類映射摘要:
@Entity
@Table(name = "CUSTOMER")
public class Customer {
@Id
@Column(name = "id")
private String id;
//Other properties in which all of them are primitive types/String
@ElementCollection
@CollectionTable(name = "CUSTOMER_USER", joinColumns = @JoinColumn(name = "customer_id"))
@Column(name = "userId")
private List<String> users;
// CONSTRUCTORS, GETTERS, SETTERS, etc.
}
users是String列表。 我們創建了兩個mysql表,如下所示:
CREATE TABLE CUSTOMER(id VARCHAR(100), PRIMARY KEY(id));
CREATE TABLE CUSTOMER_USER(customer_id VARCHAR(100), userId VARCHAR(100), PRIMARY KEY(customer_id, userId), FOREIGN KEY (customer_id) REFERENCES CUSTOMER(id));
注意:我們不會讓hibernate生成任何id值,我們將我們的ID分配給保證唯一的Customer實體。
這是我們的hibernate.cfg.xml:
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect"> org.hibernate.dialect.MySQLDialect </property>
<property name="hibernate.connection.driver_class"> com.mysql.jdbc.Driver </property>
<property name="hibernate.connection.url"> jdbc:mysql://localhost/xxx </property>
<property name="hibernate.connection.username"> xxx </property>
<property name="hibernate.connection.password"> xxx </property>
<property name="hibernate.connection.provider_class">org.hibernate.c3p0.internal.C3P0ConnectionProvider</property>
<property name="hibernate.jdbc.batch_size"> 50 </property>
<property name="hibernate.cache.use_second_level_cache">false</property>
<property name="c3p0.min_size">30</property>
<property name="c3p0.max_size">70</property>
</session-factory>
</hibernate-configuration>
我們正在創建一些線程,每個線程從Dynamo讀取數據並通過Hibernate將它們插入我們的MySQl DB。 以下是每個線程的作用:
// Each single thread brings resultItems from DynamoDB
Session session = factory.openSession();
Transaction tx = session.beginTransaction();
for(int i = 0; i < resultItems.size(); i++) {
Customer cust = new Customer(resultItems.get(i));
session.save(cust);
if(i % BATCH_SIZE == 0) {
session.flush();
session.clear();
}
}
tx.commit();
session.close();
我們擁有自己的性能監控功能,並且我們不斷記錄整體讀/寫性能。 問題是,遷移從讀取/寫入1500項/秒(平均)開始,但只要CUSTOMER和CUSTOMER_USER表中的行數增加(幾分鍾后,r / w速度大約為500項/秒)。 我對Hibernate沒有經驗,這是我的問題:
注1:我沒有編寫所有屬性,因為除用戶列表以外的其余屬性都是int,boolean,String等。
注2:所有點都經過測試,對性能沒有負面影響。 當我們不向mysql db插入任何內容時,讀取速度保持穩定數小時。
注3 :有關mysql表結構,配置設置,會話/事務,連接池數量,批量大小等的任何建議/指導將非常有用!
假設你在hibernate事務中沒有做任何其他事情而不僅僅是將數據插入這兩個表中,你可以使用StatelessSession session = sessionFactory.openStatelessSession();
而不是正常會話,這減少了維護緩存的開銷。 但是,您必須單獨保存嵌套的集合對象。 請參閱https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/batch.html
所以它可能是這樣的 -
// Each single thread brings resultItems from DynamoDB
StatelessSession session = factory.openStatelessSession();
Transaction tx = session.beginTransaction();
for(int i = 0; i < resultItems.size(); i++) {
Customer cust = new Customer(resultItems.get(i));
Long id = session.save(cust); // get the generated id
// TODO: Create a list of related customer users and assign the id to all of them and then save those customer user objects in the same transaction.
if(i % BATCH_SIZE == 0) {
session.flush();
session.clear();
}
}
tx.commit();
session.close();
在您的方案中,有25個線程將數據批量插入到一個表中。 MySQL必須維護ACID屬性,而一個表中的許多記錄的25個事務保持打開或正在提交。 這可能會導致巨大的開銷。
從數據庫遷移數據時,當與數據庫進行許多來回通信時,網絡延遲可能會導致嚴重的延遲。 在這種情況下,使用多個線程可能是有益的。 但是,在進行批量提取和批量插入時,由於數據庫驅動程序將(或應該)在不進行大量來回通信的情況下進行數據通信,因此幾乎無法獲得。
在批處理方案中,從1個線程開始,該線程讀取數據,准備批處理並將其放入隊列中,用於從准備好的批處理中寫入數據的1個線程。 保持批量較小(100到1 000條記錄)並經常提交(每100條記錄左右)。 這將最小化維護表的開銷。 如果網絡延遲是一個問題,請嘗試使用2個線程進行讀取,使用2個進行寫入(但任何性能增益可能會被維護2個線程同時使用的表的開銷所抵消)。
由於沒有生成的ID,您應該受益於hibernate配置中已有的hibernate.jdbc.batch_size選項。 hibernate.jdbc.fetch_size
選項(將其設置為250左右)也可能是有意義的。
正如@ hermant1900所提到的,使用StatelessSession
也是一個好主意。 但到目前為止,@ Rob在評論中提到了最快的方法:使用數據庫工具將數據導出到文件並將其導入MySQL 。 我很確定這也是首選方法:它花費的時間更少,處理更少,涉及的變量更少 - 總體來說更可靠。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.