我目前正在基于hibernate的应用程序中执行性能和内存调整,以进行大量批量/批量导入。 我们基本上导入一个带有产品数据的CSV文件,其中一些产品是新的(插入),一些是存在的(更新)。
我现在的重点是选择一个策略来找出哪些实体要更新,哪些实体要INSERT,而不对CSV文件中的每一行进行检查(选择是否存在)。
我目前的做法是这样的:
这种方法效果很好,测试证明它比每行进行这样一次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语句,但是如果数据库中已有数千个实体,我可以更好地控制内存使用。
您对此有何看法? 还有哪些有效的方法来确定要更新哪些实体以及批量插入哪些实体?
答案 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列表变大时)