Как удалить конкретный неверный план из кэша запросов SQL Server?

33

У нас есть один конкретный запрос SQL Server 2008 (не хранимый процесс, а та же строка SQL - выполняется каждые 5 минут), который периодически кэширует очень плохой план запроса.

Этот запрос обычно выполняется за несколько миллисекунд, но с этим неправильным планом запроса он занимает более 30 секунд.

Как хирургическим путем удалить только один неверно кэшированный план запросов из SQL Server 2008, не удаляя весь кэш запросов на сервере производственной базы данных?

Джефф Этвуд
источник

Ответы:

39

Я понял несколько вещей

select * from sys.dm_exec_query_stats

покажет все кэшированные планы запросов. К сожалению, там нет текста SQL.

Однако вы можете присоединить текст SQL к планам следующим образом:

select plan_handle, creation_time, last_execution_time, execution_count, qt.text
FROM 
   sys.dm_exec_query_stats qs
   CROSS APPLY sys.dm_exec_sql_text (qs.[sql_handle]) AS qt

Отсюда довольно тривиально добавить WHEREпредложение, чтобы найти SQL, который, как я знаю, находится в запросе, и затем я могу выполнить:

DBCC FREEPROCCACHE (plan_handle_id_goes_here)

удалить каждый план запроса из кэша плана запроса. Не совсем легко или удобно, но, похоже, работает ..

edit: дамп всего кэша запросов также будет работать, и это менее опасно, чем кажется, по крайней мере, по моему опыту:

DBCC FREESYSTEMCACHE ('ALL') WITH MARK_IN_USE_FOR_REMOVAL;
Джефф Этвуд
источник
2
совет использовать подсказку плана стоит тем не менее.
Ремус Русану
1
Я нашел это после того, как мой запрос волшебным образом обновился, это плохой план, но я планирую проверить его в следующий раз. Подсказка плана не помогает, если запрос страдает от «option-itis» - где у него много необязательных параметров, и он был оптимизирован для одного набора, а затем выполняется для другого набора. Для такого запроса не существует оптимального плана. Существует оптимальный план для одного набора параметров, который, в свою очередь, ужасен для другого набора параметров.
Nick.McDermaid
6

Если вы знаете, как выглядит хороший план, просто воспользуйтесь подсказкой плана .

Вы не можете удалить определенную запись кэша, но вы можете очистить весь пул кэша с помощью DBCC FREESYSTEMCACHE(cachename/poolname).

Вы можете получить имя кэша неверного плана запроса, если у вас есть дескриптор плана (из sys.dm_exec_requests.plan_handle для идентификатора сеанса, испытывающего проблемы во время выполнения, или из пост-выполнения sys.dm_exec_query_stats ):

select ce.name
from sys.dm_exec_cached_plans cp
join sys.dm_os_memory_cache_entries ce on cp.memory_object_address = ce.memory_object_address
where cp.plan_handle = @bad_plan

Однако все планы SQL имеют название «Планы SQL», что делает выбор правильного для DBCC FREESYSTEMCACHE ... трудным выбором.

Обновить

Неважно, забыл DBCC FREEPROCCACHE(plan_handle), да, это будет работать.

Ремус Русану
источник
1
Возможность передавать plan_handle в DBCC FREEPROCCACHE доступна в SQL Server 2008, а не в SQL Server 2005.
Марио
Что это значит, если в sys.dm_exec_cached_plansнем нет записи для plan_handlefrom sys.dm_exec_requests?
Джонатан Гилберт
@JonathanGilbert это означает, что план не был кэширован, или он был удален из кэша. См. Docs.microsoft.com/en-us/sql/relational-databases/…
Ремус Русану
Так что просто для подтверждения, хотя я только начал выполнять этот запрос , и у запроса нет подсказок о том, что он не должен кэшироваться, его можно кэшировать, поскольку SQL Server принял решение не кэшировать его? Это не потому, что он все еще работает, верно? Если он решит кешировать план, то он будет кеширован прямо с того момента, когда запрос начнет выполняться?
Джонатан Гилберт
1

Решение FREEPROCCACHE хорошо, но более прямой способ сделать это - использовать OPTION (RECOMPILE) в вашей строке SQL (вы упомянули, что это не SP), это говорит Engine о плане одноразового использования, потому что, скорее всего, вы подозреваете есть анализ параметров или ваши статистические данные сильно отличаются от запуска к запуску, и вы подозреваете, что это проблема плохого кэшированного плана.

DECLARE @SQL NVARCHAR(4000)
SELECT @SQL = 'SELECT * FROM Table WHERE Column LIKE @NAME OPTION (RECOMPILE)'
EXEC sp_executesql @SQL, N'@NAME varchar(15)', 'MyName' 
CodeCowboyOrg
источник