Guía de ajuste de SQL Nota 20: Mejora del rendimiento en el mundo real mediante el uso compartido de cursores

Este artículo es una nota para el Capítulo 20 "Mejora del rendimiento en el mundo real mediante el uso compartido de cursores" de la Guía de ajuste de SQL .

El uso compartido del cursor puede mejorar el rendimiento de la aplicación de la base de datos en órdenes de magnitud.

conceptos básicos importantes

  • objetivo del optimizador
    La priorización del uso de recursos por parte del optimizador. Con el parámetro de inicialización OPTIMIZER_MODE, puede establecer el mejor rendimiento o el mejor tiempo de respuesta del objetivo del optimizador.

  • variable de vinculación
    Un marcador de posición en una instrucción SQL que se debe reemplazar con un valor válido o una dirección de valor para que la instrucción se ejecute correctamente. Mediante el uso de variables de vinculación, puede escribir una instrucción SQL que acepte entradas o parámetros en tiempo de ejecución.

20.1 Descripción general del uso compartido del cursor

Oracle Database puede compartir cursores, que son punteros a áreas SQL privadas en el grupo compartido.

20.1.1 Acerca de los cursores

El área privada de SQL contiene información sobre sentencias de SQL analizadas y otra información específica de la sesión para su procesamiento.

Cuando un proceso de servidor ejecuta código SQL o PL/SQL, el proceso utiliza áreas privadas de SQL para almacenar valores de variables de vinculación, información de estado de ejecución de consultas y áreas de trabajo de ejecución de consultas . Las áreas privadas de SQL para la ejecución de cada declaración no se comparten y pueden contener diferentes valores y datos.

Un cursor es el nombre o identificador de un área SQL privada específica. Los cursores contienen información de estado específica de la sesión, como valores de variables de enlace y conjuntos de resultados.

Como se muestra en el siguiente diagrama, puede pensar en un cursor como un puntero en el lado del cliente y un estado en el lado del servidor. Debido a que los cursores están estrechamente relacionados con las áreas privadas de SQL, estos términos a veces se usan indistintamente.
inserte la descripción de la imagen aquí

20.1.1.1 Áreas SQL privadas y compartidas

Los cursores en áreas SQL privadas apuntan a áreas SQL compartidas en la memoria caché de la biblioteca .

A diferencia de las áreas SQL privadas, que contienen información sobre el estado de la sesión, las áreas SQL compartidas contienen el árbol de análisis y el plan de ejecución de una declaración . Por ejemplo, SELECT * FROM employeesla ejecución de tiene un plan y un árbol de análisis almacenados en un área SQL compartida. Como otro ejemplo, SELECT * FROM employeesla ejecución de , que difiere tanto en la sintaxis como en la semántica, tiene un plan y un árbol de análisis almacenados en un área SQL compartida separada.

Múltiples áreas privadas de SQL en la misma sesión o en diferentes sesiones pueden hacer referencia a una sola área de SQL compartida, un fenómeno conocido como uso compartido de cursores . Por ejemplo, ejecutar en una sesión SELECT * FROM employeesy ejecutar en una sesión diferente SELECT * FROM employees(accediendo a la misma tabla) puede usar el mismo árbol y plan de análisis. Un área SQL compartida a la que acceden varias sentencias se denomina cursor compartido.
inserte la descripción de la imagen aquí
Oracle Database utiliza los siguientes pasos para determinar automáticamente si una declaración SQL emitida o un bloque PL/SQL es textualmente idéntica a otra declaración que se encuentra actualmente en la memoria caché de la biblioteca:

  1. Calcula el valor hash del texto de la declaración .
  2. La base de datos busca hashes coincidentes para declaraciones SQL existentes en el grupo compartido. Son posibles las siguientes opciones:
    – No hay ningún valor hash coincidente.
    En este caso, actualmente no existe ninguna instrucción SQL en el grupo compartido, por lo que la base de datos realiza un análisis exhaustivo . Esto finalizará la verificación del grupo compartido.
    – Hay un hash coincidente.
    En este caso, la base de datos pasa al siguiente paso, que es la coincidencia de texto.
  3. La base de datos compara el texto de la sentencia coincidente con el texto de la sentencia hash para determinar si son iguales. Son posibles las siguientes opciones:
    – Falló la coincidencia de texto.
    En este caso, el proceso de coincidencia de texto se detiene, lo que resulta en un análisis difícil.
    – El texto coincide con éxito.
    En este caso, la base de datos pasa al siguiente paso: determinar si el SQL puede compartir el cursor principal existente.
    Para que se produzca la coincidencia de texto, el texto de una instrucción SQL o un bloque PL/SQL debe ser idéntico carácter por carácter , incluidos los espacios, las mayúsculas y los comentarios. Por ejemplo, las siguientes sentencias no pueden usar la misma área SQL compartida:
    SELECT * FROM employees;
    SELECT * FROM Employees;
    SELECT *  FROM employees;
    
    En general, las declaraciones de SQL que difieren solo en los literales (121 y 247 en este ejemplo) no pueden usar la misma área de SQL compartida. Por ejemplo, las siguientes declaraciones no se analizan en la misma región de SQL:
    SELECT count(1) FROM employees WHERE manager_id = 121;
    SELECT count(1) FROM employees WHERE manager_id = 247;
    
    La única excepción a esta regla es cuando el parámetro CURSOR_SHARING se ha establecido en FORCE, en cuyo caso declaraciones similares pueden compartir regiones de SQL.

20.1.1.2 Cursores principales y secundarios

Cada instrucción SQL analizada tiene un cursor principal y uno o más cursores secundarios.

El cursor principal almacena el texto de la instrucción SQL. Dos declaraciones comparten el mismo cursor principal si su texto es el mismo. Sin embargo, si el texto es diferente, la base de datos crea un cursor principal separado.

En este ejemplo, las dos primeras declaraciones son sintácticamente diferentes (la letra "c" está en minúsculas en la primera declaración y en mayúsculas en la segunda), pero semánticamente idénticas. Estas sentencias tienen diferentes cursores principales debido a las diferencias de sintaxis. La tercera declaración es sintácticamente idéntica a la primera declaración (minúscula "c"), pero semánticamente diferente porque se refiere a la tabla de clientes en un esquema diferente. Dado que la sintaxis es la misma, la tercera declaración puede compartir un cursor principal con la primera declaración.

SQL> CONNECT oe@inst1
Enter password: *******
Connected.
SQL> SELECT COUNT(*) FROM customers;

  COUNT(*)
----------
       319

SQL> SELECT COUNT(*) FROM Customers;

  COUNT(*)
----------
       319

SQL> CONNECT sh@inst1
Enter password:  *******
Connected.
SQL> SELECT COUNT(*) FROM customers;

  COUNT(*)
----------
    55500

La siguiente consulta V$SQL indica dos nodos principales. La instrucción con el ID de SQL 8h916vv2yw400, la versión en minúscula "c" de la instrucción, tiene un cursor principal y dos cursores secundarios: secundario 0 y secundario 1. La instrucción con el ID de SQL 5rn2uxjtpz0wd, la versión "c" mayúscula de la instrucción, tiene un cursor principal diferente y solo un cursor secundario: el secundario 0.

SQL> CONNECT SYSTEM@inst1
Enter password:  *******
Connected.

COL SQL_TEXT FORMAT a30
COL CHILD# FORMAT 99999
COL EXEC FORMAT 9999
COL SCHEMA FORMAT a6
SELECT SQL_ID, PARSING_SCHEMA_NAME AS SCHEMA, SQL_TEXT, 
CHILD_NUMBER AS CHILD#, EXECUTIONS AS EXEC FROM V$SQL 
WHERE SQL_TEXT LIKE '%ustom%' AND SQL_TEXT NOT LIKE '%SQL_TEXT%' ORDER BY SQL_ID;

SQL_ID        SCHEMA SQL_TEXT                       CHILD#  EXEC
------------- ------ ------------------------------ ------ -----
5rn2uxjtpz0wd OE     SELECT COUNT(*) FROM Customers      0     1
8h916vv2yw400 OE     SELECT COUNT(*) FROM customers      0     1
8h916vv2yw400 SH     SELECT COUNT(*) FROM customers      1     1
20.1.1.2.1 Cursores principales y V$SQLAREA

La vista V$SQLAREA contiene una fila para cada cursor principal.

En el siguiente ejemplo, la consulta V$SQLAREA muestra dos cursores principales, cada uno identificado con un SQL_ID diferente . VERSION_COUNT indica el número de cursores secundarios .
Hay un error en el texto original de este SQL

COL SQL_TEXT FORMAT a30
SELECT SQL_TEXT, SQL_ID, VERSION_COUNT, HASH_VALUE
FROM   V$SQLAREA
WHERE  SQL_TEXT LIKE '%ustomer%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%';
 
SQL_TEXT                       SQL_ID        VERSION_COUNT HASH_VALUE
------------------------------ ------------- ------------- ----------
SELECT COUNT(*) FROM customers 8h916vv2yw400             2 3320713216
SELECT COUNT(*) FROM Customers 5rn2uxjtpz0wd             1 1935639437

En el resultado anterior, SELECT * FROM customersVERSION_COUNT de 2 indica varios cursores secundarios, lo cual es necesario porque la declaración se ejecutó en dos objetos diferentes. Por el contrario, la declaración SELECT * FROM Customers (tenga en cuenta la "E" mayúscula) se ejecuta solo una vez, por lo que hay un cursor principal y un cursor secundario (VERSION_COUNT es 1).

20.1.1.2.2 Cursores secundarios y V$SQL

Cada cursor principal tiene uno o más cursores secundarios.

Un subcursor contiene un plan de ejecución, variables de vinculación, metadatos sobre los objetos a los que se hace referencia en la consulta, el entorno del optimizador y otra información. A diferencia de los cursores principales, los cursores secundarios no almacenan el texto de la instrucción SQL .

