【发布时间】:2016-03-24 23:59:13
【问题描述】:
这是一个简化为连接两个索引的性能问题。采取以下设置:
CREATE TABLE ZZ_BASE AS SELECT dbms_random.random AS ID, DBMS_RANDOM.STRING('U',10) AS STR FROM DUAL CONNECT BY LEVEL <=1000000;
CREATE INDEX ZZ_B_I ON ZZ_BASE(ID ASC);
CREATE TABLE ZZ_CHILD AS SELECT dbms_random.random AS ID, DBMS_RANDOM.STRING('U',10) AS STR FROM DUAL CONNECT BY LEVEL <=1000000;
CREATE INDEX ZZ_C_I ON ZZ_CHILD(ID ASC);
-- As @Flado pointed out, the following is required so index scanning can be done
ALTER TABLE ZZ_BASE MODIFY (ID CONSTRAINT NN_B NOT NULL);
ALTER TABLE ZZ_CHILD MODIFY (ID CONSTRAINT NN_C NOT NULL); -- given the join below not mandatory.
现在我想 LEFT OUTER JOIN 这两个表,只输出已经索引的 ID 字段。
SELECT ZZ_BASE.ID
FROM ZZ_BASE
LEFT OUTER JOIN ZZ_CHILD ON (ZZ_BASE.ID = ZZ_CHILD.ID);
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000K| 9765K| | 4894 (2)| 00:00:30 |
|* 1 | HASH JOIN OUTER | | 1000K| 9765K| 16M| 4894 (2)| 00:00:30 |
| 2 | INDEX FAST FULL SCAN| ZZ_B_I | 1000K| 4882K| | 948 (3)| 00:00:06 |
| 3 | INDEX FAST FULL SCAN| ZZ_C_I | 1000K| 4882K| | 948 (3)| 00:00:06 |
----------------------------------------------------------------------------------------
如您所见,无需访问表,只需访问索引即可。但是按照常识,HASH-joining并不是连接这两个索引的最佳方式。如果这两个表要大得多,则必须制作一个非常大的哈希表。
更有效的方法是对这两个索引进行 SORT-MERGE。
SELECT /*+ USE_MERGE(ZZ_BASE ZZ_CHILD) */ ZZ_BASE.ID
FROM ZZ_BASE
LEFT OUTER JOIN ZZ_CHILD ON (ZZ_BASE.ID = ZZ_CHILD.ID);
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000K| 9765K| | 6931 (3)| 00:00:42 |
| 1 | MERGE JOIN OUTER | | 1000K| 9765K| | 6931 (3)| 00:00:42 |
| 2 | INDEX FULL SCAN | ZZ_B_I | 1000K| 4882K| | 2258 (2)| 00:00:14 |
|* 3 | SORT JOIN | | 1000K| 4882K| 22M| 4673 (4)| 00:00:29 |
| 4 | INDEX FAST FULL SCAN| ZZ_C_I | 1000K| 4882K| | 948 (3)| 00:00:06 |
-----------------------------------------------------------------------------------------
但似乎第二个索引已排序,即使它已经是(“如果存在索引,则数据库可以避免对第一个数据集进行排序。但是,无论索引如何,数据库总是对第二个数据集进行排序"1)
基本上,我想要的是一个使用 SORT-MERGE 连接并立即开始输出记录的查询,即:
- 没有 HASH 连接,因为它首先必须创建一个哈希表(如果存储在磁盘上,IO 开销),因此不会立即输出。
- 没有嵌套循环连接,虽然它会输出 立即,索引戳的 log(N) 复杂性和非顺序索引读取的大 IO 开销,以防索引很大。
【问题讨论】:
-
Oracle 有时会执行直通排序(它什么都不做并且是非阻塞的),它被标记为 NO_SORT,所以我想它可能适用于这种情况(或者至少应该是) .
-
您引用的评论(来源?)也适用于表扫描,不一定适用于索引扫描
-
在索引很大的情况下,非顺序索引读取的大 IO 开销。 按索引顺序读取索引,索引全扫描,也是非顺序 IO磁盘级别。索引快速全扫描可以做顺序 IO,但是以非排序顺序获取数据。索引在磁盘上没有按顺序排列。
-
@Shannon Severance 明白了这一点。优化器 FAST FULL SCAN 您的索引然后对其进行排序比在此索引上使用 SEQUENTIAL SCAN 便宜。
-
基本上,我想要的是一个使用 SORT-MERGE 连接并立即开始输出记录的查询......根据您引用的文档,您将拥有与 Oracle 合作,以便他们更改 SORT-MERGE 以在可用时在两个源上使用已排序的源。
标签: oracle algorithm join query-performance