Diferencia de rendimiento entre el índice agrupado y el no agrupado

22

Estaba leyendo Clusteredy Non Clustered Indexes.

Clustered Index- Contiene páginas de datos. Eso significa que la información completa de la fila estará presente en la columna de índice agrupado.

Non Clustered Index- Solo contiene la información del Localizador de filas en forma de columna Índice agrupado (si está disponible) o el Identificador de archivo + Número de página + Total de filas en una página. Esto significa que el motor de consulta debe dar un paso adicional para localizar los datos reales.

Consulta : ¿cómo puedo verificar la diferencia de rendimiento con la ayuda de un ejemplo práctico, ya que sabemos que la tabla solo puede tener uno Clustered Indexy proporciona sortingen el Clustered Index Columny Non Clustered Indexno proporciona sortingy puede admitir 999 Non Clustered Indexesin SQL Server 2008y 249 in SQL Server 2005.


fuente
2
¿Diferencia de rendimiento cuando haces qué ?, ¿qué tipo de trabajo quieres hacer con esa mesa ?, no hay una solución única que se adapte a cada necesidad
Lamak
2
Alguna discusión tangible aquí quizás. stackoverflow.com/questions/91688/… stackoverflow.com/questions/5070529/… stackoverflow.com/questions/1251636/… Podríamos escribir una disertación sobre las diferencias entre los índices agrupados y no agrupados, pero no creo que diría cualquier cosa que no esté disponible para que la leas.
Aaron Bertrand
44
Usted escribió: "Esto significa que el motor de consultas debe dar un paso adicional para localizar los datos reales". En realidad, si todo lo que necesita son columnas cubiertas en el índice , no necesita realizar ningún paso adicional después de encontrar sus filas de destino en el índice no agrupado. Solo cuando necesita columnas no cubiertas por el índice no agrupado, SQL Server debe realizar una búsqueda de marcadores .
Nick Chammas

Respuestas:

43

Muy buena pregunta ya que es un concepto tan importante. Sin embargo, este es un gran tema y lo que voy a mostrar es una simplificación para que pueda comprender los conceptos básicos.

En primer lugar, cuando ve una tabla de pensamiento de índice agrupado . En el servidor SQL, si una tabla no contiene un índice agrupado, es un montón. Crear un índice agrupado en la tabla en realidad transforma la tabla en una estructura tipo b-tree. Su índice agrupado ES su tabla, no está separado de la tabla

¿Alguna vez se preguntó por qué solo puede tener un índice agrupado? Bueno, si tuviéramos dos índices agrupados necesitaríamos dos copias de la tabla. Contiene los datos después de todo.

Voy a tratar de explicar esto usando un ejemplo simple.

NOTA: Creé la tabla en este ejemplo y la llené con más de 3 millones de entradas aleatorias. Luego ejecutó las consultas reales y pegó los planes de ejecución aquí.

Lo que realmente necesita comprender es la notación O o la eficiencia operativa . Supongamos que tiene la siguiente tabla.

