有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java多线程数据库读取

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

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

每个线程都有不同的select语句,比如select count(*) from "+table+ " where id like '"+format + "%'"

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

任何一个机构都能取悦新方法的利弊吗?我们还有其他方法可以实施它吗


共 (3) 个答案

  1. # 1 楼答案

    网络

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

    考虑到您有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上过滤结果,它将生成包含ALL结果的临时表,然后对其进行过滤

    Sorownum不是分割结果的好选择虽然rowid似乎更好,但它也有一些问题

    罗维德

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

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

    例如,如果您有三个线程,每个线程获取1'000'000个rowid,那么一个线程可能会获取1'000'000条记录,另外两个线程各获取1条记录。因此,一个会被淹没,而另外两个会挨饿

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

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

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

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

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


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

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

  2. # 2 楼答案

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

    2. 每个工作线程获取其N(例如1000)个PKs/id并使用它们 从DBs中获取行。在这里确保使用rowlock(T-SQL) 如果您不使用SQL Server,也可以使用它的等效项。这样,线程 不会妨碍彼此。所以worker从数据库中读取N行 并对其进行处理。一旦完成,可能会向调度员发出信号 (类似于“我完成了”事件)

    这是我最初想到的想法。我想是吧 如果你想得更多,可以进一步改进

  3. # 3 楼答案

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

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

    1. 查看查询计划是什么,并查看重新调整SQL是否可以提高性能 查询计划
    2. 在数据库端做更多的处理-但是由于您正在做SELECT COUNT...,所以在这里您就无能为力了
    3. 查看是否可以根据上次运行以来的新行或更新行重新执行查询incremental computation,而不是每次都查询所有旧数据

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