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

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

实际上在我的应用程序中,我正在下载一个有12个lacs(1 lac = 100,000)记录的表,这个记录至少需要4小时才能以正常的连接速度下载,而更多的hrs连接速度慢。

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

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

提前致谢

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

如果你需要这样做而不考虑建议,那么你可以产生多个线程,它们与数据库分开连接,每个线程将拉动一小部分数据(1到多页)。 如果您说100K记录和100个线程可用,则每个线程可以提取1K记录。 同样不建议有100个线程与DB打开100个连接。 这只是一个例子。 将无数个线程限制为某个最佳值,并限制每个线程拉动的记录数。 您可以根据rownum限制从DB中提取的记录数。

正如Vikas指出的那样,如果你向客户端下载一个千兆字节的数据,你就会做一些非常错误的事情,因为他说你永远不需要下载更多适合你屏幕的记录。 但是,如果您只需要偶尔执行此操作以进行数据库复制或备份,只需使用DBMS的数据库导出function并使用DAP(或您喜欢的下载加速器)下载导出的文件。

似乎有多种方法可以“从一个完整的表中读取multithreading”。

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

第一种方式:让一个线程查询整个表,将各个行放入一个为多个工作线程提供数据的队列[请注意,如果你希望第一个线程尽可能快地设置JDBC连接的获取大小可能会有帮助] 。 缺点:一次只有一个线程正在查询初始数据库,这可能不会“最大化”数据库本身。 Pro:你没有重新运行查询,所以排序顺序不应该在你中途改变(例如,如果你的查询是select * from table_name,返回顺序有点随机,但如果你从同一个返回它结果集/查询,你不会得到重复)。 你不会有意外的重复或类似的东西。 这是一个以这种方式做的教程 。

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

问题是“按某种方式排序”这意味着对于每个查询,DB必须对整个表进行排序,这可能是也可能是不可能的,并且可能很昂贵,尤其是在表的末尾附近。 如果它被索引,这应该不是问题。 需要注意的是,如果数据中存在“缺口”,您将会进行一些无用的查询,但它们可能仍然很快。 如果您有一个ID列并且它大部分是连续的,您可能可以根据ID“chunk”。

如果您有其他一些可以关闭的列,例如每个日期具有已知“数量”的日期列,并且它已编入索引,那么您可以通过按日期分块来避免“order by”,例如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 。 缺点:您确定您的数据库是作为一个primefaces操作执行的吗? 如果没有那么可能会有两个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.01且X> = 0.02”等。 (想法受到维基百科如何获得“随机”页面的启发 – 在插入时为每一行指定一个随机浮点数)。

你真正想要避免的是在排序顺序中的某种变化。 例如,如果你没有指定一个排序顺序,并且只是像这样查询select * from table_name start by XXX limit 10 from multiple threads,那么可以想象数据库将[因为没有指定排序元素]改变它的顺序在中途返回行[例如,如果添加新数据]意味着您可以跳过行或不跳过行。

使用Hibernate的ScrollableResults慢慢读取9000万条记录也有一些相关的想法(特别是对于hibernate用户)。

另一种选择是,如果你知道某些列(比如“id”)大部分是连续的,你可以通过“by chunks”迭代(获取最大值,然后在数据上以块为单位进行迭代)。 或者其他一些“可分块”的列。

我只是觉得不得不回答这个旧post。

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

为了并行处理数据,通常将分区方案或分割方案应用于源数据。 如果数据是原始文本,这可能是在中间某处的随机分级器。 对于数据库,分区方案只不过是在查询上应用条件以允许分页的额外条件。 这可能是这样的:

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

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

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

最后它完全是传统的,没有超级先进的。