Evitando a
recompilação de SPs – Luciano Leston – Dez 2004
d) Geração do plano de execução
Recompilação em tempo de execução:
f) Mudanças no status do ambiente
g) Uso do caractere especial *
Algumas formas de diminuir a recompilação de SPs:
b) Uso de DDL e DML separadamente
c) Cuidados com tabelas temporárias
h) Sub-SPs e SPs particionadas:
Uma das maiores estratégias de
performance de um SGBD é a capacidade de reaproveitar processos de execução
anteriores, gravados fisicamente ou armazenados em cache de memória. No SQL
Server, o “Query Optimizer” é o responsável por isso. Ele seleciona a melhor
estratégia de acesso aos dados (resultados mais rápidos e eficientes) e
armazena esse processo para reutilização, de forma que, quando solicitado, o
melhor plano de execução esteja sempre disponível. Esse evento é chamado de
“compilação”. A utilização de "objetos" compilados garante um aumento
significativo de performance na maior parte das situações, uma vez que gerar um
plano de execução tem um custo bastante elevado.
Quando uma
“Stored Procedure” é chamada pela primeira vez, um plano de execução é criado
para ela e armazenado para posterior aproveitamento. A “compilação" de uma
SP não é a compilação de código que ocorre em algumas linguagens como VB e
JAVA, mas na verdade é um processo intermediário, onde o SQL analisa a
estrutura de dados e de fluxo de execução, acelerando os resultados das querys
internas. Na compilação de uma SP temos: verificação de sintaxe, localização
dos objetos, escolha dos índices e análise das constraints, definição das
operações a serem executadas em paralelo, estimativa de custo para a execução
de cada instrução e alocação da quantidade de memória (Procedure Cache) a ser
utilizada. Basicamente, o ganho de performance em se usar uma SP reside no
aproveitamento do plano que está em cache a partir da segunda execução. Se esse
plano não puder ser reaproveitado ou se uma SP recompilar muitas vezes é
possível que ela tenha um custo maior do que a execução de suas partes
isoladamente.
O plano de execução da SP armazenado é utilizado a cada nova chamada, até que uma ação ocorra para invalidar esse plano, forçando a uma nova compilação deste. Algumas ações geram automaticamente a recompilação da SP, como por exemplo, as alterações de objetos referenciados por ela (tabelas, índices, constranints, triggers, etc). Ações como essas causam a recompilação da SP no momento em que esta é chamada. Existem também (e são mais freqüentes) as recompilações em tempo de execução. Essas é que causam a maior parte da perda de performance na execução de SPs, aumentando significativamente o seu custo, podendo até inviabilizar o seu uso (principalmente em SPs muito extensas). Uma recompilação afeta todo o plano de execução e não apenas a parte das instruções que originaram o processo.
O processo de compilação se divide em quatro
etapas: Parse e Normalize, compilação das instruções T-SQL, compilação das
instruções SQL e a geração do plano de execução.
O otimizador do SQL executa uma operação chamada de “constant folding”, que é a analise das expressões no momento da compilação, evitando desperdícios na geração do plano de execução. Por exemplo, quando passamos para avaliação uma expressão como “WHERE a > 2*2 and a < 1*3” o otimizador do sql traduz a operação para “a>4 and a<3” e determina que a expressão não é valida (como resultado) e elimina a expressão do plano de execução (gera uma expressão no plano marcando seu retorno como 0).
Na arquitetura da SP, o plano de execução é dividido em duas estruturas chamadas “Compiled Plan” e “Executable Plan”:
Quando uma SP é
compilada, automaticamente é gerado um “compile lock”, que afeta não só a SP em
questão, mas todos os planos desta SP que estiverem sendo compartilhados (em
execução). Todas as execuções são suspensas até que a compilação esteja
concluída. A recompilação é a maior causa da ocorrência de "compile
locks" em SPs e triggers.
Outros motivos para
que os "compile locks" ocorram são:
·
O
usuário que executa a SP não é o seu "owner";
·
O nome
da SP não está qualificado com o "owner" no momento da execução;
·
Os
objetos referenciados dentro da SP não estão qualificados com o seu
"owner".
Exemplo de como a
falta de identificação do "owner" gera o compile lock:
Sendo o usuário
"dbo" o "owner" da SP "usp_sem_owner" e sendo
esta executada pelo usuário "paulo" com a instrução "exec
usp_sem_owner", a busca inicial pelo objeto em cache falha. Antes do
"optimizer" se preparar para execução da SP ele verifica se o nome do
"owner" está devidamente qualificado e quando não está (como no nosso
caso) é atribuído o nome do usuário executor como sendo o "owner" do
objeto. Neste caso obtemos o nome "paulo.usp_sem_owner". Uma
vez que esse objeto não existe ocorre um evento de cache chamado SP:CacheMiss.
Como o SQL não pode determinar se o plano que está em cache (dbo.usp_sem_owner)
é o referido, um lock exclusivo é colocado na SP e o otimizador se prepara para
recompilar. Nesse momento o otimizador localiza o nome do objeto e obtém o seu
"ID". Antes de compilar o plano de execução, o SQL usa o objectID
para realizar uma nova busca no cache. Se for localizado um plano anterior no
cache, ele será executado e o "compíle lock" cancelado, caso
contrário uma compilação terá inicio. Quando especificamos o "owner"
todo esse processo é realizado em uma única etapa e não causa nenhum
"compile lock".
Uma vez
que o plano está pronto para ser executado, o otimizador do sql utiliza o mesmo
algoritmo anterior, buscando desta vez pelos objetos referenciados dentro da SP
(um a um). Se eles não estiverem qualificados com o "owner", teremos
um "compile lock" para toda a SP até que ela tenha sido completamente
analisada. Caso seja necessário uma recompilação, um "compile lock"
será colocado em todos os planos compilados compartilhados, suspendendo todas
as execuções concorrentes até que a nova compilação esteja concluída.
Stored procedures com o nome
iniciando por "sp_" são consideradas pelo SQL Server como sendo do
sistema e por sua vez possuem regras específicas de manipulação. Para começar,
elas estão localizadas, por default, no banco "master". Usar esse prefixo para o nome de SPs que não
se localizem no banco "master" fará com que o SQL obtenha um evento
"SP:CacheMiss", ou seja, ele nunca encontrará a SP em cache (como
descrito acima) e a pesquisa para a localização da SP terá sempre um custo mais
elevado (não se trata mais de pesquisar dentro do banco e sim entre o master e
o banco atual). Além disso, não adianta qualificar a SP com o nome do banco,
pois como regra, ele irá sempre no "master" para depois ir ao banco
atual (ou qualificado).
Após o plano ser colocado em cache,
o SQL se prepara para a execução, verificando se não houve nenhuma alteração e
se o plano não precisa ser recompilado. Embora poucos microssegundos separem a
compilação da execução, podemos ainda ter processamento caso existam instruções
DDL a serem processadas (CREATE, ALTER, DROP, etc) no código, teremos uma nova
recompilação no momento em que seus objetos forem referenciados.
Nenhum processo de recompilação será
disparado para mudanças no valor dos parâmetros (a não ser que eles mudem a
estrutura do plano de execução a ponto de invalidá-lo), no espaço de memória,
nos recursos do sistema ou necessidade de acessar dados em cache.
Quando uma SP for realmente ser
executada, um processo chamado "memory grant scheduler" é chamado.
Esse processo controla o uso de memória usado pelo plano de execução. Ele é
responsável por algumas otimizações na execução como, por exemplo, quando
processamos uma instrução "select top 10" e nosso resultset for de
100 registros, o "memory grant scheduler" aloca memória apenas para o
resultado final e instrui a finalização da query antes do previsto (antes de
obter os 100 registros). Com isso garante-se que somente serão processados
tantos registros quanto necessários para atender a query e que não haverá
desperdício de memória e recursos. Quando o "scheduler" retorna ok
para a alocação da memória requisitada, o plano é aberto e a execução começa,
prosseguindo assim até sua conclusão.
Como o SQL interpreta uma chamada
de execução para uma SP (ou batch):
Embora todo o processo de compilação já esteja processado e o devido plano sendo executado, ainda podem ocorrer recompilações devido a processos que, durante o fluxo da SP, alterem a estrutura do que o SQL Optimizer tenha projetado (plano de execução). Existem vários eventos que podem causar isso e varias formas de evitá-los (ou minimizá-los). Eis alguns dos principais eventos que podem causar a recompilação em tempo de execução:
·
Mudanças nas tabelas ou views (ALTER TABLE ou ALTER VIEW);
·
Criação ou atualização das estatísticas (manual ou
automático);
·
A exclusão ou alteração de um índice usado no plano de
execução;
·
Um grande número de mudanças nos registros (ver ítem “b”).
Tipo de Tabela |
Resultando em uma tabela vazia |
Iniciando a partir de uma tabela
vazia |
A partir de uma tabela que
contem registros |
Permanente |
< 500 registros |
>= 500 registros |
>= 500 registros + 20% do
total de registros |
Temporária |
< 6 registros |
>= 6 registros |
>=6 registros + 20% do total
de registros |
·
Ex. de DDL: CREATE, ALTER, DROP, TRUNCATE
·
Ex. de DML: Select, Insert,
Update, Declare, Set
create procedure dbo.usp_exemplo
as
create
table dbo.Tabela1 (campo1 int) --- DDL
select
campo1 from dbo.Tabela1 --- DML=>recompila
create index IX_Tb1 on dbo.Tabela1(campo1) --- DDL
Insert
into dbo.Tabela1 Values (123) --- DML=>recompila
select campo1 from dbo.Tabela1 --- DML
create table dbo.Tabela2 (campo2
char(5)) ---
DDL
select campo2 from dbo.Tabela2 ---
DML=>recompila
Neste
caso a SP será recompilada três vezes quando for executada. No processo de
criação do plano de execução pelo otimizador do SQL ele inicialmente compila
toda a SP. Ocorre que nesse momento as tabelas “Tabela1” e “Tabela2” ainda não
existem. Por causa disso, nenhum plano de execução para as referências destas
tabelas é gerado. Eles somente serão gerados durante a execução. Quando a SP for executada pela
primeira vez, o primeiro passo é criar a tabela “Tabela1”. Em seguida, é
executado um “Select” na Tabela1,a qual não tem nenhum plano de execução. Nesse
momento a SP é recompilada para gerar o plano de execução da instrução “Select”
e de todas as outras instruções que referenciam esta tabela, no nosso caso um
“insert” e um “select” após a criação do índice. Nenhum plano é gerado para o
“select” na tabela “Tabela2” pois ela não existe ainda. O próximo passo é criar
o índice para a tabela “Tabela1”. Em seguida é executado um novo “Select” (e
agora já existe um plano para ele). Entretanto, devido à mudança no “schema” da
Tabela1, uma nova recompilação será processada e o plano de execução será
atualizado (gerado novamente). A seguir, a tabela “Tabela2” será criada e uma
instrução “Select” será executada. Devido a não existir nenhum plano de
execução a SP é recompilada novamente. Estas recompilações ocorrerão em cada
execução da SP. Para reduzirmos isso, devemos modificar a SP para que todos as
instruções DDL sejam colocadas juntas, realizando uma única recompilação. Ex:
create procedure dbo.usp_exemplo
as
-- Todas as instruções DDL primeiro
create table dbo.Tabela1 (campo1 int)
create index IX_Tabela1 on dbo.Tabela1
(campo1)
create table dbo.Tabela2 (campo2 char(5))
-- Seguida pelas
instruções DML
select campo1 from dbo.Tabela1 --- recompila uma
única vez
Insert into dbo.Tabela1 Values (123)
select campo1 from dbo.Tabela1
select
campo2 from dbo.Tabela2
Desta forma o otimizador irá compilar da seguinte maneira: Inicialmente (como no caso anterior) não existem planos para as tabelas “Tabela1” e “Tabela2” pois elas não existem. No primeiro passo todas as instruções DDL são executadas (criamos “Tabela1” e seu índice e “Tabela2”). Na primeira execução do “Select”, como ainda não existe um plano de execução, a SP será recompilada. Como todos os objetos já existem, são gerados planos de execução para todos os planos que estão faltando durante a mesma compilação. Como não existem mais alterações nos objetos referenciados, nenhuma outra compilação será necessária. É bom lembrar que o restante da execução usará o mesmo plano de execução e cache criados anteriormente.
·
Todas as instruções que se referenciam a uma tabela
temporária devem ser da tabela criada na mesma SP e não a uma SP que chamou
esta ou de uma SP a ser chamada, ou ainda a uma string executada via EXEC ou
“sp_executesql”
·
Não devem existir cursores apontando para uma tabela
temporária
·
Instruções DROP TABLE na tabela temporária não podem
aparecer antes do fim da SP. Não existe nenhum motivo para a execução de DROP
TABLE em tabelas temporárias, uma vez que elas são excluídas imediatamente após
o término da execução da SP.
·
Nunca usar instruções CREATE TABLE ou SELECT INTO em tabelas
dentro do controle de IF...ELSE ou WHILE
select campo1 from
dbo.Tabela1
option (KEEP PLAN)
CREATE
PROCEDURE dbo.usp_ModificaRegistros
AS
-- vamos considerar que exista uma tabela TestePlan com
-- a mesma definição de #T e com mais de 1000 registros
CREATE TABLE
#T (a int, b varchar(15), c decimal(9,2), d int)
INSERT INTO #T
SELECT a,b,c,d FROM dbo.TestePlan
SELECT count(1) FROM #T WHERE a = 37
Na segunda
execução da SP "usp_ModificaRegistros" o código " select
count(1) from #t where a = 37" irá
causar uma recompilação (devido ao número de registros que foram acrescentados a tabela temporária).
Alterando a SP para incluir a opção "keepfixed plan":
ALTER
PROCEDURE dbo.usp_ModificaRegistros
AS
...
SELECT count(1) FROM #T WHERE a = 37
OPTION (KEEPFIXED PLAN)
A partir
da segunda execução nenhuma recompilação irá ocorrer (será preservado o plano
escolhido na compilação inicial). Isso pode ser interpretado como uma situação
favorável, mas não podemos esquecer que, quaisquer que sejam as alterações na
tabela, não haverá uma recriação do plano de execução, ou seja, não será utilizado nenhum novo índice e
nenhuma atualização de estatística.
SET CONCAT_NULL_YIELDS_NULL
ON
Exec dbo.USP_NaoAlterarAmbiente
No exemplo acima também poderíamos substituir o uso do SET usando dentro
da SP a função ISNULL() durante as concatenações de string.
Alguns SETs não tem efeito quando colocados dentro da SP e não causam
recompilação, como no caso do SET NOCOUNT, que altera apenas a exibição no
cliente, não envolvendo nenhum processo interno do SQL ou dos planos de
execução. Existem outros casos que são as declarações implícitas de ambiente
colocadas na SP como, por default, SET ANSI_NULLS ON e SET QUOTED_IDENTIFIER
ON. Esses SETs são armazenados na coluna status da tabela de sistema sysobjetcs
e somente poderão ser alteradas se a SP for excluída e recriada com outras
definições.
Abaixo uma lista com as instruções SET e seu efeitos sobre a SP:
Recompilam
a SP |
ANSI_DEFAULTS |
ANSI_NULL_DFLT_ON |
ANSI_NULL_DFLT_OFF |
ANSI_NULLS |
ANSI_PADDING |
ANSI_WARNINGS |
ARITHABORT |
CONCAT_NULL_YIELDS_NULL |
FORCEPLAN |
FIPS_FLAGGER |
LANGUAGE |
NUMERIC_ROUNDABORT |
OFFSETS |
QUOTED_IDENTIFIER |
Não
recompilam a SP |
ARITHIGNORE |
CURSOR_CLOSE_ON_COMMIT |
DATEFIRST |
DATEFORMAT |
DEADLOCK_PRIORITY |
FMTONLY |
IDENTITY_INSERT |
IMPLICIT_TRANSACTIONS |
LOCK_TIMEOUT |
NOCOUNT |
NOEXEC |
PARSEONLY |
QUERY_GOVERNOR_COST_LIMIT |
REMOTE_PROC_TRANSACTIONS |
ROWCOUNT |
STATISTICS IO |
STATISTICS PROFILE |
STATISTICS TIME |
TEXTSIZE |
TRANSACTION ISOLATION LEVEL |
XACT_ABORT |
Usando EXEC – A string passada para execução é convertida no padrão
UNICODE e por conseqüência todos os parâmetros passados (e executados) estarão
nesse formato e irão requerer uma conversão de tipo na hora da execução. O
plano de execução gerado será descartado após sua execução e mesmo que se
repita a execução não haverá cache da execução anterior (sem reaproveitamento
de cache).
Usando SP_EXECUTESQL – O uso dessa SP de sistema é muito semelhante a
instrução EXECUTE, mas trás alguns benefícios em relação a ela, afetando não só
a performance mas também reduzindo a recompilação. Embora a string passada
também seja convertida para o UNICODE, os parâmetros podem ser passados
separadamente em seu formato original, sem a necessidade de qualquer conversão
de tipo. Uma vez que foi executada a query e gerado um plano que poderá ser
reutilizado tantas vezes quanto for executado (os objetos referenciados devem
estar completamente qualificados) e os parâmetros podem ser modificados. Desta
forma a compilação ocorrerá uma única vez, mantendo-se o mesmo plano para toda
a SP – para que isso aconteça é necessário que os diferentes parâmetros não
alterem o tamanho do resultado e que as mesmas variáveis (parâmetros)
sejam re-utilizadas (se for o caso
podem ser passadas com valor NULL).
Exemplo do EXECUTE:
DECLARE @sql nvarchar(500)
SET @sql = N'select NumEstacao from dbo.Estação
where idtEstacao = 984582'
-- Um plano de execução será gerado para essa query
-- A SP será recompilada
EXECUTE (@sql)
SET @sql = N'select NumEstacao from dbo.Estação
where idtEstacao = 984538'
-- Um plano de execução será gerado para essa query
-- A SP será recompilada
EXECUTE (@sql)
Exemplo de SP_EXECUTESQL:
DECLARE @VariavelINT int
DECLARE @sql nvarchar(500)
DECLARE @parametro nvarchar(500)
SET @sql = N'select NumEstacao from Estacao
where idtEstacao = @idtEstacao'
SET @parametro = N'@idtEstacao int'
-- Um plano de execução será gerado para essa query
-- A SP será recompilada
SET @VariavelINT = 984582
EXECUTE sp_executesql @sql, @parametro, @idtEstacao= @VariavelINT
-- O plano de execução anterior será reaproveitado
-- A SP não precisa ser recompilada
SET @VariavelINT = 984538
EXECUTE sp_executesql @sql, @parametro, @idtEstacao= @VariavelINT
Diferenças
entre os dois formatos:
EXECUTE |
sp_executesql |
A capacidade do otimizador do SQL de verificar se novas strings combinam com os planos em cache é comprometida pelas alterações nos parâmetros. Em cada execução uma nova compilação é requerida. |
Como
o texto da query não muda durante as execuções, o otimizador do SQL encontra
sempre o mesmo plano em cache e reutiliza-o, não necessitando de nenhuma
recompilação. |
Toda a string contendo a query
deve ser refeita a cada execução. |
A string contendo a query é criada uma única vez. |
Cada um dos parâmetros deve ser convertido, do formato UNICODE para o seu formato original, a cada execução. |
Os parâmetros serão utilizados em seu formato original sem nenhuma conversão. |
Interpretação
do SQL no uso de parâmetros dentro da query dinâmica:
Cuidados no uso de EXEC e sp_executesql – Embora o uso de querys
dinâmicas traga muitos benefícios (redução de código, otimização de resultados,
etc) elas também podem trazer uma dor de cabeça quando se referem a permissões.
Uma vez que um usuário tem permissão para rodar uma SP, o SQL executa todo o
conteúdo dela sem verificar se existem permissões para a o uso dos objetos
dentro dela, ou seja, se um usuário não tem permissões de inclusão, alteração
ou exclusão em uma tabela T1, mas tiver permissões para executar uma usp_teste
que por sua vez altera ou exclui dados na T1, o processo será realizado sem
problemas. Mas quando usamos EXEC ou sp_executesql, o SQL re-avalia as
permissões de acesso para as tabelas dentro da query dinâmica e mesmo que o
usuário tenha permissões de execução para a SP, também terá que ter permissões
para acesso nas tabelas envolvidas na query dinâmica.
Ex:
Usuário: userTESTE
Permissões:
|
SELECT |
INSERT |
UPDATE |
DELETE |
EXEC |
Tabela “TabelaT1” |
x |
x |
X |
x |
|
Tabela “TabelaT2” |
x |
|
|
|
|
Proc “usp_Teste” |
|
|
|
|
x |
Exemplo1: .
create proc dbo.usp_teste
as
declare @teste int
select
@teste = c1 from dbo.TabelaT1
where c2 = 'teste'
select
@teste = @teste + c1
from dbo.TabelaT2 where c2 =
'teste'
delete
dbo.TabelaT2 where c2 = 'teste'
select
@teste
go
exec
dbo.usp_teste
à Este
exemplo funcionará sem problemas. Mesmo sem acesso a TabelaT2, o usuário tem
permissão para executar a SP.
Exemplo2:
.
create proc dbo.usp_teste
as
declare @teste int
declare @sql nvarchar(500)
select
@teste = c1 from dbo.TabelaT1
where c2 = 'teste'
select
@teste = @teste + c1
from dbo.TabelaT2 where c2 = 'teste'
set
@sql = 'delete from dbo.tabelat2
where c2 = ''teste'''
exec
(@sql)
select
@teste
go
exec
dbo.usp_teste
à Este exemplo não funcionará, gerando um erro de acesso, uma vez que o usuário não tem permissão para excluir dados na TabelaT2.
SP com
várias sub-consultas:
create procedure dbo.usp_TudoEmUM
(
@consulta as char(1),
@idtFrequenciaInicial int = null,
@idtFrequenciaFinal int = null,
@idtEstacaoInicial int =
null,
@idtEstacaoFinal int = null,
@idtAntenaInicial int =
null, @idtAntenaFinal int = null
)
as
if @consulta = 'F'
begin
if @idtFrequenciaInicial is not null and
@idtFrequenciaFinal is not null
begin
select idtFrequencia from dbo.frequencia
where
idtFrequencia between @idtFrequenciaInicial and
@idtFrequenciaFinal
end
end
if @consulta = 'E'
begin
if @idtEstacaoInicial is not null and
@idtEstacaoFinal is not null
begin
select idtEstacao from dbo.estacao
where
idtEstacao between @idtEstacaoInicial and
@idtEstacaoFinal
end
end
if @consulta = 'A'
begin
if @idtAntenaInicial is not null and
@idtAntenaFinal is not null
begin
select idtAntena from dbo.antena
where idtAntena between @idtAntenaInicial and
@idtAntenaFinal
end
end
Podemos
dividir (particionar) esta SP em:
create procedure dbo.usp_TudoEmUM ; 1
(@idtFrequenciaInicial int,
@idtFrequenciaFinal int)
as
select idtFrequencia from
dbo.frequencia
where idtFrequencia between @idtFrequenciaInicial and
@idtFrequenciaFinal
go
create procedure dbo.usp_TudoEmUM ; 2
(@idtEstacaoInicial int,
@idtEstacaoFinal int)
as
select idtEstacao from dbo.estacao
where idtEstacao between
@idtEstacaoInicial and
@idtEstacaoFinal
go
create procedure dbo.usp_TudoEmUM ; 3
(@idtAntenaInicial int,
@idtAntenaFinal int)
as
select idtAntena from
dbo.antena
where idtAntena between
@idtAntenaInicial and
@idtAntenaFinal
go
Exemplo
de chamadas a SPs particionadas:
EXEC dbo.usp_TudoEmUM ;3 @idtAntenaInicial =
988000,
@idtAntenaFinal = 989000
EXEC dbo.usp_TudoEmUM
;1 424000, 425000
EXEC dbo.usp_TudoEmUM ;2 @idtEstacaoInicial = 984000,
@idtEstacaoFinal =
985000
Garantimos desta forma que as SPs serão menores e as recompilações,
quando ocorrerem, serão também em planos de execução menores e mais simples.
Além disso, podemos ter uma SP com o mesmo nome que pode ser manipulada como se
fosse uma só, embora seus resultados sejam independentes, sendo diferenciadas
apenas pelo seu número de grupo. Embora gerem processos independentes, elas não
podem ser excluídas individualmente, tendo que a exclusão ser de todo o grupo
(todas as SPs do grupo são armazenadas juntas pelo SQL, formando um único
objeto). Também as alterações, mesmo que ocorram separadamente, referem-se
sempre a um único objeto, e portanto, geram a compilação de todas as versões
mesmo que somente uma seja alterada.
Embora
não seja possível se evitar completamente recompilações de SPs, podemos
observar o quanto essas recompilações podem ser diminuídas. Obviamente não
podemos levar tudo ao extremo, uma vez que recompilação não é igual a perda de
performance, pois sabemos que um plano de execução desatualizado ou mal formado
pode levar a um gargalo bem pior do que simplesmente recriá-lo. Existem
diversas ferramentas para que se possa analisar o que descrevemos aqui, a mais comum (e faz parte do suite do
MS-SQL Server) é o MS Profiler, que pode nos mostrar os momentos em que as
recompilações ocorrem.
Outra
coisa que podemos observar é que muitas mudanças ou adaptações podem ser feitas
com poucas alterações de código e que
em novas versões, observando alguns pequenos passos, poderemos minimizar em
muito a perda de performance das SPs. Muitas vezes vamos acreditar que as
mudanças em uma SP em especifico podem acrescentar pouco ou nenhum ganho de
performance, mas temos que levar em consideração a concorrência de processos
quando esta SP e outras estiverem em execução. Nem sempre o desempenho
individual representa ganho, alias individualmente podemos, às vezes, ter até
perdas, mas o que realmente importa é o resultado concorrente, onde em um
ambiente de produção que coexistem inúmeros sistemas, utilizando as mesmas
bases e os mesmos recursos, qualquer oscilação pode ser crítica.
O
objetivo principal de reunir este material é de que possamos somar
conhecimentos que nos ajudem a estarmos preparados para acomodar o constante
crescimento dos sistemas atuais e futuros. Nenhum hardware será mais eficiente
do que conhecermos como o sistema funciona e como podemos tirar o melhor
proveito de seus recursos.
·
Microsoft Knowledge Base
article: 243586 – INF: Troubleshooting Stored Procedure Recompilation
·
Microsoft Knowledge Base
article: 294942 – PRB: SET CONCAT_NULL_YIELDS_NULL May Cause Stored Procedures
to Recompile
·
Stored Procedures and Caching
– SQL Server Central by Brian Kelley – 19/11/2004
·
MSDN - Execution Plan Caching
and Reuse
·
MSDN - Query Recompilation in
SQL Server 2000