SQL Plan Management
Once the cost optimizer provides an efficient execution plan for a SQL statement, you can’t assume that the optimizer will always use that execution plan. There may be any number of changes in the database, ranging from changes such as the addition or deletion of an index, changes in the composition of data that affects factors such as selectivity and cardinality, to the obvious changes such as a database or server upgrade. In previous releases, Oracle provided the stored outlines feature to preserve SQL execution plans to prevent unexpected performance deterioration caused by a major system change such as a database upgrade. In Oracle Database 11g, Oracle provides a brand-new feature called SQL Plan Management (SPM), to preserve SQL performance across major system changes. You can use SPM to preserve SQL performance when you encounter the following types of system changes:
- Database upgrades
- New optimizer versions
- Changes in optimizer parameters
- Changes in system settings
- Changes in schema and metadata definitions
- Deployment of new application modules
Although the SQL Tuning Advisor does provide you SQL profiles to improve SQL performance of high-load statements, that’s done only after the database identifies the SQL statements as poor performing. If the poor performance resulted from an execution plan change brought about by one of the large sets of factors that influence the explain plan, you still have to wait for the ADDM to capture the bad SQL statements and for the Automatic SQL Tuning Advisor to tune that statement. This, in other words, is a reactive mechanism at best, to cope with poorly performing SQL statements. Using stored outlines is a practical alternative, but also requires manual intervention. Oracle intends SPM as a preventative mechanism from the outset. The database automatically controls SQL plan evolution with the help of what are called SQL plan baselines. SPM’s job is to capture and eval(
sqlset_name => 'testset1',
description => 'Test STS to capture AWR Data');
end;
/
You can now use the STS you just created to load selected SQL statements from the AWR. If you’re loading plans from a remote database, you can load the plans into the STS first and then export and import the STS into the target database where you want to load the SQL plans.
declare
test_cursor1 dbms_sqltune.sqlset_cursor;
begin
open test_cursor1 for
select value(p) from table(dbms_sqltune.select_workload_repository(
'SYSTEM_MOVING_WINDOW',null,null,null,null,null,null,20))p;
dbms_sqltune.load_sqlset(
sqlset_name => 'testset1',
populate_cursor => test_cursor1);
end;
/
Before you can load the top 20 SQL statements into the STS, you must first select them from the AWR baseline using a ref cursor and a predefined table function to select the columns you need. The STS testset1 now contains the top 20 SQL statements in the AWR, ordered by elapsed time.
In order to load the SQL plans from the STS as SQL plan baselines, use the LOAD_PLANS_FROM_SQLSET function of the DBMS_SPM package:
declare
test_plans pls_integer;
begin
test_plans := dbms_spm.load_plans_from_sqlset(sqlset_name => 'testset1');
end;
/
Loading SQL Plans from the Cursor Cache
Instead of using an STS to load SQL plans, you can use the cursor cache to directly load the plans or the SQL statements stored in the cache. Here’s the syntax of the LOAD_PLANS_FROM_ CURSOR_CACHE function:
declare
l_plans_loaded PLS_INTEGER;
begin
l_plans_loaded := DBMS_SPM.load_plans_from_cursor_cache(
sql_id => '6r95ktyt1vgmg');
end;
/
Selecting SQL Plan Baselines
Once you collect the SQL plans either from the AWR or from the cursor cache, the next step is to enable use of the SQL plan baselines. To do this, you must ensure the optimizer_use_sql_plan_baselines initialization parameter to true. The parameter is set to true by default, meaning SQL plan baselines are enabled by default.
Note the differences between optimizer_capture_sql_plan_baselines and optimizer_use_ sql_plan_baselines
- When the database recognizes a new SQL statement as a repeatable statement for the first time, it adds the lowest cost plan for that statement to the SQL plan baseline for that statement. The database will then use this SQL plan baseline to execute the SQL statement.
- When the initialization parameter optimizer_use_sql_plan_ baselines is set to its default value of true, it enables the use of the SQL plan baselines stored by the database in the SQL Management Base (SMB). The optimizer will look for a SQL plan baseline for the SQL statement the database is compiling. If a SQL plan baseline exists for that SQL statement, the database uses a comparative plan selection policy—that is, the optimizer will figure out the cost of each of the baseline plans and pick the one with the lowest cost. The optimizer will create a best cost plan and try to match it to a plan in the SQL plan baseline. If it finds a match, it goes ahead and executes the statement using the best cost plan. If the database fails to match the best cost plan with any plan in the SQL plan baseline, it will add the new plan to the plan history as a non-accepted plan first. So it is not used until it is verified not to cause a reduction in performance. It then chooses the lowest cost plan from the set of accepted plans in the SQL plan baseline and executes the statement using that plan for the statement after comparing it with all the accepted plans in the SQL plan baseline.
- In reality, only the outline for each plan is stored in the SMB. Therefore, the optimizer will reproduce the actual execution plan from the stored outline of the selected plan and execute the statement using that plan. If the database can’t reproduce any of the accepted plans in the baseline, say because you dropped an index, then the optimizer uses the lowest cost plan for a newly compiled SQL statement
- The end result is that the optimizer will always produce a plan that’s either the best cost plan or a baseline plan. You can query the OTHER_XML column in the PLAN_TABLE after running an explain plan on a SQL statement, to find out exactly what strategy the optimizer has adopted in a specific case.
You can view the execution plan for the specified SQL_HANDLE of a plan baseline by executing the DBMS_XPLAIN.DISPLAY_SQL_PLAN_BASELINE function.
SQL> select * from table(
dbms_xplan.display_sql_plan_baseline(
sql_handle => 'SYS_SQL_0265b48f47be1bcf',
format => 'basic'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
SQL handle: SYS_SQL_0265b48f47be1bcf
SQL text: select cols,audit$,textlength,intcols,property,flags,rowid from view$
where obj#=:1
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Plan name: SQL_PLAN_04tdnjx3vw6yg56801109 Plan id: 1451233545
Enabled: NO Fixed: NO Accepted: YES Origin: MANUAL-LOAD
--------------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 749386351
-----------------------------------------------
| Id | Operation | Name |
-----------------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | TABLE ACCESS BY INDEX ROWID| VIEW$ |
| 2 | INDEX UNIQUE SCAN | I_VIEW1 |
-----------------------------------------------
21 rows selected.
If you’re using a fixed plan, the explain plan for that statement will indicate at the end that the plan is a fixed plan.
Evolving SQL Plan Baselines
The database routinely eval(
id NUMBER,
description VARCHAR2(50)
);
DECLARE
TYPE t_tab IS TABLE OF spm_test_tab%ROWTYPE;
l_tab t_tab := t_TAB();
BEGIN
FOR i IN 1 .. 10000 LOOP
l_tab.extend;
l_tab(l_tab.last).id := i;
l_tab(l_tab.last).description := 'Description for ' || i;
END LOOP;
FORALL i IN l_tab.first .. l_tab.last
INSERT INTO spm_test_tab VALUES l_tab(i);
COMMIT;
END;
/
EXEC DBMS_STATS.gather_table_stats(USER, 'SPM_TEST_TAB', cascade=>TRUE);
Query the table using an unindexed column, which results in a full table scan.
SET AUTOTRACE TRACE
SELECT description
FROM spm_test_tab
WHERE id = 99;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=13 Card=1 Bytes=24)
1 0 TABLE ACCESS (FULL) OF 'SPM_TEST_TAB' (TABLE) (Cost=13 Card=1 Bytes=24)
Identify the SQL_ID of the SQL statement by querying the V$SQL view.
CONN sys/password@db11g AS SYSDBA
SELECT sql_id
FROM v$sql
WHERE sql_text LIKE '%spm_test_tab%'
AND sql_text NOT LIKE '%dba_sql_plan_baselines%'
AND sql_text NOT LIKE '%EXPLAIN%';
SQL_ID
-------------
gat6z1bc6nc2d
1 row selected.
Use this SQL_ID to manually load the SQL plan baseline.
SET SERVEROUTPUT ON
DECLARE
l_plans_loaded PLS_INTEGER;
BEGIN
l_plans_loaded := DBMS_SPM.load_plans_from_cursor_cache(
sql_id => 'gat6z1bc6nc2d');
DBMS_OUTPUT.put_line('Plans Loaded: ' || l_plans_loaded);
END;
/
Plans Loaded: 1
PL/SQL procedure successfully completed.
The DBA_SQL_PLAN_BASELINES view provides information about the SQL plan baselines. We can see there is a single plan associated with our baseline, which is both enabled and accepted.
CONN sys/password@db11g AS SYSDBA
SELECT sql_handle, plan_name, enabled, accepted
FROM dba_sql_plan_baselines
WHERE sql_text LIKE '%spm_test_tab%'
AND sql_text NOT LIKE '%dba_sql_plan_baselines%';
SQL_HANDLE PLAN_NAME ENA ACC
------------------------------ ------------------------------ --- ---
SYS_SQL_7b76323ad90440b9 SYS_SQL_PLAN_d90440b9b65c37c8 YES YES
1 row selected.
Flush the shared pool to force another hard parse, create an index on the ID column, then repeat the query to see the affect on the execution plan.
CONN sys/password@db11g AS SYSDBA
ALTER SYSTEM FLUSH SHARED_POOL;
CONN test/test@db11g
CREATE INDEX spm_test_tab_idx ON spm_test_tab(id);
EXEC DBMS_STATS.gather_table_stats(USER, 'SPM_TEST_TAB', cascade=>TRUE);
SET AUTOTRACE TRACE
SELECT description
FROM spm_test_tab
WHERE id = 99;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=HINT: ALL_ROWS (Cost=13 Card=1 Bytes=24)
1 0 TABLE ACCESS (FULL) OF 'SPM_TEST_TAB' (TABLE) (Cost=13 Card=1 Bytes=24)
Notice the query doesn't use the newly created index, even though we forced a hard parse. Looking at the DBA_SQL_PLAN_BASELINES view we can see why.
CONN sys/password@db11g AS SYSDBA
SELECT sql_handle, plan_name, enabled, accepted
FROM dba_sql_plan_baselines
WHERE sql_handle = 'SYS_SQL_7b76323ad90440b9';
SQL_HANDLE PLAN_NAME ENA ACC
------------------------------ ------------------------------ --- ---
SYS_SQL_7b76323ad90440b9 SYS_SQL_PLAN_d90440b9b65c37c8 YES YES
SYS_SQL_7b76323ad90440b9 SYS_SQL_PLAN_d90440b9ed3324c0 YES NO
2 rows selected.
The SQL plan baseline now contains a second plan, but it has not yet been accepted.
Note: If you don't see the new row in the DBA_SQL_PLAN_BASELINES view go back and rerun the query from "spm_test_tab" until you do. It sometimes takes the server a few attempts before it notices the need for additional plans.
The following query uses the EVOLVE_SQL_PLAN_BASELINE function to evolve the SQL plan baseline and output the associated report.
CONN sys/password@db11g AS SYSDBA
SET LONG 10000
SELECT DBMS_SPM.evolve_sql_plan_baseline(sql_handle => 'SYS_SQL_7b76323ad90440b9')
FROM dual;
DBMS_SPM.EVOLVE_SQL_PLAN_BASELINE(SQL_HANDLE=>'SYS_SQL_7B76323AD90440B9')
--------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Evolve SQL Plan Baseline Report
-------------------------------------------------------------------------------
Inputs:
-------
SQL_HANDLE = SYS_SQL_7b76323ad90440b9
PLAN_NAME =
TIME_LIMIT = DBMS_SPM.AUTO_LIMIT
VERIFY = YES
DBMS_SPM.EVOLVE_SQL_PLAN_BASELINE(SQL_HANDLE=>'SYS_SQL_7B76323AD90440B9')
--------------------------------------------------------------------------------
COMMIT = YES
Plan: SYS_SQL_PLAN_d90440b9ed3324c0
-----------------------------------
Plan was verified: Time used .05 seconds.
Passed performance criterion: Compound improvement ratio >= 15.4.
Plan was changed to an accepted plan.
Baseline Plan Test Plan Improv. Ratio
------------- --------- -------------
Execution Status: COMPLETE COMPLETE
DBMS_SPM.EVOLVE_SQL_PLAN_BASELINE(SQL_HANDLE=>'SYS_SQL_7B76323AD90440B9')
--------------------------------------------------------------------------------
Rows Processed: 1 1
Elapsed Time(ms): 2 0
CPU Time(ms): 2 0
Buffer Gets: 46 3 15.33
Disk Reads: 0 0
Direct Writes: 0 0
Fetches: 0 0
Executions: 1 1
-------------------------------------------------------------------------------
Report Summary
DBMS_SPM.EVOLVE_SQL_PLAN_BASELINE(SQL_HANDLE=>'SYS_SQL_7B76323AD90440B9')
--------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Number of SQL plan baselines verified: 1.
Number of SQL plan baselines evolved: 1.
1 row selected.
The DBA_SQL_PLAN_BASELINES view shows the second plan as been accepted.
CONN sys/password@db11g AS SYSDBA
SELECT sql_handle, plan_name, enabled, accepted
FROM dba_sql_plan_baselines
WHERE sql_handle = 'SYS_SQL_7b76323ad90440b9';
SQL_HANDLE PLAN_NAME ENA ACC
------------------------------ ------------------------------ --- ---
SYS_SQL_7b76323ad90440b9 SYS_SQL_PLAN_d90440b9b65c37c8 YES YES
SYS_SQL_7b76323ad90440b9 SYS_SQL_PLAN_d90440b9ed3324c0 YES YES
2 rows selected.
Repeating the earlier test shows the more efficient plan is now available for use.
CONN test/test@db11g
SET AUTOTRACE TRACE
SELECT description
FROM spm_test_tab
WHERE id = 99;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=2 Card=1 Bytes=24)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'SPM_TEST_TAB' (TABLE) (Cost=2 Card=1 Bytes=24)
2 1 INDEX (RANGE SCAN) OF 'SPM_TEST_TAB_IDX' (INDEX) (Cost=1 Card=1)
Transferring SQL Plan Baselines
Dropping Plans and BaselinesThe DROP_SQL_PLAN_BASELINE function can drop a specific plan from a baseline, or all plans if the plan name is not specified.
CONN sys/password@db11g AS SYSDBA
SET SERVEROUTPUT ON
DECLARE
l_plans_dropped PLS_INTEGER;
BEGIN
l_plans_dropped := DBMS_SPM.drop_sql_plan_baseline (
sql_handle => NULL,
plan_name => 'SYS_SQL_7b76323ad90440b9');
DBMS_OUTPUT.put_line(l_plans_dropped);
END;
/
Evolving SQL Plans with the SQL Tuning Advisor
You can also evolve SQL plan baselines by running the SQL Tuning Advisor. This applies to both manual and automatic executions of the SQL Tuning Advisor task. When the SQL Tuning Advisor recommends accepting a SQL profile, it does so because the explain plan with the SQL profile is better than the original explain plan for the untuned statement. Once you accept the SQL profile recommendation and implement it, the advisor automatically adds the plan to the SQL plan baseline for that SQL statement.
Fixed SQL Plan Baselines
If a SQL plan baseline contains one or more enabled plans for which the fixed attribute value is set to yes, the baseline is considered fixed. You can set the fixed attribute to YES for any plan you want, thereby limiting the set of possible plans for a given SQL statement. You usually fix one plan per baseline and, because the optimizer will give preference to the fixed plans over the nonfixed implications of marking a plan as fixed when there are multiple plans that are marked as fixedplans in the SQL plan baseline, it will use the fixed plan. Remember that this will make the optimizer pick the fixed plan even if some of the nonfixed plans are actually cheaper, with a lower cost of execution. The database doesn’t evolve a fixed SQL plan baseline because the optimizer doesn’t add any new execution plans to a fixed SQL plan baseline. You may, however, evolve even a fixed SQL plan baseline by manually loading a new plan either from the cursor cache or a SQL Tuning Set.
SQL Plan Baseline Attributes
A SQL plan baseline has several attributes that you can change in order to fine-tune the SQL Plan Management feature. The DBA_SQL_PLAN_BASELINES view provides detailed information about all the SQL plan baselines stored in the SMB. Here’s a query that shows how to find out key information about the SQL plan baselines in your database:
SQL> select sql_handle,plan_name,origin,enabled,accepted,fixed,autopurge
from dba_sql_plan_baselines;
SQL_HANDLE PLAN_NAME ORIGIN ENABLED ACC FIXED AUTOPURGE
---------------------------------------- ---------------------------------------- -------------- --------- --- --------- ---------
SYS_SQL_0265b48f47be1bcf SQL_PLAN_04tdnjx3vw6yg56801109 MANUAL-LOAD YES YES NO YES
SYS_SQL_03d675f2172c4dff SQL_PLAN_07pmpy8bksmgz6d032274 MANUAL-LOAD YES YES NO YES
SYS_SQL_123f1ade69e70788 SQL_PLAN_14gsuvtnyf1w841dc0d01 MANUAL-LOAD YES YES NO YES
...............................................................
The following is a brief explanation of the key attributes of a SQL plan baseline:
- SQL_HANDLE, SQL_TEXT, and PLAN_NAME identify the SQL statement.
- The ORIGIN attribute denotes whether the plan was loaded manually (MANUAL-LOAD) and tuned by you (MANUAL-SQLTUNE), or if the database automatically captured the load (AUTO-CAPTURE) and tuned
- it (AUTO-SQLTUNE).
- enabled specifies whether the baseline plan is enabled by the optimizer for use.
- accepted shows whether the plan has been verified and found not to lead to a performance regression.
- fixed indicates whether this plan is one of the “fixed” cases. If any of the plans in a baseline are fixed, the database will consider the best plan among the fixed plans only. All other plans are ignored in preference to the fixed plans.
- auto_purge shows whether the plan is automatically purged by the database.
Note that any plans that you load manually will always have the accepted status because they are deemed to be verified plans. You can also disable an accepted plan by removing the enabled setting through the ALTER_SQL_PLAN_BASELINE view. Here’s an example:
declare
l_altered_plan pls_integer;
begin
l_altered_plan := dbms_spm.alter_sql_plan_baseline(
sql_handle => 'SYS_SQL_0265b48f47be1bcf',
plan_name => 'SQL_PLAN_04tdnjx3vw6yg56801109',
attribute_name => 'enabled',
attribute_value => 'NO');
dbms_output.put_line('Plan altered: ' || l_altered_plan);
end;
/
A job must have the enabled and the accepted status in order for the optimizer to consider its use.
The SQL Management Base
The database stores all SPM related information, such as statement logs, plan histories, and SQL profiles as well as the SQL plan baselines, in a new component of the data dictionary called the SQL Management Base (SMB). The database stores the SMB in the SYSAUX tablespace. You must, therefore, ensure that the SYSAUX tablespace is online because SPM will be disabled if it can’t access the SYSAUX tablespace. Configuring the SQL Management Base
You configure the SQL Management Base by setting values for two parameters, space_budget_percent and plan_retention_weeks. You can view the current values of these two parameters by querying the DBA_SQL_ MANAGEMENT_CONFIG view, as shown here:
SQL> select parameter_name,parameter_value
from dba_sql_management_config
PARAMETER_NAME PARAMETER_VALUE
------------------------------ ---------------
SPACE_BUDGET_PERCENT 10
PLAN_RETENTION_WEEKS 53
The space_budget_percent parameter determines the percentage of space the SMB can take up in the SYSAUX tablespace. You can select a value between 1 and 50 percent for this parameter. The default space limit is 10 percent of the size of the SYSAUX tablespace. The default unused plan retention period is one year and one week, which means a plan will be automatically purged if it has not been used for more than a year. When the space occupied by SQL management base exceeds the defined space budget limit, a weekly database alert is generated.
You can do one of the following to make the alert warnings go away:
- Increase the size of the SYSAUX tablespace
- Increase the SMB space limit
- Purge outdated SQL plan baselines or SQL profiles to clear up space in the SMB
You can modify the value of the space_budget_percent parameter by executing the DBMS_SPM.CONFIGURE procedure, as shown here:
SQL> exec dbms_spm.configure('space_budget_percent',20);
Purging Policies
A weekly purging task that’s part of the automated tasks that run during the maintenance windows takes care of removing older unused baselines, to conserve space in the SQL Management Base. By default, the database purges all SQL plans that the database hasn’t used in over a year (53 weeks, to be precise). You can, however, change this setting by adjusting the value of the plan_retention_weeks parameter to a value between 5 weeks and 523 weeks. The following example shows how to change the plan retention period to 2 years:
SQL> exec dbms_spm.configure('plan_retention_weeks',105);
drop all the sql plan baseline history
SET SERVEROUT ON;
DECLARE
v_plans_dropped pls_integer;
CURSOR C1 IS SELECT DISTINCT SQL_HANDLE FROM DBA_SQL_PLAN_BASELINES;
BEGIN
FOR I IN C1 LOOP
v_plans_dropped:=dbms_spm.drop_sql_plan_baseline(sql_handle=>I.SQL_HANDLE);
END LOOP;
END;
/
参考至:《McGraw.Hill.OCP.Oracle.Database.11g.New.Features.for.Administrators.Exam.Guide.Apr.2008》
http://www.oracle-base.com/articles/11g/sql-plan-management-11gr1.php http://docs.oracle.com/cd/E11882_01/server.112/e41573/optplanmgmt.htm#PFGRF95123
https://community.oracle.com/thread/1103054?start=0&tstart=0