AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 163189
Accepted
pacreely
pacreely
Asked: 2017-02-04 13:15:18 +0800 CST2017-02-04 13:15:18 +0800 CST 2017-02-04 13:15:18 +0800 CST

A consulta simples foi serial após a otimização, precisa dela paralela

  • 772

Eu herdei um sistema legado que possui um procedimento armazenado que oferece suporte a um aplicativo da Web. Ele é executado milhares de vezes por dia. O aplicativo permite que um usuário insira detalhes parciais do cliente e o procedimento armazenado subjacente executará comandos LIKE para retornar uma lista restrita de possíveis clientes ao aplicativo Web.

Ele ainda permite a entrada parcial de um ID de cliente (valor inteiro), portanto, se o valor '123' for inserido no aplicativo, o procedimento armazenado deverá retornar todos os clientes com um ID contendo '123', ou seja, '612345' ou '222123 '.....sim, eu sei, louco, digitar '1' retorna um conjunto de dados enorme, mas não podemos alterar o App.

A pesquisa do ID do cliente é a parte mais lenta do procedimento armazenado. Eu "otimizei" o procedimento armazenado (em DEV) para que ele use menos E/S e menos CPU....

Veja como recriar o problema.

--CREATE TEST TABLE
CREATE TABLE dbo.Test(ID INT IDENTITY(1,1) PRIMARY KEY
                        , Val Float
                        , CodeVal CHAR(100)
                        )
GO

--GENERATE A FEW MILLION TEST RECORD
INSERT INTO dbo.Test
SELECT
    RAND()
    ,CONVERT(varchar(255), NEWID())
FROM 
    sys.objects --Contains 639 Rows
GO 10000

--CREATE INDEX ON ID FIELD
CREATE UNIQUE NONCLUSTERED INDEX idx ON dbo.Test(ID)

A consulta existente executa um Parallel INDEX SCAN.

DECLARE @ID VARCHAR(20) = '123456'

--DROP TABLE #TMP1
CREATE TABLE #TMP1(ID INT)

INSERT INTO #TMP1
SELECT  ID 
FROM    dbo.Test
WHERE   CONVERT(VARCHAR(20),ID) LIKE '%'+@ID+'%'

Então percebi que, se um usuário inserir uma ID de cliente parcial de '123456', o conjunto de resultados nunca poderá conter um valor menor que '123456'. Então adicionei uma linha extra ao Código (veja abaixo), agora tenho um INDEX SEEK com IO reduzido e tempo de CPU reduzido. Mas, para meu horror, acabou Serial, então agora leva uma eternidade. Se isso for ativado, o tempo de espera do usuário saltará de 4 segundos para 20 segundos.

INSERT INTO #TMP1
SELECT  ID 
FROM    dbo.Test
WHERE   CONVERT(VARCHAR(20),ID) LIKE '%'+@ID+'%'
AND     ID >= @ID   --New line of code

Agora cheguei ao ponto em que estou codificando cegamente na esperança de que seja paralelo.

Alguém pode explicar por que isso está acontecendo (além da resposta de estoque de "o otimizador decidiu que era melhor")?

E alguém pode descobrir como fazer a consulta ficar paralela?

O sinalizador de rastreamento 8649 não é uma opção para mim.

E eu já li este artigo muito útil de Paul White .

ATUALIZAR:

Parece que o exemplo fornecido produz resultados variados dependendo de uma combinação de System Spec e Row count na tabela de teste.

Desculpe se você não pode recriar o problema. Mas isso é parte da resposta ao meu problema.

ATUALIZAÇÃO: (Plano de Execução)

Aaron: Desculpe por, por exemplo, ser frustrante se não funcionar no seu sistema. (Não posso compartilhar o plano de execução real da minha empresa por motivos de InfoSec)

Eu recriei o problema no meu sistema doméstico.

