java多线程数据库读取
在我们的Java应用程序中,我需要从oracle数据库中读取大约8000万条记录。我正试图为此重新设计多线程程序。目前,我们正在使用Java5线程池,其中10个线程基于主键模式并行读取数据库。每个线程将读取不同的模式,如001*和002*
如何提高该计划的性能?我正在考虑设计模式,让引导线程读取数据库并将处理委托给子线程。在我们现有的设计中,不同的线程通过10个jdbc连接访问表。使用新方法,我将只有一个线程读取表
每个线程都有不同的select语句,比如select count(*) from "+table+ " where id like '"+format + "%'"
好的,读rowid模式听起来不错,但按rowid或rownum读好吗
任何一个机构都能取悦新方法的利弊吗?我们还有其他方法可以实施它吗
# 1 楼答案
网络
<>首先,由于使用了 RoWID考虑到您有8000万条记录要传输,这可能是对您的最佳性能提升,尽管这取决于您的线程所做的工作
显然,增加带宽也有助于解决网络问题
磁盘性能
在对代码进行更改之前,请在任务运行时检查硬盘驱动器负载,也许它无法处理那么多I/O(同时读取10个线程)
迁移到SSD/RAID或群集数据库可能会解决此问题。而在这种情况下,更改访问数据库的方式将不起作用
多线程可以解决CPU问题,但数据库主要依赖于磁盘系统
罗努姆
如果要使用rowid和rownum实现它,可能会遇到几个问题
1)rownum是为每个查询的结果动态生成的。所以如果查询没有显式 排序,并且每次运行查询时,某些记录可能具有不同的rownum
例如,您第一次运行它,得到如下结果:
然后第二次运行它,因为没有显式排序,dbms(出于自身已知的原因)决定返回如下结果:
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 楼答案
创建一个调度线程,读取N行的PKs(id)。 在这里,您可以进行某种缓存—读取N=1000行,将它们交给Worker1, 读下N=1000行,把它们交给Worker2,等等。这样,你就不需要了 在dispatcher线程的内存中保留超过N=1000个ID(PKs)。一旦你 将工件(工件为N=1000 ID)传递给工作线程, 您可以在dispatcher线程中处理它们(无需保留它们)
每个工作线程获取其N(例如1000)个PKs/id并使用它们 从DBs中获取行。在这里确保使用rowlock(T-SQL) 如果您不使用SQL Server,也可以使用它的等效项。这样,线程 不会妨碍彼此。所以worker从数据库中读取N行 并对其进行处理。一旦完成,可能会向调度员发出信号 (类似于“我完成了”事件)
这是我最初想到的想法。我想是吧 如果你想得更多,可以进一步改进
# 3 楼答案
正如评论中所指出的,多线程可能没有帮助,甚至可能使事情变得更糟
任何查询的标准备选方案包括:
SELECT COUNT...
,所以在这里您就无能为力了李>这些都不能保证成功。这取决于你想做什么