当前位置: 首页 > 知识库问答 >
问题:

多线程数据库读取

盖斌
2023-03-14

在我们的Java应用程序中,我需要从oracle数据库中读取8000万记录。我试图为此重新设计多线程程序。目前,我们使用Java5个线程池,10个线程基于主键模式并行读取数据库。每个线程将读取不同的模式,如001*和002*。

如何提高该计划的性能?我正在考虑设计模式,让引导线程读取数据库并将处理委托给子线程。在我们现有的设计中,不同的线程通过10个jdbc连接访问表。使用新方法,我将只有一个线程读取表。

对于每个线程,我们都有不同的select语句,比如selectcount(*)和id类似于“format”%%“的“table”。

好的,读rowid模式听起来不错,但按rowid或rownum读好吗?

任何机构可以请利弊的新方法,还有其他方法,我们可以实施它。

共有3个答案

米夕
2023-03-14

正如评论中所指出的,多线程可能没有帮助,甚至可能使事情变得更糟。

任何查询的标准备选方案是:

  1. 看看查询计划是什么,看看重新调整SQL是否可以改进查询计划。
  2. 在数据库端做更多的处理-但是因为您正在做一个SELECT COUNT...
  3. 看看是否可以重做查询,根据上次运行以来的新行或更新行进行增量计算,而不是每次都查询所有旧数据。

这些都不能保证成功。这取决于你想做什么。

琴光亮
2023-03-14

