我有一张表 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列
答案 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';
希望这会对你有所帮助。