CREATE TABLE [dbo].[Customer](
[CustomerID] [int] IDENTITY(1,1) NOT NULL,
[CustomerName] [varchar](100) NOT NULL,
[CustomerSurname] [varchar](100) NOT NULL,
CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED 
(
[CustomerID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF
  , IGNORE_DUP_KEY = OFF,ALLOW_ROW_LOCKS  = ON
  , ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

Entonces, aquí tenemos una tabla básica con una clave agrupada en CustomerID (la clave principal está agrupada de forma predeterminada). Por lo tanto, la tabla se organiza / ordena en función de la clave principal CustomerID. Los niveles intermedios contendrán los valores de CustomerID. Las páginas de datos contendrán la fila completa, por lo tanto, es la fila de la tabla.

También crearemos un índice no agrupado en el campo CustomerName. El siguiente código lo hará.

CREATE NONCLUSTERED INDEX [ix_Customer_CustomerName] ON [dbo].[Customer] 
 (
[CustomerName] ASC
 )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF
  , SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF
  , DROP_EXISTING = OFF, ONLINE = OFF
  , ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

Entonces, en este índice, encontrará en las páginas de datos / nodos de nivel de hoja un puntero a los niveles intermedios en el índice agrupado. El índice se organiza / ordena alrededor del campo CustomerName. Por lo tanto, el nivel intermedio contiene los valores de CustomerName y el nivel de hoja contendrá el puntero (estos valores de puntero son en realidad los valores de clave principal o la columna CustomerID).

Correcto, si ejecutamos la siguiente consulta:

SELECT * FROM Customer WHERE CustomerID = 1 

SQL probablemente leerá el índice agrupado a través de una operación de búsqueda. Una operación de búsqueda es una búsqueda binaria que es mucho más eficiente que una exploración que es una búsqueda secuencial. Entonces, en nuestro ejemplo anterior, se lee el índice y, mediante una búsqueda binaria, SQL puede eliminar los datos que no coinciden con los criterios que estamos buscando. Vea la captura de pantalla adjunta para el plan de consulta.

ingrese la descripción de la imagen aquí

Entonces, el número de operaciones o la notación O para la operación de búsqueda es la siguiente:

  1. Realice una búsqueda binaria en el índice agrupado comparando el valor buscado con los valores en el nivel intermedio.
  2. Devuelva los valores que coinciden (recuerde, ya que el índice agrupado contiene todos los datos, puede devolver todas las columnas del índice, ya que son los datos de la fila)

Entonces son dos operaciones. Sin embargo, si ejecutamos la siguiente consulta:

SELECT * FROM Customer WHERE CustomerName ='John'

SQL ahora usará el índice no agrupado en CustomerName para realizar la búsqueda. Sin embargo, dado que este es un índice no agrupado, no contiene todos los datos de la fila.

Por lo tanto, SQL realizará la búsqueda en los niveles intermedios para encontrar los registros que coinciden y luego realizará una búsqueda utilizando los valores devueltos para realizar otra búsqueda en el índice agrupado (también conocido como la tabla) para recuperar los datos reales. Esto suena confuso, lo sé, pero sigue leyendo y todo quedará claro.

Dado que nuestro índice no agrupado solo contiene el campo CustomerName (los valores de campo indexados almacenados en los nodos intermedios) y el puntero a los datos que es CustomerID, el índice no tiene registro del CustomerSurname. El CustomerSurname se debe obtener del índice o la tabla agrupados.

Cuando ejecuto esta consulta obtengo el siguiente plan de ejecución:

ingrese la descripción de la imagen aquí

Hay dos cosas importantes que debes notar en la captura de pantalla anterior

  1. SQL dice que me falta un índice (el texto en verde). SQL sugiere que cree un índice en CustomerName que incluya CustomerID y CustomerSurname.
  2. También verá que el 99% del tiempo de la consulta se dedica a realizar una búsqueda de clave en el índice de clave principal / índice agrupado.

¿Por qué SQL vuelve a sugerir el índice en CustomerName? Bueno, dado que el índice contiene solo el CustomerID y el CustomerName SQL todavía tiene que encontrar el CustomerSurname de la tabla / índices agrupados.

Si creáramos el índice e incluyéramos la columna CustomerSurname en el índice, SQL podría satisfacer la consulta completa simplemente leyendo el índice no agrupado. Es por eso que SQL sugiere que cambie mi índice no agrupado.

Aquí puede ver la operación adicional que SQL debe hacer para obtener la columna CustomerSurname de la clave agrupada

Por lo tanto, el número de operaciones es el siguiente:

  1. Realice una búsqueda binaria en un índice no agrupado comparando el valor buscado con los valores en el nivel intermedio
  2. Para los nodos que coinciden, lea el nodo de nivel de hoja que contendrá el puntero para los datos en el índice agrupado (los nodos de nivel de hoja contendrán los valores de clave principal por cierto).
  3. Para cada valor devuelto, lea el índice agrupado (la tabla) para obtener los valores de la fila aquí, leeríamos el CustomerSurname.
  4. Devolver filas coincidentes

Eso son 4 operaciones para obtener los valores. El doble de la cantidad de operaciones necesarias en comparación con la lectura del índice agrupado. Le muestra que su índice agrupado es su índice más poderoso, ya que contiene todos los datos.

Tan solo para aclarar un último punto. ¿Por qué digo que el puntero en el índice no agrupado es el valor de la clave primaria? Bueno, para demostrar que los nodos de nivel de hoja del índice no agrupado contienen el valor de la clave primaria, cambio mi consulta a:

SELECT CustomerID
FROM Customer
WHERE CustomerName='Jane'

En esta consulta, SQL puede leer el CustomerID del índice no agrupado. No necesita hacer una búsqueda en el índice agrupado. Esto se puede ver en el plan de ejecución que se ve así.

ingrese la descripción de la imagen aquí

Observe la diferencia entre esta consulta y la consulta anterior. No hay búsqueda. SQL puede encontrar todos los datos en el índice no agrupado

Esperemos que pueda comenzar a comprender que el índice agrupado es la tabla y los índices no agrupados NO contienen todos los datos. La indexación acelerará las selecciones debido al hecho de que se pueden realizar búsquedas binarias, pero solo los índices agrupados contienen todos los datos. Por lo tanto, una búsqueda en un índice no agrupado casi siempre dará como resultado que los valores se carguen desde el índice agrupado. Estas operaciones adicionales hacen que los índices no agrupados sean menos eficientes que un índice agrupado.

Espero que esto aclare las cosas. Si algo no tiene sentido, publique un comentario e intentaré aclararlo. Es bastante tarde aquí y mi cerebro se siente un poco plano. Tiempo para un toro rojo.

Namphibian
fuente
Tengo una pregunta. POR QUÉ es la búsqueda que busca un índice en el índice no agrupado en CustomerName para esta consulta SELECT * FROM Customer WHERE CustomerName = 'John'. Como es un índice no agrupado, el nombre del cliente no se ordenará. Por lo tanto, no se debe realizar una exploración de índice
ckv
Por cierto Gran respuesta totalmente entendida, excepto la pregunta anterior.
ckv
1
Un índice se ordena en el orden de los datos. Por ejemplo, se ordenaría según el nombre del Cliente, ya que es el valor indexado. Entonces está ordenado. Recuerde que todavía tiene que escanear el nivel de hoja o las páginas.
Namphibian
9

"Esto significa que el motor de consultas debe dar un paso adicional para localizar los datos reales".

No necesariamente: si el índice está cubriendo una consulta determinada, no se debe realizar ningún viaje a las páginas de datos. Además, con las columnas incluidas, se pueden agregar columnas adicionales a un índice no agrupado para que cubra sin alterar el tamaño de la clave.

Entonces, la respuesta final es: depende (de mucha más información de la que realmente puede cubrir en una sola pregunta): debe comprender todas las capacidades de los índices y el plan de ejecución para una consulta determinada puede diferir de sus expectativas.

Una regla general que tengo es que una tabla siempre tiene un índice agrupado (y generalmente en una identidad o GUID secuencial), pero los índices no agrupados se agregan para el rendimiento. Pero siempre hay excepciones: las tablas de montón tienen un lugar, los índices agrupados más amplios tienen un lugar. Los índices aparentemente redundantes que son más estrechos para ajustarse a más filas por página tienen un lugar. etcétera etcétera.

Y no me preocuparía por los límites en los diversos índices permitidos; eso seguramente no entrará en juego en muchos ejemplos del mundo real.

Cade Roux
fuente
2
+1 para there are always exceptions- demasiadas personas omiten esto y piensan que cada índice agrupado debería ser un int identityno importa qué.
JNK