GuilleSQL :: Microsoft SQL Server, SSIS, y más !!

Creación de Indices Agrupados (CLUSTERED) y No Agrupados (NONCLUSTERED)

Volver a: [SELECT INTO, INSERT INTO y el LOG de SQL Server: alternativas para cargar tablas en un Data Warehouse]


Este capítulo describe varios casos de creación de índices, tanto Agrupados (CLUSTERED) como No Agrupados (NONCLUSTERED), mostrando los resultados obtenidos de su creación en diferentes escenarios, tanto teniendo suficiente espacio de Datos y Log, como sin tener espacio suficiente (los ficheros tienen que crecer), considerando también la ejecución de estas creaciones de índices en Modo de Recuperación Completo (RECOVERY FULL) y Sencillo (RECOVERY SIMPLE).

Una tarea habitual en un Data Warehouse, como parte de los procesos de carga y transformación de datos (ETL), es la creación de índices. Son muchos los aspectos que debemos estudiar al respecto, y con el objetivo de optimizar nuestros procesos de carga y el rendimiento final del sistema que se está diseñando.

El Modo de Recuperación es un factor vital en el rendimiento, lo que en ocasiones obliga a configurar las bases de datos del propio Data Warehouse en Modo de Recuperación Sencillo (o de Copia Masiva), o bien, mantenerlas en Modo de Recuperación Completo, y utilizar el Modo de Recuperación Sencillo en otras bases de datos previas (Temporales y de Stagging) sobre las que descargar una buena parte del coste de los procesos de carga. Aunque en principio, no es recomendable utilizar el Modo de Recuperación Sencillo para entornos de producción, si existe un entorno en el cual cabe la posibilidad de saltarse esta regla, es en un Data Warehouse, bajo la premisa de que ante una eventual pérdida de datos, siempre será posible recuperar el último backup completo, y seguidamente volver a ejecutar los procesos de carga posteriores al momento de recuperación. Evidentemente, habría que estudiar cada caso, pero cierto es que cabe la posibilidad.

Un correcto dimensionamiento de los ficheros de la base de datos también resulta fundamental, evitando así el esfuerzo de los crecimientos de los ficheros. Existen más detalles, como mantener el sistema sin Fragmentación (sin en base de datos ni en el Sistema de Ficheros), suficiente memoria, etc.

Otro tema importante, es la elección del tipo de índice más apropiado. Hasta SQL Server 2000, principalmente podíamos decidir entre:

  • Índices Agrupados (CLUSTERED). Cualquier tabla, puede tener ninguno o un índice agrupado (CLUSTERED). Los índices agrupados son la propia tabla, es decir, cuando creamos un índice agrupado sobre una tabla, estamos convirtiendo a la propia tabla en un índice, lo cual tiene varias implicaciones.

    A efectos de rendimiento, es importante tener en cuenta, que una tabla con un índice agrupado se almacenará de forma ordenada, lo cual, facilitará la satisfacción de consultas con cláusulas ORDER BY o GROUP BY coincidentes con los campos del índice, por poner un ejemplo representativo.
  • Índices No Agrupados (NONCLUSTERED). Se trata de estructuras ajenas a la propia tabla, que consumen su propio almacenamiento. Esto permite, que la tabla se pueda almacenar sobre un Grupo de Ficheros (File Group) y los índices no agrupados sobre un Grupo de Fichero diferente (por ejemplo, sobre un volumen distinto al que se accede por un camino de fibra separado). Es posible crear índices no agrupados, sobre tablas sin índices y sobre tablas con índices (incluso aunque tenga un índice agrupado).

    Los índices No Agrupados, suelen ser especialmente efectivos para satisfacer cláusulas WHERE sobre los campos del índice que devuelven un conjunto muy pequeño de registros. También son muy apropiados para satisfacer consultas cuyos campos están todos incluidos en el índice (de este modo, no es necesario acceder a la tabla, pues toda la información está en el índice).

Esta introducción a los índices agrupados y no agrupados es muy básica. Si los estudiamos más a fondo, podemos ver consideraciones importante varias, como por ejemplo la defragmentación. En SQL Server no es posible defragmentar una tabla, sólo un índice. Otra cosa, es que la tabla tenga un índice agrupado, en cuyo caso, al defragmentar dicho índice estaremos defragmentando la tabla.