Aqui está o Plano de Execução, (vou fazer o upload completo para Post-the-Plan) insira a descrição da imagem aqui

minha contagem de linhas atual de dbo.Test é 2124160, o índice não agrupado tem 2627 páginas e 0,04% de fragmentação, abaixo estão minhas informações de estatísticas.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 1 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 1 ms.
Table 'Test'. Scan count 3, logical reads 2652, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 688 ms,  elapsed time = 368 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 2 ms.
Table 'Test'. Scan count 1, logical reads 1106, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 281 ms,  elapsed time = 302 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

O procedimento armazenado é usado cerca de 5.000 vezes por dia pelo pessoal do Call-center que lida com os clientes. Uma espera maior de 16 segundos custará pouco mais de 22 horas por dia de funcionários esperando por um resultado. Em um turno de 8 horas, isso equivale a 3 funcionários em tempo integral, custando aproximadamente £ 36k anualmente. Lá se vai o dinheiro para atualizar para as licenças do SQL Sentry (grande fã), atualmente temos o Spotlight (ainda um bom produto) mas quero algo com maior profundidade.

ATUALIZAÇÃO: (Número Mágico)

Com 12.000 linhas, ambas as consultas eram seriais.

Com 2.124.160 linhas, uma consulta fica paralela.

Com 66.000.000 de linhas, ambas ficam paralelas.

Definitivamente, há uma regra/métrica que o otimizador usa, e é diferente se você tiver um Index Scan ou Index Seek. Isso significa que a conversão de uma consulta de Scan para Seek pode ter um efeito adverso em tabelas/índices de um determinado tamanho?

ATUALIZAÇÃO: (Solução)

Obrigado Joe, você acertou em cheio com o Grau Máximo de Paralelismo.

Eu usei:

OPTION (OPTIMIZE FOR (@ID  = 1))

UNKNOWN não fez o truque para o meu ambiente.

Para quem estiver interessado, aqui está um artigo útil de Kendra Little .

sql-server t-sql
  • 1 1 respostas
  • 1550 Views

