Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/343.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 用Camel并行处理大型SQL表_Java_Apache Camel_Informix - Fatal编程技术网

Java 用Camel并行处理大型SQL表

Java 用Camel并行处理大型SQL表,java,apache-camel,informix,Java,Apache Camel,Informix,我正在尝试使用ApacheCamel每天从Informix表中处理大约700万行,但我不知道如何实现 我的第一次尝试是使用非常少的数据集(大约50k行)使用.splitbody.parallelProcessing,如下所示: 当我在.beanQueryTable.class、queryData.splitbody.parallelProcessing上尝试使用500k行时,这当然会导致OutOfMemory错误,因为它在解析查询之前首先尝试缓存查询中的所有数据。我尝试将fetchSize设置为

我正在尝试使用ApacheCamel每天从Informix表中处理大约700万行,但我不知道如何实现

我的第一次尝试是使用非常少的数据集(大约50k行)使用.splitbody.parallelProcessing,如下所示:

当我在.beanQueryTable.class、queryData.splitbody.parallelProcessing上尝试使用500k行时,这当然会导致OutOfMemory错误,因为它在解析查询之前首先尝试缓存查询中的所有数据。我尝试将fetchSize设置为100左右,但我得到了相同的错误,使用maxRows只能得到我指定的行数,而忽略其余的行数

我的下一次尝试是使用Camel的一个组件,比如and,并尝试使用拆分器在单独的线程中处理每一行,但我遇到了同样的问题

sql:

jdbc:

我上一次尝试是对sql使用maxMessagesPerPoll,对jdbc组件使用outputType=StreamList,但不幸的是,前者一次只处理一行,而且它必须是一个使用者才能使用,而后者给了我一个java.sql.SQLException:Cursor not open exception

sql:

jdbc:

最终目标是能够在不消耗太多内存的情况下处理数百万行,从而防止OutOfMemory错误。如果可能的话,我的想法是:

在quartz cron触发器上创建我的查询 获得并分组N个数量的结果 在获取另一组结果时,将一组结果发送到另一个线程中进行处理 重复此步骤,直到处理完所有数据 我知道这个问题类似于,但答案对我的情况没有帮助。我还注意到,在sql组件的文档中,producer有一个outputType=StreamList选项,但它是在版本2.18和更高版本上实现的,而我有版本2.14.1

任何帮助和提示都会非常有用

谢谢

其他一些信息: ApacheCamel版本:2.14.1
数据库:Informix

经过相当多的研究、更多的尝试和错误以及来自NotaJD的测试,我找到了一个解决方案,可以解决仍然测试的问题。实际上,这两种解决方案都是相同的,但它们的执行方式不同

信息: 为了便于解释,我将使用以下信息:

表中有700万条记录行 AggregationStrategyImpl通过以下内容扩展AggregationStrategy: 返回exchange正文中的列表 当列表>=50000时,聚合谓词完成 聚合超时设置为30000毫秒 CustomThreadPool是Camel的ThreadPoolBuilder类的伪实现: 游泳池人数:100 最大池大小:50000 最大队列大小:500 时间单位:毫秒 KeepAliveTime:30000 这两种实现都是自动连接的 解决方案1: 代码仍将每天在Quartz cron计时器00:01上运行,但这次我的QueryTable.class将获取要执行的正确查询,而不是SELECT*,我现在指定了所需的列并将其设置为exchange正文

.to("jdbc:dataSourceInformix?resetAutoCommit=false&outputType=StreamList").split(body()).streaming()
.bean(TransformRecord.class, "process")
Camel jdbc组件将从exchange主体获取查询,将resetAutoCommit设置为false,这样它就不会抛出游标not open错误,将输出设置为streaming并拆分执行流,因此我不会一次查询所有记录,而是逐个查询。然后,通过TransformRecord.class将获取的每条记录转换为适当的POJO

这次我使用聚合组件创建记录列表。aggregationStrategyImpl包含聚合逻辑以及完成谓词和超时,因此当我达到一定数量的记录或发生超时时,列表将发送到direct:start processing

更多关于此源代码和ApacheCamel文档中的聚合实现的信息

在这里,我分割获得的列表,并使用自定义线程池创建N个线程来分别分析和处理每条记录。这样我就可以并行处理列表,而不是逐个处理。我本可以使用.splitbody.parallelProcessing,但默认的线程池设置在以后可能不是最佳的

更多关于ApacheCamel文档、notes和Red Hat文档上的线程池实现的信息

解决方案2: 对于此解决方案,基本上是extact相同的执行,但有以下更改:

