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一个分区,能不能保留全局索引

ORACLE表索引和分区

oracle 全局索引和局部索引的区别和作用

Oracle-创建索引分区

oracle表分区问题 做过数据库表分区的高手请进,非常感谢

oracle 大表怎么建索引