如何使用 Java 高效地准备包含大数据的映射表
Posted
技术标签:
【中文标题】如何使用 Java 高效地准备包含大数据的映射表【英文标题】:How to prepare mapping table with large data efficiently using Java 【发布时间】:2020-08-05 21:20:24 【问题描述】:假设我有两个表,TABLE_A
和 TABLE_B
。我想创建第三个表TABLE_C
,它只是一个包含两列的映射表,主键TABLE_A
和TABLE_B
。 TABLE_A 和 TABLE_B 分别包含 8000 万和 1.5 亿条记录。目前,我已经在 DB2(即我的 DB)中编写了一个存储过程,它将首先连接并从两个表中选择记录并使用游标进行迭代。在此迭代期间,我将插入第三个表并进行中间提交。
现在这个存储过程要运行很长时间(大约 5 小时)才能完成,因为在 DB2 存储过程中不可能进行多行插入(这就是我所理解的)。是否可以通过一次获取记录并使用 JDBC Batch 从 Java 插入来将其移入 Java,以便插入将在多行中工作。但是如何在Java中保存这么大的结果集而不会出现OutOfMemoryError
有人可以建议最好的方法吗?
更新:
DECLARE GLOBAL TEMPORARY TABLE SESSION.TABLE_DGTT AS (
select a.type_id as type_id,
a.a_id as a_id,
b.b_id as b_id
from TABLE_A a
inner join TABLE_B b on
AND b.process_date = i_order_date
AND b.tgt_date = CASE WHEN
a.tgt_date < i_order_date
THEN i_order_date ELSE a.tgt_date END
where not exists ( select 1 from TABLE_C c where
a.type_id = c.type_id
AND a.a_id = c.a_id)
AND a.process_date <= i_order_date
)
WITH NO DATA NOT LOGGED ON COMMIT PRESERVE ROWS ;
DECLARE C1 CURSOR FOR SELECT * FROM SESSION.TABLE_DGTT;
LOAD FROM C1 OF CURSOR INSERT INTO TABLE_C NONRECOVERABLE;
【问题讨论】:
旁注:您的CASE
是一个简单的MAX(a.tgt_date, i_order_date)
。制作 DGTT 所花费的大部分时间将是 WHERE NOT EXISTS
子句 - 如果您将其作为一次性加载,您应该能够直接运行 INSERT
(进入TABLE_C
) 声明。
【参考方案1】:
值得尝试LOAD command using the ADMIN_CMD procedure 使用基于源表的SELECT 语句的游标。它适用于大数据负载。
如果您不熟悉 LOAD 实用程序,请使用 NONRECOVERABLE
选项。最好阅读 LOAD 选项以了解副作用。
【讨论】:
【参考方案2】:我会完全避免逐行处理,因为对于这类问题,它总是比基于集合的替代方法慢得多。
如果速度是最重要的考虑因素,您应该会发现,在可能的情况下使用未记录的技术在数据库内部执行操作是最快的方法。但有时其他因素比速度更重要。
考虑更改您的存储过程,以使用基于集合的方法。
如果单个查询(多表连接)足以指定最终结果集(对于您的table_C
,那么您不需要任何临时表,您可以考虑要么使用未记录的加载来填充最终表(见下文)或普通记录的插入。
如果处理需要一个或多个临时表来创建最终结果集,一种选择是在存储过程中创建一个或多个not logged
DGTT(声明全局临时表),其结构与最终表匹配(您的问题中的 TABLE_C)。如果需要多个工作单元来填充 DGTT(即需要多个 insert
、update
、delete
、merge
语句),您可以将 on commit preserve rows
用于 DGTT。
然后使用 SQL 填充该会话表,避免逐行处理(不要使用游标填充 DGTT,使用集合处理,例如 insert into session.xxxx (...) select...
或 merge
等)。您可以根据需要使用尽可能多的语句来填充会话表并提交以释放任何锁。
如果合适的话,压缩 DGTT。如果合适的话,对 DGTT 进行分区(通过哈希)。如果合适,动态索引 DGTT 并运行它。所有这些都可以在存储过程中实现。
当您对 DGTT 内容正确感到满意时,您就可以填充决赛桌了。
请记住,如果您确实需要在编写示例中的最终表 (table_c) 之前暂存结果集或进行额外的处理或验证,则只需要 DGTT。
要填充最终表(来自具有单个多表连接的原始源表,或者来自您已完成任何所需处理的 DGTT),您可以或者使用通过 ADMIN_CMD 存储过程取消记录 load
命令(如果您的 Db2 服务器平台支持此功能),或者您可以使用(慢得多)INSERT INTO TABLE_C(...) SELECT ... FROM...
。对于已记录的插入,根据事务日志的可用容量,您可能需要批处理此步骤。
要使用load
(通过admin_cmd())需要仔细考虑和计划,并且需要理解所涉及的问题和权衡,因此请仔细研究文档或聘请有经验的程序员来做工作。
此解决方案需要编程技能、能力和经验。
【讨论】:
两个疑惑;您说“然后使用 SQL 填充该表,避免逐行处理。”如何避免逐行处理?其次,从 DGTT 再次使用光标进行填充,该光标将插入到 TableC 中,就像它在我现有的 SP 中一样? 已添加说明。 使用“插入”方法填充到 DGTT 应该在多个集合中完成,对吗?但是怎么可能你有任何链接/参考作为例子吗 哪一部分你不明白?您是否从未在存储过程中使用过未记录的 DGTT?将您的 DGTT 定义为 unlogged 和on commit preserve rows
并且您可以在存储过程中任意频繁地插入/更新/删除/合并,对于隔离级别而言,尽可能多的提交是有意义的。为什么你需要一个参考?
对不起,我不是数据库专家。一旦我声明了 DGTT,我将如何在多个批次中使用 insert into session.xxxx (...) select... 因为我在存储过程中进行操作。以上是关于如何使用 Java 高效地准备包含大数据的映射表的主要内容,如果未能解决你的问题,请参考以下文章
java大文件读写操作,java nio 之MappedByteBuffer,高效文件/内存映射
JavaNIO的深入研究4内存映射文件I/O,大文件读写操作,Java nio之MappedByteBuffer,高效文件/内存映射