También hay consideraciones en la creación y eliminación de índices, así como en la reindexación. Si tenemos un índice agrupado y múltiples índices no agrupados, si eliminamos el índice agrupado, estaremos implícitamente eliminando y volviendo a crear los siete índices no agrupados. Del mismo modo, si volvemos a crear el índice agrupado, volveremos a eliminar y crear los siete índices no agrupados de forma implícita. Esta problemática, es el motivo de que existan sentencias como CREATE INDEX DROP EXISTING, ALTER INDEX REBUID o DBCC DBREINDEX.

Es importante tener claro la importación de Reindexar y Defragmentar índices en SQL Server, así como las diferentes alternativas que tenemos para realizar estas tareas.

Podemos seguir viendo detalles, como el Factor de llenado, el orden de los campos en el índice, la satisfacción de los planes de ejecución, las estadísticas de los índices, índices únicos o no únicos, utilizar campos NULL en índices, etc. Un estudio más avanzado, queda fuera del alcance de este artículo (requeriría un libro completo), aunque si quisiera intentar transmitir la existencia de estos detalles para poder tenerlos presentes.

Con SQL Server 2005, se introduce la inclusión de índices no agrupados, a través de la sentencia CREATE NONCLUSTERED INDEX INCLUDE. Para entenderlo mejor, es importante conocer el concepto de Cobertura de Índice.

El concepto de Cobertura de Índice está relacionado con qué campos de una consulta están contenidos en un índice. Así, si todos los campos de una consulta están contenidos en un índice, es posible que dicha consulta se resuelva utilizando dicho índice, sin necesidad de acceder a la propia tabla (pa qué, si ya lo tiene todo en el índice). Una técnica de optimización de base de datos, es ampliar la Cobertura de Índice. Si tenemos una consulta que para resolverse completamente a través de un índice, requiere agregar a dicho índice un campo adicional de la tabla, la acción de alterar dicho índice para agregar un nuevo campo, se denomina ampliar la Cobertura de Índice. Como siempre, habría que estudiar cada caso, para ver hasta que punto sale rentable, pero bueno, el concepto es este.

Entendido qué es la Cobertura de Índice, ahora será más fácil entender los índices no agrupados con inclusión. Este tipo de índices, permiten incluir campos adicionales que serán almacenados en los nodos hoja (estos son los campos especificados en la cláusula INCLUDE). Estos campos no serán propiamente indexados, pero si una consulta los requiere podrá recuperarlos directamente del índice sin necesidad de realizar un Lookup (una búsqueda) sobre la correspondiente fila de la correspondiente tabla. En las pruebas que he realizado, y en los casos en que he aplicado este tipo de índices en entornos de Producción, si es cierto que existen casos en los que se consiguen mejoras de rendimiento interesantes.

A continuación, se muestran unas pruebas representativas de creaciones de índices, algo muy habitual en un entorno de Data Warehouse, en algunas ocasiones porque creamos nuevas tablas que necesitamos indexar (aunque sean tablas temporales), en otras ocasiones resulta más rápido eliminar los índices de una tabla antes de cargarla (insertarla y actualizarla por doquier) volviendo a crear los índices al final del proceso.

Sin más, pasamos a ver los resultados de las pruebas realizadas.

Creación de índices No Agrupados (NONCLUSTERED) sobre tabla sin índices (Heap)

Los resultados obtenidos utilizando el Modo de Recuperación Completo (Recovery FULL) han sido los siguientes:

  • Creación índice No Agrupado sobre dos campos VARCHAR(45). Tiempo de ejecución de 8 segundos. La base de datos crece desde su tamaño inicial de 58MB de Datos y 1MB de Log a 89MB de Datos y 29MB de Log.

    Si la base de datos tenía suficiente espacio de Datos y Log (es decir, está correctamente dimensionada), el tiempo de ejecución se limita a 3 segundos.

  • Creación índice No Agrupado sobre un campo entero (INT). Tiempo de ejecución de 4 segundos. La base de datos crece desde su tamaño inicial de 58MB de Datos y 1MB de Log a 61MB de Datos y 5MB de Log.

    Si la base de datos tenía suficiente espacio de Datos y Log (es decir, está correctamente dimensionada), el tiempo de ejecución se limita a 2 segundos.