Si la instrucción puede reutilizar un cursor principal, la base de datos verifica si la instrucción puede reutilizar un cursor secundario existente. La base de datos realiza varias comprobaciones, incluidas las siguientes:

  • La base de datos compara los objetos a los que se hace referencia en la declaración emitida con los objetos a los que hacen referencia las declaraciones en el grupo para asegurarse de que sean todos iguales.
    Las referencias a objetos de esquema dentro de una instrucción SQL o un bloque PL/SQL deben resolverse en el mismo objeto dentro del mismo esquema. Por ejemplo, si dos usuarios emiten la siguiente declaración SQL y cada usuario tiene su propia tabla de empleados, la siguiente declaración no es idéntica porque hace referencia a una tabla de empleados diferente para cada usuario:
SELECT * FROM employees;

Nota: las bases de datos pueden compartir cursores en tablas temporales privadas, pero solo dentro de la misma sesión. La base de datos asocia el identificador de sesión como parte del contexto del cursor. Durante la resolución suave, la base de datos solo puede compartir cursores secundarios si el ID de sesión actual coincide con un ID de sesión en el contexto del cursor.

  • La base de datos determina si los modos del optimizador son los mismos.
    Por ejemplo, las sentencias SQL deben optimizarse utilizando el mismo objetivo del optimizador.

Ejemplo 20-2 Múltiples subcursores
V$SQL describe sentencias que residen actualmente en la memoria caché de la biblioteca. Contiene una fila para cada cursor secundario, como se muestra en el siguiente ejemplo:

COL SQL_TEXT FORMAT A30
COL USR FORMAT A6
SELECT SQL_TEXT, SQL_ID, USERNAME AS USR, CHILD_NUMBER AS CHILD#, 
       HASH_VALUE, PLAN_HASH_VALUE AS PLAN_HASHV
FROM   V$SQL s, DBA_USERS d
WHERE  SQL_TEXT LIKE '%ustomer%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%'
AND    d.USER_ID = s.PARSING_USER_ID;

SQL_TEXT                       SQL_ID        USR        CHILD# HASH_VALUE PLAN_HASHV
------------------------------ ------------- ------ ---------- ---------- ----------
SELECT COUNT(*) FROM customers 8h916vv2yw400 OE              0 3320713216 1140397121
SELECT COUNT(*) FROM Customers 5rn2uxjtpz0wd OE              0 1935639437 1140397121
SELECT COUNT(*) FROM customers 8h916vv2yw400 SH              1 3320713216  237477902
20.1.1.2.3 Discrepancias de cursores y V$SQL_SHARED_CURSOR

Si un cursor principal tiene varios cursores secundarios, la vista V$SQL_SHARED_CURSOR proporciona información sobre por qué no se comparten los cursores. Para varios tipos de incompatibilidades, la columna TRANSLATION_MISMATCH indica una falta de coincidencia con el valor Y o N.

Ejemplo 20-3 Falta de coincidencia de traducción
En este ejemplo, la columna TRANSLATION_MISMATCH muestra que dos sentencias (SELECT * FROM empleados) se refieren a diferentes objetos, lo que da como resultado un valor de TRANSLATION_MISMATCH de Y para la última sentencia. Dado que no se puede compartir, cada declaración tiene un cursor secundario separado, como lo indican 0 y 1 para CHILD_NUMBER.

SELECT S.SQL_TEXT, S.CHILD_NUMBER, s.CHILD_ADDRESS, 
       C.TRANSLATION_MISMATCH
FROM   V$SQL S, V$SQL_SHARED_CURSOR C
WHERE  SQL_TEXT LIKE '%ustomer%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%'
AND S.CHILD_ADDRESS = C.CHILD_ADDRESS;

SQL_TEXT                       CHILD_NUMBER CHILD_ADDRESS    T
------------------------------ ------------ ---------------- -
SELECT COUNT(*) FROM customers            0 00000000A7208390 N
SELECT COUNT(*) FROM customers            1 00000000A7192130 Y
SELECT COUNT(*) FROM Customers            0 00000000A71F3EF8 N

20.1.2 Acerca de los cursores y el análisis

Si la aplicación emite una declaración y Oracle Database no puede reutilizar el cursor, debe crear una nueva versión ejecutable del código de la aplicación. Esta operación se llama análisis duro .

Un análisis suave es cualquier análisis que no es un análisis duro y ocurre cuando la base de datos puede reutilizar el código existente . Algunos análisis suaves consumen menos recursos que otros. Por ejemplo, Oracle Database puede realizar varias optimizaciones y almacenar cursores secundarios en un área de SQL compartida si el cursor principal de la declaración ya existe. Sin embargo, Oracle Database también debe almacenar el cursor principal en un área de SQL compartida si el cursor principal no existe, lo que genera una sobrecarga de memoria adicional.

En efecto, el análisis duro vuelve a compilar la instrucción antes de ejecutarla. El análisis duro de sentencias SQL antes de cada ejecución es similar a volver a compilar un programa C antes de cada ejecución . El análisis duro realiza las siguientes operaciones :

  • Comprobar la sintaxis de las sentencias SQL
  • Comprobar la semántica de las sentencias SQL
  • Verifique los derechos de acceso del usuario que emite la declaración
  • Crear un plan de ejecución
  • Acceda al caché de la biblioteca y al caché del diccionario de datos varias veces para verificar el diccionario de datos

Un aspecto particularmente intensivo en recursos del análisis duro es acceder a la memoria caché de la biblioteca y a la memoria caché del diccionario de datos varias veces para verificar el diccionario de datos. Cuando la base de datos accede a estas áreas, utiliza un dispositivo de serialización llamado latch para que su definición no cambie durante la verificación. La contención de bloqueos temporales aumenta el tiempo de ejecución de sentencias y reduce la simultaneidad.

Por todas las razones anteriores, la sobrecarga de CPU y memoria del análisis duro puede causar problemas de rendimiento graves. Estos problemas son especialmente evidentes en las aplicaciones web que aceptan la entrada del usuario desde formularios y luego generan dinámicamente sentencias SQL. Real World Performance Group recomienda encarecidamente minimizar los análisis duros tanto como sea posible .

https://youtu.be/WcRcmfSIajc

20.1.3 Acerca de los literales y las variables de vinculación

Las variables de enlace son fundamentales para compartir el cursor en las aplicaciones de Oracle Database.

20.1.3.1 Literales y Cursores

Algunas aplicaciones de Oracle usan literales en lugar de variables de vinculación cuando construyen sentencias SQL.

Por ejemplo, la declaración SELECT SUM(salary) FROM hr.employees WHERE employee_id < 101 usa el valor literal 101 como ID de empleado. De forma predeterminada, Oracle Database no puede aprovechar el uso compartido del cursor cuando declaraciones similares no utilizan variables de vinculación. Por lo tanto, Oracle Database trata una declaración idéntica, excepto por el valor 102 o cualquier otro valor aleatorio, como una declaración completamente nueva que debe analizarse de forma exhaustiva.

Real-World Performance Group ha identificado aplicaciones que usan texto como una causa común de problemas de rendimiento, escalabilidad y seguridad . En el mundo real, no es raro escribir aplicaciones rápidamente sin tener en cuenta el uso compartido del cursor. Un ejemplo típico es una aplicación de "screen scraping" que copia el contenido de un formulario web y luego concatena cadenas para construir dinámicamente sentencias SQL.

Los principales problemas causados ​​por el uso de valores literales incluyen:

  • Las aplicaciones que concatenan el texto ingresado por los usuarios finales son vulnerables a los ataques de inyección SQL. Esta amenaza solo puede eliminarse reescribiendo la aplicación para usar variables de vinculación.
  • Si cada instrucción se analiza de forma exhaustiva, los cursores no se comparten, por lo que la base de datos tiene que consumir más memoria para crear los cursores.
  • La base de datos Oracle debe bloquear el grupo compartido y la memoria caché de la biblioteca durante las resoluciones forzadas. A medida que aumenta la cantidad de análisis duros, también aumenta la cantidad de procesos que esperan para bloquear el grupo compartido. Esta situación reduce la concurrencia y aumenta la contención.

https://youtu.be/EBdZ-RE2HFs

Ejemplo 20-6 Compartir texto y cursor

Considere una aplicación que ejecuta las siguientes instrucciones, que difieren solo en palabras:

SELECT SUM(salary) FROM hr.employees WHERE employee_id < 101;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 120;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 165;

La siguiente consulta V$SQLAREA muestra que las tres declaraciones requieren tres cursores principales diferentes. Cada cursor principal requiere su propio cursor secundario, como lo indica VERSION_COUNT.

COL SQL_TEXT FORMAT a30
SELECT SQL_TEXT, SQL_ID, VERSION_COUNT, HASH_VALUE
FROM   V$SQLAREA
WHERE  SQL_TEXT LIKE '%mployee%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%';

SQL_TEXT                              SQL_ID VERSION_COUNT HASH_VALUE
------------------------------ ------------- ------------- ----------
SELECT SUM(salary) FROM hr.emp b1tvfcc5qnczb             1  191509483
loyees WHERE employee_id < 165
SELECT SUM(salary) FROM hr.emp cn5250y0nqpym             1 2169198547
loyees WHERE employee_id < 101
SELECT SUM(salary) FROM hr.emp au8nag2vnfw67             1 3074912455
loyees WHERE employee_id < 120

20.1.3.2 Vincular variables y cursores

Puede desarrollar aplicaciones de Oracle para usar variables de vinculación en lugar de literales.

Las variables de enlace son marcadores de posición en las consultas. Por ejemplo, la declaración SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id usa la variable de vinculación: emp_id como ID de empleado.

