Tengo la siguiente tabla con 7,5 millones de registros:
CREATE TABLE [dbo].[TestTable](
[Id] [int] IDENTITY(1,1) NOT NULL,
[TestCol] [nvarchar](50) NOT NULL,
[TestCol2] [nvarchar](50) NOT NULL,
[TestCol3] [nvarchar](50) NOT NULL,
[Anonymised] [tinyint] NOT NULL,
[Date] [datetime] NOT NULL,
CONSTRAINT [PK_TestTable] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Noto que cuando hay un índice no agrupado en el campo de fecha:
CREATE NONCLUSTERED INDEX IX_TestTable_Date ON [dbo].[TestTable] ([Date])
-y ejecuto la siguiente consulta:
UPDATE TestTable
SET TestCol='*GDPR*', TestCol2='*GDPR*', TestCol3='*GDPR*', Anonymised=1
WHERE [Date] <= '25 August 2016'
-los datos que devuelve la operación de acceso al índice se ordenan para que coincidan con el orden de las claves de PK / CX, lo que reduce el rendimiento.
Me sorprendió descubrir que eliminar el índice del campo de fecha en realidad mejora el rendimiento de la consulta en aproximadamente un 30% porque ya no realiza la clasificación:
Mi teoría, y esto puede ser obvio para los más experimentados entre ustedes, es que ha descubierto que la columna de fecha está ordenada implícitamente exactamente igual que la clave principal / índice agrupado.
Entonces mi pregunta es: ¿es posible aprovechar este hecho para mejorar el rendimiento de mi consulta?
fuente
[Date]
pero enDESC
orden? Simplemente curioso ya que el predicado es<=
. Además, si el índice activadoDate
(por defecto, elACS
orden) ayuda a otras consultas, entonces ¿tal vez pueda intentar agregar una sugerencia de tabla a la ACTUALIZACIÓN para forzarlo a usar la PK? O, tal vez divida esto en dos partes: cree una tabla temporal, complete con[Id]
base en[Date] <= '25 August 2016'
, y luego elimine laWHERE
de la ACTUALIZACIÓN y agregueFROM dbo.TestTable tt INNER JOIN #tmp ids ON ids.[Id] = tt.[Id]
. Después de todo, es una ACTUALIZACIÓN y necesita encontrar las filas reales, índice o no.Respuestas:
Me burlé de los datos de prueba que reproducen principalmente su problema:
Estadísticas para la consulta que usa el índice no agrupado:
Estadísticas para la consulta que usa el índice agrupado:
Llegando a su pregunta:
Si. Puede usar el índice no agrupado que ya tiene para encontrar eficientemente el
id
valor máximo que necesita actualizarse. Si guarda eso en una variable y lo filtra en contra, obtendrá un plan de consulta para la actualización que realiza el análisis de índice agrupado (sin la clasificación) que se detiene antes y, por lo tanto, hace menos IO. Aquí hay una implementación:Ejecutar estadísticas para la nueva consulta:
Además del plan de consulta:
Dicho todo esto, su deseo de hacer la consulta más rápida me sugiere que planea ejecutar la consulta más de una vez. En este momento, su consulta tiene un filtro abierto en la
date
columna. ¿Es realmente necesario anonimizar las filas más de una vez? ¿Puede evitar actualizar o escanear filas que ya estaban anonimizadas? Sin duda, debería ser más rápido actualizar un rango de fechas con fechas en ambos lados. También puede agregar laAnonymised
columna a su índice, pero ese índice deberá actualizarse durante suUPDATE
consulta. En resumen, evite procesar los mismos datos una y otra vez si puede.La consulta original que tiene con la ordenación es más lenta debido al trabajo realizado en el
Clustered Index Update
operador. La cantidad de tiempo dedicado a la búsqueda de índice y la clasificación es de solo 407 ms. Puedes ver esto en el plan real. El plan se ejecuta en modo de fila, por lo que el tiempo dedicado a la ordenación es el tiempo de ese operador junto con cada operador secundario:Eso deja al operador de clasificación con aproximadamente 1600 ms de tiempo. SQL Server necesita leer páginas del índice agrupado para realizar la actualización. Puede ver que el
Clustered Index Update
operador realiza 1205921 lecturas lógicas. Puede leer más sobre cómo ordenar las optimizaciones para DML y la captación previa optimizada en esta publicación de blog de Paul White .El otro plan de consulta que tiene (sin la clasificación) toma 683 ms para el escaneo de índice agrupado y aproximadamente 550 ms para el
Clustered Index Update
operador. El operador de actualización no hace ninguna E / S para esta consulta.La respuesta simple de por qué el plan con la clasificación es más lenta es que SQL Server realiza más lecturas lógicas en el índice agrupado para ese plan en comparación con el plan de exploración de índice agrupado. Incluso si todos los datos necesarios están en la memoria, todavía hay una sobrecarga y un costo para hacer esas lecturas lógicas. Es mucho más difícil obtener una mejor respuesta, ya que hasta donde yo sé, los planes no le darán más detalles. Es posible usar PerfView u otra herramienta basada en el rastreo de ETW para comparar pilas de llamadas entre las consultas:
A la izquierda está la consulta que realiza el escaneo de índice agrupado y a la derecha está la consulta que realiza el ordenamiento. Marqué las pilas de llamadas en azul o rojo que solo aparecen en una consulta. No es sorprendente que las diferentes pilas de llamadas con un alto número de ciclos de CPU muestreados para la consulta de clasificación parezcan tener que ver con las lecturas lógicas necesarias para realizar la actualización en el índice agrupado. Además, existen diferencias en el número de ciclos muestreados entre las consultas para la misma operación. Por ejemplo, la consulta con la clasificación gasta 31 ciclos en la adquisición de bloqueos, mientras que la consulta con el escaneo solo gasta 9 ciclos en la adquisición de bloqueos.
Sospecho que SQL Server está eligiendo el plan más lento debido a una limitación de costos del operador del plan de consulta. Quizás parte de la diferencia en el tiempo de ejecución se deba al hardware o su edición de SQL Server. En cualquier caso, SQL Server no puede darse cuenta de que la columna de fecha está ordenada implícitamente exactamente igual que el índice agrupado. Los datos se devuelven del análisis de índice agrupado en orden de clave agrupado, por lo que no es necesario realizar una ordenación en un intento de optimizar IO al realizar la actualización del índice agrupado.
fuente