Ir al contenido
← Todos los artículos
PerformanceAWROracle Database

Tuning de Oracle con AWR: cómo leer el reporte y encontrar el SQL que te está costando el servidor

Guía práctica para diagnosticar rendimiento en Oracle Database con AWR y ASH: qué secciones leer primero, cómo interpretar el perfil de esperas y qué hacer con el top SQL antes de comprar hardware.

Equipo TioneSolutions · 15 de octubre de 2026 · 11 min de lectura

Cuando un sistema sobre Oracle “se pone lento”, la reacción más común es mirar la CPU del servidor, ver que está al 90 % y pedir cotización de un servidor más grande. En la mayoría de los casos que hemos atendido, ese servidor nuevo habría sido un desperdicio: la CPU estaba al 90 % porque un puñado de sentencias SQL hacían millones de lecturas lógicas que no necesitaban hacer. El reporte AWR te dice exactamente cuáles son. Esta guía explica cómo leerlo con criterio.

Qué es AWR y qué necesitas para usarlo

El Automatic Workload Repository toma fotografías (snapshots) del estado de la instancia cada hora por defecto y las guarda ocho días. Un reporte AWR compara dos snapshots y resume todo lo que pasó entre ellos: tiempo de base de datos, esperas, SQL más costoso, I/O por archivo, estadísticas de memoria.

Requisitos:

  • Oracle Enterprise Edition con Diagnostics Pack licenciado. AWR y ASH son parte de ese pack. Si tienes Standard Edition o no tienes el pack, usa Statspack (gratuito, menos detallado) o las vistas V$ directamente. Consultar DBA_HIST_* sin licencia es una violación de contrato que Oracle sí audita.
  • Snapshots que cubran el periodo problemático. Si el problema ocurre de 10:00 a 10:40, un reporte de 09:00 a 11:00 diluye la señal. Puedes tomar snapshots manuales con DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT antes y después de reproducir el problema.

Para generar el reporte:

-- Como SYS o usuario con privilegios sobre DBMS_WORKLOAD_REPOSITORY
@?/rdbms/admin/awrrpt.sql
-- Elige html, el rango de snapshots y el nombre del archivo

Una regla práctica: un reporte de 30 a 60 minutos sobre el periodo del problema vale más que uno de 24 horas. El promedio de un día esconde el pico que te interesa.

Las cinco secciones que debes leer, en orden

1. Load Profile: ¿cuánto trabajo hace la base por segundo?

Está al inicio. Mira DB Time(s) por segundo frente a DB CPU(s). Si tienes 8 núcleos y DB CPU por segundo es 7.5, la base está saturando CPU. Si DB Time es 40 y DB CPU es 6, el resto del tiempo la base está esperando algo (I/O, bloqueos, red), no procesando.

Fíjate también en Logical reads y Physical reads por segundo. Cientos de miles de lecturas lógicas por segundo con pocas transacciones suelen indicar SQL con planes ineficientes: full scans donde debería haber acceso por índice, o índices que devuelven demasiadas filas.

2. Top 10 Foreground Events: ¿en qué espera la base?

Esta tabla es el corazón del reporte. Cada fila es un tipo de espera con su porcentaje del DB Time. Interpretación de las más comunes:

EventoQué significa normalmenteDónde mirar después
DB CPULa base procesa. Si domina, el problema es SQL que consume CPU: lecturas lógicas excesivas, funciones PL/SQL en el WHERE, ordenamientos grandes.SQL ordered by CPU / Gets
db file sequential readLecturas de un bloque, típicas de acceso por índice. Alto porcentaje + alto tiempo promedio (>10 ms) apunta a almacenamiento lento.SQL ordered by Reads, Tablespace IO
db file scattered readLecturas multibloque: full table scans. Si es alto, hay consultas barriendo tablas completas.SQL ordered by Reads
direct path readFull scans en paralelo o tablas grandes que evitan el buffer cache. Normal en reporting, sospechoso en OLTP.SQL ordered by Reads
log file syncLos commits esperan a que el redo se escriba en disco. Tiempo promedio >5 ms indica discos de redo lentos; conteo altísimo indica commits por fila en un loop.Instance Activity (user commits), I/O de redo
enq: TX - row lock contentionSesiones bloqueadas por otras sesiones. Es diseño de aplicación, no infraestructura.Segments by Row Lock Waits
buffer busy waitsVarias sesiones pelean por el mismo bloque. Tablas con inserción masiva concurrente o índices sobre secuencias.Segments by Buffer Busy Waits
library cache lock / cursor: pin S wait on XParseo excesivo: SQL sin bind variables, o DDL en horario pico.Parse statistics, SQL ordered by Parse Calls

Si DB CPU y las lecturas lógicas dominan, el camino es SQL. Si dominan las esperas de I/O con tiempos promedio altos, puede ser almacenamiento, pero antes revisa si el I/O es necesario: un plan malo genera I/O que ningún disco rápido va a compensar.

3. SQL ordered by Elapsed Time, CPU, Gets y Reads

Aquí está el “5 % del SQL que consume el 80 % de la base”. Ordena mentalmente por lo que dominó en los eventos de espera:

  • Si dominó CPU: SQL ordered by Gets (lecturas lógicas) y by CPU Time.
  • Si dominó I/O: SQL ordered by Reads (lecturas físicas).
  • Si hay quejas de pantallas lentas: SQL ordered by Elapsed Time, y mira la columna Elapsed per Exec. Una sentencia que se ejecuta 50.000 veces a 0,1 s pesa más que una que se ejecuta una vez en 30 s.