El equipo de Real-World Performance descubrió que las aplicaciones que usan variables de vinculación funcionan mejor, escalan mejor y son más seguras. Los principales beneficios de usar variables de vinculación incluyen:

  • Las aplicaciones que usan variables de vinculación no son vulnerables a los mismos ataques de inyección SQL que las aplicaciones que usan literales.
  • Oracle Database puede aprovechar el uso compartido del cursor cuando la misma instrucción utiliza variables de vinculación y compartir planes y otra información cuando diferentes valores están vinculados a la misma declaración.
  • Oracle Database evita la sobrecarga de bloquear el grupo compartido y la memoria caché de la biblioteca necesarios para el análisis exhaustivo.

Ejemplo 20-7 Vincular variables y cursores compartidos

El siguiente ejemplo crea una variable de enlace emp_id usando el comando VARIABLE en SQL*Plus, luego ejecuta una consulta con tres valores de enlace diferentes (101, 120 y 165):

VARIABLE emp_id NUMBER

EXEC :emp_id := 101;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id;
EXEC :emp_id := 120;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id;
EXEC :emp_id := 165;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id;

La siguiente consulta V$SQLAREA muestra una instrucción SQL única:

COL SQL_TEXT FORMAT a34
SELECT SQL_TEXT, SQL_ID, VERSION_COUNT, HASH_VALUE
FROM   V$SQLAREA
WHERE  SQL_TEXT LIKE '%mployee%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%';

SQL_TEXT                           SQL_ID        VERSION_COUNT HASH_VALUE
---------------------------------- ------------- ------------- ----------
SELECT SUM(salary) FROM hr.employe 4318cbskba8yh             1 615850960
es WHERE employee_id < :emp_id

Un valor VERSION_COUNT de 1 indica que la base de datos reutiliza el mismo cursor secundario en lugar de crear tres cursores secundarios separados. Esta reutilización es posible mediante el uso de variables de vinculación .

20.1.3.3 Vincular vista previa de variables

En la búsqueda de variables de vinculación (también conocida como búsqueda de vinculación), el optimizador busca el valor en una variable de vinculación cuando la base de datos realiza un análisis completo de la declaración.

El optimizador no analiza los valores de las variables de vinculación antes de cada análisis. En su lugar, el optimizador solo busca la primera vez que se llama al optimizador, que es durante el análisis duro.

Cuando la consulta usa literales, el optimizador puede usar los valores literales para encontrar el mejor plan. Sin embargo, cuando una consulta utiliza variables de vinculación, el optimizador debe elegir el mejor plan en ausencia de literales en el texto SQL. Esta tarea puede ser muy difícil. Al observar los valores vinculados durante el análisis duro inicial, el optimizador puede determinar la cardinalidad de las condiciones de la cláusula WHERE como si se usaran literales, mejorando así los planes.

Debido a que el optimizador solo analiza los valores límite durante el análisis exhaustivo, es posible que los planes no sean óptimos para todos los valores límite posibles. El siguiente ejemplo ilustra este principio.

Ejemplo 20-8 Los literales dan como resultado diferentes planes de ejecución

Suponga que ejecuta las siguientes declaraciones, que ejecutan tres declaraciones diferentes con diferentes literales (101, 120 y 165), y luego muestra el plan de ejecución para cada declaración:

SET LINESIZE 167
SET PAGESIZE 0
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 101;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 120;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 165;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());

La base de datos analizó las tres declaraciones que no eran idénticas. El resultado de DISPLAY_CURSOR (editado para mayor claridad) muestra que el optimizador eligió el mismo plan de exploración de rango de índice para las dos primeras declaraciones, pero un plan de exploración de tabla completa para la declaración usando el literal 165:

SQL_ID	cn5250y0nqpym, child number 0
-------------------------------------
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 101

Plan hash value: 2410354593

-------------------------------------------------------------------------------------
|Id| Operation                            | Name         |Rows|Bytes|Cost(%CPU)|Time|
-------------------------------------------------------------------------------------
| 0| SELECT STATEMENT                     |               |  |   |2 (100)|          |
| 1|  SORT AGGREGATE                      |               |1 | 8 |       |          |
| 2|   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES     |1 | 8 |2 (0)  | 00:00:01 |
|*3|    INDEX RANGE SCAN                  | EMP_EMP_ID_PK |1 |   |1 (0)  | 00:00:01 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("EMPLOYEE_ID"<101)

SQL_ID	au8nag2vnfw67, child number 0
-------------------------------------
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 120

Plan hash value: 2410354593

-------------------------------------------------------------------------------------
|Id| Operation                            | Name         |Rows|Bytes|Cost(%CPU)|Time|
-------------------------------------------------------------------------------------
| 0| SELECT STATEMENT                     |               |  |   |2 (100)|          |
| 1|  SORT AGGREGATE                      |               |1 | 8 |       |          |
| 2|   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES     |20|160|2 (0)  | 00:00:01 |
|*3|    INDEX RANGE SCAN                  | EMP_EMP_ID_PK |20|   |1 (0)  | 00:00:01 |
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("EMPLOYEE_ID"<120)

SQL_ID	b1tvfcc5qnczb, child number 0
-------------------------------------
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 165

Plan hash value: 1756381138

-------------------------------------------------------------------------
| Id  | Operation          | Name      |Rows| Bytes |Cost(%CPU)| Time   |
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |           |    |       | 2 (100)|          |
|   1 |  SORT AGGREGATE    |           |  1 |     8 |        |          |
|*  2 |   TABLE ACCESS FULL| EMPLOYEES | 66 |   528 | 2   (0)| 00:00:01 |
-------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("EMPLOYEE_ID"<165)

El resultado anterior muestra que el optimizador considera que las exploraciones de tablas completas son más eficientes que las exploraciones de índice para consultas que devuelven más filas.

Ejemplo 20-9 Las variables de vinculación provocan la reutilización del cursor

Este ejemplo reescribe la consulta realizada en el Ejemplo 20-8 para usar variables de vinculación en lugar de literales. Vincula los mismos valores (101, 120 y 165) a la variable de vinculación: emp_id, y luego muestra el plan de ejecución para cada valor:

VAR emp_id NUMBER

EXEC :emp_id := 101;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());
EXEC :emp_id := 120;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());
EXEC :emp_id := 165;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());

El resultado de DISPLAY_CURSOR muestra que el optimizador eligió exactamente el mismo plan para las tres declaraciones:

SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id

Plan hash value: 2410354593

-------------------------------------------------------------------------------------
| Id | Operation                            | Name      |Rows|Bytes|Cost (%CPU)|Time|
-------------------------------------------------------------------------------------
|  0 | SELECT STATEMENT                     |               | |  |2 (100)|          |
|  1 |  SORT AGGREGATE                      |               |1|8 |       |          |
|  2 |   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES     |1|8 |  2 (0)| 00:00:01 |
|* 3 |    INDEX RANGE SCAN                  | EMP_EMP_ID_PK |1|  |  1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("EMPLOYEE_ID"<:EMP_ID)

Por el contrario, cuando la declaración anterior se ejecuta literalmente, el optimizador elige el escaneo completo de la tabla menos costoso cuando el valor de identificación del empleado es 165. Ese es el problema que resuelve el uso compartido adaptativo del cursor.

20.1.4 Acerca del ciclo de vida de los cursores compartidos

Cuando el optimizador analiza una nueva declaración de SQL que no es DDL, la base de datos asignará una nueva área de SQL compartida. La cantidad de memoria requerida depende de la complejidad de la declaración.

Una base de datos puede descartar una región SQL compartida del grupo compartido incluso si la región corresponde a un cursor abierto que no se ha utilizado durante mucho tiempo. Si posteriormente se usa un cursor abierto para ejecutar su declaración, la base de datos volverá a analizar la declaración y asignará una nueva área SQL compartida. La base de datos no elimina los cursores cuyas declaraciones se están ejecutando o cuyas filas no se han obtenido por completo.

Las regiones SQL compartidas pueden dejar de ser válidas debido a cambios en los objetos de esquema dependientes o en las estadísticas del optimizador. Oracle Database utiliza dos técnicas para administrar el ciclo de vida del cursor: invalidación e invalidación de desplazamiento.

20.1.4.1 Cursor marcado como no válido

Cuando un área SQL compartida se marca como no válida, la base de datos puede eliminarla del grupo compartido, junto con los cursores válidos que no se han utilizado durante algún tiempo.

En algunos casos, la base de datos debe ejecutar declaraciones asociadas con un área SQL compartida no válida en el grupo compartido. En este caso, la base de datos analiza la declaración antes de ejecutarla.

La base de datos marca inmediatamente una región SQL compartida dependiente como no válida cuando se cumplen las siguientes condiciones:

  • Cuando el parámetro NO_INVALIDATE es FALSO, DBMS_STATS recopila estadísticas para tablas, grupos de tablas o índices.
  • Las declaraciones SQL se refieren a objetos de esquema que luego se modifican mediante declaraciones DDL mediante la invalidación inmediata del cursor (el valor predeterminado).
    Puede especificar manualmente la invalidación inmediata en declaraciones como ALTER TABLE ... IMMEDIATE VALIDATION y ALTER INDEX ... IMMEDIATE VALIDATION , o establecer el parámetro de inicialización CURSOR_INVALIDATION en IMMEDIATE a nivel de sesión o de sistema.
    Nota: Las declaraciones DDL que utilizan la cláusula DEFERRED VALIDATION anulan la configuración INMEDIATA del parámetro de inicialización CURSOR_INVALIDATION.

Cuando se cumplan las condiciones anteriores, la base de datos volverá a analizar la declaración afectada la próxima vez que se ejecute.

Cuando la base de datos invalida un cursor, se incrementa el valor de VSQL.INVALIDACIONES (por ejemplo, de 0 a 1), y se incrementa el valor de VSQL.INVALIDACIONES (por ejemplo, de 0 a 1), y se incrementa VSQL.INVALIDACIONES (por ejemplo, de 0 a 1), y VSQ L. I N V A L I D A T I ON S incrementa el valor (por ejemplo, de 0 a 1 ) , y V SQL.OBJECT_STATUS muestra INVALID_UNAUTH.

