Foto de Roberto Sobrinho
Roberto Sobrinho

17/06/2026

CPU do momento no Oracle: medindo consumo por sessão com delta de DB CPU

Quando o banco satura de CPU, a pergunta é só uma: quem está queimando processador agora? As views de sessão respondem com tempo acumulado desde o login, e isso quase não ajuda no calor do incidente. Aqui eu mostro como medir o CPU do momento, por sessão e por SQL, tirando duas fotos de DB CPU e olhando só a diferença. É o que faz o s_cpu.sql, da família g_gold.

O cenário: achar a CPU do momento

Standalone ou RAC, tanto faz. CPU do servidor perto de 100%, aplicação reclamando de lentidão, e você precisa achar a sessão culpada em segundos. Sem montar baseline de AWR, sem esperar o próximo snapshot, sem abrir o Enterprise Manager.

O incômodo de olhar CPU por sessão é que a estatística mostra tempo acumulado. Uma sessão logada há seis horas aparece com um número gigante mesmo parada agora. Outra que entrou faz dez segundos e está comendo um core inteiro aparece com um número pequeno. O acumulado mente sobre o presente.

A ideia: medir o delta, não o acumulado

A estatística DB CPU em V$SESS_TIME_MODEL guarda o tempo de CPU da sessão em microsegundos, desde que ela existe. Sozinho, esse valor é o acumulado da vida inteira da sessão. Mas se você lê o número, espera N segundos e lê de novo, a diferença entre as duas leituras é exatamente o CPU consumido naquele intervalo.

O s_cpu.sql faz isso dentro de um bloco PL/SQL. Tira o snapshot 1 do DB CPU por sessão, dorme N segundos, tira o snapshot 2 junto com os dados da sessão, e calcula a diferença. O resultado é a CPU do momento de cada sessão naquela janela, mais o percentual de quanto de um core ela ocupou.

As views por trás

Duas views sustentam a medição da CPU do momento.

GV$SESS_TIME_MODEL é a versão global da V$SESS_TIME_MODEL. No RAC ela traz a coluna INST_ID junto de SID, STAT_NAME e VALUE, e devolve as linhas de todas as instâncias ativas. O que importa é a linha onde STAT_NAME = 'DB CPU', com VALUE em microsegundos. Divide por 1.000.000 e vira segundos.

GV$SESSION dá o resto: usuário, SQL_ID, SQL_CHILD_NUMBER, STATE, EVENT, MACHINE e LAST_CALL_ET. Quando a sessão está ACTIVE, o LAST_CALL_ET é o tempo em segundos desde que ela entrou na chamada atual. Serve para saber se a sessão acabou de começar ou se está num call longo.

O join é por INST_ID e SID, filtrando só sessões ativas, com usuário preenchido e fora de espera ociosa (wait_class diferente de Idle).

O script

O s_cpu.sql pergunta o intervalo (de 1 a 60 segundos, Enter usa 10), tira os dois snapshots e imprime o resultado já ordenado por CPU. O coração dele é o bloco PL/SQL com os dois loops de coleta.

s_cpu.sql — carregado online do GitHub dbasobrinho ver no GitHub ↗
Carregando o script direto do GitHub…

Observação: o bloco acima é carregado ao online a partir do repositório no GitHub dbasobrinho, então reflete sempre a última versão publicada. Para baixar o script atualizado, acesse github.com/dbasobrinho/g_gold/blob/master/s_cpu.sql.

Quando você roda o script, a primeira coisa que ele faz é pedir o intervalo de medição em segundos. Esse intervalo é a janela entre as duas fotos do DB CPU: o script lê o valor, espera esse tempo e lê de novo. Você digita um número de 1 a 60, ou só tecla Enter para usar 10 segundos. Intervalo maior deixa a leitura mais estável (lembrando do buffer de 5 segundos do time model); intervalo menor fica mais instantâneo, porém com mais ruído.

O percentual de core sai de uma conta simples: delta_cpu / intervalo * 100. Se uma sessão queimou 9,8 segundos de CPU numa janela de 10 segundos, ela ocupou perto de 98% de um core inteiro naquele instante.

A saída fica assim, com o TOP de SQL por CPU do momento no topo e a lista de sessões logo abaixo, ordenada do maior consumidor para o menor:

Saída do s_cpu.sql medindo a CPU do momento por sessão e SQL no Oracle (dados fictícios)

Exemplo de saída do s_cpu.sql mostrando a CPU do momento num intervalo de 20 segundos, com instância, usuários e máquinas fictícios.

Os nomes acima são fictícios. No seu ambiente vão aparecer os usuários, machines e SQL_IDs reais das sessões ativas.

