SQL Server consumindo CPU demais?

    Guia completo de diagnóstico e solução — as 6 causas mais comuns, as queries via DMVs e como corrigir cada uma.

    CPU em 100% no SQL Server raramente tem uma causa única — e "reiniciar o serviço" nunca é a solução, só um adiamento do próximo incidente. Este guia cobre as causas mais comuns, as queries de diagnóstico que um DBA sênior roda primeiro, e como corrigir cada uma.

    FALE COM UM EXPERT SQL SERVER

    CPU sustentada em nível crítico?

    A HTI faz diagnóstico completo de SQL Server — DMVs, plan cache, waits, MAXDOP e tempdb — com plano de correção priorizado por impacto.

    Por onde começar: isolar a causa antes de agir

    O erro mais comum sob pressão é começar a mexer em configuração antes de saber o que está de fato consumindo a CPU. As três perguntas que precisam de resposta antes de qualquer ajuste:

    1. É uma ou poucas queries específicas consumindo CPU, ou é consumo distribuído entre muitas sessões?
    2. É um pico pontual (job de manutenção, ETL, backup) ou um padrão sustentado ao longo do dia?
    3. A CPU alta é do processo sqlservr.exe ou de outro processo no mesmo servidor (antivírus, backup de terceiros, outro serviço)?

    A query abaixo já responde à primeira pergunta — identifica as queries que mais consumiram CPU desde o último restart do serviço (ou desde a última limpeza do plan cache):

    sql
    SELECT TOP 20
        qs.total_worker_time / 1000 AS total_cpu_ms,
        qs.execution_count,
        (qs.total_worker_time / qs.execution_count) / 1000 AS avg_cpu_ms,
        qs.total_elapsed_time / 1000 AS total_duration_ms,
        SUBSTRING(qt.text, (qs.statement_start_offset/2) + 1,
            ((CASE qs.statement_end_offset
                WHEN -1 THEN DATALENGTH(qt.text)
                ELSE qs.statement_end_offset END
                - qs.statement_start_offset)/2) + 1) AS query_text,
        DB_NAME(qt.dbid) AS database_name
    FROM sys.dm_exec_query_stats qs
    CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt
    ORDER BY qs.total_worker_time DESC;

    total_worker_time é a coluna que mede tempo de CPU consumido, não total_elapsed_time (que mede tempo total, incluindo espera de I/O e bloqueio) — confundir as duas é um erro comum de diagnóstico.

    As 6 causas mais comuns

    1. Estatísticas desatualizadas gerando planos de execução ruins

    O otimizador de consultas do SQL Server decide o plano de execução com base em estimativas de cardinalidade — que vêm das estatísticas da tabela. Estatísticas desatualizadas (comum após grandes cargas de dados, DELETEs em massa ou AUTO_UPDATE_STATISTICS desabilitado) levam o otimizador a escolher planos ineficientes, frequentemente trocando um seek por um scan completo.

    sql
    -- Verificar quando as estatísticas foram atualizadas pela última vez
    SELECT
        OBJECT_NAME(s.object_id) AS table_name,
        s.name AS stats_name,
        STATS_DATE(s.object_id, s.stats_id) AS last_updated,
        sp.rows,
        sp.modification_counter
    FROM sys.stats s
    CROSS APPLY sys.dm_db_stats_properties(s.object_id, s.stats_id) sp
    WHERE OBJECT_NAME(s.object_id) NOT LIKE 'sys%'
    ORDER BY sp.modification_counter DESC;

    2. Parameter sniffing — o plano certo para o parâmetro errado

    O SQL Server compila e armazena em cache o plano de execução da primeira chamada de uma stored procedure com parâmetros. Se essa primeira chamada usou um valor atípico (ex: um cliente com volume de pedidos muito acima da média), o plano fica otimizado para aquele caso — e degradado para todos os outros. É a causa clássica de "a procedure roda rápido às vezes e lenta em outras, com os mesmos dados".

    sql
    -- Forçar recompilação a cada execução (uso pontual para diagnóstico)
    EXEC dbo.MinhaProcedure @Parametro = 123 WITH RECOMPILE;
    
    -- Ou, na definição da procedure, para casos recorrentes:
    -- OPTION (OPTIMIZE FOR (@Parametro UNKNOWN))

    3. MAXDOP e Cost Threshold for Parallelism fora do padrão recomendado

    Configuração default de MAXDOP = 0 permite que o SQL Server use todos os cores disponíveis para uma única query — o que pode gerar contenção severa em servidores multi-core sob alta concorrência, visível como waits do tipo CXPACKET/CXCONSUMER. A Microsoft recomenda valores específicos de MAXDOP conforme o número de cores e nós NUMA do servidor, quase nunca o default.

    sql
    -- Verificar configuração atual
    EXEC sp_configure 'max degree of parallelism';
    EXEC sp_configure 'cost threshold for parallelism';
    
    -- Waits mais comuns associados a paralelismo mal ajustado
    SELECT wait_type, wait_time_ms, waiting_tasks_count
    FROM sys.dm_os_wait_stats
    WHERE wait_type IN ('CXPACKET', 'CXCONSUMER')
    ORDER BY wait_time_ms DESC;

    4. Contenção de tempdb consumindo CPU e gerando espera

    Ambientes com um único arquivo de dados no tempdb sofrem contenção de página em estruturas internas (PFS, GAM, SGAM) sob alta concorrência — cada sessão disputando a mesma página gera overhead de CPU adicional, além da espera. A recomendação padrão da Microsoft é múltiplos arquivos de dados de tamanho igual, tipicamente um por core lógico até o limite de 8.

    sql
    -- Verificar número de arquivos de dados do tempdb hoje
    SELECT name, physical_name, size/128 AS size_mb
    FROM tempdb.sys.database_files
    WHERE type = 0;

    5. Queries ad hoc não parametrizadas inflando o plan cache

    Aplicações que geram SQL dinâmico sem parametrização (concatenando valores diretamente na query) forçam o SQL Server a compilar um plano novo para cada variação — consumindo CPU em compilação que deveria ser gasta em execução. Forced Parameterization no nível do banco pode mitigar isso sem mudança de código, mas o ideal é corrigir na aplicação.

    sql
    -- Taxa de compilação e recompilação (perfmon counters via DMV)
    SELECT cntr_value AS batch_requests_sec
    FROM sys.dm_os_performance_counters
    WHERE counter_name = 'Batch Requests/sec';
    
    SELECT cntr_value AS sql_compilations_sec
    FROM sys.dm_os_performance_counters
    WHERE counter_name = 'SQL Compilations/sec';

    6. Índices ausentes ou fragmentados forçando scans completos

    Um índice ausente força o otimizador a escanear a tabela inteira para encontrar as linhas necessárias — trabalho de CPU proporcional ao tamanho da tabela, não ao tamanho do resultado. Índices existentes mas altamente fragmentados têm efeito parecido, em menor escala.

    sql
    -- Sugestões de índice ausente identificadas pelo próprio otimizador
    SELECT
        migs.avg_total_user_cost * migs.avg_user_impact * (migs.user_seeks + migs.user_scans) AS improvement_measure,
        mid.statement AS table_name,
        mid.equality_columns,
        mid.inequality_columns,
        mid.included_columns
    FROM sys.dm_db_missing_index_group_stats migs
    JOIN sys.dm_db_missing_index_groups mig ON migs.group_handle = mig.index_group_handle
    JOIN sys.dm_db_missing_index_details mid ON mig.index_handle = mid.index_handle
    ORDER BY improvement_measure DESC;

    Antes de sair mudando configuração

    • Nunca aplique um WITH RECOMPILE permanente em produção como solução — é uma ferramenta de diagnóstico, não uma correção. Recompilar toda execução tem custo de CPU próprio.
    • Não mude MAXDOP no servidor inteiro sem medir o impacto — o valor certo depende do padrão de carga (OLTP vs. Data Warehouse) tanto quanto do hardware.
    • Confirme que a CPU alta é do sqlservr.exe antes de qualquer ajuste — já vimos casos em que o "SQL Server consumindo CPU" era, na verdade, um job de antivírus escaneando os arquivos .mdf/.ldf em tempo real.

    Precisa de ajuda para diagnosticar seu ambiente agora?

    Se o servidor está com CPU sustentada em nível crítico, não espere o Health Check completo — fale com o time de atendimento emergencial.

    FALE COM UM EXPERT SQL SERVER

    CPU sustentada em nível crítico?

    A HTI faz diagnóstico completo de SQL Server — DMVs, plan cache, waits, MAXDOP e tempdb — com plano de correção priorizado por impacto.

    Artigo do cluster técnico Consultoria SQL Server da HTI Tecnologia — 35+ anos sustentando bancos de dados críticos no Brasil.