Ejemplo 20-10 Fuerza la invalidación del cursor configurando NO_INVALIDATE=FALSE

SQL> SELECT COUNT(*) FROM sales;

  COUNT(*)
----------
    918843

SQL> SELECT PREV_SQL_ID, SQL_ID FROM V$SESSION WHERE SID = SYS_CONTEXT('userenv', 'SID');

SQL_ID
-------------
1y17j786c7jbh

SQL> SELECT CHILD_NUMBER, EXECUTIONS,
  2  PARSE_CALLS, INVALIDATIONS, OBJECT_STATUS 
  3  FROM V$SQL WHERE SQL_ID = '1y17j786c7jbh';

CHILD_NUMBER EXECUTIONS PARSE_CALLS INVALIDATIONS OBJECT_STATUS
------------ ---------- ----------- ------------- -------------
           0          1           1             0 VALID

SQL> EXEC DBMS_STATS.GATHER_TABLE_STATS(null,'sales',no_invalidate => FALSE);

PL/SQL procedure successfully completed.

SQL> SELECT CHILD_NUMBER, EXECUTIONS,
  2  PARSE_CALLS, INVALIDATIONS, OBJECT_STATUS
  3  FROM V$SQL WHERE SQL_ID = '1y17j786c7jbh';

CHILD_NUMBER EXECUTIONS PARSE_CALLS INVALIDATIONS OBJECT_STATUS
------------ ---------- ----------- ------------- --------------
           0          1           1             1 INVALID_UNAUTH

20.1.4.2 Cursores marcados como rodantes no válidos

Cuando el cursor se marca como no válido (V$SQL.IS_ROLLING_INVALID es Y), la base de datos realizará gradualmente un análisis exhaustivo durante un largo período de tiempo.

Nota: cuando V$SQL.IS_ROLLING_REFRESH_INVALID es Y, el objeto subyacente ha cambiado, pero no es necesario volver a compilar el cursor. La base de datos actualiza los metadatos en el cursor.

Propósito de la invalidación de desplazamiento

Dado que el aumento drástico de los análisis duros puede degradar significativamente el rendimiento, la invalidación de desplazamiento (también conocida como invalidación retrasada) es útil para las cargas de trabajo que invalidan muchos cursores al mismo tiempo. La base de datos asigna a cada cursor inválido un período de tiempo generado aleatoriamente. Las regiones de SQL que caducan al mismo tiempo suelen tener períodos de tiempo diferentes.

El análisis duro ocurre solo cuando la consulta que accede al cursor se ejecuta después de que haya expirado el período de tiempo. De esta manera, la base de datos distribuye la sobrecarga del análisis duro a lo largo del tiempo.

Nota: Si la instrucción de SQL paralelo se marca como no válida, la base de datos realizará un análisis completo la próxima vez que se ejecute, independientemente de si el tiempo ha expirado. En un entorno de Oracle Real Application Clusters (Oracle RAC), esta tecnología garantiza la coherencia entre los planes de ejecución del servidor de ejecución paralelo y el coordinador de consultas.

Una analogía con la falla rodante podría ser el reemplazo gradual de los muebles de oficina desgastados. En lugar de reemplazar todos los muebles a la vez, forzando un desembolso financiero significativo, una empresa asignó diferentes fechas de vencimiento a cada mueble. En el transcurso de un año, un artículo se usa hasta que se reemplaza, momento en el cual se incurre en costos.

Especificación de falla retrasada

De forma predeterminada, DDL especifica que las declaraciones que acceden a los objetos utilizan la invalidación inmediata del cursor. Por ejemplo, si crea una tabla o índice, los cursores que hagan referencia a esa tabla o índice utilizarán la invalidación inmediata.

Si la declaración DDL admite la invalidación retrasada del cursor, el comportamiento predeterminado se puede anular con declaraciones como ALTER TABLE ... INVALIDACIÓN DIFERIDA. Las opciones dependen de la instrucción DDL. Por ejemplo, ALTER INDEX solo admite INVALIDACIÓN DIFERIDA cuando también se especifican las opciones INUSABLE o RECONSTRUCCIÓN.

Una alternativa a DDL es establecer el parámetro de inicialización CURSOR_INVALIDATION en DEFERRED a nivel de sesión o de sistema. Las instrucciones DDL que usan la cláusula INVALIDACIÓN INMEDIATA anulan la configuración DEFERRED del parámetro de inicialización CURSOR_INVALIDATION.

cuando ocurre una falla de desplazamiento

Si el atributo INVALIDACIÓN DIFERIDA se aplica a un objeto, ya sea como resultado de DDL o de la configuración de parámetros de inicialización, las declaraciones que acceden a ese objeto pueden estar sujetas a una invalidación demorada. La base de datos marca un área SQL compartida como no válida en cualquiera de los siguientes casos:

  • Cuando el parámetro NO_INVALIDATE se establece en DBMS_STATS.AUTO_INVALIDATE, DBMS_STATS recopila estadísticas para tablas, grupos de tablas o índices. Esta es la configuración predeterminada.

  • Sin evitar el uso de la invalidación retrasada, emita una de las siguientes sentencias con invalidación retrasada:

    • ALTER TABLE en tablas particionadas
    • ALTER TABLE … PARALELO
    • ALTERAR ÍNDICE... INUTILIZABLE
    • ALTERAR ÍNDICE… RECONSTRUIR
    • CREAR ÍNDICE
    • ÍNDICE DE GOTA
    • TRUNCATE TABLE en tablas particionadas

    Un subconjunto de instrucciones DDL requiere la invalidación inmediata de los cursores para DML (INSERT, UPDATE, DELETE o MERGE), pero las instrucciones SELECT no. Una serie de factores relacionados con una declaración DDL en particular y los cursores afectados determinan si una base de datos de Oracle utiliza la invalidación retrasada.

20.2 CURSOR_SHARING y sustitución de variable de vinculación

Este tema explica qué es el parámetro de inicialización CURSOR_SHARING y cómo configurarlo en diferentes valores afecta la forma en que Oracle Database usa las variables de vinculación.

20.2.1 Parámetro de inicialización CURSOR_SHARING

El parámetro de inicialización CURSOR_SHARING controla cómo la base de datos maneja las declaraciones con variables de vinculación.

En Oracle Database 12c, este parámetro admite los siguientes valores:

  • EXACTO
    Este es el valor predeterminado. La base de datos solo permite declaraciones con el mismo texto para compartir un cursor. La base de datos no intentará reemplazar valores literales con variables de vinculación generadas por el sistema. En este caso, el optimizador genera un plan para cada instrucción en función del valor literal.

  • La base de datos FORCE reemplaza todos los literales con variables de vinculación generadas por el sistema . Para declaraciones idénticas después de vincular literales de sustitución de variables, el optimizador usa el mismo plan.

NOTA: El valor SIMILAR para CURSOR_SHARING está en desuso.

Puede configurar CURSOR_SHARING a nivel de sistema o de sesión, o usar la sugerencia CURSOR_SHARING_EXACT a nivel de instrucción.

20.2.2 Comportamiento de análisis cuando CURSOR_SHARING = FORCE

Cuando la instrucción SQL usa literales en lugar de variables de enlace, establecer el parámetro de inicialización CURSOR_SHARING en FORCE permite que la base de datos reemplace los literales con variables de enlace generadas por el sistema . Con esta técnica, la base de datos a veces puede reducir el número de cursores principales en un área de SQL compartida.

Nota: si la declaración usa una cláusula ORDER BY, el cursor no necesariamente se puede compartir, aunque la base de datos puede realizar una sustitución literal en esa cláusula, porque diferentes valores para los números de columna como literales significan resultados de consulta diferentes y pueden tener diferentes planes de ejecución. Los números de columna en la cláusula ORDER BY afectan la planificación y ejecución de consultas, por lo que la base de datos no puede compartir dos cursores con diferentes números de columna .

Cuando CURSOR_SHARING se establece en FORCE, la base de datos realiza los siguientes pasos durante el análisis:

  1. Copie todos los literales en la declaración a PGA y reemplácelos con variables de vinculación generadas por el sistema
-- 替换前
SELECT SUBSTR(last_name, 1, 4), SUM(salary) 
FROM   hr.employees 
WHERE  employee_id < 101 GROUP BY last_name
-- 替换后
SELECT SUBSTR(last_name, :"SYS_B_0", :"SYS_B_1"), SUM(salary) 
FROM   hr.employees 
WHERE  employee_id < :"SYS_B_2" GROUP BY last_name
  1. Busque en el grupo compartido la misma declaración (mismo valor de hash de SQL)
    Si no se encuentra la misma declaración, la base de datos realiza un análisis completo. De lo contrario, la base de datos continúa con el siguiente paso.
  2. Análisis suave de sentencias de ejecución

Como se muestra en los pasos anteriores, establecer el parámetro de inicialización CURSOR_SHARING en FORCE no disminuye el recuento de resolución. Por el contrario, FORCE permite que la base de datos realice un análisis suave en lugar de un análisis duro en determinadas circunstancias. Además, FORCE no protege contra los ataques de inyección SQL porque Oracle Database vincula los valores después de que se haya producido cualquier inyección.

Ejemplo 20-11 Reemplazo de literales con variables de vinculación del sistema
Este ejemplo establece CURSOR_SHARING en FORCE a nivel de sesión, ejecuta tres declaraciones que contienen literales y muestra el plan para cada declaración:

ALTER SESSION SET CURSOR_SHARING=FORCE;
SET LINESIZE 170
SET PAGESIZE 0
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 101;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 120;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 165;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());