1 respostas

  • Voted
  1. Best Answer
    Joe Obbish
    2017-02-04T15:19:20+08:002017-02-04T15:19:20+08:00

    Suspeito que você esteja tendo problemas com o limite de custo para o parâmetro de paralelismo.

    Use a opção de limite de custo para paralelismo para especificar o limite no qual o Microsoft SQL Server cria e executa planos paralelos para consultas. O SQL Server cria e executa um plano paralelo para uma consulta somente quando o custo estimado para executar um plano serial para a mesma consulta for maior que o valor definido no limite de custo para paralelismo.

    Consultas mais longas geralmente se beneficiam de planos paralelos; a vantagem de desempenho anula o tempo adicional necessário para inicializar, sincronizar e encerrar planos paralelos. A opção de limite de custo para paralelismo é usada ativamente quando uma combinação de consultas curtas e longas é executada. As consultas curtas executam planos seriais, enquanto as consultas mais longas usam planos paralelos. O valor do limite de custo para paralelismo determina quais consultas são consideradas curtas e, portanto, devem ser executadas usando planos seriais.

    Na minha máquina de teste cost threshold for parallelismtem o valor padrão de 5. Abaixo segue uma tabela de MAXDOP 1custos para a antiga e a nova consulta:

    ╔═════════╦═══════════════════╦═══════════════════╗
    ║  Rows   ║ Cost of Old Query ║ Cost of New Query ║
    ╠═════════╬═══════════════════╬═══════════════════╣
    ║   12000 ║ 0.037717          ║ 0.0199238         ║
    ║ 1610200 ║ 6.28954           ║ 1.86659           ║
    ║ 4610200 ║ 17.9959           ║ 6.46108           ║
    ╚═════════╩═══════════════════╩═══════════════════╝
    

    Como esperado, com 1610200 linhas, a consulta antiga fica paralela, mas a nova consulta é serial. Isso ocorre porque o custo DOP 1 era superior a 5, portanto, qualificava-se para um plano paralelo. O SQL Server estimou um custo menor para o plano paralelo, então ele foi paralelo. Com 4610200 linhas, ambas as consultas são paralelas. Você pode ver resultados ligeiramente diferentes em sua máquina.

    Há duas coisas trabalhando contra você obter um plano paralelo aqui. A primeira é que o SQL Server não tem como saber que essa consulta é essencial para o seu negócio e que você deseja lançar o máximo de recursos possível. Ele não sabe sobre suas estimativas de que sua organização pode perder dezenas de milhares de dólares. Ele apenas a vê como outra consulta barata/trivial. A segunda é que o otimizador de consulta sempre atribuirá à consulta o mesmo custo estimado, independentemente do valor de @ID. Ele usa estimativas padrão porque você está trabalhando com uma variável. Você pode contornar isso com uma RECOMPILEdica, mas se isso for executado milhares de vezes por dia, pode não ser a melhor ideia.

    Minha recomendação é usar a OPTIMIZE FORdica de consulta para aumentar o número estimado de linhas da busca de índice clusterizado. Isso aumentará o custo do plano e deve aumentar a probabilidade de usar um plano paralelo. De BOL :

    OPTIMIZE FOR ( @variable_name { UNKNOWN | = literal_constant } [ , ...n ] )

    Instrui o otimizador de consulta a usar um valor específico para uma variável local quando a consulta é compilada e otimizada. O valor é usado apenas durante a otimização da consulta e não durante a execução da consulta.

    @nome variável

    É o nome de uma variável local usada em uma consulta, à qual um valor pode ser atribuído para uso com a dica de consulta OPTIMIZE FOR.

    literal_constante

    É um valor constante literal a ser atribuído a @variable_name para uso com a dica de consulta OPTIMIZE FOR. literal_constant é usado apenas durante a otimização da consulta e não como o valor de @variable_name durante a execução da consulta. literal_constant pode ser de qualquer tipo de dados do sistema SQL Server que possa ser expresso como uma constante literal. O tipo de dados de literal_constant deve ser implicitamente conversível para o tipo de dados que @variable_name referencia na consulta.

    Para sua consulta, você pode otimizar para um valor de '1'. Isso deve aumentar bastante o custo do plano. Na verdade, nos meus testes isso fez com que a consulta tivesse o mesmo custo estimado da antiga sem o filtro. Isso faz sentido porque o SQL Server não poderá filtrar nenhuma linha com um predicado de ID > '1'. Se você usar essa dica, deverá experimentar o mesmo comportamento paralelo/serial de antes da alteração do código.

    DECLARE @ID VARCHAR(20) = '123456';
    
    CREATE TABLE #TMP1 (ID INT);
    
    INSERT INTO #TMP1 -- cost is 96.8547
    SELECT  ID 
    FROM    dbo.Test
    WHERE   CONVERT(VARCHAR(20),ID) LIKE '%'+@ID+'%'
    AND     ID >= @ID  
    OPTION (OPTIMIZE FOR (@ID = '1'));
    

    planos de consulta

    Observe que essa dica pode ter efeitos negativos se a consulta se tornar mais complexa. Inflar artificialmente a contagem de linhas estimada pode causar uma concessão de memória de consulta maior do que o necessário, por exemplo. Se seus dados de produção forem grandes o suficiente para que a dica não seja necessária, recomendo não usá-la. Se sua consulta em produção for diferente da listada na pergunta teste cuidadosamente antes de usar.

    • 1

relate perguntas

  • SQL Server - Como as páginas de dados são armazenadas ao usar um índice clusterizado

  • Preciso de índices separados para cada tipo de consulta ou um índice de várias colunas funcionará?

  • Quando devo usar uma restrição exclusiva em vez de um índice exclusivo?

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Como determinar se um Índice é necessário ou necessário

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve