Using Plan Guides
At times, you might find it
necessary to use query hints to improve the performance of queries for a
particular query or a small set of queries. Although this may be easy
to do when you have access to the application code, often the particular
queries to be modified are embedded within a third-party application,
and alteration of the queries themselves is virtually impossible. Also,
if you start hard-coding query hints in your application code, changing
them as necessary when data volumes change or when upgrading to a new
version of SQL Server can be a difficult undertaking.
The plan guides feature in SQL
Server 2008 provides an ideal solution for such scenarios by offering
another mechanism for injecting query hints into the original query
without having to modify the query itself. The plan guides mechanism
uses an internal lookup system table, based on information in the sys.plan_guides catalog view, to map the original query to a substitute query or query template.
When a SQL statement is submitted, it is first compared
against the cached plans to check for a match. If a match exists, the
cached query plan is used to execute the query. If no cached plan exists
for the query, the Query Optimizer next looks for a match against the
set of existing plan guides, if any, stored in the current database for a
match. If an active plan guide that matches the SQL statement is found,
the original matching statement is substituted with the one from the
plan guide, the query plan is compiled and cached, and the query is
executed using the plan generated from the plan guide.
Queries that can benefit
from plan guides are generally those that are parameter based and those
that are likely performing poorly because they use cached query plans
whose parameter values do not represent a more representative scenario.
The plan guides feature essentially consists of two stored procedures to create, drop, enable, and disable plan guides and the sys.plan_guides
metadata view that describes the stored plan guides. Plan guides are
created and administered by using the two system stored procedures:
sp_create_plan_guide
sp_control_plan_guide
The syntax for these procedures is as follows:
sp_create_plan_guide [ @name = ] N'plan_guide_name'
, [ @stmt = ] N'statement_text'
, [ @type = ] N'{ OBJECT | SQL | TEMPLATE }'
, [ @module_or_batch = ]
{
N'[ schema_name. ] object_name'
| N'batch_text'
| NULL
}
, [ @params = ] { N'@parameter_name data_type [ ,...n ]' | NULL }
, [ @hints = ] { N'OPTION ( query_hint [ ,...n ] )'
| N'XML_execution plan'
| NULL }
sp_control_plan_guide [ @operation = ] N'<control_option>'
[ , [ @name = ] N'plan_guide_name' ]
<control_option>::=
{
DROP
| DROP ALL
| DISABLE
| DISABLE ALL
| ENABLE
| ENABLE ALL
}
Note that in SQL Server 2008, the sp_create_plan_guide stored procedure enables you to pass an XML execution plan directly in the @hints parameter instead of embedding the output in a USE PLAN hint. This capability simplifies the process of applying a fixed query plan as a plan guide hint.
In addition, a new stored procedure, sp_create_plan_guide_from_handle, allows you to create one or more plan guides from a query plan in the plan cache. The syntax for sp_create_plan_guide_from_handle is as follows:
sp_create_plan_guide_from_handle [ @name = ] N'plan_guide_name'
, [ @plan_handle = ] plan_handle
, [ [ @statement_start_offset = ] { statement_start_offset | NULL } ]
Instead of specifying an actual XML execution plan, you pass the handle for a query plan currently in the plan cache to the @plan_handle parameter. A plan_handle can be obtained from the sys.dm_exec_query_stats DMV. If the cached
plan contains multiple queries in a SQL batch, you can specify the
starting position of the statement within the batch via the @statement_start_offset parameter. The statement offset corresponds to the statement_start_offset column in the sys.dm_exec_query_stats
dynamic management view. If no statement offset is specified, a plan
guide is created for each statement in the batch using the query plan
for the specified plan handle. The resulting plan guides are equivalent
to plan guides that use the USE PLAN query hint to force the use of a specific plan.
Creating Plan Guides
Plan guides can be created to match queries executed in the following contexts:
An OBJECT
plan guide matches queries that execute in the context of T-SQL stored
procedures, scalar functions, or multistatement table-valued functions.
A SQL
plan guide matches queries that execute in the context of ad hoc T-SQL
statements and batches that are not part of a stored procedure or other
compiled database object.
A TEMPLATE plan guide matches ad hoc queries that parameterize to a specified form. These plan guides are used to override the current SET PARAMETERIZATION database option.
In the sp_create_plan_guide statement, you specify the query that you want optimized and provide the OPTION
clause with the query hints necessary to optimize the query in the
manner desired, or an XML execution plan for the query plan you want the
query to use. When the query executes, SQL Server matches the query to
the plan guide and applies the forced query plan to the query at
runtime.
The plan guide can specify any of the following query hints individually or combined with others, when applicable:
{HASH | ORDER} GROUP
{CONCAT | HASH | MERGE} UNION
{LOOP | MERGE | HASH} JOIN
FAST n
FORCE ORDER
MAXDOP number_of_processors
OPTIMIZE FOR ( @variable_name = literal_constant ) [ ,...n ]
OPTIMIZE FOR UNKNOWN
RECOMPILE
ROBUST PLAN
KEEP PLAN
KEEPFIXED PLAN
EXPAND VIEWS
MAXRECURSION number
TABLE HINT (object_name [ , table_hint [ [, ]...n ] ] )
USE PLAN <xmlplan>
PARAMETERIZATION { SIMPLE | FORCED }
The PARAMETERIZATION { SIMPLE | FORCED }
query hint can be used only within a plan guide, and it specifies
whether a query is parameterized as part of compiling a query plan. This
option overrides the current setting of the PARAMETERIZATION option set at the database level.
Listing 3 provides a sample plan guide created for a simple SQL statement.
Listing 3. Creating a Plan Guide for a Simple SQL Statement
sp_create_plan_guide @name = N'PlanGuide1', @stmt = N'SELECT COUNT(*) AS Total FROM dbo.sales s, dbo.titles t WHERE s.title_id = t.title_id and t.pubdate BETWEEN ''1/1/2004'' AND ''1/1/2006'' ', @type = N'SQL', @module_or_batch = NULL, @params = NULL, @hints = N'OPTION (HASH JOIN)'
|
For plan guides of type 'SQL' or 'TEMPLATE' to match a query successfully, the values for batch_text and @parameter_name data_type [,...n ]
must be provided in exactly the same format as their counterparts
submitted by the application. Specifically, they must match character
for character, including comments and whitespaces.
Tip
When you are creating plan guides, be careful to specify the query in the @stmt parameter and any parameter names and values in the @params
parameter exactly as they are received from the application. The best
way to ensure this is to capture the batch or statement text from SQL
Server Profiler. Also, as with the XML query plans passed to the USE PLAN query hint, single-quoted literal values, such as '1/1/2000', need to be delimited with single quotation marks escaped by additional single quotation marks, as shown in Listing 1.
Managing Plan Guides
You use the sp_control_plan_guide stored procedure to enable, disable, or drop a plan guide. The following example drops the plan guide created in Listing 3:
sp_control_plan_guide N'DROP', N'PlanGuide1'
To execute sp_control_plan_guide on a plan guide of type OBJECT (for example, a plan guide created for a stored procedure), you must have at least ALTER permission on the object that is referenced by the plan guide. For all other plan guides, you must have at least ALTER DATABASE permission. Attempting to drop or alter a function or stored procedure that is referenced by a plan guide results in an error.
In SQL Server 2008, it
is possible to define multiple plan guides for the same query. However,
only one plan guide can be active at a time. You can use sp_control_plan_guide to enable and disable plan guides.
Validating Plan Guides
The new system function sys.fn_validate_plan_guide
can be used to validate a plan guide. Plan guides can become invalid
after changes such as dropping an index are made to the physical design
of the database. By validating a plan guide, you can determine whether
the plan guide can be used unmodified by the query optimizer. The sys.fn_validate_plan_guide
function returns the first error message encountered when the plan
guide is applied to its query. If the plan guide is valid, an empty
rowset is returned.
The sys.plan_guides Catalog View
All plan guides are stored in the sys.plan_guides
database system catalog view. You can get information about the plan
guides defined in a database by running a query against the sys.plan_guides catalog view, as in the following example:
select name, is_disabled, scope_type_desc, scope_object_id,
parameters, hints, query_text from sys.plan_guides
Table 1 describes the columns in the sys.plan_guides catalog view.
Table 1. sys.plan_guides Columns
Column Name | Description |
---|
plan_guide_id | Unique identifier of the plan guide. |
Name | Name of the plan guide. |
create_date | Date and time the plan guide was created. |
modify_date | Date the plan guide was last modified. |
is_disabled | 1 = disabled and 0 = enabled. |
query_text | Text of the query on which the plan guide is created. |
scope_type | Scope of the plan guide: 1 = OBJECT, 2 = SQL, and 3 = TEMPLATE. |
scope_type_desc | Description of scope of the plan guide: OBJECT, SQL, or TEMPLATE. |
scope_object_id | If scope_type is OBJECT, the object_id of the object defining the scope of the plan guide; otherwise, NULL. |
scope_batch | If scope_type is SQL, the text of the SQL batch. If NULL, either the batch type is not SQL or scope_type is SQL, and the value of query_text applies. |
parameters | The string defining the list of parameters associated with the plan guide. If NULL, no parameter list is associated with the plan guide. |
hints | The query OPTION hints associated with the plan guide. |
Plan Guide Best Practices
Following are some of the recommended best practices for using the USE PLAN query hint and the plan guides feature:
- The USE PLAN query hint and plan guides should be used only
when other standard query tuning options, such as tuning indexes and
ensuring the table has current statistics, have been extensively tried
and have failed to produce the necessary results. When a query plan is
forced by using either the USE PLAN query hint or a plan guide,
it prevents the Query Optimizer from adapting to changing data
distributions, new indexes, or improved query execution algorithms in
successive SQL Server releases or service packs.
- You need to be sure to have a
full understanding of query optimization and of the implications and
long-term ramifications of forcing query plans.
- You
should try to force only a small fraction of the workload. If you find
you are forcing more than a few dozen queries, you should check whether
other issues with the configuration could be limiting performance,
including insufficient system resources, incorrect database
configuration settings, missing indexes, poorly written queries, and
other factors.
- It is not advisable to attempt to code by hand or modify the XML execution plan that is specified in the USE PLAN
query hint. You should capture and use a plan produced by SQL Server
itself. The XML execution plan is a lengthy and complex listing, and
improper changes could prevent it from identically matching one of the
Query Optimizer–generated plans, which would result in the USE PLAN hint being ignored.
- The USE PLAN
query hint should not be directly embedded into the application code
because that would make the maintenance of the application across query
plan and SQL Server version changes difficult to manage. Also, embedding
USE PLAN directly into the query generally prevents the plan for the query from being cacheable. The USE PLAN hint is intended primarily for ad hoc performance tuning and test purposes, and for use with the plan guides feature.
- The
plan guides created for an application should be well documented and
regularly backed up because they constitute an integral part of the
application’s performance tuning.
You should also retain the scripts that you used to create plan guides
and treat them as you would other source code for an application.
- After creating a plan guide, you should test to make sure that it is being applied to the intended queries.
Verifying That a Plan Guide Is Being Applied
When you have a plan guide
defined, you might want to verify that the application query is making
use of the plan guide. You can follow these steps to confirm whether a
plan guide is being used:
1. | After
creating the plan guide, run SQL Server Profiler and configure it to
capture the query text and XML execution plan for the application and
the query in question and start the Profiler trace.
|
2. | Run your application and cause it to invoke the query in question.
|
3. | Stop
the profiler trace and collect the query plan by right-clicking the
Showplan XML Statistics Profile event that corresponds to the query and
then selecting the Extract Event Data option.
|
4. | Save the event data to a file.
|
5. | Open the Showplan.xml file in any text file viewer or Internet Explorer to examine the XML code.
|
6. | If the plan guide was used to generate the query plan, the XML execution plan output contains the PlanGuideDB and PlanGuideName tags, as shown in the following example:
<ShowPlanXML xmlns= "http://schemas.microsoft.com/sqlserver/2004/07/showplan" Version="1.0" Build="9.00.1282.00"> <BatchSequence> <Batch> <Statements> <StmtSimple PlanGuideDB="bigpubs2008" PlanGuideName="PlanGuide1"> ... </StmtSimple> </Statements> </Batch> </BatchSequence> </ShowPlanXML>
|
As another option to help
determine whether or not plan guides are being used, you can use two new
event classes available in the SQL Server 2008 Profiler: Plan Guide
Successful and Plan Guide Unsuccessful. These new event classes make it
easier to verify whether plan guides are being used by the Query
Optimizer. For example, if SQL Server cannot produce an execution plan
for a query that contains a plan guide when the initial plan guide
compilation is performed, the query is automatically compiled without
using the plan
guide, and the Plan Guide Unsuccessful event is raised.
Note
Note that the plan guide
events are raised only during the initial compilation of a query that
contains a plan guide when the query plan gets loaded into the plan
cache. If you do not see either of these events, you may need to flush
the plan cache using DBCC FREEPROCCACHE.
In addition, there are two new Performance Monitor counters: Guided Plan Executions/sec and Misguided Plan Executions/sec.
These counters are available in the SQL Server, SQL Statistics Object.
They report the number of plan executions in which the query plan has
been successfully or unsuccessfully generated using a plan guide.
Creating and Managing Plan Guides in SSMS
SQL Server 2008 enables you to create, delete, enable, disable, or
script plan guides within SSMS. The plan guides options are available in
the Programmability folder in Object
Explorer. To create a new plan guide in SSMS, right-click on Plan Guides
and select the New Plan Guide option. This brings up the dialog shown
in Figure 1.
To
manage existing plan guides in SSMS, you can right-click on the plan
guide name to bring up the context menu that allows you to enable,
disable, delete, script, or view the properties of the plan guide, as
shown in Figure 2.
Limiting Query Plan Execution with the Query Governor
Another tool for managing query
plans in SQL Server 2008 is the query governor. Because SQL Server uses a
cost-based Query Optimizer, the cost of executing a given query is
always estimated before the query is actually executed. The query
governor enables you to set a cost threshold to prevent certain
long-running queries from being executed. This is not so much an
optimization tuning tool as it is a performance problem prevention tool.
For example, if you have an
application with an ad hoc reporting front end, you have no way of
controlling what the user is going to request from the database and the
type of query generated. The query governor allows you to prevent a
runaway query from executing and using up valuable CPU and memory
resources by processing a poorly formed query. You can set the query
governor cost limit for the current user session by setting the
session-level property QUERY_GOVERNOR_COST_LIMIT:
SET QUERY_GOVERNOR_COST_LIMIT value
The value specified is the
maximum length of time, in seconds, a query is allowed to run. If the
Query Optimizer estimates the query would take longer than the specified
value, SQL Server does not execute it.
Although
the option is specified in seconds, it is a relative value that
corresponds to the estimated subtree cost, as calculated by the Query
Optimizer. In other words, if you set the query governor cost limit to 100,
it prevents the execution of any queries whose estimated subtree cost
is greater than 100 seconds. The estimated subtree cost time is based on
the query cost algorithm in SQL Server and might not map exactly to how
long the query actually takes to run on your own system. The actual
runtime depends on a number of factors: CPU speed, I/O speed, network
speed, the number of rows returned over the network, and so on. You need
to correlate the Query Optimizer runtime estimate to how long the query
actually takes to run on your system to set the query governor cost
limit to a value related to actual query runtime.
The best way to figure out how to set the query governor is to run your queries with the STATISTICS PROFILE and STATISTICS TIME session settings enabled.You then compare the values in the TotalSubtree Cost column for the first row of the STATISTICS PROFILE output with the elapsed time displayed by STATISTICS TIME
for your query. If you do this for a number of your queries, you might
be able to come up with an average correlation of the actual runtimes
with the Query Optimizer’s estimated query cost. For example, if the
average cost estimate is 30 seconds and the actual runtimes are 15
seconds, you may need to double the setting for query governor cost
limit to correspond to the actual execution time threshold; in other
words, if you want the threshold to be 60 seconds for this example, you
would want to set the query governor threshold to 120.
To configure a query governor
threshold for all user connections, you can also set it at the server
level. In SSMS, right-click the server in the Object Browser and choose
Properties from the menu. In the Server Properties dialog, select the
Connections page. Enable the Use Query Governor to Prevent Long-Running
Queries check box and specify the desired cost threshold (see Figure 3). The cost threshold is specified in the same units as specified for the QUERY_GOVERNOR_COST_LIMIT session setting.
Alternatively, you can configure the server-wide query governor setting by using sp_configure:
sp_configure query governor cost limit, 100