El siguiente resultado de DISPLAY_CURSOR, editado para mejorar la legibilidad, muestra que las tres declaraciones usan el mismo plan. El optimizador eligió el plan de escaneo de rango de índice porque observó el primer valor (101) vinculado a la variable de vinculación del sistema y eligió ese plan como el mejor plan para todos los valores. De hecho, este plan no es el mejor plan para todos los valores. Cuando el valor es 165, la exploración completa de la tabla es más eficaz.

SQL_ID	cxx8n1cxr9khn, child number 0
-------------------------------------
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :"SYS_B_0"

Plan hash value: 2410354593

-------------------------------------------------------------------------------------
| Id  | Operation                           | Name       |Rows|Bytes|Cost(%CPU)|Time|
-------------------------------------------------------------------------------------
|  0 | SELECT STATEMENT                     |               |  |   |2 (100)|        |
|  1 |  SORT AGGREGATE                      |               |1 | 8 |       |        |
|  2 |   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES     |1 | 8 |2 (0)  |00:00:01|
|* 3 |    INDEX RANGE SCAN                  | EMP_EMP_ID_PK |1 |   |1 (0)  |00:00:01|
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("EMPLOYEE_ID"<101)

La consulta en V$SQLAREA confirma que Oracle Database reemplazó el texto con la variable de vinculación del sistema: "SYS_B_0" y creó un cursor principal y un cursor secundario (VERSION_COUNT=1) para las tres declaraciones, lo que significa que todas las ejecuciones comparten el mismo plan de .

COL SQL_TEXT FORMAT a36
SELECT SQL_TEXT, SQL_ID, VERSION_COUNT, HASH_VALUE
FROM   V$SQLAREA
WHERE  SQL_TEXT LIKE '%mployee%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%';

SQL_TEXT                             SQL_ID        VERSION_COUNT HASH_VALUE
------------------------------------ ------------- ------------- ----------
SELECT SUM(salary) FROM hr.employees cxx8n1cxr9khn             1  997509652
 WHERE employee_id < :"SYS_B_0"

20.3 Uso compartido adaptativo del cursor

El uso compartido adaptativo del cursor permite múltiples planes de ejecución para una sola declaración que contiene variables de vinculación .

El uso compartido del cursor es "adaptativo" en el sentido de que el cursor ajusta su comportamiento para que la base de datos no siempre use el mismo plan para cada ejecución o vincular el valor de la variable.

20.3.1 Propósito del uso compartido adaptativo del cursor

Con la visualización enlazada, el optimizador mira el valor de una variable de enlace definida por el usuario la primera vez que se llama al cursor.

El optimizador determina la cardinalidad de cualquier condición de la cláusula WHERE como si se usaran literales en lugar de variables de vinculación. Sin embargo, si una columna en la cláusula WHERE tiene datos sesgados, puede haber un histograma en esa columna. Cuando el optimizador observa los valores de una variable de vinculación definida por el usuario y elige un plan, es posible que ese plan no funcione para todos los valores.

En el uso compartido adaptativo del cursor, la base de datos supervisa los datos a los que se accede a lo largo del tiempo para diferentes valores límite, lo que garantiza que se seleccione el mejor cursor para un valor límite particular. Por ejemplo, el optimizador puede elegir un plan para un valor límite de 10 y un plan diferente para un valor límite de 50. El uso compartido del cursor es "adaptativo" en el sentido de que el cursor ajusta su comportamiento para que el optimizador no siempre elija los mismos valores de las variables del plan para cada ejecución o vinculación. Por lo tanto, el optimizador detecta automáticamente cuándo diferentes ejecuciones de una declaración se beneficiarían de diferentes planes de ejecución.

Nota: El uso compartido adaptable del cursor es independiente del parámetro de inicialización CURSOR_SHARING . El uso compartido adaptable del cursor se aplica por igual a las declaraciones que contienen variables de vinculación definidas por el usuario y generadas por el sistema. El uso compartido de cursor adaptativo no se aplica a declaraciones que contienen solo literales.

20.3.2 Cómo funciona el uso compartido adaptativo del cursor: ejemplo

El uso compartido adaptativo del cursor supervisa las instrucciones que utilizan variables de vinculación para determinar si el nuevo plan es más eficiente.

Supongamos que una aplicación ejecuta la siguiente declaración cinco veces, cada vez vinculando un valor diferente:

SELECT * FROM employees WHERE salary = :sal AND department_id = :dept

También se supone en este ejemplo que existe un histograma en al menos una columna en el predicado. La base de datos procesa esta declaración de la siguiente manera:

  1. La primera vez que la aplicación emite una declaración, lo que resulta en un análisis difícil. Durante el análisis, la base de datos realiza las siguientes tareas:
    • Revise las variables de vinculación para generar un plan inicial.
    • Marca el cursor como sensible al enlace. Un cursor sensible a la vinculación es un cursor cuyo plan óptimo puede depender del valor de una variable de vinculación. Para determinar si los diferentes planes son beneficiosos, la base de datos supervisa el comportamiento de los cursores sensibles a la vinculación utilizando diferentes valores de vinculación.
    • Almacene metadatos sobre el predicado, incluida la cardinalidad del valor vinculado (en este ejemplo, suponga que solo se devuelven 5 filas).
    • Cree un plan de ejecución (en este caso, acceso de índice) basado en los valores vistos.
  2. La base de datos ejecuta el cursor, almacenando valores vinculados y estadísticas de ejecución en el cursor.
  3. La aplicación emite la declaración por segunda vez, utilizando una variable de vinculación diferente, lo que hace que la base de datos realice un análisis suave y encuentre un cursor coincidente en la memoria caché de la biblioteca.
  4. La base de datos implementa cursores.
  5. La base de datos realiza las siguientes tareas posteriores a la ejecución:
    • La base de datos compara las estadísticas de ejecución de la segunda ejecución con las estadísticas de la primera ejecución.
    • La base de datos observa todos los patrones estadísticos ejecutados previamente antes de decidir si marca el cursor como consciente de la vinculación. En este ejemplo, suponga que la base de datos decide que el cursor reconoce los enlaces.
  6. La aplicación emite la declaración por tercera vez, utilizando una variable de vinculación diferente, lo que provoca un análisis suave. Debido a que los cursores son conscientes de los enlaces, la base de datos hace lo siguiente:
    • Determina si la base del nuevo valor está en el mismo rango que la base almacenada. En este ejemplo, la cardinalidad es similar: 8 filas en lugar de 5.
    • Reutilice el plan de ejecución en un cursor secundario existente.
  7. La base de datos implementa cursores.
  8. La aplicación emite la declaración por cuarta vez, utilizando una variable de vinculación diferente, lo que provoca un análisis suave. Debido a que los cursores son conscientes de los enlaces, la base de datos hace lo siguiente:
    • Determina si la base del nuevo valor está en el mismo rango que la base almacenada. En este ejemplo, la cardinalidad es bastante diferente: 102 filas (en una tabla con 107 filas) en lugar de 5 filas.
    • No se encontró ningún cursor secundario coincidente.
  9. La base de datos realiza un análisis riguroso. Como resultado, la base de datos hace lo siguiente:
    • Cree un nuevo cursor secundario utilizando el segundo plan de ejecución (exploración de tabla completa en este caso)
    • Almacene metadatos sobre el predicado en el cursor, incluida la cardinalidad de los valores vinculados
  10. La base de datos ejecuta el nuevo cursor.
  11. La base de datos almacena los nuevos valores vinculados y las estadísticas de ejecución en el nuevo cursor secundario.
  12. La aplicación emite la declaración por quinta vez, utilizando una variable de vinculación diferente, lo que provoca un análisis suave. Debido a que los cursores son conscientes de los enlaces, la base de datos hace lo siguiente:
    • Determina si la base del nuevo valor está en el mismo rango que la base almacenada. En este ejemplo, la base es 20.
    • No se encontró ningún cursor secundario coincidente.
  13. La base de datos realiza un análisis riguroso. Como resultado, la base de datos hace lo siguiente:
    • Cree un nuevo cursor secundario utilizando el tercer plan de ejecución (acceso de índice en este caso)
    • Asegúrese de que este plan de ejecución de acceso al índice sea el mismo que el plan de ejecución de acceso al índice utilizado cuando se ejecutó la declaración por primera vez.
    • Fusiona dos subcursores que contienen planes de acceso de índice, lo que implica almacenar las estadísticas de cardinalidad combinadas en un subcursor y eliminar el otro
  14. La base de datos accede al plan de ejecución utilizando el índice para ejecutar el cursor.

20.3.3 Cursores sensibles a la vinculación

Un cursor sensible a la vinculación es un cursor cuyo plan óptimo puede depender del valor de una variable de vinculación.

La base de datos comprueba el valor límite al calcular la cardinalidad y considera la consulta "sensible" para planificar cambios basados ​​en diferentes valores límite. La base de datos supervisa el comportamiento de los cursores sensibles a la vinculación que utilizan diferentes valores de vinculación para determinar si los diferentes planes son beneficiosos.

El optimizador utiliza los siguientes criterios para decidir si un cursor es sensible al enlace:

  • El optimizador ha analizado los valores límite para generar estimaciones de cardinalidad.
  • El enlace se utiliza para predicados de igualdad o rango.

Para cada consulta ejecutada con un nuevo valor vinculado, la base de datos registra las estadísticas de ejecución del nuevo valor y las compara con las estadísticas de ejecución del valor anterior. Si las estadísticas de ejecución varían mucho, la base de datos marca el cursor como consciente de la vinculación.

Ejemplo 20-12 Columnas con un sesgo de datos significativo
Este ejemplo supone que la columna hr.employees.department_id tiene un sesgo de datos significativo. SYSTEM ejecuta el siguiente código de configuración para agregar 100 000 empleados en el departamento 50 a la tabla de empleados en el esquema de ejemplo, para un total de 100 107 filas, y luego recopila las estadísticas de la tabla:

