SQL Plan Baselines

Download as txt, pdf, or txt
Download as txt, pdf, or txt
You are on page 1of 11

SQL Plan Base lines:

====================

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.

Introduction to SQL Plan Management:


------------------------------------
Oracle SQL Plan Management (SPM) is a feature in Oracle Database that captures all
the historical execution plans for a query. With that, you can create a baseline
for the good plan from the execution plans available in SPM and enable that
baseline to ensure that the system picks only the good plan from the baseline.

Ex:

The following steps will show how to configure and use SQL Baselines:

CONNECT / AS SYSDBA
GRANT ADMINISTER SQL MANAGEMENT OBJECT TO SH;

1. Connect to the SH schema:


CONNECT sh/sh

2. Create a table MY_CUSTOMERS with some test data:


CREATE TABLE sh.MY_CUSTOMERS AS
SELECT * FROM sh.CUSTOMERS NOLOGGING;

3. Obtain a column CUST_VALID containing skewed values (about 1 percent of rows


contain value 'I', others containing value 'A':
UPDATE sh.MY_CUSTOMERS SET CUST_VALID = 'I'
WHERE CUST_VALID = 'A' AND MOD(CUST_ID,100) <> 0;
COMMIT;

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.

CREATE INDEX MY_CUSTOMERS_IX1 ON sh.MY_CUSTOMERS (CUST_VALID);

6. Execute the same query in step 4:


SELECT /* TEST */ COUNT(*) FROM sh.MY_CUSTOMERS
WHERE CUST_VALID = 'I';

7. Query the data dictionary to see if there are execution plans that are not yet
accepted:

SELECT SQL_HANDLE, PLAN_NAME, ENABLED, ACCEPTED, FIXED FROM DBA_SQL_PLAN_BASELINES;

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

8. Clean the database:


DECLARE
l_plan pls_integer;
BEGIN
l_plan := DBMS_SPM.DROP_SQL_PLAN_BASELINE( -
sql_handle => 'SQL_daf1a2b47e99b128');
END;
/

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;
/

DROP TABLE sh.MY_CUSTOMERS;


CONNECT / AS SYSDBA
REVOKE ADMINISTER SQL MANAGEMENT OBJECT FROM SH;

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;
/

To control the SQL Plan Management there are two parameters:

1. OPTIMIZER_CAPTURE_SQL_PLAN_BASELINES: If set to TRUE, automatic capturing of SQL


Plans is enabled; the default value is FALSE
2. OPTIMIZER_USE_SQL_PLAN_BASELINES: If set to TRUE, SQL Plan Management is
enabled; this parameter is enabled by default

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?

Here is my version, feel free to use it:

The Execution Plan is series of steps or instructions performed by the database to


get or retrieve the data for the report or the sql statement. There it is, in plain
English.

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.

What Is SQL Plan History?:


---------------------------
The SQL Plan History is a collection of all the generated execution plans for a
specific SQL statement, if the statement has been executed at least 2 times (it has
to be a repeatable statement).

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).

What Is SQL Plan Baseline?:


---------------------------
The SQL Plan Baseline is a set of all the ACCEPTED plans from the Plan History for
a specific SQL Statement.
Just like the Plan History, SQL Plan Baseline is not created by default. It is
enabled by setting the following parameter to TRUE

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).

What Is SQL Statement Log?


---------------------------
SQL Statement Log contains the signature of the SQL statements the optimizer
evaluated. The signature is created when the statement is parsed, and is
represented as a number.
The data for the SQL Statement Log is stored in SQLLOG$. This table is populated
only when the SQL Plan History and capture is enabled.

What Is SQL Management Base (SMB)?


-----------------------------------
The SQL Management Base is a logical repository, stored in the data dictionary, in
the SYSAUX tablespace. There are four components
that make up the SQL Management Base:

SQL Management Log


SQL Plan History
SQL Profiles
SQL Patches

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.

What is SQL Plan Management (SPM)?:


-------------------------------------
SQL Plan Management, SPM for short, was introduced in 11g, and the purpose of it
was to preserve the performance of SQL statements during major system changes, such
as database upgrades, optimizer parameter changes, system settings changes,changes
in schema definitions and others.

The SPM has three main components:


Plan Capture ( SQL Plan Baselines)
Plan Selection (how the plans are selected)
Plan Evolution (how plans are verified and evolved from NON-ACCEPTED to ACCEPTED
plans)

The goal of the SPM is to avoid plan regressions.

One Easy Step To Get Started With SQL Plan Baselines:


-----------------------------------------------------
Let me show you how to get started, and how the logic of the optimizer works when
selecting the execution plans.

1. How To Turn On Capture Of SQL Plan Baselines?


2. How Does The Database Determine If A SQL Statement Is Repeatable?
3. How Does The Optimizer Select The Plan From SQL Plan Baseline?

1). How To Turn On Capture Of SQL Plan Baselines?:-


If you have doubts about being able to use or not the components of SQL Plan
Management, because of licensing, rest assured you are allowed to use them.
Using SQL Plan Management (SPM) and SQL Plan Baselines does not require additional
licensing costs. These are part of the Oracle Enterprise Edition. You can check out
Maria Colgan’s post on licensing for these components here.
By default SQL Plan Baselines or SQL Plan History is not captured. Just check
SQLLOG$ table, most likely there are no rows returned.

