oracle表分区和索引分区
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了oracle表分区和索引分区相关的知识,希望对你有一定的参考价值。
参考技术A一 分区表技术概述
二 分区索引技术概述
⑴ 本地前缀分区索引
适用场景:
如果历史数据整理非常频繁、而且不能承受全局分区索引重建的长时间带来的索引不可用
同时、日常交易性能尚能接受、则建议设计为本地非前缀分区索引
注意:本地分区索引不能保证唯一性(除非分区键是约束的一部分)
缺点:
主要体现在数据的高可用性方面
当DROP分区后、全局分区索引则全部INVALID、除非REBULID
但数据量越大、重建索引的时间越长
一般来讲,如果需要将数据按照某个值逻辑聚集,多采用范围分区。如基于时间数据的按“年”、“月”等分区就是很典型的例子。在许多情况下,范围分区都能利用到分区消除特性( = >= <= between…and 等筛选条件下)。
如果在表里无法找到一个合适的属性来按这个属性完成范围分区,但你又想享受分区带来的性能与可用性的提升,则可以考虑使用散列分区。(适合使用 = IN 等筛选条件)
如果数据中有一列或有一组离散值,且按这一列进行分区很有意义,则这样的数据就很适合采用列表分区。
如果某些数据逻辑上可以进行范围分区,但是得到的范围分区还是太大,不能有效管理,则可以考虑使用组合分区(范围分区+hash 或范围分区+列表分区)。
create table products_table
(
id number(2),
name varchar2(50),
sale_date date
)
partition by range(sale_date)
interval (numtoyminterval(1,\'month\'))
(
partition p_month_1 values less than (to_date(\'2016-01-01\',\'yyyy-mm-dd\'))
)
如图,取 products_table 中的 sale_date 列作为分区键创建按月自增分区;
所有销售时间在 ‘2016-01-01’之前的记录都会被放入 p_month_1 分区;
销售时间在‘2016-01-01’之后的记录在插入时Oracle会自动创建记录所属月的分区;
比如当有销售时间分别为 2016年1月20日 与 2016年2月20日 的两条记录插入时,Oracle会分别创建一个上限值为 ‘2016-01-31’的分区和一个上限值为‘2016-02-29’的分区来存储这两条记录
https://www.cnblogs.com/Dreamer-1/p/6132776.html
Oracle Hash Join - 探测表:分区上的索引?
【中文标题】Oracle Hash Join - 探测表:分区上的索引?【英文标题】:Oracle Hash Join - Probe Table: Index over Partition? 【发布时间】:2021-12-13 01:36:34 【问题描述】:表P
(父)和C
(子)在cat
上有10 个分区,在effective_date
上有316 个子分区。
表P
具有以下索引create index ix_p_cat on p (cat);
。
与执行完整分区访问相比,对优化器而言,使用分区列上的索引进行索引范围扫描如何可能更可取(成本更低)?
我的想法是在任何一种情况下都需要来自 P 的相同数量的数据块,因此最好避免读取额外的索引块。但是,优化器不同意。
以下是两个解释计划。第一个是显示优化器想使用索引来构建哈希表,第二个是提示不使用索引。
分析表和索引。
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 - SEL$58A6D7F6
2 - SEL$58A6D7F6 / p@SEL$1
3 - SEL$58A6D7F6 / p@SEL$1
6 - SEL$58A6D7F6 / C@SEL$1
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 - SEL$58A6D7F6
4 - SEL$58A6D7F6 / p@SEL$1
7 - SEL$58A6D7F6 / C@SEL$1
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 - SEL$58A6D7F6 / p@SEL$1
- no_index(p)
Note
-----
- this is an adaptive plan
【问题讨论】:
【参考方案1】:具有数千个子分区但少于一百万行的表可能有大量的空段空间,这会导致奇怪的优化器决策。运行以下查询以查看表和索引使用了多少空间:
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 数据库上重新创建您的表,并且表“P”的每个分区消耗了 2.5 GB 的空间,尽管实际数据应该只需要几兆字节。每个系统的确切值都会有所不同,但我猜大多数系统都会有很大的值。 Oracle 段通常是用于容纳超过一千行的重型数据结构;如果 Oracle 一次分配一个字节,性能会很差,因此它通常一次分配兆字节。但如果你有 316 个子分区,那么这些兆字节加起来。
通常,选择大部分数据的最佳方法是使用全表扫描或全(子)分区扫描。但是,如果表有这么多浪费的空间,使用小索引并按 ROWID 查找每一行比全扫描所有大部分为空的段更有效。
您可以通过使用更少的子分区、调整段分配设置或像这样缩小表来解决此问题:
alter table p enable row movement;
alter table p shrink space;
begin
dbms_stats.gather_table_stats(user, 'P');
end;
/
【讨论】:
以上是关于oracle表分区和索引分区的主要内容,如果未能解决你的问题,请参考以下文章
oracle里,truncate一个分区,能不能保留全局索引