DELETE FROM hr.employees WHERE employee_id > 999;

ALTER TABLE hr.employees DISABLE NOVALIDATE CONSTRAINT emp_email_uk;

DECLARE
v_counter NUMBER(7) := 1000;
BEGIN
 FOR i IN 1..100000 LOOP    
 INSERT INTO hr.employees 
   VALUES (v_counter, null, 'Doe', '[email protected]', null,'07-JUN-02',
     'AC_ACCOUNT', null, null, null, 50);
 v_counter := v_counter + 1;
 END LOOP;
END;
/
COMMIT; 

BEGIN
  DBMS_STATS.GATHER_TABLE_STATS (ownname = 'hr',tabname => 'employees');
END;
/

ALTER SYSTEM FLUSH SHARED_POOL;

La siguiente consulta muestra un histograma para la columna employee.department_id:

COL TABLE_NAME FORMAT a15
COL COLUMN_NAME FORMAT a20
COL HISTOGRAM FORMAT a9

SELECT TABLE_NAME, COLUMN_NAME, HISTOGRAM
FROM   DBA_TAB_COLS
WHERE  OWNER = 'HR'
AND    TABLE_NAME = 'EMPLOYEES'
AND    COLUMN_NAME = 'DEPARTMENT_ID';

TABLE_NAME      COLUMN_NAME          HISTOGRAM
--------------- -------------------- ---------
EMPLOYEES       DEPARTMENT_ID        FREQUENCY

Ejemplo 20-13 Consulta de cardinalidad baja

Este ejemplo continúa el ejemplo del Ejemplo 20-12. La siguiente consulta muestra que el valor 10 tiene una cardinalidad extremadamente baja para la columna department_id, ocupando .00099% de las filas:

VARIABLE dept_id NUMBER
EXEC :dept_id := 10;
SELECT COUNT(*), MAX(employee_id) FROM hr.employees WHERE department_id = :dept_id;

  COUNT(*) MAX(EMPLOYEE_ID)
---------- ----------------
         1              200

El optimizador elige un escaneo de rango de índice, como se esperaba para tales consultas de baja cardinalidad:

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR);

PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID	a9upgaqqj7bn5, child number 0
-------------------------------------
select COUNT(*), MAX(employee_id) FROM hr.employees WHERE department_id = :dept_id

Plan hash value: 1642965905

-------------------------------------------------------------------------------------
| Id| Operation                           | Name       |Rows|Bytes|Cost (%CPU)|Time |
-------------------------------------------------------------------------------------
| 0| SELECT STATEMENT                     |                   |  |  |2(100)|        |
| 1|  SORT AGGREGATE                      |                   |1 |8 |      |        |
| 2|   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES         |1 |8 |2  (0)|00:00:01|
|*3|    INDEX RANGE SCAN                  | EMP_DEPARTMENT_IX |1 |  |1  (0)|00:00:01|
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("DEPARTMENT_ID"=:DEPT_ID)

La siguiente consulta V$SQL obtiene información sobre los cursores:

COL BIND_AWARE FORMAT a10
COL SQL_TEXT FORMAT a22
COL CHILD# FORMAT 99999
COL EXEC FORMAT 9999
COL BUFF_GETS FORMAT 999999999
COL BIND_SENS FORMAT a9
COL SHARABLE FORMAT a9

SELECT SQL_TEXT, CHILD_NUMBER AS CHILD#, EXECUTIONS AS EXEC, 
       BUFFER_GETS AS BUFF_GETS, IS_BIND_SENSITIVE AS BIND_SENS, 
       IS_BIND_AWARE AS BIND_AWARE, IS_SHAREABLE AS SHARABLE
FROM   V$SQL
WHERE  SQL_TEXT LIKE '%mployee%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%';

SQL_TEXT               CHILD#  EXEC  BUFF_GETS BIND_SENS BIND_AWARE SHARABLE
---------------------- ------ ----- ---------- --------- ---------- --------
SELECT COUNT(*), MAX(e      0     1        196         Y          N        Y
mployee_id) FROM hr.em
ployees WHERE departme
nt_id = :dept_id

El resultado anterior muestra un subcursor ejecutado una vez para una consulta de cardinalidad baja. Un cursor se marca como sensible a la vinculación porque el optimizador cree que el mejor plan puede depender del valor de una variable de vinculación.

Cuando un cursor se marca como sensible a la vinculación, Oracle Database monitorea el comportamiento del cursor con diferentes valores de vinculación para determinar si los diferentes planes para diferentes valores de vinculación son más eficientes. La base de datos marca este cursor como sensible a la vinculación porque el optimizador usa el histograma en la columna id_departamento para calcular la selectividad del predicado WHERE id_departamento = :id_dept. Debido a que la presencia de un histograma indica que la columna está sesgada, los diferentes valores de las variables de vinculación pueden requerir diferentes planes.

Ejemplo 20-14 Consulta de alta cardinalidad

Este ejemplo continúa el ejemplo del Ejemplo 20-13. El siguiente código vuelve a ejecutar la misma consulta con un valor de 50, que ocupa el 99,9% de las filas:

EXEC :dept_id := 50;
SELECT COUNT(*), MAX(employee_id) FROM hr.employees WHERE department_id = :dept_id;

  COUNT(*) MAX(EMPLOYEE_ID)
---------- ----------------
    100045	     100999

Aunque esta consulta no selectiva sería más eficaz con un análisis de tabla completo, el optimizador elige el mismo análisis de rango de índice que se usa para department_id=10. Esto se debe a que la base de datos asume que los planes existentes en el cursor se pueden compartir:

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR);

PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID	a9upgaqqj7bn5, child number 0
-------------------------------------
SELECT COUNT(*), MAX(employee_id) FROM hr.employees WHERE department_id = :dept_id

Plan hash value: 1642965905

-------------------------------------------------------------------------------------
| Id| Operation                           | Name       |Rows|Bytes|Cost (%CPU)|Time |
-------------------------------------------------------------------------------------
| 0| SELECT STATEMENT                     |                   |  |  |2(100)|        |
| 1|  SORT AGGREGATE                      |                   |1 |8 |      |        |
| 2|   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES         |1 |8 |2  (0)|00:00:01|
|*3|    INDEX RANGE SCAN                  | EMP_DEPARTMENT_IX |1 |  |1  (0)|00:00:01|
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("DEPARTMENT_ID"=:DEPT_ID)

La consulta V$SQL muestra que el subcursor ahora se ha ejecutado dos veces:

SELECT SQL_TEXT, CHILD_NUMBER AS CHILD#, EXECUTIONS AS EXEC, 
       BUFFER_GETS AS BUFF_GETS, IS_BIND_SENSITIVE AS BIND_SENS, 
       IS_BIND_AWARE AS BIND_AWARE, IS_SHAREABLE AS SHARABLE
FROM   V$SQL
WHERE  SQL_TEXT LIKE '%mployee%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%';

SQL_TEXT               CHILD#  EXEC  BUFF_GETS BIND_SENS BIND_AWARE SHARABLE
---------------------- ------ ----- ---------- --------- ---------- --------
SELECT COUNT(*), MAX(e      0     2       1329         Y          N        Y
mployee_id) FROM hr.em
ployees WHERE departme
nt_id = :dept_id

20.3.4 Cursores conscientes de la vinculación

Un cursor con reconocimiento de enlace es un cursor con reconocimiento de enlace que puede usar diferentes planes para diferentes valores de enlace.

Después de hacer consciente el enlace del cursor, el optimizador elige un plan para ejecuciones futuras basado en los valores enlazados y sus estimaciones de cardinalidad. Entonces, "consciente de la unión" significa esencialmente "el mejor plan para los valores límite actuales".

Al ejecutar una declaración con un cursor con reconocimiento de vinculación, el optimizador utiliza un algoritmo interno para determinar si debe marcar el cursor como con reconocimiento de vinculación. Esa decisión depende de si el cursor produce patrones de acceso a datos significativamente diferentes para diferentes valores de enlace, lo que da como resultado un costo de rendimiento diferente al esperado.

Si la base de datos marca el cursor como vinculante, la próxima vez que el cursor se ejecute, la base de datos hará lo siguiente:

  • Generar un nuevo plan basado en valores vinculados
  • El cursor sin procesar generado para la instrucción está marcado como no compartible (V$SQL.IS_SHAREABLE es N). El cursor original ya no está disponible y es elegible para envejecer desde el caché de la biblioteca

Cuando la misma consulta se ejecuta repetidamente con diferentes valores de vinculación, la base de datos agrega y clasifica los nuevos valores de vinculación en la "firma" de la instrucción SQL (incluido el entorno del optimizador, la configuración de NLS, etc.). La base de datos verifica el valor límite y considera si el valor límite actual da como resultado un volumen de datos significativamente diferente o si el plan existente es suficiente. La base de datos no necesita crear un nuevo plan para cada nuevo valor.

Considere una situación en la que ejecuta una declaración con 12 valores enlazados diferentes (dos para cada valor), lo que hace que la base de datos active 5 análisis duros y cree 2 planes adicionales. Debido a que la base de datos realizó 5 análisis duros, creó 5 nuevos cursores secundarios, aunque algunos cursores tenían el mismo plan de ejecución que los cursores existentes. La base de datos marca los cursores redundantes como inutilizables, lo que significa que estos cursores eventualmente dejarán de estar disponibles en la memoria caché de la biblioteca.

Durante el análisis duro inicial, el optimizador en realidad mapea la relación entre el valor límite y el plan de ejecución apropiado. Después de esta fase inicial, la base de datos finalmente alcanza un estado estable. Ejecutar con el nuevo valor vinculado hace que se elija el mejor cursor secundario en la memoria caché sin un análisis exhaustivo. Por lo tanto, el número de resoluciones no varía con el número de valores límite diferentes.

