SQL That Tunes Itself: Oracle 12c's Built-In Tuning Features
SQL That Tunes Itself: Oracle 12c's Built-In Tuning Features
SQL That Tunes Itself: Oracle 12c's Built-In Tuning Features
(Almost)
Jim Czuprynski
Zero Defect Computing, Inc.
My Credentials
30+ years of database-centric IT experience
Oracle DBA since 2001
Oracle 9i, 10g, 11g OCP and Oracle ACE
Director
> 100 articles on databasejournal.com and
ioug.org
Teach core Oracle DBA courses (Grid + RAC,
Exadata, Performance Tuning, Data Guard)
Regular speaker at Oracle OpenWorld, IOUG
COLLABORATE, OUG Norway, and Hotsos
Oracle-centric blog (Generally, It Depends)
Our Agenda
Statistics: “It’s Vegas, Baby!”
Cardinality Feedback
Adaptive Plans
Automatic Re-Optimization (ARO)
SQL Plan Directives
Automatic Extended Statistics Gathering
Adaptive SQL Plan Management
SPM Evolve Advisor
Q+A
It’s Vegas, Baby!
Oracle Optimizer = Vegas Oddsmaker
Get
Execution
Plan Right:
Easy Street
Get Execution Plan Wrong: Meet “Tony the Ant”
We teach the
Optimizer to “count
cards”
4
Adaptive Execution Plans
(AEP)
5
Statistics Feedback
Originally introduced as Cardinality Feedback in
Oracle 11gR2 as part of Adaptive Cursor Sharing
Captures actual execution statistics during query
execution
Compares expected vs. actual cardinality during
first execution of query
During second execution, optimizer uses actual
execution statistics to reparse statement’s plan
Works best for non-skewed row sources with
limited volatility
Adaptive Execution Plans (AEP)
The optimizer can now adaptively recognize and
capture multiple potential execution sub-plans within
an existing execution plan:
AEP constructs dynamic plans automatically
AEP dynamic statistics collector buffers each row
set
If new row count exceeds prior counts during statement
execution, the optimizer will choose alternative favored
subplan (e.g. HASH JOIN instead of NESTED LOOP)
Otherwise, AEP will utilize the original sub-plan
Largest AEP benefit: Sub-plans whose row sets
contain dramatically-skewed data
Adaptive Execution Plans In Action
SELECT /*+ MONITOR DYN_1 */
C.cust_last_name
FROM
ap.invoices I
,sh.customers C
Plan= hash
WHERE I.customer_id value: 608225413
C.cust_id
AND C.cust_city IN ('Chicago','Los Angeles')
-----------------------------------------------------------------
ORDER BY C.cust_last_name;
| Id | Operation | Name | E-Rows |
-----------------------------------------------------------------
SELECT PLAN_TABLE_OUTPUT
| 0 | SELECT STATEMENT
FROM TABLE(DBMS_XPLAN.DISPLAY(format |
=> 'ALLSTATS LAST |
ADAPTIVE +NOTE')); 497 |
Note
-----
- this is an adaptive plan (rows marked '-' are inactive)
Automatic Re-Optimization
(ARO)
Automatic Re-Optimization (ARO)
For some statements, ARO features may help to
overcome intrinsic limitations of AEP dynamic plans:
The optimizer discovers an inefficiency during a
statement’s first execution that AEP cannot
resolve (e.g. order in which row sets are joined)
During the next execution, the optimizer gathers
additional statistics to improve the join order
All subsequent executions of the same statement
improve as more execution statistics and optimizer
statistics are gathered
Automatic Re-Optimization: 1st Execution
SELECT /*+ MONITOR GATHER_PLAN_STATISTICS ARO_1 */
SQL> GRANT SELECT ANY DICTIONARY TO ap;
INV.cust_id
,C.cust_last_name cust_name
,SUM(INV.xtd_amt) xtd_amt
,SUM(S.quantity_sold) qty_sold
,SUM(s.amount_sold) amt_sold
FROM
First Execution:
sh.customers C
,sh.products child
SQL_ID d0g29dw05u1jv, P number 0
Plan hash value: 1879733942
,sh.sales S
,(
--------------------------------------------------------------------------------------------------------------------------
SELECT
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
--------------------------------------------------------------------------------------------------------------------------
I.customer_id cust_id
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.02 | 400 |
| ,LI.product_id
1 | SORT GROUP BY prod_id | | 1 | 521 | 1 |00:00:00.02 | 400 |
| 2 | NESTED,SUM(LI.extended_amt)
LOOPS xtd_amt | | 1 | 521 | 238 |00:00:00.01 | 400 |
| 3 | NESTED LOOPS | | 1 | 521 | 238 |00:00:00.01 | 396 |
| 4 |
FROM
NESTED LOOPS | | 1 | 4 | 1 |00:00:00.01 | 230 |
| 5 | ap.invoices I
VIEW | | 1 | 4 | 1 |00:00:00.01 | 227 |
| 6 | HASH GROUP BY
,ap.invoice_items LI | | 1 | 4 | 1 |00:00:00.01 | 227 |
|* 7 | HASH JOIN | | 1 | 1250 | 500 |00:00:00.01 | 227 |
|* 8 | WHERE I.invoice_id
TABLE ACCESS FULL = LI.invoice_id | INVOICES | 1 | 500 | 500 |00:00:00.01 | 7 |
|* 9 | AND I.taxable_amt
TABLE ACCESS FULL > 1000.00 | INVOICE_ITEMS | 1 | 1250 | 500 |00:00:00.01 | 220 |
| 10 | TABLE ACCESS BY INDEX ROWID < 100.00 | CUSTOMERS
AND LI.extended_amt | 1 | 1 | 1 |00:00:00.01 | 3 |
|* 11 | INDEX UNIQUE SCAN | CUSTOMERS_PK | 1 | 1 | 1 |00:00:00.01 | 2 |
| 12 | AND LI.taxable_ind
PARTITION RANGE ALL < 'Y' | | 1 | 130 | 238 |00:00:00.01 | 166 |
| 13 | GROUP
TABLE BY
ACCESS BY LOCAL INDEX ROWID BATCHED| SALES | 28 | 130 | 238 |00:00:00.01 | 166 |
| 14 | BITMAP CONVERSION TO ROWIDS | | 16 | | 238 |00:00:00.01 | 32 |
|* 15 |
I.customer_id
BITMAP INDEX SINGLE VALUE | SALES_CUST_BIX | 16 | | 5 |00:00:00.01 | 32 |
|* 16 | INDEX,LI.product_id
UNIQUE SCAN | PRODUCTS_PK | 238 | 1 | 238 |00:00:00.01 | 4 |
--------------------------------------------------------------------------------------------------------------------------
) INV
WHERE INV.cust_id = C.cust_id
AND S.cust_id = C.cust_id
AND S.prod_id = P.prod_id
GROUP BY
INV.cust_id
,C.cust_last_name
ORDER BY
INV.cust_id
,C.cust_last_name;
Automatic Re-Optimization: 2nd Execution
-----
Second Execution:
-- Which SQL statement(s) are currently qualified to use either:
SQL_ID d0g29dw05u1jv, child number 1
-- (a) a Dynamic Plan; or
Plan hash value: 1879733942
-- (b) Automatic Re-Optimization?
-----
--------------------------------------------------------------------------------------------------------------------------
Verification Results:
| Id |Identified
Operation for Dynamic Execution or Auto Re-Optimization
| Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
COL Plans
shid FORMAT A15 HEADING "SQL Hash ID"
(from V$SQL)
--------------------------------------------------------------------------------------------------------------------------
COL child_number
| FORMAT 999999
0 | SELECT STATEMENT HEADING
| "Child|#" | 1 | | 1 |00:00:00.01 | 400 |
Auto |
COL psn
| 1 | SORT GROUP BY FORMAT A08 HEADING
|
Resolved "Parsing|Schema"
Reopti-
1 | 1 | 1 |00:00:00.01 | 400 |
| 2 | NESTED LOOPS | | 1 | 238 | 238 |00:00:00.01 | 400 |
COL cost
| 3 | NESTED Child
LOOPSFORMAT 999999999
Parsing HEADING
Optimizer Adaptable
| "Optimizer|Cost"
mizable| 1 | 238 | 238 |00:00:00.01 | 396 |
COL SQL
is_rap
| 4Hash
| FORMAT
IDNESTED#LOOPS A12 Cost
Schema HEADING
Plan?
| "Resolved|Adaptable|Plan?"
Plan? | 1SQL
| Text 1 | 1 |00:00:00.01 | 230 |
| 5 | VIEW -------
COL ---------------
is_aro --------
FORMAT |
A12 ---------- ------------
HEADING |
------------ 1--------------------------------------
| 1 |
"Auto|Reopti-|mizable|Plan?" 1 |00:00:00.01 | 227 |
| 6 |
4v116s6qby228 HASH GROUP 0BYAP 245 | | Y 1SELECT
| 1 | MONITOR
/*+ 1 |00:00:00.01 | 227 |
GATHER_PLAN_STATIST
COL sql_text
|* 7 | FORMAT A80
HASH JOIN HEADING
| "SQL Text"| WRAP
1 | 500 | 500 |00:00:00.01 | 227 |
|* 8 | TABLE ACCESS FULL | INVOICES | 1 | 500 | 500 |00:00:00.01 | 7 |
|* 9 | TABLE ACCESS FULL | INVOICE_ITEMS | 1 | 500 | 500 |00:00:00.01 | 220 |
TTITLE "Plans TABLE
| 10 |
Identified for Dynamic Execution
ACCESS BY INDEX ROWID
or Auto Re-Optimization|(from
| CUSTOMERS | 1 | 1 |
V$SQL)"
1 |00:00:00.01 | 3 |
SELECT
|* 11 | INDEX UNIQUE SCAN | CUSTOMERS_PK | 1 | 1 | 1 |00:00:00.01 | 2 |
sql_id
| 12 | shid PARTITION RANGE ALL | | 1 | 238 | 238 |00:00:00.01 | 166 |
| 13 | TABLE ACCESS BY LOCAL INDEX ROWID BATCHED| SALES | 28 | 238 | 238 |00:00:00.01 | 166 |
,child_number
| 14 | BITMAP CONVERSION TO ROWIDS | | 16 | | 238 |00:00:00.01 | 32 |
,parsing_schema_name
|* 15 | BITMAP INDEX psn
SINGLE VALUE | SALES_CUST_BIX | 16 | | 5 |00:00:00.01 | 32 |
|* 16 | INDEX UNIQUE
,optimizer_cost SCAN
cost | PRODUCTS_PK | 238 | 1 | 238 |00:00:00.01 | 4 |
--------------------------------------------------------------------------------------------------------------------------
,is_resolved_adaptive_plan is_rap
,is_reoptimizable is_aro
Predicate Information (identified by operation id):
---------------------------------------------------
,sql_text
FROM 78 v$sql
- access("I"."INVOICE_ID"="LI"."INVOICE_ID")
- filter("I"."TAXABLE_AMT">1000)
WHERE 9 (sql_text NOT LIKE 'EXPLAIN
- filter(("LI"."EXTENDED_AMT"<100 PLAN FOR%' AND sql_text NOT LIKE '%DBMS_XPLAN%')
AND "LI"."TAXABLE_IND"<'Y'))
11 - access("INV"."CUST_ID"="C"."CUST_ID")
AND15 parsing_schema_name IN ('AP','HR','OE','SH','SYSTEM')
- access("S"."CUST_ID"="C"."CUST_ID")
AND16 ((is_resolved_adaptive_plan
- access("S"."PROD_ID"="P"."PROD_ID") = 'Y') OR (is_reoptimizable = 'Y'));
-----
Note
-----
- statistics feedback used for this statement
SQL Plan Directives (SPD)
SQL Plan Directives
Oracle 12cR1 offers the capability to capture and retain
compilation and execution statistics within the data
dictionary:
Before, a statement’s compilation and execution
statistics were retained only within the Shared Pool
Now these statistics will be retained within the data
dictionary instead as SQL Plan Directives (SPDs)
SPDs are not SQL statement specific!
They pertain to best methods to process row sets
Therefore, multiple future queries may benefit
DBMS_XPLAN.DISPLAY … +NOTES tells if an SPD
has been used against an existing SQL statement
New data dictionary views capture SPD metadata
SPD: First Pass
-- Increase dynamic sampling rate for SPD creation
ALTER SESSION SET OPTIMIZER_DYNAMIC_SAMPLING = 4;
First Execution:
SELECT
Plan hash /*+ MONITOR
value: GATHER_PLAN_STATISTICS SPD_2A */
929742582
C.cust_city, C.cust_state_province
,SUM(S.quantity_sold) qty_sold
---------------------------------------------------------------------------------------------------------
| Id ,SUM(s.amount_sold)
| Operation amt_sold | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------------------------
| FROM
0 | SELECT STATEMENT | | 1 | | 26 |00:00:00.28 | 8551 |
|* 1 sh.customers
| FILTER C | | 1 | | 26 |00:00:00.28 | 8551 |
| ,sh.products
2 | SORT GROUP PBY | | 1 | 1 | 389 |00:00:00.28 | 8551 |
| SPD3 |
,sh.salesNESTED
Metadata: S LOOPS | | 1 | | 3459 |00:00:00.15 | 8551 |
| 4 | NESTED LOOPS | | 1 | 325 | 3459 |00:00:00.14 | 5092 |
WHERE S.cust_id = C.cust_id
| Available
5 | SQL NESTED
Plan Directives
LOOPS for Selected Schemas
| (from DBA_SQL_PLAN_DIR*
| 1Views)
| 325 | 3459 |00:00:00.12 | 1624 |
AND
6 | S.prod_id
| Abbrev = P.prod_id
PARTITION RANGE ALL | | 1 | 325 | 3459 |00:00:00.11 | 1620 |
Last SPD
AND
|* SPD | S.quantity_sold
7 ID Used On TABLE ACCESS <FULL
Owner 3 Object Name
| SALESObject Type | SubObject
28 |Name 325 State
| 3459 |00:00:00.21 |
SQL Plan Directive 1620
Reason|
AND
8 | S.amount_sold
|* -------- INDEX UNIQUE
------------------- > 1599.99
SCAN
-------- | PRODUCTS_PK
--------------- ------------| --------------------
3459 | 1 ---------------
| 3459 |00:00:00.01 | 4 |
----------------------------------------
|* 61430811
9 | INDEX UNIQUE SHSCAN SALES | TABLE
CUSTOMERS_PK | 3459 | 1 NEW
| 3459 SINGLE TABLE |CARDINAL
|00:00:00.02 ITY |
3468 MISESTIMATE
GROUP BY cust_city, C.cust_state_province
61430811 SH SALES COLUMN QUANTITY_SOLD NEW SINGLE TABLE CARDINAL ITY MISESTIMATE
| 61430811
10 | TABLE ACCESS BY SH INDEX ROWID| CUSTOMERS
SALES COLUMN | AMOUNT_SOLD
3459 | 1 NEW
| 3459 |00:00:00.02
SINGLE TABLE |CARDINAL
3459
ITY |
MISESTIMATE
HAVING SUM(S.quantity_sold) > 30
---------------------------------------------------------------------------------------------------------
ORDER BY cust_city, C.cust_state_province;
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(SUM("S"."QUANTITY_SOLD")>30)
7 - filter(("S"."AMOUNT_SOLD">1599.99 AND "S"."QUANTITY_SOLD"<3))
8 - access("S"."PROD_ID"="P"."PROD_ID")
9 - access("S"."CUST_ID"="C"."CUST_ID")
Note
-----
- dynamic statistics used: dynamic sampling (level=4)
SPD: Subsequent Passes
Second Execution:
Plan hash value: 929742582
---------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E -Rows | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 26 |00:00:00.17 | 8551 |
|* 1 | FILTER | | 1 | | 26 |00:00:00.17 | 8551 |
| 2 | SORT GROUP BY | | 1 | 1 | 389 |00:00:00.17 | 8551 |
| 3 | NESTED LOOPS | | 1 | | 3459 |00:00:00.04 | 8551 |
| 4 | NESTED LOOPS | | 1 | 325 | 3459 |00:00:00.03 | 5092 |
| 5 | NESTED LOOPS | | 1 | 325 | 3459 |00:00:00.01 | 1624 |
| 6 | PARTITION RANGE ALL | | 1 | 325 | 3459 |00:00:00.01 | 1620 |
|* 7 | TABLE ACCESS FULL | SALES | 28 | 325 | 3459 |00:00:00.06 | 1620 |
|* 8 | INDEX UNIQUE SCAN | PRODUCTS_PK | 3459 | 1 | 3459 |00:00:00.01 | 4 |
|* 9 | INDEX UNIQUE SCAN | CUSTOMERS_PK | 3459 | 1 | 3459 |00:00:00.02 | 3468 |
| 10 | TABLE ACCESS BY INDEX ROWID| CUSTOMERS | 3459 | 1 | 3459 |00:00:00.01 | 3459 |
---------------------------------------------------------------------------------------------------------
1 - filter(SUM("S"."QUANTITY_SOLD")>30)
7 - filter(("S"."AMOUNT_SOLD">1599.99 AND "S"."QUANTITY_SOLD"<3))
8 - access("S"."PROD_ID"="P"."PROD_ID")
9 - access("S"."CUST_ID"="C"."CUST_ID")
Note
-----
- dynamic statistics used: dynamic sampling (level=4)
- 1 SQL Plan Directive used for this statement
SPD Metadata:
Available SQL Plan Directives for Selected Schemas (from DBA_SQL_PLAN_DIR* Views)
20
Adaptive SPM
21
Adaptive SQL Plan Management
Automatic Plan Evolution (APE) now available via package
DBMS_SPM
By default, a new automatic task runs during regular
maintenance window
Non-accepted plans (NAPs) are re-evaluated for automatic
evolution:
Most recently added plans get precedence
NAPs that still perform poorly: Wait 30 days
Any NAPs that perform better are automatically enabled
New SPM report procedure shows results of Automatic Plan
Evolution
SPM Evolve Advisor
In prior releases:
All SQL Plan evolution had to be performed manually
Gathering SPM advice on whether a SQL Plan could
evolve required DBA intervention
In Oracle 12cR1:
Automatic SQL Plan Evolution tasks included as part of
regularly-scheduled maintenance tasks
Manual advice and implementation also supported via new
DBMS_SPM procedures
Warning! Tuning Pack licensing is required