如何在Java中实现多个线程来下载单个表数据?

Posted

技术标签:

【中文标题】如何在Java中实现多个线程来下载单个表数据?【英文标题】:How to implement several threads in Java for downloading a single table data? 【发布时间】:2012-01-09 16:15:07 【问题描述】:

如何实现多个线程的多个/相同连接,以便快速下载单个大表数据。

实际上,在我的应用程序中,我正在下载一个包含 12 个 lac(1 lac = 100,000)条记录的表,在正常连接速度下下载至少需要 4 小时,而在连接速度较慢的情况下则需要更多小时。

因此需要在 Java 中实现多个线程来下载具有多个/相同连接对象的单个表数据。但不知道该怎么做。

如何在多个线程中定位一个记录指针,然后如何将所有线程记录添加到一个大文件中??

提前致谢

【问题讨论】:

众所周知,Download Accelerator Plus (DAP) 下载文件是为了打开并行下载。这种技术在我的情况下可行吗? 你的问题不是很清楚。您正在下载的这个表是什么——它是 Web 服务器上的文件,还是数据库中的表?你是怎么下载的? Web 下载加速器通过使用非常特定的 HTTP 功能来工作,该功能可以请求文件的一部分。没有任何通用方法可以通过任何 Internet 协议进行部分传输。 你的问题不是很清楚。您要在哪里下载文件?你的客户是什么?您尝试下载的服务器是什么?你下载的是静态文件还是动态生成的数据? 我正在使用 JDBC 连接下载位于服务器上的 AS400 数据库表的记录,该服务器具有非常多的记录,这需要花费大量时间。现在需要在 Java 中实现多个线程来下载具有多个/相同连接的单个大表数据。这在 Java 中怎么可能??? 【参考方案1】:

首先,将如此庞大的数据获取并下载到客户端是不可取的。如果您需要用于显示目的的数据,那么您不需要更多适合您的屏幕的记录。您可以对数据进行分页并一次获取一页。如果您正在获取它并在内存中进行处理,那么您的客户端肯定会耗尽内存。

如果您需要执行此操作而不考虑建议,那么您可以生成多个线程,这些线程具有与数据库的单独连接,其中每个线程将提取一小部分数据(1 到多个页面)。如果您说 100K 记录和 100 个线程可用,那么每个线程可以提取 1K 记录。再次建议不要有 100 个线程和 100 个与 DB 的打开连接。这只是一个例子。将线程数限制为某个最佳值,并限制每个线程正在提取的记录数。您可以根据 rownum 限制从数据库中提取的记录数。

【讨论】:

谢谢维卡斯。其实我的应用程序是一个Eclipse插件应用程序,用户可以通过一个小的RCP工具一次将所有在线数据下载到DB2 Express-C数据库中,使用插件可以随时查看,无需进一步上网。但不知道,如何在多个线程中使用 ROWID。请给出一些提示。 有关实现分页的更多信息,请参阅以下链接。 decipherinfosys.com/Paging_Data.pdf【参考方案2】:

正如 Vikas 指出的那样,如果您正在将千兆字节的数据下载到客户端,那么您就做错了,正如他所说,您永远不需要下载更多适合您屏幕的记录.但是,如果您只是偶尔出于数据库复制或备份目的而需要这样做,只需使用 DBMS 的数据库导出功能并使用 DAP(或您喜欢的下载加速器)下载导出的文件。

【讨论】:

我同意。如果您需要将数据复制/备份到客户端数据库,您应该使用导出功能。从服务器端导出数据下载转储文件并导入到客户端数据库。【参考方案3】:

看来“多线程从全表读取”有多种方式。

第零方式:如果您的问题只是“我用完 RAM 将整个表读入内存”,那么您可以尝试以某种方式一次处理一行(或一批行),然后处理下一批等. 从而避免将整个表加载到内存中(但仍然是单线程,因此可能很慢)。

第一种方式:让一个线程查询整个表,将单个行放入一个队列中,该队列为多个工作线程提供数据尽可能]。缺点:一次只有一个线程在查询初始数据库,这可能不会“最大化”您的数据库本身。优点:您没有重新运行查询,因此排序顺序不应在中途改变(例如,如果您的查询是 select * from table_name,则返回顺序有些随机,但如果您从同一个返回结果集/查询,你不会得到重复的)。你不会有意外的重复或类似的东西。这是 tutorial 这样做的。

第二种方式:分页,基本上每个线程都知道它应该选择哪个块(在这个例子中是XXX),所以它知道“我应该像select * from table_name order by something start with XXX limit 10一样查询表”。然后每个线程基本上一次处理(在这种情况下)10 [XXX 是线程之间由调用线程递增的共享变量]。