>

  • 创建一个调度线程,读取N行的PKs(id)。在这里,您可以进行某种缓存—读取N=1000行,将它们交给Worker1,读取下一个N=1000行,将它们交给Worker2,等等。这样,您不需要在dispatcher线程的内存中保留超过N=1000个ID(PK)。一旦您将工作(工作是N=1000个ID)传递给工作线程,您就可以在dispatcher线程中处理它们(无需保留它们)。

    每个工作线程获取其N(例如1000)个PKs/id,并使用它们从DBs获取行。如果不使用SQL Server,请确保在此处使用rowlock(T-SQL)或其等效项。这样,线程就不会相互干扰。所以worker从数据库中读取N行并处理它们。一旦完成,它可能会向调度器发出信号(类似于“我完成了”事件)。

    这是我最初想到的想法。我想如果你想得更多的话,它还可以进一步完善。

  • 濮景龙
    2023-03-14

    首先,由于使用ROWID和RONNUM是供应商锁,所以您应该考虑使用数据库存储的例程。它可以显著减少将数据从数据库传输到应用服务器的开销(特别是当它们位于不同的机器上并通过网络连接时)。

    考虑到您有8000万记录要传输,这对您来说可能是最好的性能提升,尽管这取决于您的线程所做的工作类型。

    显然,增加带宽也有助于解决网络问题。

    在对代码进行更改之前,请在任务运行时检查硬盘驱动器负载,也许它无法处理那么多I/O(同时读取10个线程)。

    迁移到SSD/RAID或群集数据库可能会解决此问题。而在这种情况下,更改访问数据库的方式将不起作用。

    多线程可以解决CPU问题,但数据库主要依赖于磁盘系统。

    如果要使用rowid和rownum实现它,可能会遇到几个问题。

    1) rownum是为每个查询的结果动态生成的。所以,若查询并没有显式排序,并且每次运行查询时某些记录可能具有不同的rownum,那个么。

    例如,你第一次运行它,得到这样的结果:

    some_column | rownum
    ____________|________
         A      |    1
         B      |    2
         C      |    3
    

    然后第二次运行它,因为没有显式排序,dbms(出于自身已知的原因)决定返回如下结果:

    some_column | rownum
    ____________|________
         C      |    1
         A      |    2
         B      |    3
    

    2) 第1点还意味着,若要筛选rownum上的结果,它将生成包含所有结果的临时表,然后对其进行筛选

    所以rownum不是分割结果的好选择。虽然罗维德看起来好些,但也有一些问题。

    如果查看ROWID描述,您可能会注意到“ROWID值唯一地标识数据库中的一行”。

    正因为如此,而且当您删除一行时,rowid序列中有一个“洞”,rowid可能在表记录中分布不均匀。

    因此,例如,如果您有三个线程,每个线程获取1'000'000 rowid,则有可能一个线程将获得1'000'000个记录,而另外两个线程将分别获得1个记录。所以一个人会不堪重负,而另外两个人会挨饿。

    在您的情况下,这可能不是什么大问题,尽管它很可能是您当前使用主键模式所面临的问题。

    或者,如果您首先在dispatcher中获取所有rowid,然后将它们等分(就像peter.petrov建议的那样),那么就可以做到这一点,尽管获取8000万个id听起来仍然很多,但我认为最好使用一个返回块边界的sql查询来进行拆分。

    或者,您可以通过为每个任务提供少量的rowid并使用Java7中引入的Fork-Join框架来解决这个问题,但是应该谨慎使用。

    还有一点很明显:rownum和rowid都不能跨数据库移植。

    因此,最好有自己的“切分”列,但随后您必须确保它将记录拆分成或多或少相等的块。

    还请记住,如果要在多个线程中执行此操作,那么检查数据库使用的锁定模式很重要,可能它只是为每次访问锁定表,那么多线程是没有意义的。

    正如其他人所建议的那样,您最好首先找出性能低下的主要原因(网络、磁盘、数据库锁定、线程不足,或者可能只是查询不理想—请检查查询计划)。

     类似资料:
    • 我应该设计一个组件,该组件应该通过在Java中使用多线程来实现以下任务,因为文件非常大/多,而且任务必须在很短的窗口内完成: 读取多个csv/xml文件并将所有数据保存在数据库中 读取数据库并将数据写入单独的csv文件 我对多线程很陌生 请告诉我您建议在Java或Spring Batch中使用传统的多线程。这里的输入源是多个的,输出源也是多个的。

    • 我试图更多地熟悉Redis,我发现Redis6的发行说明如下: Redis6进入了一个新时代:虽然它保留了一个核心的单线程数据访问接口,但I/O现在是线程化的。 保留“核心单线程数据访问接口”是否意味着所有数据访问命令(如GET、SET),无论redis实例中的哪个数据库,都必须通过这个接口(而不是使用多个redis实例时的多个数据访问接口)? 谢了!

    • 本文向大家介绍利用Tensorflow的队列多线程读取数据方式,包括了利用Tensorflow的队列多线程读取数据方式的使用技巧和注意事项,需要的朋友参考一下 在tensorflow中,有三种方式输入数据 1. 利用feed_dict送入numpy数组 2. 利用队列从文件中直接读取数据 3. 预加载数据 其中第一种方式很常用,在tensorflow的MNIST训练源码中可以看到,通过feed_d

    • 有一天我在想线程,我开始怀疑如果多个线程根本不修改它,它们是否可以访问相同的信息(不使用同步(锁))。 一个例子是10个线程读取1个类,由于我们称之为M的线程,这些线程调用访问器从这个类中获取信息,但不以任何方式修改信息。 同时,线程M偶尔会修改这个类中的数据(这将改变10个线程在“重启”后正在进行的计算结果) 会有问题吗?问题是其中一个线程将线程M修改过的信息“放回”。最重要的是,是否有一天访问

    • 我试图从Firebase数据库中读取数据,我已经到处阅读和查找,但我已经走到了死胡同。 这就是我所做的一切。 依赖项: 实现'com.google.firebase: Firebase存储: 9.2.1' 实现'com。谷歌。firebase:firebase数据库:9.2。1' 实现'com。谷歌。firebase:firebase授权:9.2。1' 实现'com。谷歌。火基:火基核心:9.2。

    • 我有spring批处理应用程序,配置步骤如下: 像这样的读者: 当我执行应用程序,得到这个错误: 组织。springframework。jdbc。UncategorizedSQLException:尝试处理下一行失败;SQL的未分类SQLException[select*from User];SQL状态[99999];错误代码[17289];最后一行之后的结果集;嵌套的例外是java。sql。SQ