Anota el SQL Id de las tres o cuatro sentencias que encabezan las listas. Casi siempre son las mismas en varias de ellas. Luego obtén el plan real que usó la base, no el estimado:

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('&sql_id', NULL, 'ALLSTATS LAST'));
-- Si el cursor ya no está en memoria:
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_AWR('&sql_id'));

Lo que buscas en el plan: TABLE ACCESS FULL sobre tablas grandes con filtros selectivos, NESTED LOOPS con millones de iteraciones, HASH JOIN que desborda a disco (TempSpc), y diferencias grandes entre E-Rows (estimadas) y A-Rows (reales), que indican estadísticas desactualizadas o falta de histogramas.

4. Segment Statistics: ¿qué objetos sufren?

Segments by Logical Reads, by Physical Reads, by Row Lock Waits y by Buffer Busy Waits te dicen sobre qué tablas e índices cae la carga. Un índice que aparece en el top de lecturas lógicas con una tabla pequeña suele estar fragmentado o ser el índice equivocado para el patrón de acceso.

5. Instance Efficiency y Parse Statistics

Execute to Parse % bajo (menos de 60–70 %) y Soft Parse % menor que 95 % indican que la aplicación envía SQL con literales en lugar de bind variables. Eso llena la shared pool, dispara library cache waits y gasta CPU en parsear. La corrección es en la aplicación; mientras tanto, CURSOR_SHARING=FORCE es un parche con efectos secundarios que conviene medir antes de aplicarlo en producción.

ASH: cuando el problema dura minutos, no horas

AWR promedia una hora. Si la lentitud dura cinco minutos, ASH (Active Session History) te da muestras por segundo de qué estaba haciendo cada sesión activa:

SELECT sql_id, event, COUNT(*) samples
FROM   v$active_session_history
WHERE  sample_time BETWEEN TO_DATE('2026-10-15 10:05','YYYY-MM-DD HH24:MI')
                       AND TO_DATE('2026-10-15 10:15','YYYY-MM-DD HH24:MI')
GROUP  BY sql_id, event
ORDER  BY samples DESC
FETCH FIRST 15 ROWS ONLY;

El reporte ashrpt.sql hace lo mismo con formato. Para un incidente puntual, ASH responde en diez minutos lo que AWR tarda en mostrar.

Los quick wins que suelen aparecer

En orden de frecuencia en los diagnósticos que hemos hecho:

  1. Estadísticas obsoletas o sin histogramas en tablas con distribución sesgada. Solución: DBMS_STATS.GATHER_TABLE_STATS con METHOD_OPT adecuado y, si aplica, estadísticas incrementales en tablas particionadas.
  2. Índice faltante para un predicado que filtra mucho. Antes de crearlo, verifica cuántos índices ya tiene la tabla: cada índice encarece los INSERT/UPDATE.
  3. Planes inestables: la misma sentencia alterna entre un plan bueno y uno malo según los valores de bind (bind peeking). Solución: SQL Plan Baselines para fijar el plan correcto sin tocar el código.
  4. SGA mal dimensionada: Buffer Cache Hit % bajo con memoria libre en el servidor. Revisa Buffer Pool Advisory y SGA Target Advisory en el mismo reporte; Oracle te dice cuánto ganarías con cada incremento.
  5. Commits por fila en procesos batch. La sección Instance Activity Stats muestra user commits; si son decenas de miles por minuto en un proceso nocturno, el cambio es en el PL/SQL (commit cada N filas).
  6. PL/SQL en el WHERE: una función llamada por cada fila evaluada. Function result cache o reescritura como JOIN suelen reducir el costo en órdenes de magnitud.

Qué no hacer

  • No subir parámetros al azar. Aumentar SGA_TARGET sin evidencia del advisory, o activar OPTIMIZER_INDEX_COST_ADJ, puede empeorar otros planes.
  • No aplicar hints como primera opción. Un hint congela una decisión del optimizador para siempre, incluso cuando los datos cambian. Prefiere estadísticas correctas y baselines.
  • No reiniciar la instancia “para limpiar”. Pierdes el buffer cache, la shared pool y la evidencia. Si el problema es recurrente, volverá en una hora.
  • No comprar hardware sin un reporte AWR del periodo del problema que muestre esperas de I/O con tiempos altos y SQL ya optimizado. Es la única justificación técnica válida.

Cómo medir que funcionó

Toma el AWR del mismo periodo (mismo día de la semana, misma hora) antes y después del cambio. Compara DB Time total, el top de esperas y el Elapsed per Exec de las sentencias que tocaste. Un informe de tuning serio entrega esos tres números, no una percepción de “ahora está más rápido”.


Si tu base de datos tiene síntomas de rendimiento y nadie ha mirado un AWR del periodo del problema, el diagnóstico inicial toma entre tres y cinco días hábiles y suele terminar con quick wins aplicados en la primera semana.

¿Tienes un problema, una mejora o un proyecto en mente?

Agenda un diagnóstico gratuito de 30 minutos. Conversamos sobre tu base de datos, aplicaciones, nube o reportería y te damos una recomendación clara de por dónde empezar.

Agendar diagnóstico