Lendo a saída campo a campo:

  • TOP CPU (20s): a linha de cima é o ranking dos três SQL_IDs que mais consumiram CPU na janela medida, cada um com o total de segundos de CPU. É o resumo de quem mais pesou, antes de você descer para a tabela.
  • SID/SERIAL: identifica a sessão, no formato sid,serial#,@inst_id (o @1 é o número da instância no RAC).
  • USERNAME: o usuário Oracle dono da sessão.
  • SQL_ID/CHILD: o SQL_ID em execução e, entre colchetes, o número do cursor filho (child number).
  • %CPU: quanto de um core inteiro a sessão ocupou no intervalo. 100% é um core cheio; pode passar disso por arredondamento e pelo buffer do time model.
  • CPU_s: os segundos de CPU que a sessão queimou na janela, o delta puro entre os dois snapshots.
  • STATE/WAIT: ON CPU se a sessão estava rodando na CPU, ou o nome do wait event se estava esperando por algo.
  • MACHINE: a máquina cliente de onde veio a conexão.
  • ET: o LAST_CALL_ET, ou seja, há quantos segundos a sessão está na chamada atual.

Validação

Checagem rápida: rode o s_cpu.sql enquanto dispara um SQL pesado conhecido em outra sessão. O SQL_ID dele tem que aparecer no topo, com %CPU alto e STATE em ON CPU. Se aparecer, a medição está pegando o que deveria.

Troubleshooting

O SLEEP se adapta à versão

A partir da versão 1.3, o script escolhe o SLEEP sozinho. Ele tenta primeiro o DBMS_SESSION.SLEEP, que é público e existe no 18c em diante. Se o banco for mais antigo e esse procedure não existir, o tratamento de exceção cai para o DBMS_LOCK.SLEEP. Ou seja, roda nos dois mundos sem você precisar comentar ou descomentar linha nenhuma.

Uma ressalva para bases antigas (11g, 12c): se a conta não tiver GRANT EXECUTE ON DBMS_LOCK para o usuário, o fallback também falha, aí com ORA-06550 / PLS-00201. Isso não é defeito do script, é questão de privilégio: peça o grant ou rode com uma conta que já tenha execute em DBMS_LOCK.

Problema: intervalos muito curtos dão número impreciso

Sintoma: com intervalo de 1 ou 2 segundos, o resultado oscila demais entre execuções.

Causa: o time model bufferiza até 5 segundos de dados antes de publicar na view. Em janelas muito curtas, parte do CPU consumido ainda não apareceu no VALUE.

Solução: use intervalos de 5 a 10 segundos para leitura estável. Foi por isso que o default do script é 10.

O script é só leitura. Não altera parâmetro, não toca em dado, não mata sessão. O risco prático é próximo de zero, fora o bloqueio temporário da própria sessão.

Validação técnica

Confirmado na documentação oficial:

V$SESS_TIME_MODEL mostra tempo acumulado da sessão em microsegundos, com valores cumulativos e inteiros de 8 bytes (Oracle 19c Database Reference). A estatística DB CPU existe nessa view e na V$SYS_TIME_MODEL. As views GV$ trazem a coluna INST_ID e agregam as informações de todas as instâncias ativas do RAC (Oracle Database Reference). O DBMS_SESSION.SLEEP foi adicionado no 18c e o DBMS_LOCK.SLEEP ficou deprecated na mesma versão, continuando disponível por compatibilidade (Oracle 18c).

Conclusão

O s_cpu.sql resolve uma pergunta específica e comum: qual sessão está consumindo a CPU do momento. Dois snapshots de DB CPU, a diferença entre eles, e pronto, você tem o consumo do instante em vez do acumulado que não diz nada no meio de um incidente. O script está na família g_gold, no GitHub: github.com/dbasobrinho/g_gold. Depois de achar o SQL que mais pesa, o passo seguinte costuma ser tunar: às vezes um índice funcional já resolve.

Referências oficiais

  • V$SESS_TIME_MODEL (Oracle Database Reference, 19c): a view de onde sai a estatística DB CPU, em microsegundos.
  • DBMS_SESSION (Oracle Database PL/SQL Packages and Types Reference, 19c): o procedure SLEEP, público a partir do 18c.
  • DBMS_LOCK (Oracle Database PL/SQL Packages and Types Reference, 19c): o procedure SLEEP usado no fallback, que exige grant de execute.

COMUNIDADE DBA SOBRINHO

🔥 NOVAS VAGAS TODO DIA WhatsApp

100% grátis

Compartilhe

Facebook
Twitter
LinkedIn
WhatsApp
Email
Print