¿Hay algún beneficio plausible para el iterador de spool en el primer plan?
Esto depende de lo que considere "plausible", pero la respuesta según el modelo de costos es sí. Por supuesto, esto es cierto, porque el optimizador siempre elige el plan más barato que encuentra.
La verdadera pregunta es por qué el modelo de costos considera que el plan con el carrete es mucho más barato que el plan sin él. Considere los planes estimados creados para una tabla nueva (de su secuencia de comandos) antes de que se agreguen filas a la tienda delta:
DELETE Fact.RecordedMetricsDetail
WHERE MeasurementTime < DATEADD(day,-1,GETUTCDATE())
OPTION (RECOMPILE);
El costo estimado de este plan es de 771,734 unidades enormes :
El costo está casi todo asociado con la Eliminación de índice agrupado, porque se espera que las eliminaciones den como resultado una gran cantidad de E / S aleatorias. Esta es solo la lógica genérica que se aplica a todas las modificaciones de datos. Por ejemplo, se supone que un conjunto desordenado de modificaciones a un índice b-tree da como resultado E / S en gran medida aleatorias, con un alto costo de E / S asociado.
Los planes de cambio de datos pueden presentar una Clasificación para presentar filas en un orden que promoverá el acceso secuencial, exactamente por estos motivos de costo. El impacto se exacerba en este caso porque la tabla está particionada. Muy particionado, de hecho; su script crea 15,000 de ellos. Las actualizaciones aleatorias de una tabla muy particionada tienen un costo especialmente alto ya que el precio para cambiar particiones (conjuntos de filas) a mitad de flujo también tiene un alto costo.
El último factor importante a tener en cuenta es que la consulta de actualización simple anterior (donde 'actualizar' significa cualquier operación de cambio de datos, incluida una eliminación) califica para una optimización llamada "intercambio de conjuntos de filas", donde se utiliza el mismo conjunto de filas interno para el escaneo y actualizando la mesa. El plan de ejecución aún muestra dos operadores separados, pero no obstante, solo se utiliza un conjunto de filas.
Menciono esto porque poder aplicar esta optimización significa que el optimizador toma una ruta de código que simplemente no considera los beneficios potenciales de la clasificación explícita para reducir el costo de E / S aleatorias. Cuando la tabla es un árbol b, esto tiene sentido, porque la estructura está inherentemente ordenada, por lo que compartir el conjunto de filas proporciona automáticamente todos los beneficios potenciales.
La consecuencia importante es que la lógica de costeo para el operador de actualización no considera este beneficio de ordenación (promoción de E / S secuenciales u otras optimizaciones) donde el objeto subyacente es el almacén de columnas. Esto se debe a que las modificaciones del almacén de columnas no se realizan en el lugar; usan una tienda delta. Por lo tanto, el modelo de costos refleja una diferencia entre las actualizaciones de conjuntos de filas compartidas en b-trees versus los almacenes de columnas.
Sin embargo, en el caso especial de un almacén de columnas particionado (¡muy!), Podría haber un beneficio en el orden conservado, ya que realizar todas las actualizaciones a una partición antes de pasar a la siguiente podría ser ventajoso desde el punto de vista de E / S .
Aquí se reutiliza la lógica de costo estándar para los almacenes de columnas, por lo que un plan que conserva el orden de la partición (aunque no el orden dentro de cada partición) tiene un costo menor. Podemos ver esto en la consulta de prueba usando el indicador de rastreo no documentado 2332 para requerir una entrada ordenada al operador de actualización. Esto establece la DMLRequestSort
propiedad en verdadero en la actualización y obliga al optimizador a producir un plan que proporcione todas las filas para una partición antes de pasar a la siguiente:
DELETE Fact.RecordedMetricsDetail
WHERE MeasurementTime < DATEADD(day,-1,GETUTCDATE())
OPTION (RECOMPILE, QUERYTRACEON 2332);
El costo estimado para este plan es mucho más bajo, con 52.5174 unidades:
Esta reducción en el costo se debe al menor costo estimado de E / S en la actualización. El Spool introducido no realiza ninguna función útil, excepto que puede garantizar la salida en orden de partición, como lo requiere la actualización con DMLRequestSort = true
(el escaneo en serie de un índice de almacén de columnas no puede proporcionar esta garantía). El costo del carrete en sí mismo se considera relativamente bajo, especialmente en comparación con la reducción (probablemente poco realista) del costo en la actualización.
La decisión sobre si se requiere una entrada ordenada al operador de actualización se toma muy pronto en la optimización de consultas. Las heurísticas utilizadas en esta decisión nunca se han documentado, pero se pueden determinar mediante prueba y error. Parece que el tamaño de cualquier tienda delta es una entrada a esta decisión. Una vez realizada, la elección es permanente para la compilación de consultas. Ninguna USE PLAN
pista tendrá éxito: el objetivo del plan ha ordenado la entrada a la actualización o no.
Hay otra forma de obtener un plan de bajo costo para esta consulta sin limitar artificialmente la estimación de cardinalidad. Una estimación suficientemente baja para evitar el Spool probablemente dará como resultado que DMLRequestSort sea falso, lo que resultará en un costo de plan estimado muy alto debido a la E / S aleatoria esperada. Una alternativa es utilizar el indicador de traza 8649 (plan paralelo) junto con 2332 (DMLRequestSort = true):
DELETE Fact.RecordedMetricsDetail
WHERE MeasurementTime < DATEADD(day,-1,GETUTCDATE())
OPTION (RECOMPILE, QUERYTRACEON 2332, QUERYTRACEON 8649);
Esto da como resultado un plan que utiliza la exploración paralela en modo por lotes por partición y un intercambio de recopilación de secuencias de preservación de orden (fusión):
Dependiendo de la efectividad en tiempo de ejecución del pedido de particiones en su hardware, esto puede funcionar mejor de los tres. Dicho esto, las grandes modificaciones no son una gran idea en el almacén de columnas, por lo que la idea de cambio de partición es casi seguramente mejor. Si puede hacer frente a los largos tiempos de compilación y las elecciones de planes extravagantes que a menudo se ven con objetos particionados, especialmente cuando el número de particiones es grande.
La combinación de muchas características relativamente nuevas, especialmente cerca de sus límites, es una excelente manera de obtener planes de ejecución deficientes. La profundidad del soporte del optimizador tiende a mejorar con el tiempo, pero el uso de 15,000 particiones del almacén de columnas probablemente siempre significará que vives en tiempos interesantes.
OPTION (QUERYRULEOFF EnforceHPandAccCard)
el carrete desaparece. Supongo que HP podría ser "Protección de Halloween". Sin embargo, tratar de usar ese plan con unaUSE PLAN
pista falla (al igual que intentar usar el plan de laOPTIMIZE FOR
solución alternativa también)