5

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

我现在的重点是选择一种策略来找出要更新哪些实体以及要插入哪些实体,而无需对 CSV 文件中的每一行进行检查(如果存在则选择)。

我目前的做法是这样的:

  1. 构建数据库内所有对象的哈希图。
  2. 遍历 CSV 并使用 hashmap 来决定是更新还是插入。

这种方法效果很好,测试证明它比对每一行进行这样的单个 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}

顺便说一句:我也已经假设像(if i % 30 == 0) session.flush; session.clear();

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

文件 {sku1, sku2, sku3, sku6} - updatemap {sku1, sku2, sku3} = newinserts {sku6}

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

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

您对此有何想法?还有哪些其他有效的方法可以找出要更新哪些实体以及要批量插入哪些实体?

4

2 回答 2

2

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

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

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

于 2012-08-26T17:28:11.060 回答
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 将更新 sku。但是这些运算符是特定于数据库的,我不确定看到了多少性能提升。但是恕我直言,看起来比where in条款更好的选择(尤其是当 csv 列表变大时)

于 2012-08-26T19:04:32.230 回答