Oracle Hash Join - 探测 Table:分区索引?
Oracle Hash Join - Probe Table: Index over Partition?
Table P
(父)和 C
(子)在 cat
上有 10 个分区,在 effective_date
上有 316 个子分区。
Table P
具有以下索引 create index ix_p_cat on p (cat);
.
与执行完整分区访问相比,使用分区列上的索引进行索引范围扫描比优化器更可取(成本更低)怎么可能?
我的想法是,在任何一种情况下都需要来自 P 的相同数量的数据块,因此最好避免读取额外的索引块。但是,优化器不同意。
下面是两个讲解计划。第一个显示优化器想要使用索引来构建哈希 [=37=],第二个显示不使用索引的提示。
Tables和索引分析。
Oracle 企业版 19c。提前致谢。
select C.some_col
from "P"
join "C"
on P.code = C.code
and P.cat = :cat
and C.cat = :cat
;
------------------------------------------------------------------------------------------------------------------------
| id | Operation | name | rows | Bytes | cost (%CPU)| time | Pstart| Pstop |
------------------------------------------------------------------------------------------------------------------------
| 0 | select statement | | 275G| 5127G| 1280K (58)| 00:00:51 | | |
|* 1 | hash join | | 275G| 5127G| 1280K (58)| 00:00:51 | | |
| 2 | table access by global index ROWID BATCHED| P | 60363 | 412K| 1642 (1)| 00:00:01 | ROWID | ROWID |
|* 3 | index range scan | IX_P_CAT | 60363 | | 231 (0)| 00:00:01 | | |
| 4 | partition LIST single | | 41M| 510M| 539K (1)| 00:00:22 | key | key |
| 5 | partition range all | | 41M| 510M| 539K (1)| 00:00:22 | 1 | 316 |
| 6 | table access full | C | 41M| 510M| 539K (1)| 00:00:22 | | |
------------------------------------------------------------------------------------------------------------------------
query block name / object alias (identified by operation id):
-------------------------------------------------------------
1 - SELA6D7F6
2 - SELA6D7F6 / p@SEL
3 - SELA6D7F6 / p@SEL
6 - SELA6D7F6 / C@SEL
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("P"."CODE"="C"."CODE")
3 - access("P"."CAT"=:CAT)
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - (#keys=1) "C"."SOME_COL"[NUMBER,22], "C"."SOME_COL"[NUMBER,22]
2 - "P"."CODE"[CHARACTER,2]
3 - "P".ROWID[ROWID,10]
4 - "C"."CODE"[CHARACTER,2], "C"."SOME_COL"[NUMBER,22]
5 - "C"."CODE"[CHARACTER,2], "C"."SOME_COL"[NUMBER,22]
6 - "C"."CODE"[CHARACTER,2], "C"."SOME_COL"[NUMBER,22]
Note
-----
- this is an adaptive plan
无索引
select /*+ no_index(P) */
C.some_col
from "P"
join "C"
on P.code = C.code
and P.cat = :cat
and C.cat = :cat
;
-----------------------------------------------------------------------------------------------
| id | Operation | name | rows | Bytes | cost (%CPU)| time | Pstart| Pstop |
-----------------------------------------------------------------------------------------------
| 0 | select statement | | 275G| 5127G| 1287K (58)| 00:00:51 | | |
|* 1 | hash join | | 275G| 5127G| 1287K (58)| 00:00:51 | | |
| 2 | partition LIST single| | 60363 | 412K| 8152 (1)| 00:00:01 | key | key |
| 3 | partition range all | | 60363 | 412K| 8152 (1)| 00:00:01 | 1 | 316 |
| 4 | table access full | P | 60363 | 412K| 8152 (1)| 00:00:01 | | |
| 5 | partition LIST single| | 41M| 510M| 539K (1)| 00:00:22 | key | key |
| 6 | partition range all | | 41M| 510M| 539K (1)| 00:00:22 | 1 | 316 |
| 7 | table access full | C | 41M| 510M| 539K (1)| 00:00:22 | | |
-----------------------------------------------------------------------------------------------
query block name / object alias (identified by operation id):
-------------------------------------------------------------
1 - SELA6D7F6
4 - SELA6D7F6 / p@SEL
7 - SELA6D7F6 / C@SEL
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("P"."CODE"="C"."CODE")
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - (#keys=1) "C"."SOME_COL"[NUMBER,22], "C"."SOME_COL"[NUMBER,22]
2 - "P"."CODE"[CHARACTER,2]
3 - "P"."CODE"[CHARACTER,2]
4 - "P"."CODE"[CHARACTER,2]
5 - "C"."CODE"[CHARACTER,2], "C"."SOME_COL"[NUMBER,22]
6 - "C"."CODE"[CHARACTER,2], "C"."SOME_COL"[NUMBER,22]
7 - "C"."CODE"[CHARACTER,2], "C"."SOME_COL"[NUMBER,22]
Hint Report (identified by operation id / query block name / object alias):
Total hints for statement: 1
---------------------------------------------------------------------------
4 - SELA6D7F6 / p@SEL
- no_index(p)
Note
-----
- this is an adaptive plan
具有数千个子分区但少于一百万行的 table 可能有大量空段 space,这将导致奇怪的优化器决策。 运行 下面的查询来查看你的 table 和索引使用了多少 space:
select segment_name, sum(bytes)/1024/1024 mb, count(*) segment_count
from dba_segments
where segment_name in ('P', 'C', 'IX_P_CAT')
group by segment_name;
我试图在我的 19c 数据库上重新创建您的 tables,table“P”的每个分区消耗了 2.5 GB 的 space,即使实际数据只需要一个几兆字节。每个系统的确切值都会有所不同,但我猜大多数系统都会有一个很大的值。 Oracle 段通常是旨在容纳一千多行的重型数据结构;如果 Oracle 一次分配一个字节,性能会很糟糕,因此它通常一次分配 MB。但是如果你有 316 个子分区,这些兆字节加起来。
通常,select 大部分数据的最佳方法是使用完整 table 扫描或完整(子)分区扫描。但是如果 table 浪费了这么多 space,使用小索引并按 ROWID 查找每一行比完全扫描所有几乎为空的段更有效。
您可以通过使用更少的子分区、调整段分配设置或像这样缩小 table 来解决此问题:
alter table p enable row movement;
alter table p shrink space;
begin
dbms_stats.gather_table_stats(user, 'P');
end;
/
Table P
(父)和 C
(子)在 cat
上有 10 个分区,在 effective_date
上有 316 个子分区。
Table P
具有以下索引 create index ix_p_cat on p (cat);
.
与执行完整分区访问相比,使用分区列上的索引进行索引范围扫描比优化器更可取(成本更低)怎么可能?
我的想法是,在任何一种情况下都需要来自 P 的相同数量的数据块,因此最好避免读取额外的索引块。但是,优化器不同意。
下面是两个讲解计划。第一个显示优化器想要使用索引来构建哈希 [=37=],第二个显示不使用索引的提示。
Tables和索引分析。
Oracle 企业版 19c。提前致谢。
select C.some_col
from "P"
join "C"
on P.code = C.code
and P.cat = :cat
and C.cat = :cat
;
------------------------------------------------------------------------------------------------------------------------
| id | Operation | name | rows | Bytes | cost (%CPU)| time | Pstart| Pstop |
------------------------------------------------------------------------------------------------------------------------
| 0 | select statement | | 275G| 5127G| 1280K (58)| 00:00:51 | | |
|* 1 | hash join | | 275G| 5127G| 1280K (58)| 00:00:51 | | |
| 2 | table access by global index ROWID BATCHED| P | 60363 | 412K| 1642 (1)| 00:00:01 | ROWID | ROWID |
|* 3 | index range scan | IX_P_CAT | 60363 | | 231 (0)| 00:00:01 | | |
| 4 | partition LIST single | | 41M| 510M| 539K (1)| 00:00:22 | key | key |
| 5 | partition range all | | 41M| 510M| 539K (1)| 00:00:22 | 1 | 316 |
| 6 | table access full | C | 41M| 510M| 539K (1)| 00:00:22 | | |
------------------------------------------------------------------------------------------------------------------------
query block name / object alias (identified by operation id):
-------------------------------------------------------------
1 - SELA6D7F6
2 - SELA6D7F6 / p@SEL
3 - SELA6D7F6 / p@SEL
6 - SELA6D7F6 / C@SEL
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("P"."CODE"="C"."CODE")
3 - access("P"."CAT"=:CAT)
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - (#keys=1) "C"."SOME_COL"[NUMBER,22], "C"."SOME_COL"[NUMBER,22]
2 - "P"."CODE"[CHARACTER,2]
3 - "P".ROWID[ROWID,10]
4 - "C"."CODE"[CHARACTER,2], "C"."SOME_COL"[NUMBER,22]
5 - "C"."CODE"[CHARACTER,2], "C"."SOME_COL"[NUMBER,22]
6 - "C"."CODE"[CHARACTER,2], "C"."SOME_COL"[NUMBER,22]
Note
-----
- this is an adaptive plan
无索引
select /*+ no_index(P) */
C.some_col
from "P"
join "C"
on P.code = C.code
and P.cat = :cat
and C.cat = :cat
;
-----------------------------------------------------------------------------------------------
| id | Operation | name | rows | Bytes | cost (%CPU)| time | Pstart| Pstop |
-----------------------------------------------------------------------------------------------
| 0 | select statement | | 275G| 5127G| 1287K (58)| 00:00:51 | | |
|* 1 | hash join | | 275G| 5127G| 1287K (58)| 00:00:51 | | |
| 2 | partition LIST single| | 60363 | 412K| 8152 (1)| 00:00:01 | key | key |
| 3 | partition range all | | 60363 | 412K| 8152 (1)| 00:00:01 | 1 | 316 |
| 4 | table access full | P | 60363 | 412K| 8152 (1)| 00:00:01 | | |
| 5 | partition LIST single| | 41M| 510M| 539K (1)| 00:00:22 | key | key |
| 6 | partition range all | | 41M| 510M| 539K (1)| 00:00:22 | 1 | 316 |
| 7 | table access full | C | 41M| 510M| 539K (1)| 00:00:22 | | |
-----------------------------------------------------------------------------------------------
query block name / object alias (identified by operation id):
-------------------------------------------------------------
1 - SELA6D7F6
4 - SELA6D7F6 / p@SEL
7 - SELA6D7F6 / C@SEL
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("P"."CODE"="C"."CODE")
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - (#keys=1) "C"."SOME_COL"[NUMBER,22], "C"."SOME_COL"[NUMBER,22]
2 - "P"."CODE"[CHARACTER,2]
3 - "P"."CODE"[CHARACTER,2]
4 - "P"."CODE"[CHARACTER,2]
5 - "C"."CODE"[CHARACTER,2], "C"."SOME_COL"[NUMBER,22]
6 - "C"."CODE"[CHARACTER,2], "C"."SOME_COL"[NUMBER,22]
7 - "C"."CODE"[CHARACTER,2], "C"."SOME_COL"[NUMBER,22]
Hint Report (identified by operation id / query block name / object alias):
Total hints for statement: 1
---------------------------------------------------------------------------
4 - SELA6D7F6 / p@SEL
- no_index(p)
Note
-----
- this is an adaptive plan
具有数千个子分区但少于一百万行的 table 可能有大量空段 space,这将导致奇怪的优化器决策。 运行 下面的查询来查看你的 table 和索引使用了多少 space:
select segment_name, sum(bytes)/1024/1024 mb, count(*) segment_count
from dba_segments
where segment_name in ('P', 'C', 'IX_P_CAT')
group by segment_name;
我试图在我的 19c 数据库上重新创建您的 tables,table“P”的每个分区消耗了 2.5 GB 的 space,即使实际数据只需要一个几兆字节。每个系统的确切值都会有所不同,但我猜大多数系统都会有一个很大的值。 Oracle 段通常是旨在容纳一千多行的重型数据结构;如果 Oracle 一次分配一个字节,性能会很糟糕,因此它通常一次分配 MB。但是如果你有 316 个子分区,这些兆字节加起来。
通常,select 大部分数据的最佳方法是使用完整 table 扫描或完整(子)分区扫描。但是如果 table 浪费了这么多 space,使用小索引并按 ROWID 查找每一行比完全扫描所有几乎为空的段更有效。
您可以通过使用更少的子分区、调整段分配设置或像这样缩小 table 来解决此问题:
alter table p enable row movement;
alter table p shrink space;
begin
dbms_stats.gather_table_stats(user, 'P');
end;
/