Hibernate Mass Insert / update:这是一个很好的方法吗?

时间:2012-08-26 16:48:47

标签: java sql hibernate jdbc

我目前正在基于hibernate的应用程序中执行性能和内存调整,以进行大量批量/批量导入。 我们基本上导入一个带有产品数据的CSV文件,其中一些产品是新的(插入),一些是存在的(更新)。

我现在的重点是选择一个策略来找出哪些实体要更新,哪些实体要INSERT,而不对CSV文件中的每一行进行检查(选择是否存在)。

我目前的做法是这样的:

  1. 构建数据库内所有对象的散列图。
  2. 迭代CSV并使用hashmap决定是否更新或插入。
  3. 这种方法效果很好,测试证明它比每行进行这样一次IF EXISTS检查的速度要快。

    如果数据库中有很多实体,我关心的是内存大小。

    现在我考虑使用上述方法的略微变化,我想知道意见。 基本上我想做的是用多行进行多批IF EXISTS检查(例如SELECT FROM table where sku IN (sku1, sku2, sku3)

    这是一些伪代码:

    1. Database contains: db{sku1, sku2,sku3,sku5}
    
    2. file contains: file {sku1, sku2, sku3, sku6}
    
    3. Expected result: 
       updates: {sku1, sku2, sku3}
       inserts{sku6}
    
    4. Algorithm
    
       have a map to keep database entities which need updates
       updatemap {}
       now iterate over the file in e.g. batches of 2 rows (for demo purposes)
       1st iteration: foreach (select where sku IN (sku1, sku2) limit 2) as elem
        -> updatemap.add(elem)  -> elem is asumed to be a persistent entity here
        -> myDAO.update(elem)   -> executes Spring's getHibernateTemplate().update() under the hood
    
       -> updatemap contents after 1st loop {sku1, sku2}
    
       2nd iteration: foreach (select where sku IN (sku3, sku6) limit) 2 as elem
        -> updatemap.add(elem)    
        -> myDAO.update(elem)
    
       -> updatemap contents after 3nd loop {sku1, sku2, sku3}
    

    btw:我也已经假设像(if i % 30 == 0) session.flush; session.clear();

    这样的东西了

    现在我们知道所有已更新的元素。不在updatemap中的所有skus基本上都是插入的,我们可以使用简单的集算术来确定那些

      

    file {sku1,sku2,sku3,sku6} - updatemap {sku1,sku2,sku3} = newinserts {sku6}

    现在我们可以继续为剩余的CSV行插入。

    结论 我的假设是,由于文件内容的分块,我可以限制使用的内存量。我有比我最初的方法更多的SELECT语句,但是如果数据库中已有数千个实体,我可以更好地控制内存使用。

    您对此有何看法? 还有哪些有效的方法来确定要更新哪些实体以及批量插入哪些实体?

2 个答案:

答案 0 :(得分:2)

我遇到了完全相同的问题,涉及数百万条记录,并且与您完全一样解决了这个问题。对于侧面观察者来说可能不明显的约束是我们不能使用常规的Hibernate方式进行load-mutate-update,因为这会产生过多的冗余流量。

仔细阅读后,我的方法与您的方法不同,因为除了处理单个块之外,我不会保留任何信息。在继续下一个之前,我完整地处理了块,包括所有插入和更新。只有这样,您才能拥有可扩展的解决方案。

对我来说,最薄弱的一点是使用executeUpdate,它不会使用JDBC批处理API。我计划进行自定义实现,但对于我的特定用例,我发现每个块不需要使用多个executeUpdate

答案 1 :(得分:0)

我的想法

1)当你这样做时 SELECT FROM table where sku IN (sku1, sku2, sku3) )

当找不到sku时,每个查询可能会执行全表扫描,如果你对n个传递中的剩余实体执行此操作,则最坏情况下可能需要n *个表扫描。

也许更简单的方法是为csv中的所有实体创建一个重复的表(skus可能只有一列,并执行MINUS以插入新的skus)

 select sku from dup_table
  MINUS  //(EXCEPT for Mysql)
 select sku from table`

您可以将这些记录保存到新表(dup_table2)中,并在dup_table上执行另一个MINUS将使skus更新。但这些运算符是特定于数据库的,我不确定看到多少性能增益。但恕我直言看起来比where in子句更好(特别是当csv列表变大时)