Pregunta

La situación

Tengo algunos problemas con mi plan de ejecución de consultas para una consulta de tamaño mediano sobre una gran cantidad de datos en Oracle 11.2.0.2.0.Para acelerar las cosas, introduje un filtro de rango que hace más o menos algo como esto:

PROCEDURE DO_STUFF(
    org_from VARCHAR2 := NULL,
    org_to   VARCHAR2 := NULL)

  -- [...]
  JOIN organisations org
    ON (cust.org_id = org.id
   AND ((org_from IS NULL) OR (org_from <= org.no))
   AND ((org_to   IS NULL) OR (org_to   >= org.no)))
  -- [...]

Como puedes ver, quiero restringir el JOIN de organisations utilizando un rango opcional de números de organización.El código del cliente puede llamar DO_STUFF con (se supone que es rápido) o sin (muy lento) la restricción.

El problema

El problema es que PL/SQL creará variables de enlace para lo anterior org_from y org_to parámetros, que es lo que esperaría en la mayoría de los casos:

  -- [...]
  JOIN organisations org
    ON (cust.org_id = org.id
   AND ((:B1 IS NULL) OR (:B1 <= org.no))
   AND ((:B2 IS NULL) OR (:B2 >= org.no)))
  -- [...]

La solución alternativa

Solo que en este caso, medí que el plan de ejecución de la consulta era mucho mejor cuando simplemente alineaba los valores, es decir,cuando la consulta ejecutada por Oracle es en realidad algo así como

  -- [...]
  JOIN organisations org
    ON (cust.org_id = org.id
   AND ((10 IS NULL) OR (10 <= org.no))
   AND ((20 IS NULL) OR (20 >= org.no)))
  -- [...]

Por "mucho", me refiero a entre 5 y 10 veces más rápido.Tenga en cuenta que la consulta se ejecuta muy raramente, es decir.una vez al mes.Entonces no necesito almacenar en caché el plan de ejecución.

Mis preguntas

  • ¿Cómo puedo insertar valores en PL/SQL?Se acerca de EJECUTAR INMEDIATAMENTE, pero preferiría que PL/SQL compilara mi consulta y no realizara una concatenación de cadenas.

  • ¿Acabo de medir algo que sucedió por coincidencia o puedo suponer que incluir variables es mejor (en este caso)?La razón por la que pregunto es porque creo que las variables vinculantes obligan a Oracle a idear una general plan de ejecución, mientras que los valores en línea permitirían analizar estadísticas de índice y columna muy específicas.Así que puedo imaginar que esto no es sólo una coincidencia.

  • ¿Me estoy perdiendo de algo?¿Quizás haya una forma completamente diferente de lograr una mejora en el plan de ejecución de consultas, además de la incorporación de variables (tenga en cuenta que también he probado bastantes sugerencias, pero no soy un experto en ese campo)?

¿Fue útil?

Solución

En uno de tus comentarios dijiste:

"También verifiqué varios valores de vinculación. Con bind variables obtengo algo FULL ESCÁNERES DE TABLA, mientras que con codificación valores, el plan se ve mucho mejor ".

Hay dos caminos. Si pasa NULL para los parámetros, entonces está seleccionando todos los registros. En esas circunstancias, una exploración de tabla completa es la forma más eficaz de recuperar datos. Si pasa valores, las lecturas indexadas pueden ser más eficientes, porque solo está seleccionando un pequeño subconjunto de la información.

Cuando formula la consulta utilizando variables de vinculación, el optimizador debe tomar una decisión: ¿debería suponer que la mayoría de las veces pasará valores o que pasará nulos? Difícil. Así que mírelo de otra manera: ¿es más ineficiente hacer un escaneo completo de la tabla cuando solo necesita seleccionar un subconjunto de registros, o hacer lecturas indexadas cuando necesita seleccionar todos los registros?

Parece que el optimizador se ha inclinado a escanear tablas completas como la operación menos ineficiente para cubrir todas las eventualidades.

Mientras que cuando codifica los valores, el Optimizador sabe inmediatamente que 10 IS NULL se evalúa como FALSO, por lo que puede sopesar los méritos de usar lecturas indexadas para encontrar los registros del subconjunto deseado.


Entonces, ¿qué hacer? Como dices, esta consulta solo se ejecuta una vez al mes, creo que solo se requeriría un pequeño cambio en los procesos comerciales para tener consultas separadas: una para todas las organizaciones y otra para un subconjunto de organizaciones.


"Por cierto, eliminando la cláusula: R1 IS NULL no cambia el plan de ejecución mucho, que me deja con el otro lado de la condición OR,: R1 <= org.no donde NULL no tendría sentido de todos modos, ya que org.no NO es NULO "