// .to("direct:start-processing")
.to("seda:start-processing?size=1&blockWhenFull=true")
.end();
// from("direct:start-processing")
from("seda:start-processing?size=1&blockWhenFull=true")
// continues normally
这样做的目的是异步将列表发送给进程,允许最多1个其他列表在内存中排队,并在队列已满时暂停父线程。因此,父线程将返回并收集另一批记录,而不是等待处理记录列表。这还意味着,如果处理路径尚未完成,新记录将不会被抛出,父线程将等待,直到它可以将批发送到SEDA内存队列

有关SEDA组件的更多信息,请参阅其中的Apache Camel SEDA组件文档

骗局 结论: 对于解决方案1,它应该需要更长的时间才能完成,因为它首先处理所有数据,然后再从查询中收集更多记录,但是内存消耗应该要少得多,因为它是在聚合谓词中控制的

对于解决方案2,它应该快得多,因为它在处理前一批记录的同时从查询中收集下一批记录,但内存消耗会大得多,因为它最多可以容纳3个列表:正在处理的一个,SEDA队列中的一个和父线程收集的最新批在队列已满时暂停


我说过,我仍在测试这些解决方案,因为500k记录可以工作,但我仍在为将在其中实现此功能的服务器计算最佳线程池设置。我已经研究过Java中的线程,但是除了系统的体系结构、RAM和试错之外,似乎真的没有什么可以参考的。

经过相当多的研究、更多的试错和NotaJD的测试,我找到了一个解决方案,可以解决仍然测试的问题。实际上,这两种解决方案都是相同的,但它们的执行方式不同

信息: 为了便于解释,我将使用以下信息:

表中有700万条记录行 AggregationStrategyImpl通过以下内容扩展AggregationStrategy: 返回exchange正文中的列表 当列表>=50000时,聚合谓词完成 聚合超时设置为30000毫秒 CustomThreadPool是Camel的ThreadPoolBuilder类的伪实现: 游泳池人数:100 最大池大小:50000 最大队列大小:500 时间单位:毫秒 KeepAliveTime:30000 这两种实现都是自动连接的 解决方案1: 代码仍将每天在Quartz cron计时器00:01上运行,但这次我的QueryTable.class将获取要执行的正确查询,而不是SELECT*,我现在指定了所需的列并将其设置为exchange正文

.to("jdbc:dataSourceInformix?resetAutoCommit=false&outputType=StreamList").split(body()).streaming()
.bean(TransformRecord.class, "process")
Camel jdbc组件将从exchange主体获取查询,将resetAutoCommit设置为false,这样它就不会抛出游标not open错误,将输出设置为streaming并拆分执行流,因此我不会一次查询所有记录,而是逐个查询。然后,通过TransformRecord.class将获取的每条记录转换为适当的POJO

这次我使用聚合组件创建记录列表。aggregationStrategyImpl包含聚合逻辑以及完成谓词和超时,因此当我达到一定数量的记录或发生超时时,列表将发送到direct:start processing

更多关于此源代码和ApacheCamel文档中的聚合实现的信息

在这里,我分割获得的列表,并使用自定义线程池创建N个线程来分别分析和处理每条记录。这样我就可以并行处理列表,而不是逐个处理。我本可以使用.splitbody.parallelProcessing,但默认的线程池设置在以后可能不是最佳的

更多关于ApacheCamel文档、notes和Red Hat文档上的线程池实现的信息

解决方案2: 对于此解决方案,基本上是extact相同的执行,但有以下更改:

// .to("direct:start-processing")
.to("seda:start-processing?size=1&blockWhenFull=true")
.end();
// from("direct:start-processing")
from("seda:start-processing?size=1&blockWhenFull=true")
// continues normally
这样做的目的是异步将列表发送给进程,允许最多1个其他列表在内存中排队,并在队列已满时暂停父线程。因此,父线程将返回并收集另一批记录,而不是等待处理记录列表。这还意味着,如果处理路径尚未完成,新记录将不会被抛出,父线程将等待,直到它可以将批发送到SEDA内存队列

有关SEDA组件的更多信息,请参阅其中的Apache Camel SEDA组件文档

结论: 对于解决方案1,它应该需要更长的时间才能完成,因为它首先处理所有数据,然后再从查询中收集更多记录,但是内存消耗应该要少得多,因为它是在聚合谓词中控制的

对于解决方案2,它应该快得多,因为它在处理前一批记录的同时从查询中收集下一批记录,但内存消耗会大得多,因为它最多可以容纳3个列表:正在处理的一个,SEDA队列中的一个和父线程收集的最新批在队列已满时暂停

我说过,我仍在测试这些解决方案,因为500k记录可以工作,但我仍在为将在其中实现此功能的服务器计算最佳线程池设置。我已经研究过Java中的线程,但是除了系统的体系结构、RAM和试错之外,似乎真的没有什么可以参考的。

从文档中可以看出,StreamList工具可能是实现这一点的唯一方法。
或者,您可以创建一个类,手动进行SQL查询,获取一个游标,然后一次将批量(例如)10k行交给另一个路由进行处理。您可以使用seda队列并设置最大大小,在有可用性之前让您的自定义类睡眠,这样您就不会阻塞下游。@谢谢您对seda队列的建议,我将对此进行研究。至于游标,我尝试在QueryTable类中使用JdbcTemplate.fetchSize=100,但由于某种原因,该选项被忽略。我还尝试通过查询限制结果,尽管它在前2次迭代中有效,但随后的迭代减慢了过程并出错。JdbcTemplate是否关闭了自动提交?我记得有些驱动程序在自动提交和流式处理结果方面存在问题。另外,您如何验证是否忽略了fetchSize?此参数试图修复光标打开时返回的行数,例如,如果查询返回1000万行,它将从联机数据库中一次获取100行,不管怎样,这是理论。@NotaJD使用jdbc端点选项的resetAutoCommit=false时,它不会给我错误。至于fetchSize的验证,我设置了一个非常低的大小并尝试使用它,但它从未通过执行并抛出错误。我尝试使用RowCallbackHandler,这似乎很有效,但不幸的是,它在我的列表中的280k条记录中给了我一个超出GC开销限制的异常。您可以使用原始JdbcTemplate设置一个独立/隔离测试,执行查询并从记录集中成功读取每一行,即在rs.hasNext{rs.next;}时调用。如果它是流式处理和游标处理等,它不应该耗尽内存。从文档来看,StreamList功能可能是实现这一点的唯一方法。或者,您可以创建一个类,手动进行SQL查询,获取一个游标,然后一次将批量(例如)10k行交给另一个路由进行处理。您可以使用seda队列并设置最大大小,在有可用性之前让您的自定义类睡眠,这样您就不会阻塞下游。@谢谢您对seda队列的建议,我将对此进行研究。至于游标,我尝试在QueryTable类中使用JdbcTemplate.fetchSize=100,但由于某种原因,该选项被忽略。我还尝试通过查询限制结果,尽管它在前2次迭代中有效,但随后的迭代减慢了过程并出错。JdbcTemplate是否关闭了自动提交?我记得有些驱动程序在自动提交和流式处理结果方面存在问题。另外,您如何验证是否忽略了fetchSize?此参数试图修复光标打开时返回的行数,例如,如果查询返回1000万行,它将从联机数据库中一次获取100行,不管怎样,这是理论。@NotaJD使用jdbc端点选项的resetAutoCommit=false时,它不会给我错误。至于fetchSize的验证,我设置了一个非常低的大小并尝试使用它,但它从未通过执行并抛出错误。我尝试使用RowCallbackHandler,这似乎很有效,但不幸的是,它在我的列表中的280k条记录中给了我一个超出GC开销限制的异常。您可以使用原始JdbcTemplate设置一个独立/隔离测试,执行查询并从记录集中成功读取每一行,即在rs.hasNext{rs.next;}时调用。如果它是流式传输和游标等,它不应该耗尽内存。
.to("jdbc:dataSourceInformix?outputType=StreamList").split(body()).streaming() // Throws exception
from("quartz2://myGroup/myTimerTransaction?cron=0+1+0+*+*+?")
.bean(QueryTable.class, "createQuery")
.to("jdbc:dataSourceInformix?resetAutoCommit=false&outputType=StreamList").split(body()).streaming()
.bean(TransformRecord.class, "process")
.aggregate(constant(true), aggregationStrategyImpl)
.completionPredicate(aggregationStrategyImpl.getCompletionPredicate())
.completionTimeout(aggregationStrategyImpl.getCompletionTimeout())
.to("direct:start-processing")
.end();
from("direct:start-processing")
.split(body()).executorService(customThreadPool.build(getContext()))
.bean(AnalyzeData.class, "analyze")
.bean(PersistData.class, "persist")
.end();
// .to("direct:start-processing")
.to("seda:start-processing?size=1&blockWhenFull=true")
.end();
// from("direct:start-processing")
from("seda:start-processing?size=1&blockWhenFull=true")
// continues normally