Los resultados obtenidos utilizando el Modo de Recuperación Sencillo (Recovery SIMPLE) han sido los siguientes:

  • Creación índice No Agrupado sobre dos campos VARCHAR(45). Tiempo de ejecución de 4 segundos. La base de datos crece desde su tamaño inicial de 58MB de Datos y 1MB de Log a 85MB de Datos y 2MB de Log.

    Si la base de datos tenía suficiente espacio de Datos y Log (es decir, está correctamente dimensionada), el tiempo de ejecución se limita a 2 segundos.

  • Creación índice No Agrupado sobre un campo entero (INT). Tiempo de ejecución de 2 segundos. La base de datos crece desde su tamaño inicial de 58MB de Datos y 1MB de Log a 61MB de Datos y 1MB de Log.

    Si la base de datos tenía suficiente espacio de Datos y Log (es decir, está correctamente dimensionada), el tiempo de ejecución se mantiene en 2 segundos.

A continuación se incluye código Transact-SQL de ejemplo, similar al utilizado en la prueba:

CREATE UNIQUE NONCLUSTERED INDEX IX_Test1
ON dbo.pruebas_indice(Campo01, Campo02)

CREATE UNIQUE NONCLUSTERED INDEX IX_Test2
ON dbo.pruebas_indice(Campo03)

Creación de índices Agrupados (CLUSTERED) sobre tabla sin índices (Heap)

Los resultados obtenidos utilizando el Modo de Recuperación Completo (Recovery FULL) han sido los siguientes:

  • Creación índice No Agrupado sobre dos campos VARCHAR(45). Tiempo de ejecución de 12 segundos. La base de datos crece desde su tamaño inicial de 58MB de Datos y 1MB de Log a 123MB de Datos y 62MB de Log.

    Si la base de datos tenía suficiente espacio de Datos y Log (es decir, está correctamente dimensionada), el tiempo de ejecución se limita a 3 segundos.

  • Creación índice No Agrupado sobre un campo entero (INT). Tiempo de ejecución de 20 segundos. La base de datos crece desde su tamaño inicial de 58MB de Datos y 1MB de Log a 124MB de Datos y 62MB de Log.

    Si la base de datos tenía suficiente espacio de Datos y Log (es decir, está correctamente dimensionada), el tiempo de ejecución se limita a 5 segundos.

Los resultados obtenidos utilizando el Modo de Recuperación Sencillo (Recovery SIMPLE) han sido los siguientes:

  • Creación índice No Agrupado sobre dos campos VARCHAR(45). Tiempo de ejecución de 7 segundos. La base de datos crece desde su tamaño inicial de 58MB de Datos y 1MB de Log a 124MB de Datos y 3MB de Log.

    Si la base de datos tenía suficiente espacio de Datos y Log (es decir, está correctamente dimensionada), el tiempo de ejecución se limita a 3 segundos.

  • Creación índice No Agrupado sobre un campo entero (INT). Tiempo de ejecución de 9 segundos. La base de datos crece desde su tamaño inicial de 58MB de Datos y 1MB de Log a 123MB de Datos y 2MB de Log.

    Si la base de datos tenía suficiente espacio de Datos y Log (es decir, está correctamente dimensionada), el tiempo de ejecución se limita a 3 segundos.

A continuación se incluye código Transact-SQL de ejemplo, similar al utilizado en la prueba:

CREATE UNIQUE CLUSTERED INDEX IX_Test1 ON dbo.pruebas_indice(Campo01, Campo02) CREATE UNIQUE CLUSTERED INDEX IX_Test2 ON dbo.pruebas_indice(Campo03)

Conclusiones

Por último, sólo incluir una valoración adicional. Si bien estamos encontrando diferencias de unos pocos segundos, el problema que realmente debemos evaluar, es la extrapolación de estos resultados a un entorno de Data Warehouse real, donde trabajaremos con tablas mucho más grandes, y dónde posiblemente nuestros procesos de carga se ejecuten simultáneamente con los procesos de carga de otros DataMart desarrollados por otros compañeros, empresas, o quizás incluso por nosotros mismos (guerra de recursos).

