¿Por qué el optimizador elegiría Clustered Index + Sort en lugar de Non-Clustered Index?


11

Dado el siguiente ejemplo:

IF OBJECT_ID('dbo.my_table') IS NOT NULL
    DROP TABLE [dbo].[my_table];
GO

CREATE TABLE [dbo].[my_table]
(
    [id]    int IDENTITY (1,1)  NOT NULL PRIMARY KEY,
    [foo]   int                 NULL,
    [bar]   int                 NULL,
    [nki]   int                 NOT NULL
);
GO

/* Insert some random data */
INSERT INTO [dbo].[my_table] (foo, bar, nki)
SELECT TOP (100000)
    ABS(CHECKSUM(NewId())) % 14,
    ABS(CHECKSUM(NewId())) % 20,
    n = CONVERT(INT, ROW_NUMBER() OVER (ORDER BY s1.[object_id]))
FROM 
    sys.all_objects AS s1 
CROSS JOIN 
    sys.all_objects AS s2
GO

CREATE UNIQUE NONCLUSTERED INDEX [IX_my_table]
    ON [dbo].[my_table] ([nki] ASC);
GO

Si busco todos los registros ordenados por [nki](Índice no agrupado):

SET STATISTICS TIME ON;
SELECT id, foo, bar, nki FROM my_table ORDER BY nki;
SET STATISTICS TIME OFF;

SQL Server Execution Times: CPU time = 266 ms, elapsed time = 493 ms

El optimizador elige el índice agrupado y luego aplica un algoritmo de clasificación.

ingrese la descripción de la imagen aquí

Execution plan

Pero si lo fuerzo a usar el índice no agrupado:

SET STATISTICS TIME ON;
SELECT id, foo, bar, nki FROM my_table WITH(INDEX(IX_my_TABLE));
SET STATISTICS TIME OFF;

SQL Server Execution Times: CPU time = 311 ms, elapsed time = 188 ms

Luego usa un índice no agrupado con una búsqueda clave:

ingrese la descripción de la imagen aquí

Execution plan

Obviamente, si el índice no agrupado se transforma en un índice de cobertura:

CREATE UNIQUE NONCLUSTERED INDEX [IX_my_table]
    ON [dbo].[my_table] ([nki] ASC)
    INCLUDE (id, foo, bar);
GO

Luego usa solo este índice:

SET STATISTICS TIME ON;
SELECT id, foo, bar, nki FROM my_table ORDER BY nki;
SET STATISTICS TIME OFF;

SQL Server Execution Times: CPU time = 32 ms, elapsed time = 106 ms

ingrese la descripción de la imagen aquí

Execution plan


Pregunta

  • ¿Por qué SQL Server usa el índice agrupado más un algoritmo de clasificación en lugar de usar un índice no agrupado, incluso si el tiempo de ejecución es un 38% más rápido en el último caso?

1
¿Querías dejar el ORDER BY en tu consulta de índice forzado?
Forrest

Respuestas:


9

¿Por qué SQL Server usa el índice agrupado más un algoritmo de clasificación en lugar de usar un índice no agrupado, incluso si el tiempo de ejecución es un 38% más rápido en el último caso?

Debido a que SQL Server utiliza un optimizador basado en costos basado en estadísticas, no en información de tiempo de ejecución.

Durante el proceso de estimación de costos para esta consulta, en realidad evalúa el plan de búsqueda, pero estima que tomará más esfuerzo. (Tenga en cuenta el "Costo estimado del subárbol" al pasar el mouse sobre SELECT en el plan de ejecución). Eso tampoco es necesariamente una mala suposición: en mi máquina de prueba, el plan de búsqueda toma 6 veces la CPU del tipo / escaneo.

Mire la respuesta de Rob Farley sobre por qué SQL Server podría costar más el plan de búsqueda.


9

Si comparara el número de lecturas requeridas en 100,000 búsquedas con lo que implica hacer una ordenación, podría tener una idea rápida de por qué el Optimizador de consultas calcula que CIX + Sort sería la mejor opción.

La ejecución de búsqueda termina siendo más rápida porque las páginas que se leen están en la memoria (incluso si borra el caché, tiene muchas filas por página, por lo que está leyendo las mismas páginas una y otra vez, pero con diferentes cantidades de fragmentación o diferente presión de memoria de otra actividad, este podría no ser el caso). Realmente no tomaría tanto tiempo hacer que CIX + Sort vaya más rápido, pero lo que está viendo es porque el costo de una lectura no tiene en cuenta el bajo costo relativo de ir a las mismas páginas repetidamente.


