2010-06-23 78 views
4

我正在嘗試使用JPA在大對象圖上進行級聯保存。例如(我的對象圖是大了一點,但足夠接近):JPA插入緩慢的對象圖

@Entity 
@Table(name="a") 
public class A { 
    private long id; 
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "a") 
    private Collection<B> bs; 
} 

@Entity 
@Table(name="b") 
public class B { 
    private long id; 
    @ManyToOne 
    private A a; 
} 

所以我想堅持一個擁有超過100點的B的集合。代碼只是

em.persist(a); 

問題是,它很慢。我的保存時間約爲1300毫秒。我查看了正在生成的SQL,效率非常低下。事情是這樣的:

select a_seq.nextval from dual; 
select b_seq.nextval from dual; 
select b_seq.nextval from dual; 
select b_seq.nextval from dual; 
... 
insert into a (id) values (1); 
insert into b (id, fk) values (1, 1); 
insert into b (id, fk) values (2, 1); 
insert into b (id, fk) values (3, 1); 
... 

目前使用的TopLink作爲持久性提供,但我試過的EclipseLink也冬眠。後端是oracle 11g。問題實際上是如何將sql放在一起。這些操作中的每一個都是分散完成的,而不是批量完成,所以如果我的應用服務器和數據庫服務器之間的網絡延遲爲5毫秒,則執行200次離散操作會增加1秒。我試過增加我的序列的分配大小,但只有一點幫助。我也試過直接使用JDBC作爲批處理聲明:

for...{ 
    statement = connection.prepareStatement(sql); 
    statement.addBatch(); 
} 
statement.executeBatch(); 

對於我的數據模型需要花費大約爲33ms進行直接JDBC批處理。 Oracle本身對於100多個插入需要5ms。

是否有任何使JPA(我現在堅持1.0)......無需鑽研供應商特定的東西,如冬眠批量插入更快?

謝謝!

回答

2

的解決辦法是讓JDBC批處理和沖洗,清除了EntityManager定期(而不是批量大小相同),但我不知道的廠商中立的方式做到這一點:

  • 使用Hibernate,您必須設置hibernate.jdbc.batch_size配置選項。請參閱Chapter 13. Batch processing

  • 對於EclipseLink,它看起來像是批處理寫入模式。見傑夫·薩瑟蘭的帖子this thread(應該也可以指定大小)。

  • this blog post的意見,批量寫入不可用的TopLink要點:(

+0

感謝您的迴應!將發佈我在下面做的事情! – user364939 2010-06-25 15:38:08

+0

感謝您的信息,非常好 – Greg 2011-04-06 23:23:16

1

感謝帕斯卡爾的反應。我已經做了一些測試,我能顯著提高性能。

由於沒有優化我有一個插入,大約需要1100毫秒用我加入的persistence.xml的EclipseLink:

<property name="eclipselink.jdbc.batch-writing" value="JDBC"/> 
    <property name="eclipselink.jdbc.batch-writing.size" value="1000"/> 

我嘗試了其他屬性(Oracle-JDBC等),但JDBC看起來能夠提供最佳性能。這使插入下降到約900毫秒。所以200ms的性能相當適中。增加序列分配大小節省了大量資金。我不是做這件事的粉絲。我發現爲了適應JPA而增加我的序列的INCREMENT BY是很髒的。增加這些時間使每個插件的時間降低到大約600ms。所以總共大約500毫秒被削減與這些增強。

所有這些都很好,很棒,但它仍然比JDBC批處理速度慢得多。爲便於編碼,JPA的代價相當高昂。

+0

感謝您的反饋。我應該注意到'allocateSize'。 +1 – 2010-06-25 16:27:34

2

好奇你爲什麼會發現將INCREMENT BY增加爲髒?這是一種優化,它減少了調用數據庫以檢索下一個序列值的次數,並且是在INSERT之前在客戶機中分配id值的數據庫客戶機中使用的常見模式。我不認爲這是JPA或ORM問題,並且在JDBC比較中的成本應該是相同的,因爲它必須在INSERT之前爲每個新行檢索新的序列號。如果您在JDBC情況下有不同的方法,那麼我們應該能夠使EclipseLink JPA遵循相同的方法。

JPA的成本可能在隔離INSERT場景中最明顯,因爲您沒有從重複讀取事務或共享緩存中獲得任何好處,並且取決於您爲支付這些新實體的價格而付出的緩存配置flush/commit中的緩存。

請注意,創建第一個EntityManager也需要花費所有的元數據處理,類加載,可能的編織和元模型初始化。確保你保持這個時間超出你的比較。在您的真實應用程序中,只會發生一次,所有後續的EntityManager都將從共享元數據中受益。

如果您還有其他需要讀取這些實體的場景,那麼將它們放入緩存的成本可以降低其檢索成本。根據我的經驗,我可以使整個應用程序的總體速度比典型的手寫JDBC解決方案快得多,但它在整個併發用戶集合中保持平衡,而不是在單獨的測試用例上。

我希望這會有所幫助。很高興提供更多指導和EclipseLink JPA及其性能和可伸縮性選項。

Doug

+0

感謝您的回覆。對於像oracle這樣的數據庫進行序列獲取,我不確定爲什麼你不能在插入語句(my_seq.nextval)中放置這個權利。多次這樣做的網絡延遲會導致減速。在oracle中獲取下一個序列值所需的時間在統計上是不顯着的。 – user364939 2010-12-10 00:21:29

+2

在大多數數據庫中,在INSERT語句內賦值非常快。挑戰在於,您通常還需要應用程序中用於緩存,維護身份或級聯主鍵的應用程序的新值。如果您的數據庫支持在INSERT中使用nextval,則它還必須從INSERT中返回值以供JPA提供程序使用。 – 2011-02-08 13:24:29

+0

好點..... – user364939 2011-09-29 23:45:58