Ejemplo 20-15 Cursores compatibles con enlaces
Este ejemplo continúa el ejemplo de "Cursores compatibles con enlaces". El siguiente código emite una segunda consulta de empleados con la variable de vinculación establecida en 50:

EXEC :dept_id := 50;
SELECT COUNT(*), MAX(employee_id) FROM hr.employees WHERE department_id = :dept_id;

  COUNT(*) MAX(EMPLOYEE_ID)
---------- ----------------
    100045           100999

Durante las dos primeras ejecuciones, la base de datos estaba monitoreando el comportamiento de la consulta y determinó que diferentes valores de enlace causaron que la consulta difería significativamente en cardinalidad. En función de esta diferencia, la base de datos ajusta su comportamiento para que no siempre se comparta el mismo plan para esa consulta. Por lo tanto, el optimizador genera un nuevo plan basado en el valor límite actual, que es 50:

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR);

PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID	a9upgaqqj7bn5, child number 1
-------------------------------------
SELECT COUNT(*), MAX(employee_id) FROM hr.employees WHERE department_id = :dept_id

Plan hash value: 1756381138

---------------------------------------------------------------------------
| Id  | Operation         | Name      | Rows  | Bytes | Cost(%CPU)| Time  |
---------------------------------------------------------------------------
|  0 | SELECT STATEMENT   |           |       |      |254 (100)|          |
|  1 |  SORT AGGREGATE    |           |     1 |    8 |         |          |
|* 2 |   TABLE ACCESS FULL| EMPLOYEES |  100K | 781K |254  (15)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("DEPARTMENT_ID"=:DEPT_ID)

La siguiente consulta V$SQL obtiene información sobre los cursores:

SELECT SQL_TEXT, CHILD_NUMBER AS CHILD#, EXECUTIONS AS EXEC, 
       BUFFER_GETS AS BUFF_GETS, IS_BIND_SENSITIVE AS BIND_SENS, 
       IS_BIND_AWARE AS BIND_AWARE, IS_SHAREABLE AS SHAREABLE
FROM   V$SQL
WHERE  SQL_TEXT LIKE '%mployee%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%';

SQL_TEXT               CHILD#  EXEC  BUFF_GETS BIND_SENS BIND_AWARE SHAREABLE
---------------------- ------ ----- ---------- --------- ---------- ---------
SELECT COUNT(*), MAX(e      0     2       1329         Y          N         N
mployee_id) FROM hr.em
ployees WHERE departme
nt_id = :dept_id

SELECT COUNT(*), MAX(e      1     1        800         Y          Y         Y
mployee_id) FROM hr.em
ployees WHERE departme
nt_id = :dept_id