4

Decidí profundizar un poco en esta pregunta y descubrí algunos documentos interesantes que hablan sobre cómo y cuándo usar o tal vez mejor, no (forzar) el uso de un índice no agrupado.

Según lo sugerido por los comentarios de John Eisbrener , uno de los blogs más mencionados, incluso en otros blogs, es este interesante artículo de Kimberly L. Tripp:

pero no es el único, si estás interesado puedes echar un vistazo a estas páginas:

Como puede ver, todos se mueven alrededor del concepto del punto de inflexión .

Citado del artículo de KL Tripp

¿Cuál es el punto de inflexión?

Es el punto donde el número de filas devueltas " ya no es lo suficientemente selectivo ". SQL Server elige NO utilizar el índice no agrupado para buscar las filas de datos correspondientes y, en su lugar, realiza una exploración de tabla.

Cuando SQL Server usa un índice no agrupado en un montón, básicamente obtiene una lista de punteros a las páginas de la tabla base. Luego utiliza estos punteros para recuperar las filas con una serie de operaciones llamadas Búsquedas de ID de fila (RID). Esto significa que, al menos, usará tantas lecturas de página como el número de filas devueltas, y tal vez más. El proceso es algo similar con un índice agrupado como la tabla base, con el mismo resultado: más lecturas.

Pero, cuando se produce ese punto de inflexión?

Por supuesto, como la mayoría de las cosas en esta vida, depende ...

No, en serio, ocurre entre el 25% y el 33% del número de páginas en la tabla, dependiendo de cuántas filas por página. Pero hay más factores que debes considerar:

Citado del artículo de ITPRoToday

Otros factores que afectan el punto de inflexión Aunque el costo de las búsquedas de RID es el factor más importante que afecta el punto de inflexión, existen otros factores:

  • La E / S física es mucho más eficiente al escanear un índice agrupado. Los datos de índice agrupados se colocan secuencialmente en el disco en orden de índice. En consecuencia, hay muy poco recorrido lateral de la cabeza en el disco, lo que mejora el rendimiento de E / S.
  • Cuando el motor de la base de datos está escaneando un índice agrupado, sabe que existe una alta probabilidad de que las próximas páginas en la pista del disco aún contengan los datos que necesita. Por lo tanto, comienza a leer con antelación en fragmentos de 64 KB en lugar de las páginas normales de 8 KB. Esto también da como resultado una E / S más rápida.

Ahora si ejecuto mis consultas nuevamente usando estadísticas IO:

SET STATISTICS IO ON;
SELECT id, foo, bar, nki FROM my_table WHERE nki < 20000 ORDER BY nki ;
SET STATISTICS IO OFF;

Logical reads: 312

SET STATISTICS IO ON;
SELECT id, foo, bar, nki FROM my_table WITH(INDEX(IX_my_TABLE));
SET STATISTICS IO OFF;

Logical reads: 41293

La segunda consulta necesita más lecturas lógicas que la primera.

¿Debo evitar el índice no agrupado?

No, un índice agrupado puede ser útil, pero vale la pena tomarse un tiempo y hacer un esfuerzo adicional para analizar lo que está tratando de lograr con él.

Citado del artículo de KL Tripp

¿Entonces, qué debería hacer? Depende. Si conoce bien sus datos y realiza algunas pruebas exhaustivas, puede considerar usar una pista (hay algunas cosas inteligentes que puede hacer programáticamente en sps, intentaré dedicarle una publicación a esto pronto). Sin embargo, una opción mucho mejor (si es posible) es considerar cubrir (ese es realmente mi punto principal :). En mis consultas, la cobertura no es realista porque mis consultas quieren todas las columnas (el mal SELECT *) pero, si sus consultas son más limitadas Y son de alta prioridad, es mejor que tenga un índice de cobertura (en muchos casos) sobre una pista porque un índice que cubre una consulta, nunca consejos.

Esa es la respuesta al rompecabezas por ahora, pero definitivamente hay mucho más para sumergirse. El punto de inflexión puede ser algo muy bueno, y generalmente funciona bien. Pero, si descubre que puede forzar un índice y obtener un mejor rendimiento, es posible que desee investigar un poco y ver si es esto. Luego considere qué tan probable es una pista para ayudar y ahora sabe dónde puede concentrarse.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.