Análisis detallado de planes de ejecución
Los planes de ejecución en SQL Server son esenciales para entender cómo se procesan las consultas y optimizar su rendimiento. A continuación se describen operaciones comunes y estrategias para gestionarlos.
Eliminación de planes en caché
Para liberar la memoria del caché de planes de ejecución, se pueden utilizar comandos del sistema. Esto es útil al realizar cambios significativos en la base de datos o durante pruebas de rendimiento.
DBCC FREEPROCCACHE
DBCC FLUSHPROCINDB (DB_ID('NombreBaseDatos'))
Tipos de operaciones en planes de ejecución
- Table Scan (Escaneo de tabla): Ocurre cuando no hay índices adecuados, lo que resulta en un recorrido completo de la tabla, consumiendo recursos elevados.
- Clustered Index Scan (Escaneo de índice agrupado): Similar al escaneo de tabla, ya que el índice agrupado contiene todos los datos de la tabla. Implica revisar todas las filas para encontrar coincidencias.
- Index Scan (Escaneo de índice no agrupado): Se realiza sobre las columnas incluidas en el índice no agrupado, mejorando la eficiencia al reducir los datos examinados.
- Key Lookup (Búsqueda por clave): Permite acceder directamente a los datos mediante claves, ofreciendo un rendimiento superior en comparación con los escaneos.
- RID Lookup (Búsqueda por RID): Utiliza identificadores de fila cuando el índice no agrupado no cubre todas las columnas necesarias y no existe un índice agrupado.
- Clustered Index Seek (Búsqueda en índice agrupado): Accede a datos específicos directamente en la estructura del índice agrupado, optimizando consultas filtradas por clave.
- Index Seek (Búsqueda en índice no agrupado): Localiza datos en el índice no agrupado basándose en valores de clave, evitando recorridos innecesarios.
Gestión de fragmentación en bases de datos
La fragmentación ocurre cuando los datos se distribuyen de manera desordenada en los archivos de la base de datos, afectando la eficiencia de las operaciones de E/S.
Causas de la fragmentación
En SQL Server, los datos se almacenan en páginas de 8060 bytes, organizadas en estructuras de árbol B. Cuando las actualizaciones o inserciones exceden el espacio disponible en una página, se produce una división, generando fragmentación interna o externa.
Identificación de fragmentación
Para diagnosticar el nivel de fragmentación, se pueden ejecutar comandos de diagnóstico que proporcionan métricas clave como la densidad de escaneo y el porcentaje de páginas desordenadas.
-- Ver fragmentación en todos los índices de la base de datos actual
DBCC SHOWCONTIG WITH ALL_INDEXES
-- Ver fragmentación en una tabla específica
DBCC SHOWCONTIG ('NombreTabla') WITH ALL_INDEXES
-- Ver fragmentación de un índice concreto
DBCC SHOWCONTIG ('NombreTabla', 'NombreIndice')
Los resultados incluyen indicadores como:
- Densidad de escaneo (Scan Density): Valores cercanos al 100% indican bajo fragmentamiento.
- Fragmentación lógica (Logical Scan Fragmentation): Porcentaje de páginas fuera de orden; idealmente menor al 10%.
- Densidad promedio de página (Average Page Density): Refleja el espacio libre interno; valores bajos sugieren fragmentación interna.
Reducción de fragmentación
Para reorganizar los índices y mejorar el rendimiento, se utilizan comandos de reconstrucción. Es recomendable monitorear la fragmentación antes y después de estas operaciones.
-- Evaluar fragmentación antes de la reconstrucción
DECLARE @id_tabla INT
SET @id_tabla = OBJECT_ID('NombreTabla')
DBCC SHOWCONTIG(@id_tabla)
-- Reconstruir un índice específico con relleno al 100%
DBCC DBREINDEX('NombreTabla', 'NombreIndice', 100)
-- Reconstruir todos los índices de una tabla
DBCC DBREINDEX('NombreTabla', '', 100)
Para abordar la fragmentación en toda la base de datos, se puede automatizar el proceso usando un cursor que recorra todas las tablas de usuario.
USE NombreBaseDatos;
DECLARE @nombre_tabla VARCHAR(100)
DECLARE cursor_tablas CURSOR FOR
SELECT [name] FROM sysobjects WHERE xtype = 'u' ORDER BY id
OPEN cursor_tablas
FETCH NEXT FROM cursor_tablas INTO @nombre_tabla
WHILE @@FETCH_STATUS = 0
BEGIN
DBCC DBREINDEX(@nombre_tabla, '', 90)
FETCH NEXT FROM cursor_tablas INTO @nombre_tabla
END
CLOSE cursor_tablas
DEALLOCATE cursor_tablas
Identificación de consultas con alto consumo de recursos
Para detectar consultas que consumen tiempo de CPU excesivo, se pueden consultar las estadísticas de ejecución del sistema. Esto permite priorizar la optimización de las operaciones más costosas.
SELECT TOP 20
total_worker_time / 1000 AS [CPU_Total_ms],
execution_count AS [Conteo_Ejecuciones],
qs.total_worker_time / qs.execution_count / 1000 AS [CPU_Promedio_ms],
last_execution_time AS [Ultima_Ejecucion],
max_worker_time / 1000 AS [CPU_Maxima_ms],
SUBSTRING(
qt.text,
qs.statement_start_offset / 2 + 1,
(CASE WHEN qs.statement_end_offset = -1
THEN DATALENGTH(qt.text)
ELSE qs.statement_end_offset
END - qs.statement_start_offset) / 2 + 1
) AS [Fragmento_SQL],
qt.text AS [SQL_Completo],
DB_NAME(qt.dbid) AS [Nombre_Base_Datos],
OBJECT_NAME(qt.objectid, qt.dbid) AS [Nombre_Objeto]
FROM sys.dm_exec_query_stats qs WITH(NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt
WHERE execution_count > 1
ORDER BY total_worker_time DESC