【发布时间】:2011-07-10 17:33:30
【问题描述】:
我必须从具有数百万行的表中选择所有行(以预加载 Coherence 数据网格。)如何将此查询拆分为多个可由多个线程同时执行的查询?
我首先想到的是统计所有记录并做:
SELECT ...
WHERE ROWNUM BETWEEN (packetNo * packetSize) AND ((packetNo + 1) * packetSize)
但这没有用。现在我被困住了。
任何帮助将不胜感激。
【问题讨论】:
我必须从具有数百万行的表中选择所有行(以预加载 Coherence 数据网格。)如何将此查询拆分为多个可由多个线程同时执行的查询?
我首先想到的是统计所有记录并做:
SELECT ...
WHERE ROWNUM BETWEEN (packetNo * packetSize) AND ((packetNo + 1) * packetSize)
但这没有用。现在我被困住了。
任何帮助将不胜感激。
【问题讨论】:
如果您拥有企业版许可证,实现此目标的最简单方法是并行查询。
对于一次性或即席查询,请使用 PARALLEL 提示:
select /*+ parallel(your_table, 4) */ *
from your_table
/
提示中的数字是您要执行的从属查询的数量;在这种情况下,数据库将运行四个线程。
如果您希望对表发出的每个查询都是可并行化的,则永久更改表定义:
alter table your_table parallel (degree 4)
/
请注意,数据库不会总是使用并行查询;优化器将决定它是否合适。并行查询仅适用于跨多个分区的全表扫描或索引范围扫描。
有许多警告。并行查询要求我们有足够的核来满足提议的线程数;如果我们只有一个双核 CPU,将并行度设置为 16 不会神奇地加快查询速度。此外,我们需要备用 CPU 容量;如果服务器已经受 CPU 限制,那么并行执行只会让事情变得更糟。最后,I/O 和存储子系统需要能够满足并发需求; SAN 在这里可能非常无用。
在性能方面,与往常一样,在投入生产之前,对具有代表性的环境中的实际数据量进行一些基准测试至关重要。
如果您没有企业版怎么办?好吧,可以手动模拟并行执行。 Tom Kyte 称之为“Do-It-Yourself Parallelism”。我自己也使用过这种技术,效果很好。
关键是计算出适用于表的总范围 ROWID,并将它们拆分到多个作业中。与此线程中提出的其他一些解决方案不同,每个作业只选择它需要的行。 Kyte 先生在一个旧的 AskTom 线程中总结了该技术,包括重要的拆分脚本:find it here。
拆分表格并启动线程是一项手动任务:一次性完成还不错,但经常执行会很烦人。因此,如果您运行的是 11g 第 2 版,您应该知道有一个新的 PL/SQL 包 DBMS_PARALLEL_EXECUTE 可以为我们自动执行此操作。
【讨论】:
您确定查询的并行执行会更快吗?只有当巨大的表存储在具有许多磁盘的磁盘阵列上或者它被分区到多个磁盘上时,才会出现这种情况。在所有其他情况下,对表的顺序访问会快很多倍。
如果您确实必须拆分查询,则必须以某种方式拆分它,以便仍然可以对每个部分进行顺序访问。请张贴表格的DLL,以便我们给出具体答案。
如果数据的处理或加载到数据网格是瓶颈,那么你最好用一个进程读取数据,并在进一步处理之前拆分数据。
假设读取速度很快并且进一步的数据处理是瓶颈,您可以例如读取数据并将其写入非常简单的文本文件(例如固定长度或 CSV)。在每 10,000 行之后,您启动一个新文件并生成一个线程或进程来处理刚刚完成的文件。
【讨论】:
试试这样的:
select * from
( select a.*, ROWNUM rnum from
( <your_query_goes_here, with order by> ) a
where ROWNUM <= :MAX_ROW_TO_FETCH )
where rnum >= :MIN_ROW_TO_FETCH;
【讨论】:
您是否考虑过在 ROWNUM 上使用 MOD 10 一次提取十分之一的数据?
SELECT A.*
FROM Table A
WHERE MOD(ROWNUM,10) = 0;
【讨论】:
MOD() 应用于数字主键仍将执行十次全表扫描,但至少它会捕获整个表。无论哪种方式,执行全表扫描的十个线程都比执行一次全表扫描的单个线程差得多。