Quais fatores de custo entram no otimizador escolhendo diferentes tipos de carretéis?

15

Spoolum

No SQL Server, existem vários tipos de spools. Os dois que me interessam são os spools de tabela e de índice , fora das consultas de modificação .

As consultas somente leitura, principalmente no lado interno de uma associação de Nested Loops, podem usar um spool de Tabela ou Índice para reduzir potencialmente a E / S e melhorar o desempenho da consulta. Esses carretéis podem ser ansiosos ou preguiçosos . Assim como você e eu.

Minhas perguntas são:

  • Quais fatores entram na escolha de Spool de Tabela vs. Spool de Índice
  • Quais fatores entram na escolha entre Eager e Lazy Spools
Erik Darling
fonte

Respostas:

12

Isso é um pouco amplo, mas acho que entendo a verdadeira pergunta e responderei de acordo. Só vou falar sobre tabela vs spool de índice. Eu não acho muito correto ver lá como sendo uma escolha entre spools de tabela e índice. Como você sabe, é possível em uma única subárvore obter um spool de índice, um spool de tabela ou um spool de índice e um spool de tabela. Eu acredito que geralmente é correto dizer que você recebe um spool de índice nas seguintes condições:

  1. O otimizador de consulta tem um motivo para transformar uma junção em uma aplicação
  2. O otimizador de consulta realmente executa a transformação na aplicação
  3. O otimizador de consulta usa a regra para adicionar um spool de índice (no mínimo, deve ser seguro usá-lo)
  4. O plano com o spool de índice é selecionado

Você pode ver a maioria deles com demonstrações simples. Comece criando um par de pilhas:

DROP TABLE IF EXISTS dbo.X_10000_VARCHAR_901;
CREATE TABLE dbo.X_10000_VARCHAR_901 (ID VARCHAR(901) NOT NULL);

INSERT INTO dbo.X_10000_VARCHAR_901 WITH (TABLOCK)
SELECT TOP (10000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;


DROP TABLE IF EXISTS dbo.X_10000_VARCHAR_800;
CREATE TABLE dbo.X_10000_VARCHAR_800 (ID VARCHAR(800) NOT NULL);

INSERT INTO dbo.X_10000_VARCHAR_800 WITH (TABLOCK)
SELECT TOP (10000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

Para a primeira consulta, não há nada a procurar:

SELECT *
FROM dbo.X_10000_VARCHAR_901 a
CROSS JOIN dbo.X_10000_VARCHAR_901 b
OPTION (MAXDOP 1);

Portanto, não há motivo para o otimizador transformar a junção em uma aplicação. Você acaba com um carretel de mesa devido a razões de custo. Portanto, esta consulta falha no primeiro teste.

insira a descrição da imagem aqui

Para a próxima consulta, é justo esperar que o otimizador tenha um motivo para considerar uma aplicação:

SELECT *
FROM dbo.X_10000_VARCHAR_901 a
INNER JOIN dbo.X_10000_VARCHAR_901 b ON a.ID = b.ID 
OPTION (LOOP JOIN, MAXDOP 1);

Mas não é para ser:

insira a descrição da imagem aqui

Esta consulta falha no segundo teste. Uma explicação completa está aqui . Citando a parte mais relevante:

O otimizador não considera criar um índice rapidamente para ativar uma aplicação; em vez disso, a sequência de eventos geralmente é o inverso: transformar para aplicar porque existe um bom índice.

Posso reescrever a consulta para incentivar o otimizador a considerar uma aplicação:

SELECT *
FROM dbo.X_10000_VARCHAR_901 a
INNER JOIN dbo.X_10000_VARCHAR_901 b ON a.ID >= b.ID AND a.ID <= b.ID
OPTION (MAXDOP 1);

Mas ainda não há nenhum spool de índice:

insira a descrição da imagem aqui

Esta consulta falha no terceiro teste. No SQL Server 2014, havia um limite de comprimento de chave de índice de 900 bytes. Isso foi estendido no SQL Server 2016, mas apenas para índices não clusterizados. O índice para um spool é um índice em cluster, portanto, o limite permanece em 900 bytes . De qualquer forma, a regra do spool de índice não pode ser aplicada porque pode levar a um erro durante a execução da consulta.

Reduzir o tamanho do tipo de dados para 800 finalmente fornece um plano com um spool de índice:

insira a descrição da imagem aqui

O plano de spool de índice, sem surpresa, é custado significativamente mais barato que um plano sem spool: 89.7603 unidades versus 598.832 unidades. Você pode ver a diferença com a QUERYRULEOFF BuildSpooldica de consulta não documentada :

insira a descrição da imagem aqui

Esta não é uma resposta completa, mas espero que seja parte do que você estava procurando.

Joe Obbish
fonte