问题在于“按某事排序”,这意味着对于每个查询,数据库都必须对整个表进行排序,这可能会也可能不会,而且成本可能很高,尤其是在表末尾附近。如果它被索引,这应该不是问题。这里需要注意的是,如果数据中存在“空白”,您将执行一些无用的查询,但它们可能仍然很快。例如,如果您有一个 ID 列并且它大部分是连续的,那么您也许可以根据 ID 进行“分块”。

如果您有其他一些可以关闭的列,例如每个日期具有已知“数量”的日期列,并且它已被索引,那么您可以通过分块来避免“排序依据”按日期,例如select * from table_name where date < XXX and date > YYY(也没有限制子句,尽管您可以让线程使用限制子句来处理特定的唯一日期范围,随时更新或排序和分块,因为它的范围更小,痛苦更小)。

第三种方式:执行查询以“保留”表中的行,例如 update table_name set lock_column = my_thread_unique_key where column is nil limit 10,然后是查询 select * from table_name where lock_column = my_thread_unique_key。缺点:您确定您的数据库将其作为一个原子操作执行吗?如果不是,那么两个 setter 查询可能会发生冲突或类似情况,从而导致重复或部分批处理。当心。也许围绕“选择和更新”查询同步您的流程或适当地锁定表和/或行。这样可以避免可能的冲突(例如 postgres 需要特殊的 SERIALIZABLE 选项)。

第四种方式:(与第三条相关)如果您有很大的差距并希望避免“无用”查询,则最有用:创建一个新表,为您的初始表“编号”,并使用递增的 ID [基本上是一个临时表]。然后,您可以将该表按连续 ID 块划分,并使用它来引用第一个中的行。或者,如果您在表中已有一列(或可以添加一列)仅用于批处理目的,您可以将批处理 ID 分配给行,例如 update table_name set batch_number = rownum % 20000 然后每一行都有一个分配给自己的批处理号,线程可以分配批次(或分配“每第 9 批”或不分配)。或者类似地update table_name set row_counter_column=rownum(Oracle 示例,但你明白了)。然后你会有一组连续的数字来批量处理。

第五种方式:(不确定我是否真的推荐这个,但是)在插入时为每一行分配一个“随机”浮点数。然后,如果您知道数据库的大致大小,您可以剥离其中的一部分,例如,如果 100 并且您想要 100 批“其中 x = 0.02”等。 (灵感来自***如何获得“随机”页面——在插入时为每一行分配一个随机浮点数)。

您真正要避免的事情是在中途对排序顺序进行某种更改。例如,如果您没有指定排序顺序,而只是像这样从多个线程查询select * from table_name start by XXX limit 10,那么可以想象数据库将 [因为没有指定排序元素] 更改它返回给您行的顺序 中途 [例如,如果添加了新数据] 意味着您可以跳过行或不跳过。

Using Hibernate's ScrollableResults to slowly read 90 million records也有一些相关的想法(尤其是针对hibernate用户)。

另一种选择是,如果您知道某个列(例如“id”)大部分是连续的,则可以“按块”迭代该列(获取最大值,然后对块进行数字迭代)。或者其他一些“可分块”的列。

【讨论】:

【参考方案4】:

我只是觉得有必要回答这个旧帖子。

请注意,这是大数据的典型场景,不仅要在多个线程中获取数据,还要在多个线程中进一步处理该数据。这种方法并不总是要求将所有数据累积在内存中,它可以分组和/或滑动窗口进行处理,并且只需要累积结果,或者将数据进一步传递(其他永久存储)。

为了并行处理数据,通常会对源数据应用分区方案或拆分方案。如果数据是原始文本,这可能是中间某处的随机大小。对于数据库,分区方案只不过是一个额外的 where 条件应用于您的查询以允许分页。这可能是这样的:

驱动程序:将我的数据拆分为零件,并启动 4 个工人 4 x(工人计划):给我 4 个数据中的第 1..4 部分

这可以转化为(伪)sql,如:

SELECT ...
FROM (... Subquery ...)
WHERE date = SYSDATE - days(:partition)

最后,这一切都非常传统,没有什么超先进的。

【讨论】:

我喜欢这个,或者将表格分成 4 个“已知大小”的块并在线程中处理这些块......

以上是关于如何在Java中实现多个线程来下载单个表数据?的主要内容,如果未能解决你的问题,请参考以下文章

如何在java中实现延迟几秒钟

如何在android的jni线程中实现回调

如何在java中实现跨线程的通讯

如何在android的jni线程中实现回调

如何在 Laravel 5.5 中实现多个多对多关系?

多个生产者,单个消费者