Trabajar con datos XML suele ser relativamente fácil y satisfactorio hasta que los documentos tienen un determinado tamaño. La mayor parte de las recomendaciones que hay son para datos XML de tamaño pequeño o medio, pero ¿qué pasa cuando los documentos pasan del megabyte?
Origen de la serie de artículos
Lo normal en SQL Server es trabajar con documentos XML pequeños (hasta alrededor del KB) o medianos (hasta centenas de KB). En estos casos el hecho de que SQL Server tenga que procesar dichos documentos y que la búsqueda no sea muy rápida no suele ser un problema con los servidores actuales. Pero las ineficiencias se acentúan en forma exponencial y pueden llegar a llenar la tempdb o a no terminar nunca cuando uno solo de los documentos tiene un tamaño suficiente.
Esto ocurrió con unos planes en caché donde determinado programa empaquetó más de diez y seis mil consultas en un único lote, cada una de las cuales era compleja y además diferente por lo que cada plan era no trivial y no reutilizable. Esto se pudo ver por el tamaño individual de cada plan y por el hash asignado a cada consulta. No es un caso normal y las consultas sobre planes en caché suelen funcionar bastante bien. Pero nos va a servir para analizar y probar las técnicas de optimización que podemos usar con documentos grandes.
Por cierto, veremos que tanto la información que nos aporta SQL Server como la documentación, especialmente de Michael Rys, que nos ofrece Microsoft es bastante útil aunque no sea específica de documentos XML grandes.
Documentos grandes
La consulta sobre la que vamos a hacer el estudio es una evolución de otra estándar para analizar planes en caché. La idea era un ejercicio para incluir tanto las instrucciones simples –StmtSimple– como las instrucciones condicionales –StmtCond– de los planes XML. Probada en distintos servidores funcionaba sin problemas, pero en uno de ellos llegó a llenar tempdb y no terminaba nunca. Inclusive, si ceñíamos la respuesta a un único plan, pasaba lo mimo. Este es el origen:
Por lo tanto, antes de empezar a trabajar con datos XML, tenemos que ver el tamaño porque a partir de un determinado punto se puede volver inviable el trabajar con los documentos. En este caso, si hubiese analizado la primera tabla –sys.dm_exec_cached_plans– hubiese visto que determinados planes tenían un size_in_bytes de decenas o centenas de MB. En este caso, hubiese monitorizado la tempdb, el uso de procesador y el acceso a disco para ver si mi búsqueda interferiría con el resto de procesos. Que no interfirió, pero tampoco había visos de terminar.
Documentos no XML
El primer problema que nos podemos encontrar es que el documento no sea XML y SQL Server tenga que hacer shredding al documento. Esto suele ser transparente y muy rápido, por lo que no lo solemos tener en cuenta. Y nos podemos decir que, de una manera o de otra, SQL Server tiene que hacerlo, por lo que ¿por qué nos tenemos que preocupar?
Pues porque SQL Server no puede saber, cuando compila el plan, el tamaño del documento XML. Podríamos pensar que en este caso sí lo sabe, hay un campo que nos lo dice, pero el optimador de consultas no puede tenerlo en cuenta, pues ¿de qué manera se lo decimos? Por lo tanto hace un plan general que es bueno para la gran mayoría de los casos. Pero no en el que estamos estudiando.
¿Por qué? Pues el optimador no entiende que sea necesario hacer un spool del documento en formato XML y lo hace cada vez que lo necesita. Es una de las razones por las que se llena la tempdb.
Por cierto, aunque el problema surgió en un servidor con SQL Server 2008 R2, los planes para esta consulta son los mismos en 2012 y 2014. No los he podido probar aún en 2016.
Plan de consulta
Más adelante veremos por qué, pero por ahora observemos que el plan de esta simple consulta es sumamente complejo:
El plan no se va a ver, porque es tan grande que no cabría excepto en una pantalla enorme. Para poder analizarlo habría que escribir la consulta y pedir el plan.
El problema fundamental de este plan, aparte de lo complejo, es que no se guardan los datos XML en ningún momento. No vamos a ver ningún operador tipo spool en ninguna parte.
Obtener el plan
Lo primero que vemos arriba es que se usan unas funciones internas con valores de tabla. La primera es SYSDMEXECCACHEDPLANS de donde sólo nos interesa plan_handle, y la segunda FNGETQUERYPLAN a quien pasamos el identificador para que nos devuelva el plan:
Este plan que devuelve la función no se cachea y aparentemente sólo debería tener una referencia, pero no es así. Tenemos 4 operadores «XML Reader with XPath filter» que relacionan la función que nos devuelve el plan. Esto tampoco debería ser problema aunque haya que traer y analizar cuatro veces la zona de memoria donde están los planes:
No es problema hasta que el documento es lo suficientemente grande como para tardar demasiado. Además, al haber metido en la función nodes una secuencia con dos elementos, se duplica el trabajo.
Por lo tanto, cuando trabajamos con documentos grandes, la primera optimación que tenemos que hacer es convertir en XML una sola vez el documento externo, venga de un archivo, de una columna LOB o de un objeto en memoria como es este ejemplo.
Es decir, en este caso sí nos merece la pena convertir el documento en XML guardándolo en una variable que procesamos mediante un cursor o guardándolo en una tabla temporal. El esfuerzo adicional de codificación no es que merezca la pena, es que de otra manera se vuelve imposible trabajar con esta clase de documentos. Es uno de los casos en los que tenemos que ayudar al optimador de consultas.
CONCLUSIÓN
En este primer artículo hemos visto que cuando se trabaja con documentos XML grandes los dos primeros errores son no tener en cuenta su tamaño y no tener en cuenta que hay que convertir una única vez el documento en XML.
Sin embargo, hay más razones para esto, como veremos en el siguiente artículo.
Más Información
Desde Danysoft y si rellenas este formulario, te ayudaremos a facilitarte la información que necesitas.
Dejar un comentario
¿Quieres unirte a la conversación?¡Siéntete libre de contribuir!