Bien, entonces la cosa es que tienes un par de variables de vinculación que especifican un rango . Dependiendo de la distribución de valores, diferentes rangos pueden adaptarse a diferentes planes de ejecución. Es decir, este rango (probablemente) se adaptaría a un escaneo de rango indexado ...

WHERE org.id BETWEEN 10 AND 11

... mientras que es probable que esto se ajuste más a un escaneo de tabla completo ...

WHERE org.id BETWEEN 10 AND 1199999

Ahí es donde entra en juego Bind Variable Peeking.

(dependiendo de la distribución de valores, por supuesto).

Otros consejos

Dado que los planes de consulta son consistentemente diferentes, eso implica que las estimaciones de cardinalidad del optimizador están equivocadas por algún motivo.¿Puede confirmar a partir de los planes de consulta que el optimizador espera que las condiciones no sean lo suficientemente selectivas cuando se utilizan variables de vinculación?Como estás usando 11.2, Oracle debería usar compartir cursor adaptativo por lo que no debería ser un problema de visualización de variables de vinculación (suponiendo que esté llamando a la versión con variables de vinculación muchas veces con diferentes NO valores en sus pruebas.

¿Son realmente correctas las estimaciones de cardinalidad del buen plan?Sé que dijiste que las estadísticas sobre el NO La columna es precisa, pero sospecharía de un histograma perdido que puede no ser actualizado mediante el proceso habitual de recopilación de estadísticas, por ejemplo.

Siempre puede usar una sugerencia en la consulta para forzar el uso de un índice en particular (aunque use un esquema almacenado u estabilidad del plan optimizador sería preferible desde una perspectiva de mantenimiento a largo plazo).Cualquiera de esas opciones sería preferible a recurrir a SQL dinámico.

Sin embargo, una prueba adicional a intentar sería reemplazar la sintaxis de unión de SQL 99 con la sintaxis antigua de Oracle, es decir.

SELECT <<something>>
  FROM <<some other table>> cust,
       organization org
 WHERE cust.org_id = org.id
   AND (    ((org_from IS NULL) OR (org_from <= org.no)) 
        AND ((org_to   IS NULL) OR (org_to   >= org.no)))

Obviamente, eso no debería cambiar nada, pero ha habido problemas con el analizador con la sintaxis de SQL 99, por lo que es algo que hay que comprobar.

Huele a Bind Peeking , pero solo estoy en Oracle 10, por lo que no puedo afirmar que existe el mismo problema en 11.

Esto se parece mucho a la necesidad de Adaptive Cursor Sharing, combinado con la estabilidad de SQLPlan. Creo que lo que está sucediendo es que capture_sql_plan_baselines parameter is true. Y lo mismo para use_sql_plan_baselines. Si esto es cierto, sucede lo siguiente:

  1. La primera vez que se analiza una consulta, se obtiene un plan nuevo.
  2. La segunda vez, este plan se almacena en sql_plan_baselines como un plan aceptado.
  3. Todas las ejecuciones siguientes de esta consulta utilizan este plan, independientemente de las variables de vinculación.

Si Adaptive Cursor Sharing ya está activo, el optimizador generará un plan nuevo / mejor, lo almacenará en sql_plan_baselines pero no podrá usarlo, hasta que alguien acepte este nuevo plan como un plan alternativo aceptable. Verifique dba_sql_plan_baselines y vea si su consulta tiene entradas con accepted = 'NO' and verified = null Puede utilizar dbms_spm.evolve para desarrollar el nuevo plan y hacer que se acepte automáticamente si el rendimiento del plan es al menos 1,5 veces mejor que sin el nuevo plan.

Espero que esto ayude.

Agregué esto como comentario, pero también lo ofreceré aquí.Espero que esto no sea demasiado simplista y, al ver las respuestas detalladas, puedo estar malinterpretando el problema exacto, pero de todos modos ...

Parece que la tabla de su organización tiene la columna no (org.no) que se define como un número.En su ejemplo codificado, usa números para hacer las comparaciones.

JOIN organisations org
    ON (cust.org_id = org.id
   AND ((10 IS NULL) OR (10 <= org.no))
   AND ((20 IS NULL) OR (20 >= org.no)))

En su procedimiento, está pasando varchar2 :

PROCEDURE DO_STUFF(
    org_from VARCHAR2 := NULL,
    org_to   VARCHAR2 := NULL)

Entonces, para comparar varchar2 con el número, Oracle tendrá que hacer las conversiones , por lo que esto puede causar los escaneos completos.

Solución: cambie el proceso para pasar números

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top