【发布时间】:2017-02-27 08:25:50
【问题描述】:
从 Oracle 11g 迁移到 12c 后,我们遇到了一个巨大的性能问题,例如以下 select 语句。同样的语句在 11g 中也能正常工作。
表和索引
-- Create table
create table PS_CS_ADRART_TB
(
cs_adressmandant VARCHAR2(5) not null,
cs_person_id VARCHAR2(20) not null,
cs_beziehung_id VARCHAR2(20) not null,
seqnum INTEGER not null,
effdt DATE,
eff_status VARCHAR2(1) not null,
cs_adrart_cd VARCHAR2(20) not null,
cs_adress_id VARCHAR2(20) not null,
cs_kdnr_as400 VARCHAR2(8) not null,
cs_plzgk VARCHAR2(11) not null,
cs_plz_pf VARCHAR2(15) not null,
cs_aendgr_cd VARCHAR2(20) not null,
cs_datasource_cd VARCHAR2(20) not null,
cs_betrag NUMBER(14,4) not null,
cs_belegdat DATE,
cs_adrtyp_xl VARCHAR2(2) not null,
cs_checked VARCHAR2(1) not null,
cs_journal_xl VARCHAR2(4) not null,
address2 VARCHAR2(55) not null,
row_added_dttm TIMESTAMP(6),
row_added_oprid VARCHAR2(30) not null,
row_lastmant_dttm TIMESTAMP(6),
row_lastmant_oprid VARCHAR2(30) not null,
cs_recstat_xl VARCHAR2(4) not null,
cs_update_count NUMBER(10) not null
)
tablespace CS_APP
pctfree 10
initrans 1
maxtrans 255
storage
(
initial 102416K
next 1M
minextents 1
maxextents unlimited
);
-- Create/Recreate indexes
create unique index PSACS_ADRART_TB on PS_CS_ADRART_TB (CS_ADRESSMANDANT, CS_KDNR_AS400, EFFDT)
tablespace PSINDEX
pctfree 10
initrans 2
maxtrans 255
storage
(
initial 20M
next 1M
minextents 1
maxextents unlimited
);
create index PSBCS_ADRART_TB on PS_CS_ADRART_TB (CS_PERSON_ID)
tablespace PSINDEX
pctfree 10
initrans 2
maxtrans 255
storage
(
initial 20M
next 1M
minextents 1
maxextents unlimited
);
create index PSCCS_ADRART_TB on PS_CS_ADRART_TB (CS_BEZIEHUNG_ID)
tablespace PSINDEX
pctfree 10
initrans 2
maxtrans 255
storage
(
initial 20M
next 1M
minextents 1
maxextents unlimited
);
create unique index PS_CS_ADRART_TB on PS_CS_ADRART_TB (CS_ADRESSMANDANT, CS_PERSON_ID, CS_BEZIEHUNG_ID, SEQNUM, EFFDT)
tablespace PSINDEX
pctfree 10
initrans 2
maxtrans 255
storage
(
initial 20M
next 1M
minextents 1
maxextents unlimited
);
create index PSDCS_ADRART_TB on PS_CS_ADRART_TB (CS_PLZ_PF)
tablespace PSINDEX
pctfree 10
initrans 2
maxtrans 255
storage
(
initial 40K
next 1M
minextents 1
maxextents unlimited
);
create index PS0CS_ADRART_TB on PS_CS_ADRART_TB (CS_ADRESS_ID, CS_ADRESSMANDANT, CS_PERSON_ID, CS_BEZIEHUNG_ID, SEQNUM, EFFDT)
tablespace PSINDEX
pctfree 10
initrans 2
maxtrans 255
storage
(
initial 20M
next 1M
minextents 1
maxextents unlimited
);
create index PS1CS_ADRART_TB on PS_CS_ADRART_TB (CS_KDNR_AS400, CS_ADRESSMANDANT, CS_PERSON_ID, CS_BEZIEHUNG_ID, SEQNUM, EFFDT)
tablespace PSINDEX
pctfree 10
initrans 2
maxtrans 255
storage
(
initial 20M
next 1M
minextents 1
maxextents unlimited
);
create index PS2CS_ADRART_TB on PS_CS_ADRART_TB (ROW_ADDED_DTTM, CS_ADRESSMANDANT, CS_PERSON_ID, CS_BEZIEHUNG_ID, SEQNUM, EFFDT)
tablespace PSINDEX
pctfree 10
initrans 2
maxtrans 255
storage
(
initial 40K
next 1M
minextents 1
maxextents unlimited
);
create index PS3CS_ADRART_TB on PS_CS_ADRART_TB (ROW_ADDED_OPRID, CS_ADRESSMANDANT, CS_PERSON_ID, CS_BEZIEHUNG_ID, SEQNUM, EFFDT)
tablespace PSINDEX
pctfree 10
initrans 2
maxtrans 255
storage
(
initial 40K
next 1M
minextents 1
maxextents unlimited
);
create index PS4CS_ADRART_TB on PS_CS_ADRART_TB (ROW_LASTMANT_DTTM, CS_ADRESSMANDANT, CS_PERSON_ID, CS_BEZIEHUNG_ID, SEQNUM, EFFDT)
tablespace PSINDEX
pctfree 10
initrans 2
maxtrans 255
storage
(
initial 40K
next 1M
minextents 1
maxextents unlimited
);
create index PS5CS_ADRART_TB on PS_CS_ADRART_TB (ROW_LASTMANT_OPRID, CS_ADRESSMANDANT, CS_PERSON_ID, CS_BEZIEHUNG_ID, SEQNUM, EFFDT)
tablespace PSINDEX
pctfree 10
initrans 2
maxtrans 255
storage
(
initial 40K
next 1M
minextents 1
maxextents unlimited
);
create index PS6CS_ADRART_TB on PS_CS_ADRART_TB (CS_RECSTAT_XL, CS_ADRESSMANDANT, CS_PERSON_ID, CS_BEZIEHUNG_ID, SEQNUM, EFFDT)
tablespace PSINDEX
pctfree 10
initrans 2
maxtrans 255
storage
(
initial 40K
next 1M
minextents 1
maxextents unlimited
);
表格大小
select count(*) from ps_cs_adrart_tb a
--> 41367270
选择语句
SELECT A.CS_ADRESS_ID, A.SEQNUM, TO_CHAR(A.EFFDT, 'YYYY-MM-DD')
from PS_CS_ADRART_TB A
where A.CS_ADRESSMANDANT = '001'
and a.cs_kdnr_as400 = '63916917'
and a.effdt =
(select max(b.effdt)
from ps_cs_adrart_tb b
where b.cs_adressmandant = a.cs_adressmandant
and b.cs_person_id = a.cs_person_id
and b.cs_beziehung_id = a.cs_beziehung_id
and b.seqnum = a.seqnum
and b.effdt <= trunc(sysdate)
)
说明 Oracle 11g 计划
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 89 | 2 | 00:00:01 |
| * 1 | FILTER | | | | | |
| 2 | TABLE ACCESS BY INDEX ROWID | PS_CS_ADRART_TB | 1 | 89 | 1 | 00:00:01 |
| * 3 | INDEX RANGE SCAN | PSACS_ADRART_TB | 1 | | 1 | 00:00:01 |
| 4 | SORT AGGREGATE | | 1 | 59 | | |
| 5 | FIRST ROW | | 1 | 59 | 1 | 00:00:01 |
| * 6 | INDEX RANGE SCAN (MIN/MAX) | PS_CS_ADRART_TB | 1 | 59 | 1 | 00:00:01 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
------------------------------------------
* 1 - filter("A"."EFFDT"= (SELECT MAX("B"."EFFDT") FROM "PS_CS_ADRART_TB" "B" WHERE "B"."EFFDT"<=TRUNC(SYSDATE@!) AND "B"."SEQNUM"=:B1 AND "B"."CS_BEZIEHUNG_ID"=:B2 AND "B"."CS_PERSON_ID"=:B3 AND
"B"."CS_ADRESSMANDANT"=:B4))
* 3 - access("A"."CS_ADRESSMANDANT"='001' AND "A"."CS_KDNR_AS400"='63916917')
* 6 - access("B"."CS_ADRESSMANDANT"=:B1 AND "B"."CS_PERSON_ID"=:B2 AND "B"."CS_BEZIEHUNG_ID"=:B3 AND "B"."SEQNUM"=:B4 AND "B"."EFFDT"<=TRUNC(SYSDATE@!))
说明 Oracle 12c 计划
------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 140 | 34366 | 00:00:02 |
| * 1 | HASH JOIN | | 1 | 140 | 34366 | 00:00:02 |
| 2 | TABLE ACCESS BY INDEX ROWID BATCHED | PS_CS_ADRART_TB | 1 | 89 | 1 | 00:00:01 |
| * 3 | INDEX RANGE SCAN | PS1CS_ADRART_TB | 1 | | 1 | 00:00:01 |
| 4 | VIEW | VW_SQ_1 | 41889 | 2136339 | 34365 | 00:00:02 |
| * 5 | FILTER | | | | | |
| 6 | HASH GROUP BY | | 41889 | 2471451 | 34365 | 00:00:02 |
| * 7 | INDEX RANGE SCAN | PS_CS_ADRART_TB | 12746381 | 752036479 | 34365 | 00:00:02 |
------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
------------------------------------------
* 1 - access("A"."EFFDT"="MAX(B.EFFDT)" AND "ITEM_1"="A"."CS_ADRESSMANDANT" AND "ITEM_2"="A"."CS_PERSON_ID" AND "ITEM_3"="A"."CS_BEZIEHUNG_ID" AND "ITEM_4"="A"."SEQNUM")
* 3 - access("A"."CS_KDNR_AS400"='63916917' AND "A"."CS_ADRESSMANDANT"='001')
* 5 - filter('001'='001')
* 7 - access("B"."CS_ADRESSMANDANT"='001' AND "B"."EFFDT"<=TRUNC(SYSDATE@!))
* 7 - filter("B"."EFFDT"<=TRUNC(SYSDATE@!))
Note
-----
- dynamic sampling used for this statement
如您所见,从索引PS_CS_ADRART_TB 中,只有CS_ADRESSMANDANT 和EFFDT 用于过滤数据,这非常糟糕。
对于以下稍有不同的选择语句,Oracle 12c 使用索引来确定子选择的数据。
改变外部选择的where条件
SELECT A.CS_ADRESS_ID, A.SEQNUM, TO_CHAR(A.EFFDT, 'YYYY-MM-DD')
from PS_CS_ADRART_TB A
where a.cs_kdnr_as400 = '53916917' -- without CS_ADRESSMANDANT condition
and a.effdt =
(select max(b.effdt)
from ps_cs_adrart_tb b
where b.cs_adressmandant = a.cs_adressmandant and
b.cs_person_id = a.cs_person_id
and b.cs_beziehung_id = a.cs_beziehung_id
and b.seqnum = a.seqnum
and b.effdt <= trunc(sysdate)
)
--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 89 | 2 | 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID BATCHED | PS_CS_ADRART_TB | 1 | 89 | 1 | 00:00:01 |
| * 2 | INDEX RANGE SCAN | PS1CS_ADRART_TB | 1 | | 1 | 00:00:01 |
| 3 | SORT AGGREGATE | | 1 | 59 | | |
| 4 | FIRST ROW | | 1 | 59 | 1 | 00:00:01 |
| * 5 | INDEX RANGE SCAN (MIN/MAX) | PS_CS_ADRART_TB | 1 | 59 | 1 | 00:00:01 |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
------------------------------------------
* 2 - access("A"."CS_KDNR_AS400"='53916917')
* 2 - filter("A"."EFFDT"= (SELECT MAX("B"."EFFDT") FROM "PS_CS_ADRART_TB" "B" WHERE "B"."EFFDT"<=TRUNC(SYSDATE@!) AND "B"."SEQNUM"=:B1 AND "B"."CS_BEZIEHUNG_ID"=:B2 AND "B"."CS_PERSON_ID"=:B3 AND
"B"."CS_ADRESSMANDANT"=:B4))
* 5 - access("B"."CS_ADRESSMANDANT"=:B1 AND "B"."CS_PERSON_ID"=:B2 AND "B"."CS_BEZIEHUNG_ID"=:B3 AND "B"."SEQNUM"=:B4 AND "B"."EFFDT"<=TRUNC(SYSDATE@!))
Note
-----
- dynamic sampling used for this statement
在子选择中使用min() 而不是max()
SELECT A.CS_ADRESS_ID, A.SEQNUM, TO_CHAR(A.EFFDT, 'YYYY-MM-DD'), a.cs_person_id
from PS_CS_ADRART_TB A
where a.cs_kdnr_as400 = '63916917' and a.cs_adressmandant = '001'
and a.effdt =
(select min(b.effdt)
from ps_cs_adrart_tb b
where b.cs_adressmandant = a.cs_adressmandant
and b.cs_person_id = a.cs_person_id
and b.cs_beziehung_id = a.cs_beziehung_id
and b.seqnum = a.seqnum
and b.effdt <= sysdate
);
---------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
---------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 109 | 2 | 00:00:01 |
| 1 | NESTED LOOPS | | 1 | 109 | 2 | 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID BATCHED | PS_CS_ADRART_TB | 1 | 89 | 1 | 00:00:01 |
| * 3 | INDEX RANGE SCAN | PS1CS_ADRART_TB | 1 | | 1 | 00:00:01 |
| * 4 | VIEW PUSHED PREDICATE | VW_SQ_1 | 1 | 20 | 1 | 00:00:01 |
| 5 | SORT GROUP BY | | 1 | 59 | 1 | 00:00:01 |
| * 6 | INDEX RANGE SCAN | PS_CS_ADRART_TB | 1 | 59 | 1 | 00:00:01 |
---------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
------------------------------------------
* 3 - access("A"."CS_KDNR_AS400"='63916917' AND "A"."CS_ADRESSMANDANT"='001')
* 4 - filter("A"."EFFDT"="MIN(B.EFFDT)" AND "ITEM_1"="A"."CS_ADRESSMANDANT")
* 6 - access("B"."CS_ADRESSMANDANT"='001' AND "B"."CS_PERSON_ID"="A"."CS_PERSON_ID" AND "B"."CS_BEZIEHUNG_ID"="A"."CS_BEZIEHUNG_ID" AND "B"."SEQNUM"="A"."SEQNUM" AND "B"."EFFDT"<=SYSDATE@!)
Note
-----
- dynamic sampling used for this statement
重建索引(如上所述)并更新统计数据
EXEC DBMS_STATS.delete_table_stats('SYSADM', 'PS_CS_ADRART_TB');
-- EXEC DBMS_STATS.gather_table_stats('SYSADM', 'PS_CS_ADRART_TB');
EXEC DBMS_STATS.gather_table_stats(ownname => 'SYSADM', tabname => 'PS_CS_ADRART_TB', cascade => true );
没用。
这里发生了什么?任何建议都将受到高度赞赏。提前致谢。
【问题讨论】:
-
请让我知道你为什么不投这个问题。
-
问题很清楚,标签很好,包含构建所需结构的所有语句,提供有关表大小的信息,包含许多尝试和每个语句的计划......我可以'不知道这种问题还应该包含什么... +1
-
12c 计划中的“用于此语句的动态采样 似乎表明尽管有
gather_table_stats(),但统计信息不是最新的。如果你打电话给gather_table_stats()和cascade => true?(为了排除显而易见的问题:SYSADM 真的是这些表的所有者吗?) -
@a_horse_with_no_name 我们使用选项
cascade => true调用DBMS_STATS.gather_table_stats,结果相同。是的,SYSADM是该表的所有者。该数据库是我们的 Oracle 11g 数据库的升级副本。 -
两个数据库中参数OPTIMIZER_DYNAMIC_SAMPLING的值是多少?
标签: oracle oracle11g oracle12c