SQL Plan Baselines
SQL Plan Baselines
SQL Plan Baselines
====================
A particular SQL query might perform poorly on one database (such as production)
but work fine on another database (such as development). This situation might occur
if the same query has a different execution plan on each instance. By using the SQL
plan baselines, a feature that Oracle Database first introduced in version 11g.
Ex:
The following steps will show how to configure and use SQL Baselines:
CONNECT / AS SYSDBA
GRANT ADMINISTER SQL MANAGEMENT OBJECT TO SH;
4. Execute the query over the MY_CUSTOMERS data for which we want plan stability:
SELECT /* TEST */ COUNT(*) FROM sh.MY_CUSTOMERS
WHERE CUST_VALID = 'I';
Capture a baseline from the cursor cache for the previous query:
DECLARE
l_sqlid VARCHAR2(13);
l_plan PLS_INTEGER;
BEGIN
SELECT SQL_ID INTO l_sqlid FROM V$SQL
WHERE SQL_TEXT LIKE 'SELECT /* TEST */%';
l_plan := dbms_spm.load_plans_from_cursor_cache(
sql_id => l_sqlid);
END;
/
5. Create an index on the CUST_VALID field. so accessing the MY_CUSTOMERS
table using this index may be faster than a full table scan.
7. Query the data dictionary to see if there are execution plans that are not yet
accepted:
SH@orcl>/
SQL_HANDLE PLAN_NAME
ENA ACC FIX
---------------------------------------- ---------------------------------------
--- --- ---
SQL_daf1a2b47e99b128 SQL_PLAN_dpwd2qjz9mc9869e3d8b5
YES NO NO
SQL_daf1a2b47e99b128 SQL_PLAN_dpwd2qjz9mc98cb00db41
YES YES NO
Evolve the baseline with the new execution plan: The EVOLVE_SQL_PLAN_BASELINE
procedure returns a report, from which we can see if
the newly-generated execution plan passes the performance criterion. In other
words, if it
performs better (comparing the compound improvement ratio) than the previous
baseline,
it can be accepted.
SET SERVEROUTPUT ON
SET LONG 10000
DECLARE
l_report CLOB;
BEGIN
l_report := DBMS_SPM.EVOLVE_SQL_PLAN_BASELINE(
sql_handle => 'SQL_daf1a2b47e99b128');
DBMS_OUTPUT.PUT_LINE(l_report);
END;
/
SQL_daf1a2b47e99b128
OR
DECLARE
v_dropped_plans number;
BEGIN
v_dropped_plans := DBMS_SPM.DROP_SQL_PLAN_BASELINE (
sql_handle => 'SQL_daf1a2b47e99b128');
DBMS_OUTPUT.PUT_LINE('dropped ' || v_dropped_plans || ' plans');
END;
/
Note:
We can also create a baseline using the snapshots taken from Automatic Workload
Repository or using an SQL Tuning Set. In this case, we execute the following
statement, to create a baseline from test_tuning_set.
DECLARE
l_plan pls_integer;
BEGIN
l_plan := DBMS_SPM.LOAD_PLANS_FROM_SQLSET(
sqlset_name => 'test_tuning_set');
END;
/
When we manually capture SQL Plans, the resulting plan is marked as accepted.
However, when the plans are automatically captured, we need to perform the evolving
SQL Plan baseline process, using the DBMS_SPM.evolve_sql_plan_baseline function.
By launching this function (it needs only one parameter, the sql_handle of the
statement, which we can get from the DBA_SQL_PLAN_BASELINES view), the optimizer
will determine if non-accepted plans in the baseline should be accepted. The
function returns a CLOB containing a complete report of the results.
Theory:
=======
SQL Plan Management 12c
========================
What Is An Execution Plan?:
---------------------------
Did you ever had a discussion with a user (who is not a DBA) and had to explain why
a report is running slower? Did you say: “…Well the report is slower because the
Oracle optimizer has changed the execution plan.” ? The user was just starring at
you, not understanding a word you just said.
If you would have to explain what an execution plan is, to someone that has no
Oracle database knowledge, how would you do it?
Each step within the execution plan retrieves data from the database, or prepares
the data for the next step.
The execution plan includes an access path (how it accesses the table – think: full
table scan, index scan) and it also includes the join order with a join method
(think: nested loops, hash joins)
An execution plan has a Plan Hash Value associated, in order to identify it. The
Plan Hash Value, many times referred to as PHV, is a number.
By default, SQL Plan History is not created for SQL Statements. You need to enable
it by setting the following parameter to TRUE:
optimizer_capture_sql_plan_baselines = TRUE
Once that parameter is set to true, the database will automatically create a plan
history for each repeatable SQL statement.
The first execution plan that is generated for a SQL statement, becomes part of the
plan history, and it is labeled as ACCEPTED, when Plan History is enabled. A plan
is accepted when the database verifies that the plan will not lead to performance
regression, compared to other plans from history.
The very first plan is accepted, as there is nothing to compare it to.
The state of plans in the plan history can be ACCEPTED or NON ACCEPTED.
A NON-ACCEPTED plan can become ACCEPTED, after it is verified that it will not
cause performance regression (doesn’t perform worse than the accepted plans).
optimizer_capture_sql_plan_baselines = TRUE.
The very first execution plan that is generated for a repeatable SQL statement
becomes part of the SQL Plan Baseline (and of course the Plan History).
All these components take up space in the SYSAUX tablespace. Thus you can configure
how much space (in percentage) SMB can use in SYSAUX, and how many weeks can SMB
retain the execution plans.
To check for the current configuration, query DBA_SQL_MANAGEMENT_CONFIG view.
In order to start capturing baselines and plans, you need to modify the following
parameter. By default, the value is set to FALSE.
The database will start capturing plan history and baselines for repeatable SQL
Statements.
During the automatic capture, the database will match the SQL Statement’s signature
with the values from SQLLOG$, to confirm if the statement has been executed before.
If the statement is not found (meaning it is the first execution), then the
signature is added to the log.
If the statement is found (an identical signature has been found in SQLLOG$), then
it has been confirmed it is a repeatable SQL statement.
Applies to version 12.1.0.1: When you turn on the capture (alter system set
OPTIMIZER_CAPTURE_SQL_PLAN_BASELINES = TRUE), if you run other sql statements in
the same session that turned the capture on, these sql statements are not added to
sqllog$. In order to be added, run the sql statements in another session.
VERSION
-----------------
12.1.0.1.0
COUNT(*)
----------
0 -->> no entries in SQL Plan History
COUNT(*)
----------
0 -->> no entries in SQL Plan Baseline
-->> Connect to another session. I found if I use the same session, sqllog$ doesn't
get populated.
-->> First run of this SQL Statement. Should be captured in SQLLOG$, and no SQL
Plan Baseline
SIGNATURE BATCH#
---------------------------------- ----------
3812309255651905568 1
no rows selected
-->> confirms that no SQL Plan Baseline was created.
SIGNATURE BATCH#
---------------------------------- ----------
3812309255651905568 1
SQL> select signature, sql_text from dba_sql_plan_baselines;
SIGNATURE SQL_TEXT
----------------------------------
------------------------------------------------------------
3812309255651905568 select empno, ename, sal from scott.emp where
deptno=10
SQL_ID SQL_TEXT
EXACT_MATCHING_SIGNATURE
------------- ------------------------------------------------------------
------------------------
axrp4uruswzap select empno, ename, sal from scott.emp where deptno=10
3812309255651905568
3). How Does The Optimizer Select The Plan From SQL Plan Baseline?:-
After the capture has been turned on, will the optimizer use the SQL Plan
Baselines?
The answer is YES, if the following parameter is set to TRUE, which by default is.
optimizer_use_sql_plan_baselines = TRUE
What happens behind the scene, how will the optimizer choose the baseline to use?
1. During the hard parse of a statement, the optimizer will generate a “best-cost”
execution plan.
2. Optimizer will try to find a matching plan in the SQL plan baselines.
If there is no plan at all in the SQL Plan Baseline, then the “best-cost” plan is
used to execute the statement.
If the “best-cost” plan is found also in the SQL Plan Baseline, then the “best-
cost” plan is used to execute the statement.
If the “best-cost” plan is not in the baseline, and a baseline exists, then the
“best-cost” plan is marked as un-accepted and is added to the plan history.
Depending on what type of plans are in the baseline, the optimizer will compare the
costs of the accepted plans, and use the plan with the lowest cost.
Example:
==========
The procedure for manually associating a plan baseline with a SQL statement is as
follows:
1. Identify the SQL statement for which you want a plan baseline.
2. Provide an identifier such as the SQL_ID as input to the DBMS_SPM package to
create a plan baseline for the SQL statement.
For example, suppose you have a SQL statement you’ve been working with such as the
following:
SQL> select first_name from emp where emp_id = 100;
Now query the V$SQL view to determine the SQL_ID for the query:
Keep in mind that it’s possible that a single SQL statement can have more than one
execution plan associated with it
in memory. This can happen when the SQL executes multiple times and something in
the environment changes that
causes the optimizer to choose a different plan (like updated statistics, use of
bind variables, changes with database
initialization parameters, adding/deleting a SQL profile, and so on).
You can uniquely identify a single plan via the combination of SQL_ID and the
PLAN_HASH_VALUE column of
V$SQL and use that as input to DBMS_SPM, for example:
DECLARE
plan1 PLS_INTEGER;
BEGIN
plan1 := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(sql_id => 'dv73f7y69ny8z',
plan_hash_value => 3956160932);
END;
/
2). Creating Plan Baselines for SQL Contained in SQL Tuning Set
To deal with this problem, use the most resource-intensive SQL queries in the AWR
as candidates for the creation
of plan baselines. This solution uses the technique of creating an AWR baseline. An
AWR baseline is a snapshot of
activity in the AWR designated by begin/end snapshot IDs. Listed next are the steps
for creating and populating a SQL
tuning set with high resource-consuming SQL statements found in an AWR baseline and
then creating plan baselines
for those queries:
1. Create an AWR baseline.
2. Create a SQL tuning set object.
3. Populate the SQL tuning set with the queries found in the AWR baseline.
4. Use the tuning set as input to DBMS_SPM to create a plan baseline for each query
contained
in the SQL tuning set.
If you’re unsure of the available snapshots in your database, you can run an AWR
report or select the SNAP_ID
from DBA_HIST_SNAPSHOTS:
SQL> select snap_id, begin_interval_time from dba_hist_snapshot order by 1;
Step 2: Create a SQL Tuning Set Object
Now create a SQL tuning set. This next bit of code creates a tuning set named
test1:
BEGIN
dbms_sqltune.create_sqlset(
sqlset_name => 'test1'
,description => 'STS from AWR');
END;
/
Step 3: Populate the SQL Tuning Set with High-Resource Queries Found
in AWR Baseline
Now the SQL tuning set (created in step 2) is populated with any queries found
within the AWR baseline
(created in step 1):
DECLARE
base_cur dbms_sqltune.sqlset_cursor;
BEGIN
OPEN base_cur FOR
SELECT value(x)
FROM table(dbms_sqltune.select_workload_repository(
'peak_baseline_jun14_13', null, null,'elapsed_time',
null, null, null, 15)) x;
dbms_sqltune.load_sqlset(
sqlset_name => 'test1',
populate_cursor => base_cur);
END;
/
In the prior lines of a code, the AWR baseline name is passed to the DBMS_SQLTUNE
package. The queries within
the baseline are select by the elapsed time, and the top 15 are specified. To view
the queries within the SQL tuning
set, query the data dictionary as follows:
SELECT sqlset_name, elapsed_time
,cpu_time, buffer_gets, disk_reads, sql_text
FROM dba_sqlset_statements
WHERE sqlset_name = 'test1';