El resultado anterior muestra que la base de datos creó un cursor secundario adicional (CHILD# de 1). El cursor 0 ahora está marcado como no compartible. El cursor 1 muestra muchos búfer debajo del cursor 0 y está marcado como sensible a la vinculación y consciente de la vinculación. Los cursores que reconocen la vinculación pueden usar diferentes planes para diferentes valores de vinculación, dependiendo de la selectividad de los predicados que contienen las variables de vinculación.

Ejemplo 20-16 Cursor consciente de la vinculación: elección del mejor plan

Este ejemplo continúa el ejemplo del "Ejemplo 20-15". El siguiente código ejecuta la misma consulta de empleados con un valor de 10 y cardinalidad muy baja (solo una fila):

EXEC :dept_id := 10;
SELECT COUNT(*), MAX(employee_id) FROM hr.employees WHERE department_id = :dept_id;

  COUNT(*) MAX(EMPLOYEE_ID)
---------- ----------------
         1              200

El siguiente resultado muestra que el optimizador eligió el mejor plan, un escaneo de índice, basado en una estimación de cardinalidad baja del valor límite actual de 10:

SQL> SELECT * from TABLE(DBMS_XPLAN.DISPLAY_CURSOR);

PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID	a9upgaqqj7bn5, child number 2
-------------------------------------
select COUNT(*), MAX(employee_id) FROM hr.employees WHERE department_id = :dept_id

Plan hash value: 1642965905

-------------------------------------------------------------------------------------
| Id| Operation                           | Name       |Rows|Bytes|Cost (%CPU)|Time |
-------------------------------------------------------------------------------------
| 0| SELECT STATEMENT                     |                   |  |  |2(100)|        |
| 1|  SORT AGGREGATE                      |                   |1 |8 |      |        |
| 2|   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES         |1 |8 |2  (0)|00:00:01|
|*3|    INDEX RANGE SCAN                  | EMP_DEPARTMENT_IX | 1|  |1  (0)|00:00:01|
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("DEPARTMENT_ID"=:DEPT_ID)

La salida de V$SQL ahora muestra que hay tres cursores secundarios:

SELECT SQL_TEXT, CHILD_NUMBER AS CHILD#, EXECUTIONS AS EXEC, 
       BUFFER_GETS AS BUFF_GETS, IS_BIND_SENSITIVE AS BIND_SENS, 
       IS_BIND_AWARE AS BIND_AWARE, IS_SHAREABLE AS SHAREABLE
FROM   V$SQL
WHERE  SQL_TEXT LIKE '%mployee%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%';

SQL_TEXT               CHILD#  EXEC  BUFF_GETS BIND_SENS BIND_AWARE SHAREABLE
---------------------- ------ ----- ---------- --------- ---------- ---------
SELECT COUNT(*), MAX(e      0     2       1329         Y          N         N
mployee_id) FROM hr.em
ployees WHERE departme
nt_id = :dept_id

SELECT COUNT(*), MAX(e      1     1        800         Y          Y         Y
mployee_id) FROM hr.em
ployees WHERE departme
nt_id = :dept_id

SELECT COUNT(*), MAX(e      2     1          3         Y          Y         Y
mployee_id) FROM hr.em
ployees WHERE departme
nt_id = :dept_id

Cuando el cursor se cambia al modo consciente de la vinculación, la base de datos descarta el cursor original (CHILD# de 0). Este es un gasto único. La base de datos marca el cursor 0 como no compartible (SHAREABLE es N), lo que significa que este cursor no está disponible y será el primero en desaparecer de la memoria caché del cursor.

20.3.5 Fusión de cursores

Si el optimizador crea un plan para un cursor compatible con enlaces y si ese plan es el mismo que un cursor existente, el optimizador puede realizar una combinación de cursores.

En este caso, la base de datos fusiona los cursores para ahorrar espacio en la memoria caché de la biblioteca. La base de datos aumenta el rango de selectividad del cursor para incluir la selectividad del nuevo valor límite.

Cuando una consulta utiliza una nueva variable de vinculación, el optimizador intenta encontrar un cursor que considere adecuado en función de la similitud selectiva del valor de vinculación. Si la base de datos no puede encontrar dicho cursor, se crea uno nuevo. Si el plan del nuevo cursor es el mismo que el de uno de los cursores existentes, la base de datos fusiona los dos cursores para ahorrar espacio en la memoria caché de la biblioteca. La combinación hizo que la base de datos marcara un cursor como no compartible. Si la memoria caché de la biblioteca está bajo presión de espacio, la base de datos agota primero los cursores no compartidos.

20.3.6 Vistas de uso compartido del cursor adaptativo

Puede usar la vista V$ para compartir cursores adaptativos para ver los rangos de selectividad, la información del cursor (como si el cursor es consciente de la vinculación o de la vinculación) y las estadísticas de ejecución.
Específicamente, use las siguientes vistas:

  • V$SQL muestra si el cursor es sensible a enlaces o consciente de enlaces.
  • V$SQL_CS_HISTOGRAM muestra la distribución de los recuentos de ejecución en un histograma de historial de ejecución de tres cubos.
  • Si se utiliza la selectividad para verificar el uso compartido del cursor, V$SQL_CS_SELECTIVITY muestra el ámbito de selectividad almacenado para cada predicado que incluye variables de vinculación. Contiene el texto del predicado, y los valores alto y bajo del rango de selectividad.
  • V$SQL_CS_STATISTICS resume la información utilizada por el optimizador para determinar si se debe marcar un cursor como compatible con enlaces. Para el ejemplo de ejecución, la base de datos realiza un seguimiento de las filas procesadas, las recuperaciones de búfer y el tiempo de CPU. La columna PEEKED muestra SÍ cuando el cursor se crea utilizando el conjunto de enlaces; de lo contrario, el valor es NO.

20.4 Directrices de rendimiento en el mundo real para compartir cursores

El equipo de Real-World Performance ha desarrollado una guía sobre cómo optimizar el uso compartido del cursor en las aplicaciones de Oracle Database.

20.4.1 Desarrollar aplicaciones con variables de vinculación para seguridad y rendimiento

Real-World Performance Group recomienda encarecidamente que todas las aplicaciones empresariales utilicen variables de vinculación .

Las aplicaciones de Oracle Database están diseñadas para escribirse utilizando variables de vinculación. Evite los diseños de aplicaciones que hacen que un gran número de usuarios emitan sentencias de SQL dinámicas y no compartidas .

Siempre que Oracle Database no pueda encontrar una coincidencia para una declaración en la memoria caché de la biblioteca, debe realizar un análisis completo. A pesar de los peligros de desarrollar aplicaciones usando literales, no todas las aplicaciones del mundo real usan variables de vinculación. A los desarrolladores a veces les resulta más rápido y fácil escribir programas que usan literales . Sin embargo, la reducción del tiempo de desarrollo no conduce a un mejor rendimiento y seguridad después de la implementación .

Los principales beneficios de usar variables de vinculación son los siguientes:

  • Eficiencia de los recursos
    La compilación de un programa antes de cada ejecución no utiliza los recursos de manera eficiente, pero eso es lo que hace Oracle Database cuando realiza un análisis completo. El servidor de la base de datos debe consumir mucha CPU y memoria para crear cursores, generar y evaluar planes de ejecución, etc. Al hacer que la base de datos comparta cursores, el análisis suave consume muchos menos recursos. Si la aplicación usa literales en lugar de variables de vinculación y ejecuta solo unas pocas consultas por día, es posible que el DBA no vea la sobrecarga adicional como un problema de rendimiento. Sin embargo, si la aplicación ejecuta cientos o miles de consultas por segundo, la sobrecarga adicional de recursos puede reducir fácilmente el rendimiento a niveles inaceptables. El uso de variables de vinculación hace que la base de datos realice el análisis duro solo una vez, independientemente de cuántas veces se ejecute la declaración .

  • Escalabilidad
    Cuando la base de datos realiza un análisis completo, la base de datos dedica más tiempo a adquirir y mantener latches en el grupo compartido y la memoria caché de la biblioteca. Los pestillos son dispositivos serializados de bajo nivel. Cuanto más tiempo y con mayor frecuencia la base de datos bloquea las estructuras en la memoria compartida, más larga es la cola para esos bloqueos. Cuando varias sentencias comparten el mismo plan de ejecución, disminuyen las solicitudes de bloqueos y la duración de los bloqueos. Este comportamiento aumenta la escalabilidad.

  • Rendimiento y tiempo de respuesta
    Cuando la base de datos evita volver a analizar y crear cursores constantemente, la mayor parte de su tiempo se gasta en el espacio del usuario. El equipo de Real-World Performance encontró que cambiar los literales para usar enlaces a menudo resultó en mejoras de órdenes de magnitud en el rendimiento y el tiempo de respuesta del usuario.

  • Seguridad
    La única manera de prevenir los ataques de inyección SQL es usar variables de enlace. Los usuarios malintencionados pueden explotar las aplicaciones de cadenas de conexión "inyectando" código en la aplicación.

20.4.2 No utilice CURSOR_SHARING = FORCE como solución permanente

La mejor práctica es escribir SQL compartible y usar el EXACTO predeterminado para CURSOR_SHARING.

Sin embargo, para aplicaciones con muchas declaraciones similares, establecer CURSOR_SHARING en FORCE a veces puede mejorar significativamente el uso compartido del cursor. Reemplazar literales con valores vinculados generados por el sistema reduce el uso de memoria, acelera el análisis y reduce la contención de bloqueos. Sin embargo, FORCE no es una solución de desarrollo permanente.

Como pauta general, Real-World Performance Group recomienda no establecer CURSOR_SHARING en una excepción FORCE en casos excepcionales, y solo si se cumplen todas las condiciones siguientes:

  • Las declaraciones en el grupo compartido difieren solo en valores literales.
  • Los tiempos de respuesta no son óptimos debido a la gran cantidad de errores de caché de la biblioteca.
  • Tiene errores graves de seguridad y escalabilidad en su código existente (faltan variables de vinculación) y necesita una curita temporal hasta que se solucione el código fuente.
  • Establece este parámetro de inicialización en el nivel de sesión, no en el nivel de instancia.

Establecer CURSOR_SHARING en FORCE tiene las siguientes desventajas:

  • Indica que la aplicación no utiliza variables de vinculación definidas por el usuario, lo que significa que está abierta a la inyección de SQL. Establecer CURSOR_SHARING en FORCE no corregirá los errores de inyección SQL ni hará que el código sea más seguro. Solo después de que se inyecte cualquier texto SQL malicioso, la base de datos vinculará el valor.
  • La base de datos tiene que hacer un trabajo adicional durante el análisis suave para encontrar declaraciones similares en el grupo compartido.
  • La base de datos elimina cada palabra, lo que significa que puede eliminar información útil. Por ejemplo, la base de datos elimina valores literales en las funciones SUBSTR y TO_DATE. El uso de variables de vinculación generadas por el sistema donde los literales están más optimizados puede afectar negativamente el plan de ejecución.
  • Se ha aumentado la longitud máxima (devuelta por DESCRIBE) de cualquier expresión seleccionada que contenga literales en una instrucción SELECT. Sin embargo, la longitud real de los datos devueltos no ha cambiado.
  • No se admiten las transformaciones de estrella.

20.4.3 Establecer convenciones de codificación para aumentar la reutilización del cursor

De forma predeterminada, cualquier cambio en el texto de las dos sentencias SQL evita que la base de datos comparta cursores , incluidos los nombres de las variables de vinculación. Además, los cambios en el tamaño de las variables de vinculación pueden provocar discrepancias en el cursor. Por lo tanto, el uso de variables de vinculación en el código de la aplicación no es suficiente para garantizar el uso compartido del cursor.

El Real-World Performance Team recomienda que estandarice las convenciones de espaciado y uso de mayúsculas para las instrucciones SQL y los bloques PL/SQL . También establezca convenciones para la denominación y definición de variables de vinculación . Si la base de datos no comparte cursores como se esperaba, inicie el diagnóstico consultando V$SQL_SHARED_CURSOR.

Ejemplo 20-17 Cambios en el texto SQL

En este ejemplo, una aplicación que usa variables de vinculación ejecuta siete declaraciones con el mismo valor de variable de vinculación, pero las declaraciones no son textualmente idénticas:

VARIABLE emp_id NUMBER
EXEC :emp_id := 101;

SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :EMP_ID;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :Emp_Id;
SELECT SUM(salary)  FROM hr.employees WHERE employee_id < :emp_id;
select sum(salary) from hr.employees where employee_id < :emp_id;
Select sum(salary) From hr.employees Where employee_id < :emp_id;
Select sum(salary) From hr.employees Where employee_id< :emp_id;

Una consulta de V$SQLAREA muestra que no se comparte el cursor:

COL SQL_TEXT FORMAT a35
SELECT SQL_TEXT, SQL_ID, VERSION_COUNT, HASH_VALUE
FROM   V$SQLAREA
WHERE  SQL_TEXT LIKE '%mployee%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%';

SQL_TEXT                            SQL_ID        VERSION_COUNT HASH_VALUE
----------------------------------- ------------- ------------- ----------
SELECT SUM(salary) FROM hr.employee bkrfu3ggu5315             1 3751971877
s WHERE employee_id < :EMP_ID
SELECT SUM(salary) FROM hr.employee 70mdtwh7xj9gv             1  265856507
s WHERE employee_id < :Emp_Id
Select sum(salary) From hr.employee 18tt4ny9u5wkt             1 2476929625
s Where employee_id< :emp_id
SELECT SUM(salary)  FROM hr.employe b6b21tbyaf8aq             1 4238811478
es WHERE employee_id < :emp_id
SELECT SUM(salary) FROM hr.employee 4318cbskba8yh             1  615850960
s WHERE employee_id < :emp_id
select sum(salary) from hr.employee 633zpx3xm71kj             1 4214457937
s where employee_id < :emp_id
Select sum(salary) From hr.employee 1mqbbbnsrrw08             1  830205960
s Where employee_id < :emp_id

7 rows selected.

Ejemplo 20-18 Discrepancia de longitud de enlace

El siguiente código define una variable de vinculación con una longitud diferente (20 bytes frente a 100 bytes) y luego ejecuta una declaración con el mismo texto usando el mismo valor de vinculación:

VARIABLE lname VARCHAR2(20)
EXEC :lname := 'Taylor';
SELECT SUM(salary) FROM hr.employees WHERE last_name = :lname;
VARIABLE lname VARCHAR2(100)
EXEC :lname := 'Taylor';
SELECT SUM(salary) FROM hr.employees WHERE last_name = :lname;

La siguiente consulta muestra que la base de datos no tiene cursores compartidos:

COL SQL_TEXT FORMAT a35
SELECT SQL_TEXT, SQL_ID, VERSION_COUNT, HASH_VALUE
FROM   V$SQLAREA
WHERE  SQL_TEXT LIKE '%mployee%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%';

SQL_TEXT                            SQL_ID	       VERSION_COUNT HASH_VALUE
----------------------------------- ------------- ------------- ----------
SELECT SUM(salary) FROM hr.employee buh8j4557r0h1             2 1249608193
s WHERE last_name = :lname

La razón es por la longitud de enlace:

COL BIND_LENGTH_UPGRADEABLE FORMAT a15
SELECT s.SQL_TEXT, s.CHILD_NUMBER, 
       c.BIND_LENGTH_UPGRADEABLE
FROM   V$SQL s, V$SQL_SHARED_CURSOR c
WHERE  SQL_TEXT LIKE '%employee%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%'
AND    s.CHILD_ADDRESS = c.CHILD_ADDRESS;

SQL_TEXT                            CHILD_NUMBER BIND_LENGTH_UPG
----------------------------------- ------------ ---------------
SELECT SUM(salary) FROM hr.employee            0 N
s WHERE last_name = :lname
SELECT SUM(salary) FROM hr.employee            1 Y
s WHERE last_name = :lname

20.4.4 Minimizar los cambios a nivel de sesión en el entorno del optimizador

Es una buena práctica evitar que los usuarios de su aplicación cambien el método de optimización y los objetivos de sus sesiones individuales. Cualquier cambio en el entorno del optimizador puede evitar que declaraciones idénticas compartan cursores.

Ejemplo 20-19 Desajuste del entorno

Este ejemplo muestra dos declaraciones con el mismo texto, pero no comparten un cursor:

VARIABLE emp_id NUMBER

EXEC :emp_id := 110;

ALTER SESSION SET OPTIMIZER_MODE = FIRST_ROWS;
SELECT salary FROM hr.employees WHERE employee_id < :emp_id;
ALTER SESSION SET OPTIMIZER_MODE = ALL_ROWS;
SELECT salary FROM hr.employees WHERE employee_id < :emp_id;

La consulta de V$SQL_SHARED_CURSOR muestra una discrepancia en el modo de optimización:

在这里插入代码片

Supongo que te gusta

Origin blog.csdn.net/stevensxiao/article/details/125263221
Recomendado
Clasificación