Reducir un tiempo de ejecución de 8 segundos a 2 segundos, no nos lleva a ninguna parte. Reducir un tiempo de ejecución de 8 horas a 2 horas, es algo muy distinto.

Volver a: [SELECT INTO, INSERT INTO y el LOG de SQL Server: alternativas para cargar tablas en un Data Warehouse]




Miembros de
Miembros de GITCA (Global IT Community Association)

Menu de Usuario
  Iniciar Sesión
  Registrarse
  Restablecer Contraseña
  Ventajas de Registrarse

Acerca de
  Contigo desde Oct 2007
  771 usuarios registrados
  86146 pageloads/mes
  Ranking Alexa 498160

Social Networks
Sigue a Portal GuilleSQL en Linkedin !!
Sigue a Portal GuilleSQL en Twitter !!



Archivo

Junio de 2017 (3)
Mayo de 2017 (1)
Marzo de 2017 (3)
Enero de 2017 (4)
Junio de 2016 (1)
Mayo de 2016 (2)
Abril de 2016 (2)
Septiembre de 2015 (2)
Agosto de 2015 (2)
Junio de 2015 (10)
Mayo de 2015 (4)
Abril de 2015 (8)
Marzo de 2015 (11)
Octubre de 2014 (3)
Septiembre de 2014 (7)
Agosto de 2014 (5)
Julio de 2014 (2)
Mayo de 2014 (4)
Abril de 2014 (4)
Marzo de 2014 (4)
Febrero de 2014 (1)
Enero de 2014 (5)
Diciembre de 2013 (8)
Noviembre de 2013 (2)
Octubre de 2013 (7)
Septiembre de 2013 (6)
Agosto de 2013 (1)
Julio de 2013 (6)
Junio de 2013 (11)
Mayo de 2013 (7)
Abril de 2013 (6)
Febrero de 2013 (5)
Enero de 2013 (7)
Diciembre de 2012 (12)
Noviembre de 2012 (13)
Octubre de 2012 (5)
Septiembre de 2012 (3)
Agosto de 2012 (6)
Julio de 2012 (4)
Junio de 2012 (1)
Mayo de 2012 (2)
Abril de 2012 (7)
Marzo de 2012 (16)
Febrero de 2012 (9)
Enero de 2012 (5)
Diciembre de 2011 (10)
Noviembre de 2011 (10)
Octubre de 2011 (4)
Septiembre de 2011 (5)
Agosto de 2011 (2)
Julio de 2011 (2)
Junio de 2011 (4)
Mayo de 2011 (2)
Abril de 2011 (6)
Marzo de 2011 (4)
Febrero de 2011 (10)
Enero de 2011 (5)
Diciembre de 2010 (6)
Noviembre de 2010 (4)
Octubre de 2010 (8)
Septiembre de 2010 (4)
Agosto de 2010 (1)
Julio de 2010 (3)
Mayo de 2010 (5)
Abril de 2010 (6)
Marzo de 2010 (8)
Febrero de 2010 (3)
Enero de 2010 (1)
Diciembre de 2009 (9)
Noviembre de 2009 (14)
Octubre de 2009 (2)
Septiembre de 2009 (8)
Agosto de 2009 (2)
Julio de 2009 (10)
Junio de 2009 (9)
Mayo de 2009 (10)
Abril de 2009 (9)
Marzo de 2009 (3)
Febrero de 2009 (2)
Enero de 2009 (3)
Noviembre de 2008 (2)
Octubre de 2008 (2)
Septiembre de 2008 (2)
Agosto de 2008 (5)
Julio de 2008 (5)
Junio de 2008 (1)
Mayo de 2008 (3)
Abril de 2008 (2)
Marzo de 2008 (2)
Febrero de 2008 (2)
Enero de 2008 (5)
Noviembre de 2007 (2)
Octubre de 2007 (2)






Copyright © 2007 GuilleSQL, todos los derechos reservados.