In order to start capturing baselines and plans, you need to modify the following
parameter. By default, the value is set to FALSE.

alter system set OPTIMIZER_CAPTURE_SQL_PLAN_BASELINES = TRUE;


The parameter is dynamic, so you can do it on the fly, and you can turn it on at
the session level as well.

When you turn this parameter on, 2 things are accomplished:

– SQL Plan History is turned on


– SQL Plan Baseline is turned on

The database will start capturing plan history and baselines for repeatable SQL
Statements.

2). How Does The Database Determine If A SQL Statement Is Repeatable?:-


After you turn on the capture (Step #1 from above), the SQL Statement log will
store the signature of SQL statements.
SQL signature is a numeric hash value generated during the parse phase of a SQL
Statement.
These signatures are stored in SQLLOG$.

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.

Findings from the field:

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.

Below is an example of how this works:

SQL> select version from v$instance;

VERSION
-----------------
12.1.0.1.0

SQL> show parameter optimizer_capture_sql_plan_baselines

NAME TYPE VALUE


------------------------------------ ----------- ---------
optimizer_capture_sql_plan_baselines boolean FALSE

SQL> select count(*) from sqllog$;

COUNT(*)
----------
0 -->> no entries in SQL Plan History

SQL> select count(*) from dba_sql_plan_baselines;

COUNT(*)
----------
0 -->> no entries in SQL Plan Baseline

SQL> alter session set optimizer_capture_sql_plan_baselines=TRUE;

SQL> show parameter optimizer_capture_sql_plan_baselines

NAME TYPE VALUE


------------------------------------ ----------- --------
optimizer_capture_sql_plan_baselines boolean TRUE

-->> 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

SQL> select empno, ename, sal from scott.emp where deptno=10;

EMPNO ENAME SAL


---------- ---------- ----------
7782 CLARK 2450
7839 KING 5000
7934 MILLER 1300

SQL> select * from sqllog$;

SIGNATURE BATCH#
---------------------------------- ----------
3812309255651905568 1

SQL> select signature, sql_text from dba_sql_plan_baselines;

no rows selected
-->> confirms that no SQL Plan Baseline was created.

--> Execute the same statement again:

SQL> select empno, ename, sal from scott.emp where deptno=10;

EMPNO ENAME SAL


---------- ---------- ----------
7782 CLARK 2450
7839 KING 5000
7934 MILLER 1300

SQL> select * from sqllog$;

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

-->> confirms SQL Plan Baseline has been created.

To confirm the signature:

SQL> select sql_id, sql_text , exact_matching_signature from v$sql where


exact_matching_signature=3812309255651905568;

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:
==========

1). Creating a Plan Baseline for a SQL Statement in Memory


You’re planning a database upgrade. You know from past experience that sometimes
after the database is upgraded
SQL queries can perform more poorly. This is caused by the new version of the
optimizer choosing a different
execution plan that is less efficient than the plan used by the prior version of
the optimizer. Therefore, to ensure that
the optimizer consistently chooses the same execution plan before and after the
upgrade, you want to establish a plan
baseline for the query. You want to start by establishing plan baseline for a SQL
query in memory

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:

select sql_id, plan_hash_value, sql_text


from v$sql
where sql_text like 'select first_name from emp where emp_id = 100'
and sql_text not like '%v$sql';

Here is a snippet of the output:


SQL_ID PLAN_HASH_VALUE SQL_TEXT
------------- --------------- ---------------------------------------------------
dv73f7y69ny8z 3956160932 select first_name from emp where emp_id = 100
Now that the SQL_ID has been identified, use it as input to the
DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE
function to create a plan baseline for the given query—for example:
DECLARE
plan1 PLS_INTEGER;
BEGIN
plan1 := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(sql_id => 'dv73f7y69ny8z');
END;
/
The query now should have an entry in the DBA_SQL_PLAN_BASELINES view showing that
it has an enabled plan
baseline associated with it—for example:
SQL> select sql_handle, plan_name, sql_text from dba_sql_plan_baselines;
Here’s a small snippet of the output:
SQL_HANDLE PLAN_NAME SQL_TEXT
------------------------- ----------------------------------- ---------------
SQL_30c98e5c7bb6b95d SQL_PLAN_31kcfbjxvdfaxd8a279cc select first_name...

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

You have the following scenario:


• You’re upgrading a database to a new version.
• You know from past experience that upgrading to newer versions of Oracle can
sometimes
cause SQL statements to perform poorly because the optimizer in the upgraded
version of the
database is choosing a less efficient (worse) execution plan than the optimizer
from the prior
version of the database.
• You want to ensure that a set of critical SQL statements execute with acceptable
performance
after the upgrade.

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.

Step 1: Create an AWR Baseline


The first step is to create an AWR baseline. For example, suppose you knew you had
high-load queries running
between two snapshots in your database. The following creates an AWR baseline using
two snapshot IDs:
BEGIN
DBMS_WORKLOAD_REPOSITORY.create_baseline (
start_snap_id => 2150,
end_snap_id => 2155,
baseline_name => 'peak_baseline_jun14_13');
END;
/

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';

You might also like