Tengo una consulta que demora aproximadamente 3 horas en ejecutarse en nuestro servidor, y no aprovecha el procesamiento paralelo. (alrededor de 1,15 millones de registros dbo.Deidentified
, 300 registros dbo.NamesMultiWord
). El servidor tiene acceso a 8 núcleos.
UPDATE dbo.Deidentified
WITH (TABLOCK)
SET IndexedXml = dbo.ReplaceMultiWord(IndexedXml),
DE461 = dbo.ReplaceMultiWord(DE461),
DE87 = dbo.ReplaceMultiWord(DE87),
DE15 = dbo.ReplaceMultiWord(DE15)
WHERE InProcess = 1;
y ReplaceMultiword
es un procedimiento definido como:
SELECT @body = REPLACE(@body,Names,Replacement)
FROM dbo.NamesMultiWord
ORDER BY [WordLength] DESC
RETURN @body --NVARCHAR(MAX)
¿Es el llamado a ReplaceMultiword
prevenir la formación de un plan paralelo? ¿Hay alguna manera de reescribir esto para permitir el paralelismo?
ReplaceMultiword
se ejecuta en orden descendente porque algunos de los reemplazos son versiones cortas de otros, y quiero que la coincidencia más larga tenga éxito.
Por ejemplo, puede haber 'George Washington University' y otra de 'Washington University'. Si el partido 'Washington University' fuera el primero, entonces 'George' se quedaría atrás.
Técnicamente puedo usar CLR, simplemente no estoy familiarizado con cómo hacerlo.
SELECT @var = REPLACE ... ORDER BY
se garantiza que la construcción funcione como espera. Ejemplo de elemento de conexión (consulte la respuesta de Microsoft). Por lo tanto, cambiar a SQLCLR tiene la ventaja adicional de garantizar resultados correctos, lo que siempre es bueno.Respuestas:
El UDF está evitando el paralelismo. También está causando ese carrete.
Puede usar CLR y una expresión regular compilada para hacer su búsqueda y reemplazar. No bloquea el paralelismo mientras los atributos requeridos estén presentes y probablemente sea significativamente más rápido que realizar 300
REPLACE
operaciones TSQL por llamada a la función.El código de ejemplo está abajo.
Esto depende de la existencia de un CLR UDF como se muestra a continuación (esto
DataAccessKind.None
debería significar que el carrete desaparece y que está ahí para la protección de Halloween y no es necesario ya que esto no accede a la tabla de destino).fuente
where
cláusula usando una prueba de coincidencia con la expresión regular, ya que la mayoría de las escrituras son innecesarias: la densidad de 'hits' debería ser baja, pero mis habilidades de C # (soy un chico de C ++) no lo hicieron llévame allí. Estaba pensando en las líneas de un procedimientopublic static SqlBoolean CanReplaceMultiWord(SqlString inputString, SqlXml replacementSpec)
que regresaríareturn Regex.IsMatch(inputString.ToString());
pero recibo errores en esa declaración de retorno, como `System.Text.RegularExpressions.Regex es un tipo pero se usa como una variable.En pocas palabras : al agregar criterios a la
WHERE
cláusula y dividir la consulta en cuatro consultas separadas, una para cada campo permitió que el servidor SQL proporcionara un plan paralelo e hizo que la consulta se ejecutara 4 veces más rápido que antes sin la prueba adicional en laWHERE
cláusula. Dividir las consultas en cuatro sin la prueba no hizo eso. Tampoco agregar la prueba sin dividir las consultas. La optimización de la prueba redujo el tiempo total de ejecución a 3 minutos (desde las 3 horas originales).Mi UDF original tardó 3 horas y 16 minutos en procesar 1.174.731 filas, con 1.216 GB de datos de nvarchar probados. Utilizando el CLR proporcionado por Martin Smith en su respuesta, el plan de ejecución aún no era paralelo y la tarea tomó 3 horas y 5 minutos.
Después de leer ese
WHERE
criterio podría ayudar a empujar a unUPDATE
paralelo, hice lo siguiente. Agregué una función al módulo CLR para ver si el campo tenía una coincidencia con la expresión regular:y, en
internal class ReplaceSpecification
, agregué el código para ejecutar la prueba contra la expresión regularSi todos los campos se prueban en una sola declaración, el servidor SQL no paraleliza el trabajo
Tiempo para ejecutar más de 4 1/2 horas y aún en ejecución. Plan de ejecución:
Sin embargo, si los campos se separan en declaraciones separadas, se usa un plan de trabajo paralelo y mi uso de CPU pasa del 12% con los planes en serie al 100% con los planes paralelos (8 núcleos).
Tiempo para ejecutar 46 minutos. Las estadísticas de fila mostraron que aproximadamente el 0.5% de los registros tenían al menos una coincidencia de expresiones regulares. Plan de ejecución:
Ahora, el lastre principal en el tiempo era la
WHERE
cláusula. Luego reemplacé la prueba de expresiones regulares en laWHERE
cláusula con el algoritmo Aho-Corasick implementado como un CLR. Esto redujo el tiempo total a 3 minutos y 6 segundos.Esto requirió los siguientes cambios. Cargue el ensamblaje y las funciones para el algoritmo Aho-Corasick. Cambiar la
WHERE
cláusula aY agregue lo siguiente antes del primero
UPDATE
fuente