SQL Server 2008 has a pool of memory used to store
both execution plans and data. The amount of memory allocated to
execution plans or data changes dynamically, depending on the needs of
the system. The portion of memory used to store execution plans is often
referred to as the plan cache.
The first time a cacheable
query is submitted to SQL Server, the query plan is compiled and put
into the plan cache. Query plans are read-only re-entrant structures
shared by multiple users. At most, there are two instances of a query
plan at any time in the plan cache: a serial execution plan and parallel
query execution plan. The same parallel execution plan is used for all
parallel executions, regardless of the degree of parallelism.
When you execute
subsequent SQL statements, the Database Engine first checks to see
whether an existing execution plan for the same SQL statement already
resides in the plan cache. If it finds one, SQL Server attempts to reuse
the matching execution plan, thereby saving the overhead of having to
recompile an execution plan for each ad hoc SQL statement issued. If no
matching execution plan is found, SQL Server is forced to generate a new
execution plan for the query.
The ability to reuse query
plans for ad hoc queries in addition to caching query plans for stored
procedures can help improve the performance for complex queries that are
executed frequently because SQL Server can avoid having to compile a
query plan every time it’s executed if a matching query plan is found in
memory first.
Query Plan Reuse
Query plan reuse for
stored procedures is pretty straightforward. The whole idea behind
stored procedures is to promote plan reuse. For stored procedures and
triggers, plan reuse is simply based on the procedure or trigger name.
The first time a stored procedure is executed, the query plan is
generated based on the initial parameters. On subsequent executions, SQL
Server checks the plan cache to see whether a query plan exists for a
procedure with the same name, and if one is found, it simply substitutes
the new parameter values into the existing query plan for execution.
Another method that promotes query plan reuse is using the sp_executesql stored procedure for executing dynamic SQL statements. When using sp_executesql, typically you specify a dynamic query with explicitly identified parameters for SARGs. Here’s an example:
sp_executesql N'select t.title, pubdate from bigpubs2008.dbo.authors a
join bigpubs2008.dbo.titleauthor ta on a.au_id = ta.au_id
join bigpubs2008.dbo.titles t on ta.title_id = t.title_id
where a.au_lname = @name', N'@name varchar(30)', 'Smith'
When the same query is executed again via sp_executesql,
SQL Server reuses the existing query plan (if it is still in the plan
cache) and simply substitutes the different parameter values.
Although SQL Server can
also match query plans for ad hoc SQL statements, there are some
limitations as to when a plan can be reused. For SQL Server to match SQL
statements to existing execution plans in the plan cache for ad hoc
queries, all object references in the query must be qualified with at
least the schema name, and fully qualified object names (database plus
schema name) provide increased likelihood of plan reuse. In addition,
plan caching for ad hoc queries requires an exact text match between the
queries. The text match is both case sensitive and space sensitive. For
example, the following two queries are logically identical, but because
they are not textually identical, they would not share the same query
plan:
select a.au_lname, t.title, pubdate
from authors a
join titleauthor ta on a.au_id = ta.au_id
join titles t on ta.title_id = t.title_id
select a.au_lname,
t.title,
pubdate
from authors a
join titleauthor ta on a.au_id = ta.au_id
join titles t on ta.title_id = t.title_id
Another factor that can prevent query plan reuse by matching queries is differences in certain SET
options, database options, or configuration options in effect for the
user session when the query is invoked. For example, a query might
optimize differently for one session if the ANSI_NULLS option is turned on than it would if it were turned off. The following list of SET options must match for a query plan to be reused by a session:
ANSI_PADDING
FORCEPLAN
CONCAT_NULL_YIELDS_NULL
ANSI_WARNINGS
ANSI_NULLS
QUOTED_IDENTIFIER
ANSI_NULL_DFLT_ON
ANSI_NULL_DFLT_OFF
If any one of these setting
values does not match the setting options for a cached plan, the session
generates a new query plan. Likewise, if the session is using a
different language or DATEFORMAT
setting than that used by a cached plan, it needs to generate a new
execution plan. As you can see, sometimes fairly subtle differences can
prevent plan reuse.
Simple Query Parameterization
For
certain simple queries executed without parameters, SQL Server 2008
automatically replaces constant literal values with parameters and
compiles the query plan. This simple parameterization of the query plan
increases the possibility of query plan matching for subsequent queries.
If a subsequent query differs in only the values of the constants, it
matches with the parameterized query plan and reuses the query plan.
Consider this query:
SELECT * FROM AdventureWorks.Production.Product WHERE ProductSubcategoryID = 1
The search value 1
at the end of the statement can be treated like a parameter. When the
query plan is generated for this query, the Query Optimizer replaces the
search value with a placeholder parameter, such as @p1. This process is called simple parameterization.
Using the method of simple parameterization, SQL Server 2008 recognizes
that following statement is identical to the first except for the
search value of 9 and would generate essentially the same execution plan:
SELECT * FROM AdventureWorks.Production.Product WHERE ProductSubcategoryID = 9
This query will reuse the query plan generated by the first query.
Query Plan Aging
A query plan is saved in cache
along with a cost factor that reflects the cost of actually creating the
plan when compiling the query. For ad hoc query plans, SQL Server sets
its cost to 0, which indicates that
the plan can be removed from the plan cache immediately if space is
needed for other plans. For other query plans, such as for a stored
procedure, the query plan cost is a measure of the amount of resources
required to produce it. This cost is calculated in “number of ticks.”
The maximum plan cost is 31. The plan cost is determined as follows:
Every 2 I/Os required by the plan = 1 tick (with a maximum of 19 ticks)
Every 2 context switches in the plan = 1 tick (with a maximum of 8 ticks)
Every 16 pages (128KB) of memory required for the plan = 1 tick (with a maximum of 4 ticks)
All
reusable query plans remain in cache until space is needed in the plan
cache for a new plan. When space is needed, SQL Server removes the
oldest unused execution plan from the plan cache that has the lowest
plan cost.
As plans age in cache, the
plan cost is not decremented until the size of the plan cache reaches
50% of the buffer pool size. When this occurs, the next access of the
plan cache results in the plan cost for all query plans being
decremented by 1. As plans reside in the plan cache over a period of
time and are not reused, they eventually reach a plan cache cost of 0
and thus become eligible to be removed from cache the next time plan
cache space is needed. However, when a query plan is reused, its plan
cost is reset back to its initial value. This helps ensure that
frequently accessed query plans remain in the plan cache.
Recompiling Query Plans
Certain changes in a
database over time can cause existing execution plans to become either
inefficient or invalid, based on the new state of the database. SQL
Server detects the changes that invalidate an execution plan and marks
the plan as not valid. A new plan is then automatically recompiled the
next time the query that uses that query plan is invoked. Most query
plan recompilations are required either for statement correctness or to
obtain potentially faster query execution plans. The types of conditions
that can invalidate a query plan include the following:
- Modifications made to the definition of a table or view referenced by the query using ALTER TABLE and ALTER VIEW
- Changes made to any indexes used by the execution plan
- Updates to the statistics used by the execution plan via either the UPDATE STATISTICS command or automatically
- Dropping of an index or indexed view used by the execution plan
- Execution of sp_recompile on a table referenced by the query plan
- Large numbers of changes to keys (generated by INSERT or DELETE statements from other users that modify a table referenced by the query)
- Adding or dropping a trigger on a table
- When
the number of rows in the inserted or deleted tables grows
significantly within a trigger defined on a table referenced in the
query plan
- Execution of a stored procedure with the WITH RECOMPILE option specified
To avoid the
unnecessary recompilation of statements that do not require it, SQL
Server 2008 performs statement-level recompilation: only the statement
inside the batch or stored procedure that requires recompilation is
recompiled. Statement-level recompilation helps
improve query performance because, in most cases, only a small number
of statements within a batch or stored procedure cause recompilations
and their associated penalties, in terms of CPU time and locks. These
penalties are therefore avoided for the other statements in the batch
that do not have to be recompiled.
Forcing Query Plan Recompiles
If you suspect that a query
plan that is being reused is not appropriate for the current execution
of a query, you can also manually force the query plan to be recompiled
for the query. This capability can be especially useful for
parameterized queries. Query parameterization provides a performance
benefit by minimizing compilation overhead, but a parameterized query
often provides less specific costing information to the Query Optimizer
and can result in the creation of a more general plan, which can be less
efficient than a more specific plan created for a specific set of
literal values.
If the initial
parameterized query plan generated for the query was not based on a
representative set of parameters, or if you are invoking an instance of
the query with a nonrepresentative set of search values, you might find
it necessary to force the Query Optimizer to generate a new query plan.
You can force query recompilation for a specific execution of a query by
specifying the RECOMPILE query hint.