簡體   English   中英

插入海量數據時出現Hibernate性能問題

[英]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. 對於像我們這樣的多線程任務,hibernate.cfg.xml應該是什么樣的? 我上面給出的內容是否適合這樣的任務,還是有任何錯誤/缺失點?
  2. 有50個線程,每個線程都遵循:首先從DynamoDB讀取,然后將結果插入mysql db,然后從dynamo讀取,依此類推。 因此,與休眠通信的正常運行時間不是100%。 在這種情況下,您建議設置c3p0連接池大小的min_size和max_size? 為了能夠理解這個概念,我是否還應該在hibernate.cfg.xml中設置剩余的c3p0相關標簽?
  3. 如何最大限度地提高批量插入的速度?

注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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM