Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

SQL Server Internals:Planejar Cache Pt. I – Planos de Reutilização


O SQL Server existe há mais de 30 anos e trabalho com o SQL Server há quase tanto tempo. Eu vi muitas mudanças ao longo dos anos (e décadas!) e versões deste produto incrível. Nestas postagens, compartilharei com você como vejo alguns dos recursos ou aspectos do SQL Server, às vezes com um pouco de perspectiva histórica.

Confira os blogs recentes de Kalen sobre Operadores problemáticos aqui.

A criação de planos para diagnóstico do servidor SQL pode ser cara, pois o otimizador de consulta precisa encontrar um bom plano para qualquer consulta legal enviada. O otimizador avalia várias ordens de junção, vários índices. e vários tipos de algoritmos de junção e agrupamento, dependendo da sua consulta e das tabelas envolvidas. Se a mesma consulta for executada novamente, o SQL Server poderá economizar muitos recursos reutilizando um plano existente. Mas nem sempre é possível reutilizar um plano existente e nem sempre é bom fazê-lo. Nos próximos dois artigos, veremos quando um plano é reutilizado e quando é recompilado.

Primeiro, veremos os diferentes tipos de planos e a visualização de metadados que uso com mais frequência para ver o que está no cache do meu plano. Escrevi uma visão própria que fornece as informações que considero mais úteis com mais frequência. O SQL Server armazena em cache seis tipos diferentes de planos de consulta, mas apenas dois são normalmente usados ​​para o ajuste do cache do plano. Estes são COMPILED PLAN e COMPILED PLAN STUB. Minha visão filtra todos, exceto esses dois tipos de objetos de cache. PLANOS COMPILADOS vêm em três variedades:AD HOC, PREPARADO e PROC. Vou comentar sobre os três tipos.

Mesmo olhando apenas para PLANOS COMPILED, ainda existem muitos planos em cache que geralmente precisam ser ignorados, pois são gerados pelo próprio SQL Server. Isso inclui planos que procuram por fluxo de arquivos ou índices de pesquisa de texto completo ou consultas internas que trabalham com OLTP na memória. Portanto, minha visualização adiciona filtros para tentar eliminar a maioria dos planos nos quais não estou interessado. Você pode baixar um script para criar essa visualização, chamado sp_cacheobjects , aqui.

Mesmo com todos os filtros que minha view usa, ainda existem algumas consultas internas do próprio SQL Server em cache; Eu costumo limpar o cache do plano com frequência ao fazer testes nesta área. A maneira mais simples de limpar TODOS os planos do cache é com o comando:DBCC FREEPROCCACHE.

Planos Compilados Adhoc


O tipo mais simples de plano é Adhoc. Isso é usado para consultas básicas que não se encaixam em outra categoria. Se você baixou meu script e criou minha exibição sp_cacheobjects, pode executar o seguinte. Qualquer versão do banco de dados AdventureWorks deve funcionar. Esse script faz uma cópia de uma tabela e cria alguns índices exclusivos nela. Ele também massageia o valor do Subtotal para remover quaisquer dígitos decimais.

USE AdventureWorks2016;
GO
DROP TABLE IF EXISTS newsales;
GO
-- Make a copy of the Sales.SalesOrderHeader table
SELECT * INTO dbo.newsales
FROM Sales.SalesOrderHeader;
GO
UPDATE dbo.newsales
SET SubTotal = cast(cast(SubTotal as int) as money);
GO
CREATE UNIQUE index newsales_ident
    ON newsales(SalesOrderID);
GO
CREATE INDEX IX_Sales_SubTotal ON newsales(SubTotal);
GO
-- Adhoc query plan reuse
DBCC FREEPROCCACHE;
GO
-- adhoc query
SELECT * FROM dbo.newsales
WHERE SubTotal = 4;
GO
SELECT * FROM sp_cacheobjects;
GO




Na minha saída, você vê dois planos Adhoc. Um é para a instrução SELECT do newsales table, e o outro é para o SELECT do meu sp_cacheobjects visualizar. Como o plano está armazenado em cache, se a mesma consulta EXATA for executada novamente, o mesmo plano poderá ser reutilizado e você verá as usecounts aumento de valor. No entanto, há um porém. Para que um plano Adhoc seja reutilizado, a string SQL deve ser absolutamente a mesma. Se você alterar algum caractere no SQL, a consulta não será reconhecida como a mesma consulta e um novo plano será gerado. Se eu adicionar um espaço, incluir o comentário ou uma nova quebra de linha, não será a mesma string. Se eu mudar o caso, isso significa que existem valores de código ASCII diferentes, portanto, não a mesma string.

Você pode tentar isso executando diferentes variações da minha primeira instrução SELECT do newsales tabela. Você verá uma linha diferente no cache para cada um. Depois de executar várias variações – alterando o número que estava procurando, alterando o caso, adicionando o comentário e uma nova linha, vejo o seguinte no cache. O SELECT da minha visão está sendo reutilizado, mas todo o resto tem um usecounts valor de 1.

Um requisito adicional para reutilização do plano de consulta Adhoc é que a sessão que executa a consulta deve ter as mesmas opções SET em vigor . Há outra coluna na saída que você pode ver à direita do texto da consulta, chamada SETOPTS. Esta é uma cadeia de bits com um bit para cada opção SET relevante. Se você alterar uma das opções, por exemplo, SET ANSI_NULLS OFF, a sequência de bits será alterada e o mesmo plano com a sequência de bits original não poderá ser reutilizado.

Planos compilados preparados


O segundo tipo de plano compilado em cache é um plano PREPARADO. Se sua consulta atende a um determinado conjunto de requisitos. Na verdade, ele pode ser parametrizado automaticamente. Ele aparece nos metadados como PREPARED e a string SQL mostra um marcador de parâmetro. Aqui está um exemplo:


O plano PREPARADO mostra o marcador de parâmetro como @1 e não inclui o valor real. Observe que há uma linha para uma consulta ADHOC com um valor real de 5555, mas na verdade é apenas um 'shell' da consulta real. Ele não armazena em cache todo o plano, mas apenas a consulta e alguns detalhes de identificação, para ajudar o processador de consultas a encontrar o plano parametrizado no cache. Observe o tamanho (páginausada ) é muito menor que o plano PREPARADO.

O modo de parametrização padrão, chamado de parametrização SIMPLE, é extremamente rigoroso sobre quais planos podem ser parametrizados. Na verdade, apenas as consultas mais simples são parametrizáveis ​​por padrão. As consultas que contêm JOIN, GROUP BY, OR e muitas outras construções de consulta relativamente comuns impedem que uma consulta seja parametrizada. Além de não ter nenhuma dessas construções, o mais importante para a parametrização SIMPLE é que a consulta seja SAFE. Isso significa que há apenas um plano possível, não importa quais valores sejam passados ​​para quaisquer parâmetros. (É claro que uma consulta sem parâmetros também pode ser SAFE.) Minha consulta está procurando uma correspondência exata na coluna SalesOrderID , que possui um índice exclusivo. Portanto, o índice não clusterizado existente pode ser usado para localizar qualquer linha correspondente. Não importa o valor que eu use, 55555 ou qualquer outra coisa, nunca haverá mais de uma linha, o que significa que o plano ainda será bom.

No meu exemplo de plano de consulta Adhoc, eu estava procurando valores correspondentes para SubTotal . Alguns Subtotais os valores ocorrem algumas vezes ou não ocorrem, portanto, um índice não clusterizado seria bom. Mas outros valores podem ocorrer muitas vezes, então o índice NÃO seria útil. Assim, o plano de consulta não é SEGURO e a consulta não pode ser parametrizada. É por isso que vimos um plano Adhoc para o meu primeiro exemplo.

SE você tiver consultas com JOIN ou outras construções não permitidas, você pode dizer ao SQL Server para ser mais agressivo na parametrização alterando uma opção de banco de dados:

ALTER DATABASE AdventureWorks2016 SET parameterization FORCED;
GO


Definir seu banco de dados para parametrização FORCED significa que o SQL Server parametrizará muito mais consultas, incluindo aquelas com JOIN, GROUP BY, OR, etc. Mas também significa que o SQL Server pode parametrizar uma consulta que não é SAFE. Ele pode apresentar um plano que seja bom quando apenas algumas linhas forem retornadas e, em seguida, reutilizar o plano quando muitas linhas forem retornadas. Isso pode acabar com um desempenho muito abaixo do ideal.

Uma opção final para um plano preparado é quando você prepara explicitamente um plano. Esse comportamento geralmente é invocado por meio de um aplicativo com SQLPrepare e SQLExecute APIs. Você especifica qual é a consulta com marcações de parâmetro, especifica os tipos de dados e especifica os valores específicos a serem usados. A mesma consulta pode ser executada novamente com diferentes valores específicos e o plano existente será usado. Embora o uso de planos explicitamente preparados possa ser possível para os casos em que o SQL Server não está parametrizando e você deseja, isso não impede o SQL Server de usar um plano que NÃO é bom para parâmetros subsequentes. Você precisa testar suas consultas com muitos valores de entrada diferentes e certificar-se de obter o desempenho esperado se e quando um plano for reutilizado.

Os metadados (por exemplo, meus sp_cacheobjects view) apenas mostra PREPARADO para todos os três tipos de planos:autoparametrização FORÇADA e SIMPLES e parametrização EXPLÍCITA.

Planos compilados de proc


O objtype final valor para Planos Compilados é para um procedimento armazenado, que é mostrado como Proc. Quando possível, os procedimentos armazenados são a melhor escolha para código reutilizável, devido à sua facilidade de gerenciamento a partir do próprio servidor, mas isso não significa que eles garantem sempre o melhor desempenho. Assim como usar a opção de parametrização FORCED (e também a parametrização explícita), os procedimentos armazenados usam ‘sniffing de parâmetros’. Isso significa que o primeiro valor de parâmetro passado determina o plano. Se as execuções subsequentes funcionarem bem com o mesmo plano, a detecção de parâmetros não será um problema e poderá ser realmente benéfica, pois economizará o custo de recompilação e reotimização. No entanto, se execuções subsequentes com valores diferentes não usarem o plano original, teremos problemas. Mostrarei um exemplo de detecção de parâmetros causando um problema

Vou criar um procedimento armazenado com base nas newsales tabela que usamos anteriormente. O procedimento terá uma única consulta, que filtra com base no SalesOrderID coluna, na qual construímos um índice não clusterizado. A consulta será baseada em uma desigualdade, portanto, para alguns valores, a consulta pode retornar apenas algumas linhas e usar o índice e, para outros valores, a consulta pode retornar muitas linhas. Em outras palavras, a consulta não é SEGURO.

USE AdventureWorks2016;
GO
DROP PROC IF EXISTS get_sales_range;
GO
CREATE PROC get_sales_range
   @num int
AS
    SELECT * FROM dbo.newsales
    WHERE SalesOrderID < @num;
GO


Vou usar a opção SET STATISTICS IO ON para ver quanto trabalho está sendo feito quando o procedimento é executado. Primeiro, vou executá-lo com um parâmetro que retorna apenas algumas linhas:

SET STATISTICS IO ON
GO
EXEC get_sales_range 43700;
GO


O valor STATISTICS IO informa que foram necessárias 43 leituras lógicas para retornar 41 linhas. Isso é normal para um índice não clusterizado. Agora executamos o procedimento novamente com um valor muito maior.

EXEC get_sales_range 66666;
GO
SELECT * FROM sp_cacheobjects;
GO
This time, we see that SQL Server used a whole lot more reads:


Na verdade, uma verificação de tabela nas newsales table leva apenas 843 leituras, então este é um desempenho muito pior do que uma varredura de tabela. Os sp_cacheobjects view nos mostra que o plano PROC foi reutilizado para esta segunda execução. Este é um exemplo de quando o sniffing de parâmetros NÃO é uma coisa boa.

Então, o que podemos fazer quando a detecção de parâmetros é um problema? No próximo post, direi quando o SQL Server apresentar um novo plano e não reutilizar os antigos. Veremos como você pode forçar (ou incentivar) a recompilação e também veremos quando o SQL Server recompilará automaticamente suas consultas.

O Spotlight Cloud pode revolucionar o monitoramento de desempenho e o diagnóstico do servidor SQL. Comece com sua avaliação gratuita usando o link abaixo: