Oracle分区表查询成本与非分区表查询成本

时间:2016-01-08 22:01:29

标签: oracle indexing partitioning database-administration database-partitioning

我有一张表 PO_HEADER ,有大约2000万条记录。考虑到我们将来对表的负载,我们决定对表进行分区以提高sql查询的性能。以下是用于创建新分区表的查询。

CREATE TABLE PO_HEADER_LP 
 PARTITION BY LIST (BUYER_IDENTIFIER)
(PARTITION GC66287246AA VALUES ('GC66287246AA') TABLESPACE MITRIX_TABLES,
PARTITION GC43837235JK VALUES ('GC43837235JK') TABLESPACE MITRIX_TABLES,
PARTITION GC84338293AA VALUES ('GC84338293AA') TABLESPACE MITRIX_TABLES,
PARTITION DEFAULTBUID VALUES (DEFAULT) TABLESPACE MITRIX_TABLES) 
AS SELECT * 
   FROM PO_HEADER;

create index PO_HEADER_LP_SI_IDX on PO_HEADER_LP("SUPPLIER_IDENTIFIER") TABLESPACE MITRIX_INDEXES LOCAL;

旧表PO_HEADER在" BUYER_IDENTIFIER"上有两个索引。和" SUPPLIER_IDENTIFIER"列如下:

create index PO_HEADER_BI_IDX on  PO_HEADER("BUYER_IDENTIFIER") TABLESPACE MITRIX_INDEXES;
create index PO_HEADER_SI_IDX on  PO_HEADER("SUPPLIER_IDENTIFIER") TABLESPACE MITRIX_INDEXES;

为了测试查询的性能,我在两个表上执行了以下查询。但是,令我惊讶的是,我看到第二个查询的成本几乎是第一个查询的两倍。任何人都可以知道,为什么分区表的查询成本高于普通表。在此先感谢。

select * from po_header where buyer_identifier='GC84338293AA' and supplier_identifier='GC75987723HT'; --cost: 56,941
select * from po_header_lp where buyer_identifier= 'GC84338293AA' and supplier_identifier='GC75987723HT'; --cost: 93,309

PO_HEADER,全球指数在buyer_identifier& supplier_identifier列 PO_HEADER with Global Index on buyer_identifier & supplier_identifier column

PO_HEADER_LP,其中包含supplier_identifier列上的全局索引 PO_HEADER_LP with Global Index on supplier_identifier column

PO_HEADER_LP,其中包含supplier_identifier列上的本地索引 PO_HEADER_LP with Local Index on supplier_identifier column

2 个答案:

答案 0 :(得分:1)

从你的DDL我认为,你有三个大买家(比如每个5M记录)和一些较小的买家。换句话说,这将是列出分区模式的正确设置。

您可以验证它是否适用于仅对买方进行测试访问:

EXPLAIN PLAN  SET STATEMENT_ID = 'jara1' into plan_table  FOR
select * from tab_lp where BUYER_ID = 1;
;  
SELECT * FROM table(DBMS_XPLAN.DISPLAY('plan_table', 'jara1','ALL'));

------------------------------------------------------------------------------------------------
| Id  | Operation             | Name   | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |        |  6662K|    82M|  4445   (2)| 00:00:01 |       |       |
|   1 |  PARTITION LIST SINGLE|        |  6662K|    82M|  4445   (2)| 00:00:01 |   KEY |   KEY |
|   2 |   TABLE ACCESS FULL   | TAB_LP |  6662K|    82M|  4445   (2)| 00:00:01 |     2 |     2 |
------------------------------------------------------------------------------------------------

对非分区表的相同查询应该会产生更高的成本。为什么? 在分区表中,所选买方(在您的情况下为GC84338293AA,我使用代理键)拥有自己的分区。 因此,完全扫描此分区是最佳访问。

select * from tab where BUYER_ID = 1;

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |  6596K|    81M| 14025   (1)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TAB  |  6596K|    81M| 14025   (1)| 00:00:01 |
--------------------------------------------------------------------------

   1 - filter("BUYER_ID"=1)

对于非分区表(获得大约四分之一的数据),FULL TABLE SCAN也可以, 但当然的成本更高,因为必须扫描所有数据。

注意 - 如果您在此处看到较低的费用,不切实际的低Rows次数和/或INDEX ACCESS, 这是造成<低>成本问题的原因。 所以不要担心旧的成本太低,而不是新成本太高了!

下一步是买方和供应商的访问。要得到你必须提供的答案 附加信息。

供应商过滤器的选择性如何?

即。如果谓词buyer_identifier='GC84338293AA'返回说5M记录,那么记录如何返回两列的谓词?

buyer_identifier='GC84338293AA' and supplier_identifier='GC75987723HT'

是4M还是100条记录?

如果完整谓词只返回少于供应商上的本地索引的记录,那么

如果它返回大量行(比如分区的四分之一) - 你应该保持FULL PARTITION SCAN并且不使用它。 这类似于我对非分区表的评论。

估算供应商基数

如果供应商列包含偏斜数据(这可能会欺骗CBO以计算不正确的成本),您可以在此列中定义明确的直方图。

我使用了这个语句语句,它计算完整数据的直方图(100%对于高度偏斜的数据很重要)以及表格和分区。

exec dbms_stats.gather_table_stats(ownname=>user,tabname=>'TAB_LP',granularity=>'all',estimate_percent => 100,METHOD_OPT => 'for columns SUPPLIER_ID size 254');

这适用于我的测试数据,即对于基数较低的供应商,已打开索引访问权限(在本地无前缀索引上),对于大型供应商,则使用了完整的分区扫描。

答案 1 :(得分:0)

您可以使用此脚本创建本地分区索引。

CREATE INDEX PO_HEADER_LOCAL_IDX ON PO_HEADER_LP
(BUYER_IDENTIFIER, SUPPLIER_IDENTIFIER)
LOCAL (
       PARTITION GC66287246AA,  
       PARTITION GC43837235JK,  
       PARTITION GC84338293AA,  
       PARTITION DEFAULTBUID
      );

此外,建议使用以下脚本收集新创建的分区表的统计信息:

EXEC DBMS_STATS.GATHER_TABLE_STATS('SCHEMA Name','PO_HEADER_LP');

现在,您可以再次生成以下SQL的执行计划:

select * from po_header_lp where buyer_identifier= 'GC84338293AA' and supplier_identifier='GC75987723HT';

希望这会对你有所帮助。