Tengo un caso típico en el que la detección de parámetros hace que un plan de ejecución "malo" aterrice en la memoria caché del plan, lo que hace que las ejecuciones posteriores de mi procedimiento almacenado sean muy lentas. Puedo "resolver" este problema con variables locales OPTIMIZE FOR ... UNKNOWN
, y OPTION(RECOMPILE)
. Sin embargo, también puedo sumergirme en la consulta e intentar optimizarla.
Estoy tratando de determinar si debería : dado el tiempo limitado para solucionar problemas, me gustaría saber el costo de no hacerlo. Tal como lo veo, si me quedo con OPTION(RECOMPILE)
, el efecto neto es que se recrea un plan de consulta cada vez que se ejecuta la consulta. Entonces, creo que necesito saber:
¿Cómo saber cuál es el costo de crear un plan de consulta?
Para responder a mi propia pregunta, busqué en Google (por ejemplo, con esta consulta ) y revisé la documentación de las columnas para el dm_exec_query_stats
DMV . También he inspeccionado la ventana de salida en SSMS para "Plan de consulta real" para encontrar esta información. Por último, he buscado DBA.SE . Ninguno de esos condujo a una respuesta.
¿Alguien me puede decir? ¿Es posible encontrar o medir el tiempo necesario para la creación del plan?
fuente
Respuestas:
Puede ver las propiedades del nodo raíz en el plan de consulta, por ejemplo:
(captura de pantalla del Sentry One Plan Explorer gratuito )
Esta información también está disponible consultando el caché del plan, por ejemplo, utilizando una consulta basada en las siguientes relaciones:
Para un tratamiento completo de las opciones que tiene para manejar este tipo de consultas, vea el artículo recientemente actualizado de Erland Sommarskog .
fuente
Suponiendo que el "costo" es en términos de tiempo (aunque no estoy seguro de qué más podría ser en términos de ;-), al menos, debería ser capaz de tener una idea de ello haciendo algo como lo siguiente:
El primer elemento informado en la pestaña "Mensajes" debería ser:
Ejecutaría esto al menos 10 veces y promediaría los milisegundos "CPU" y "transcurrido".
Lo ideal sería ejecutar esto en Producción para poder obtener una estimación de tiempo real, pero rara vez se permite a las personas borrar el caché del plan en Producción. Afortunadamente, a partir de SQL Server 2008, fue posible borrar un plan específico del caché. En cuyo caso puede hacer lo siguiente:
Sin embargo, dependiendo de la variabilidad de los valores que se pasan para los parámetros que causan el plan en caché "incorrecto", hay otro método para considerar que es un punto medio entre
OPTION(RECOMPILE)
yOPTION(OPTIMIZE FOR UNKNOWN)
: SQL dinámico. Sí lo dije. E incluso me refiero a SQL dinámico no parametrizado. Aquí es por qué.Claramente tiene datos que tienen una distribución desigual, al menos en términos de uno o más valores de parámetros de entrada. Las desventajas de las opciones mencionadas son:
OPTION(RECOMPILE)
generará un plan para cada ejecución y nunca podrá beneficiarse de ninguna reutilización del plan, incluso si los valores de los parámetros pasados nuevamente son idénticos a las ejecuciones anteriores. Para los procs que se llaman con frecuencia, una vez cada pocos segundos o con más frecuencia, esto lo salvará de la horrible situación ocasional, pero aún lo dejará en una situación no tan buena.OPTION(OPTIMIZE FOR (@Param = value))
generará un plan basado en ese valor en particular, que podría ayudar en varios casos pero aún así dejarlo abierto al problema actual.OPTION(OPTIMIZE FOR UNKNOWN)
generará un plan basado en lo que equivale a una distribución promedio, lo que ayudará a algunas consultas pero perjudicará a otras. Esto debería ser lo mismo que la opción de usar variables locales.Sin embargo, el SQL dinámico, cuando se hace correctamente , permitirá que los diversos valores que se pasan tengan sus propios planes de consulta separados que son ideales (bueno, tanto como lo serán). El costo principal aquí es que a medida que aumenta la variedad de valores que se pasan, aumenta el número de planes de ejecución en la memoria caché y ocupan memoria. Los costos menores son:
necesidad de validar parámetros de cadena para evitar inyecciones SQL
posiblemente necesite configurar un Certificado y un Usuario basado en Certificado para mantener una abstracción de seguridad ideal ya que Dynamic SQL requiere permisos de tabla directos.
Entonces, así es como manejé esta situación cuando tuve procesos que se llamaron más de una vez por segundo y golpearon varias tablas, cada una con millones de filas. Lo intenté,
OPTION(RECOMPILE)
pero esto resultó ser demasiado perjudicial para el proceso en el 99% de los casos que no tenían el problema de detección de parámetros / mal plan de caché. Y tenga en cuenta que uno de estos procesos tenía aproximadamente 15 consultas y solo 3 - 5 de ellos se convirtieron a SQL dinámico como se describe aquí; El SQL dinámico no se usó a menos que fuera necesario para una consulta particular.Si hay múltiples parámetros de entrada para el procedimiento almacenado, descubra cuáles se usan con columnas que tienen distribuciones de datos muy dispares (y, por lo tanto, causan este problema) y cuáles se usan con columnas que tienen distribuciones más uniformes (y no deberían causando este problema).
Cree la cadena de SQL dinámico utilizando parámetros para los parámetros de entrada de proceso que están asociados con columnas distribuidas uniformemente. Esta parametrización ayuda a reducir el aumento resultante en los planes de ejecución en la memoria caché relacionada con esta consulta.
Para los parámetros restantes que están asociados con distribuciones muy variadas, se deben concatenar en el SQL dinámico como valores literales. Dado que una consulta única está determinada por cualquier cambio en el texto de la consulta, tener
WHERE StatusID = 1
es una consulta diferente y, por lo tanto, un plan de consulta diferente que tenerWHERE StatusID = 2
.Si alguno de los parámetros de entrada de proceso que se van a concatenar en el texto de la consulta son cadenas, entonces deben validarse para proteger contra la inyección de SQL (aunque es menos probable que esto ocurra si las cadenas que se pasan son generadas por aplicación y no un usuario, pero aún así). Al menos haga esto
REPLACE(@Param, '''', '''''')
para asegurarse de que las comillas simples se conviertan en comillas simples escapadas.Si es necesario, cree un Certificado que se utilizará para crear un Usuario y firme el procedimiento almacenado de manera tal que los permisos directos de la tabla se otorguen solo al nuevo Usuario basado en el Certificado y no
[public]
a los Usuarios que de otro modo no deberían tener dichos permisos. .Proceso de ejemplo:
fuente
OPTION
en mi consulta), y no me lastimar demasiado mucho ya que este procedimiento almacenado es bien aprovechada en las pruebas de integración. - En cualquier caso: ¡gracias por tus ideas!