Creación de aplicaciones ampliables
Diseñe las aplicaciones para que utilicen los mismos recursos, independientemente de los grupos de usuarios y los volúmenes de datos, y no para sobrecargar los recursos del sistema.
Acerca de las aplicaciones escalables
Una aplicación escalable puede procesar una carga de trabajo más grande con un aumento proporcional en el uso de recursos del sistema.
Una aplicación escalable puede procesar una carga de trabajo más grande con un aumento proporcional en el uso de recursos del sistema. Por ejemplo, si duplica su carga de trabajo, una aplicación escalable utiliza el doble de recursos del sistema.
Una aplicación no escalable agota un recurso del sistema; por lo tanto, si aumenta la carga de trabajo de la aplicación, no se puede obtener más rendimiento. Las aplicaciones no escalables generan un rendimiento fijo y tiempos de respuesta deficientes.
Ejemplos de agotamiento de recursos son:
-
Agotamiento de hardware
-
Exploraciones de tablas en transacciones de gran volumen que causan una escasez inevitable de entrada/salida (E/S) del disco
-
Solicitudes de red excesivas que causan cuellos de botella en la red y la programación
-
Asignación de memoria que causa paginación e intercambio
-
Asignación excesiva de procesos y subprocesos que provoca la hiperpaginación del sistema operativo
Diseñe las aplicaciones para que utilicen los mismos recursos, independientemente de los grupos de usuarios y los volúmenes de datos, y no para sobrecargar los recursos del sistema.
Uso de Variables de Enlace para Mejorar la Escalabilidad
Las variables de enlace, utilizadas correctamente, le permiten desarrollar aplicaciones eficientes y escalables.
Una variable de enlace es un marcador de posición en una sentencia SQL que se debe sustituir por un valor o una dirección de valor válidos para que la sentencia se ejecute correctamente. Mediante variables ligadas, puede escribir una sentencia SQL que acepte entradas o parámetros en tiempo de ejecución.
Del mismo modo que un subprograma puede tener parámetros, cuyos valores suministra el invocador, una sentencia SQL puede tener marcadores de posición de variable de enlace, cuyos valores (denominados variables de enlace) se proporcionan en tiempo de ejecución. Al igual que un subprograma se compila una vez y, a continuación, se ejecuta muchas veces con diferentes parámetros, una sentencia SQL con marcadores de posición de variable de enlace se analiza una vez y, a continuación, se analiza de forma flexible con diferentes variables de enlace.
Un análisis pesado, que incluye optimización y generación de origen de fila, es una operación que consume mucha CPU. Un análisis suave, que omite la optimización y la generación del origen de fila y continúa directamente hasta la ejecución, suele ser mucho más rápido que un análisis pesado de la misma sentencia. Para obtener una visión general del procesamiento SQL, que incluye la diferencia entre un análisis pesado y ligero, consulte Conceptos de Oracle Database.
No solo es una operación de análisis pesado que utiliza mucha CPU, sino que es una operación no escalable, ya que no se puede realizar simultáneamente con muchas otras operaciones. Para obtener más información sobre la simultaneidad y la escalabilidad, consulte "Acerca de la simultaneidad y la escalabilidad".
El Ejemplo 8-1 muestra la diferencia de rendimiento entre una consulta sin una variable de enlace y una consulta semánticamente equivalente con una variable de enlace. El primero es más lento y utiliza muchos más bloqueos internos (para obtener información sobre cómo afectan los bloqueos internos a la escalabilidad, consulte). Para recopilar y mostrar estadísticas de rendimiento, en el ejemplo se utiliza la herramienta Runstats, que se describe en "Comparing Programming Techniques with Runstats".
Nota:
-
El ejemplo 8-1 muestra el costo de rendimiento para un solo usuario. A medida que se agregan más usuarios, el costo aumenta rápidamente.
-
El resultado del Ejemplo 8-1 se produjo con esta configuración:
SET SERVEROUTPUT ON FORMAT TRUNCATED
Nota:
-
El uso de variables de enlace en lugar de literales de cadena es la forma más eficaz de hacer que el código sea invulnerable a los ataques de inyección SQL. Para obtener más información, consulte Referencia de lenguaje PL/SQL de Oracle Database.
-
Las variables de enlace a veces reducen la eficiencia de los sistemas de almacenamiento de datos. Puesto que la mayoría de las consultas tardan tanto tiempo, el optimizador intenta producir el mejor plan para cada consulta en lugar de la mejor consulta genérica. El uso de variables ligadas a veces obliga al optimizador a producir la mejor consulta genérica. Para obtener información sobre cómo mejorar el rendimiento en los sistemas de almacenamiento de datos, consulte la Guía de almacenamiento de datos de Oracle Database.
Aunque el análisis ligero es más eficaz que el análisis pesado, el costo del análisis ligero de una sentencia muchas veces sigue siendo muy alto. Para maximizar la eficiencia y la escalabilidad de su aplicación, minimice el análisis. La forma más sencilla de minimizar el análisis es utilizar PL/SQL.
Ejemplo 8-1 La variable de enlace mejora el rendimiento
CREATE TABLE t ( x VARCHAR2(5) );
DECLARE
TYPE rc IS REF CURSOR;
l_cursor rc;
BEGIN
runstats_pkg.rs_start; -- Collect statistics for query without bind variable
FOR i IN 1 .. 5000 LOOP
OPEN l_cursor FOR 'SELECT x FROM t WHERE x = ' || TO_CHAR(i);
CLOSE l_cursor;
END LOOP;
runstats_pkg.rs_middle; -- Collect statistics for query with bind variable
FOR i IN 1 .. 5000 LOOP
OPEN l_cursor FOR 'SELECT x FROM t WHERE x = :x' USING i;
CLOSE l_cursor;
END LOOP;
runstats_pkg.rs_stop(500); -- Stop collecting statistics
end;
/
El resultado es similar al siguiente texto:
Run 1 ran in 740 hsec
Run 2 ran in 30 hsec
Run 1 ran in
2466.67% of the time of run 2
Name Run 1 Run 2 Difference
STAT...recursive cpu usage 729 19 -710
STAT...CPU used by this sessio 742 30 -712
STAT...parse time elapsed 1,051 4 -1,047
STAT...parse time cpu 1,066 2 -1,064
STAT...session cursor cache hi 1 4,998 4,997
STAT...table scans (short tabl 5,000 1 -4,999
STAT...parse count (total) 10,003 5,004 -4,999
LATCH.session idle bit 5,003 3 -5,000
LATCH.session allocation 5,003 3 -5,000
STAT...execute count 10,003 5,003 -5,000
STAT...opened cursors cumulati 10,003 5,003 -5,000
STAT...parse count (hard) 10,001 5 -9,996
STAT...CCursor + sql area evic 10,000 1 -9,999
STAT...enqueue releases 10,008 7 -10,001
STAT...enqueue requests 10,009 7 -10,002
STAT...calls to get snapshot s 20,005 5,006 -14,999
STAT...calls to kcmgcs 20,028 35 -19,993
STAT...consistent gets pin (fa 20,013 17 -19,996
LATCH.call allocation 20,002 6 -19,996
STAT...consistent gets from ca 20,014 18 -19,996
STAT...consistent gets 20,014 18 -19,996
STAT...consistent gets pin 20,013 17 -19,996
LATCH.simulator hash latch 20,014 11 -20,003
STAT...session logical reads 20,080 75 -20,005
LATCH.shared pool simulator 20,046 5 -20,041
LATCH.enqueue hash chains 20,343 15 -20,328
STAT...recursive calls 40,015 15,018 -24,997
LATCH.cache buffers chains 40,480 294 -40,186
STAT...session pga memory max 131,072 65,536 -65,536
STAT...session pga memory 131,072 65,536 -65,536
LATCH.row cache objects 165,209 139 -165,070
STAT...session uga memory max 219,000 0 -219,000
LATCH.shared pool 265,108 152 -264,956
STAT...logical read bytes from 164,495,360 614,400 -163,880,960
Run 1 latches total compared to run 2 -- difference and percentage
Run 1 Run 2 Diff Pct
562,092 864 -561,228 2,466.67%
PL/SQL procedure successfully completed.
Uso de PL/SQL para Mejorar la Escalabilidad
Ciertas funciones de PL/SQL pueden ayudarle a mejorar la escalabilidad de las aplicaciones.
Cómo PL/SQL Minimiza el Análisis
PL/SQL, que está optimizado para el acceso a la base de datos, almacena en caché sentencias de forma silenciosa. En PL/SQL, al cerrar un cursor, el cursor se cierra desde la perspectiva, es decir, no puede utilizarlo cuando se necesita un cursor abierto, pero PL/SQL mantiene el cursor abierto y almacena en caché su sentencia.
Si vuelve a utilizar la sentencia almacenada en caché, PL/SQL utiliza el mismo cursor, evitando así un análisis. (PL/SQL cierra las sentencias almacenadas en caché si es necesario; por ejemplo, si el programa debe abrir otro cursor, pero si lo hace, se superaría el valor init.ora de OPEN_CURSORS).
PL/SQL sólo puede almacenar en caché silenciosamente sentencias SQL que no pueden cambiar en tiempo de ejecución.
Acerca de la sentencia EXECUTE IMMEDIATE
La sentencia EXECUTE IMMEDIATE crea y ejecuta una sentencia SQL dinámica en una sola operación.
La sintaxis básica de la sentencia EXECUTE IMMEDIATE es:
EXECUTE IMMEDIATE sql_statement
sql_statement es una cadena que representa una sentencia SQL. Si la sentencia sql_statement tiene el mismo valor cada vez que se ejecuta la sentencia EXECUTE IMMEDIATE, PL/SQL puede almacenar en caché la sentencia EXECUTE IMMEDIATE. Si la sentencia sql_statement puede ser diferente cada vez que se ejecuta la sentencia EXECUTE IMMEDIATE, PL/SQL no puede almacenar en caché la sentencia EXECUTE IMMEDIATE.
Consulte además:
-
Referencia de lenguaje PL/SQL de Oracle Database para obtener información sobre EXECUTE IMMEDIATE
Acerca de OPEN FOR Statements
La sentencia OPEN FOR tiene la siguiente sintaxis básica.
La sintaxis básica de la sentencia OPEN FOR es:
OPEN cursor_variable FOR query
La aplicación puede abrir cursor_variable para varias consultas diferentes antes de cerrarla. Puesto que PL/SQL no puede determinar el número de consultas diferentes hasta el tiempo de ejecución, PL/SQL no puede almacenar en caché la sentencia OPEN FOR.
Si no necesita utilizar una variable de cursor, utilice un cursor declarado, tanto para un mejor rendimiento como para facilitar la programación. Para obtener más información, consulte Oracle Database Development Guide.
Consulte además:
-
Referencia de lenguaje PL/SQL de Oracle Database para obtener información sobre OPEN FOR
Acerca del Paquete DBMS_SQL
El paquete DBMS_SQL es una API para crear, ejecutar y describir sentencias SQL dinámicas. Debe utilizar el paquete DBMS_SQL en lugar de la sentencia EXECUTE IMMEDIATE si el compilador PL/SQL no puede determinar en tiempo de compilación el número o los tipos de variables de host de salida (seleccionar elementos de lista) o variables de enlace de entrada.
El paquete DBMS_SQL es una API para crear, ejecutar y describir sentencias SQL dinámicas. El uso del paquete DBMS_SQL requiere más esfuerzo que el uso de la sentencia EXECUTE IMMEDIATE, pero debe utilizar el paquete DBMS_SQL si el compilador PL/SQL no puede determinar en tiempo de compilación el número o los tipos de variables de host de salida (seleccionar elementos de lista) o variables de enlace de entrada.
Consulte además:
-
Referencia de lenguaje PL/SQL de Oracle Database para obtener más información sobre cuándo utilizar el paquete DBMS_SQL
-
Referencia de tipos y paquetes PL/SQL de Oracle Database para obtener información completa sobre el paquete DBMS_SQL
Acerca de SQL en Bloque
El SQL en bloque reduce el número de "viajes de ida y vuelta" entre PL/SQL y SQL, con lo que se utilizan menos recursos.
Sin SQL en bloque, recupera una fila a la vez de la base de datos (SQL), la procesa (PL/SQL) y la devuelve a la base de datos (SQL). Con SQL masivo, recupera un juego de filas de la base de datos, procesa el juego de filas y, a continuación, devuelve todo el juego a la base de datos.
Oracle recomienda utilizar SQL en bloque al recuperar varias filas de la base de datos y devolverlas a la base de datos, como en el Ejemplo 8-2. No necesita SQL en bloque si recupera varias filas pero no las devuelve; por ejemplo:
FOR x IN (SELECT * FROM t WHERE ... ) -- Retrieve row set (implicit array fetch)
LOOP
DBMS_OUTPUT.PUT_LINE(t.x); -- Process rows but do not return them
END LOOP;
El ejemplo 8-2 realiza bucles a través de una tabla t con un nombre_objeto de columna, recuperando juegos de 100 filas, procesándolos y devolviéndolos a la base de datos. (Para limitar la sentencia FETCH en bloque a 100 filas se necesita un cursor explícito).
El ejemplo 8-3 realiza el mismo trabajo que el ejemplo 8-2, sin SQL en bloque.
Como se muestra en estos informes TKPROF para el Ejemplo 8-2 y el Ejemplo 8-3, el uso de SQL en bloque para este trabajo utiliza casi un 50 % menos de tiempo de CPU:
SELECT ROWID RID, OBJECT_NAME FROM T T_BULK
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 721 0.17 0.17 0 22582 0 71825
********************************************************************************
UPDATE T SET OBJECT_NAME = :B1 WHERE ROWID = :B2
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 719 12.83 13.77 0 71853 74185 71825
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 720 12.83 13.77 0 71853 74185 71825
SELECT ROWID RID, OBJECT_NAME FROM T T_SLOW_BY_SLOW
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 721 0.17 0.17 0 22582 0 71825
********************************************************************************
UPDATE T SET OBJECT_NAME = :B2 WHERE ROWID = :B1
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 71824 21.25 22.25 0 71836 73950 71824
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 71825 21.25 22.25 0 71836 73950 71824
Sin embargo, el uso de SQL en bloque para este trabajo utiliza más tiempo de CPU y más código que el uso de una sola sentencia SQL, como muestra este informe TKPROF:
UPDATE T SET OBJECT_NAME = SUBSTR(OBJECT_NAME,2) || SUBSTR(OBJECT_NAME,1,1)
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 1.30 1.44 0 2166 75736 71825
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 2 1.30 1.44 0 2166 75736 71825
Ejemplo 8-2 SQL en bloque
CREATE OR REPLACE PROCEDURE bulk AS
TYPE ridArray IS TABLE OF ROWID;
TYPE onameArray IS TABLE OF t.object_name%TYPE;
CURSOR c is SELECT ROWID rid, object_name -- explicit cursor
FROM t t_bulk;
l_rids ridArray;
l_onames onameArray;
N NUMBER := 100;
BEGIN
OPEN c;
LOOP
FETCH c BULK COLLECT
INTO l_rids, l_onames LIMIT N; -- retrieve N rows from t
FOR i in 1 .. l_rids.COUNT
LOOP -- process N rows
l_onames(i) := substr(l_onames(i),2) || substr(l_onames(i),1,1);
END LOOP;
FORALL i in 1 .. l_rids.count -- return processed rows to t
UPDATE t
SET object_name = l_onames(i)
WHERE ROWID = l_rids(i);
EXIT WHEN c%NOTFOUND;
END LOOP;
CLOSE c;
END;
/
Ejemplo 8-3 Sin SQL en Bloque
CREATE OR REPLACE PROCEDURE slow_by_slow AS
BEGIN
FOR x IN (SELECT rowid rid, object_name FROM t t_slow_by_slow)
LOOP
x.object_name := substr(x.object_name,2) || substr(x.object_name,1,1);
UPDATE t
SET object_name = x.object_name
WHERE rowid = x.rid;
END LOOP;
END;
Consulte además:
-
Guía de desarrollo de Oracle Database para obtener una visión general de SQL masivo
-
Oracle Database Development Guide para obtener información más específica sobre cuándo utilizar SQL en bloque
-
Referencia de lenguaje PL/SQL de Oracle Database para obtener más información sobre SQL en bloque
Acerca de la simultaneidad y escalabilidad
La concurrencia es la ejecución simultánea de varias transacciones. Cuanto mejor maneje la aplicación la simultaneidad, más escalable será. Una aplicación escalable puede procesar una carga de trabajo más grande con un aumento proporcional en el uso de recursos del sistema.
La concurrencia es la ejecución simultánea de varias transacciones. Las sentencias de transacciones simultáneas pueden actualizar los mismos datos. Cuanto mejor maneje la aplicación la simultaneidad, más escalable será. Una aplicación escalable puede procesar una carga de trabajo más grande con un aumento proporcional en el uso de recursos del sistema. Por ejemplo, si duplica su carga de trabajo, una aplicación escalable utiliza el doble de recursos del sistema.
Las transacciones simultáneas deben producir resultados significativos y coherentes. Por lo tanto, una base de datos multiusuario debe proporcionar lo siguiente:
-
Concurrencia de datos, que garantiza que los usuarios puedan acceder a los datos al mismo tiempo.
-
Coherencia de datos, que garantiza que cada usuario vea una vista consistente de los datos, incluidos los cambios visibles de sus propias transacciones y las transacciones confirmadas de otros usuarios
Oracle Database mantiene la consistencia de los datos mediante un modelo de consistencia de varias versiones y varios tipos de bloqueos y niveles de aislamiento de transacciones. Para obtener una visión general del mecanismo de bloqueo de Oracle Database, consulte Conceptos de Oracle Database. Para obtener una visión general de los niveles de aislamiento de transacciones de Oracle Database, consulte Oracle Database Concepts.
Para describir el comportamiento consistente de las transacciones cuando se ejecutan simultáneamente, los investigadores de la base de datos han definido una categoría de aislamiento de transacciones denominada serializable. Una transacción serializable funciona en un entorno que parece ser una base de datos de un solo usuario. Las transacciones serializables son deseables en casos específicos, pero para el 99% de la carga de trabajo, el aislamiento confirmado de lectura es perfecto.
Oracle Database cuenta con funciones que mejoran la simultaneidad y la escalabilidad, por ejemplo, secuencias, bloqueos internos, lecturas y escrituras sin bloqueo y SQL compartido.
Consulte también: Conceptos de Oracle Database para obtener más información sobre la simultaneidad y consistencia de los datos
Acerca de las Secuencias y Simultaneidad
Las secuencias eliminan la serialización, lo que mejora la simultaneidad y la escalabilidad de la aplicación.
Una secuencia es un objeto de esquema a partir del cual varios usuarios pueden generar enteros únicos, lo que resulta muy útil cuando se necesitan claves primarias únicas.
Sin secuencias, los valores de clave primaria únicos se deben producir mediante programación. Un usuario obtiene un nuevo valor de clave primaria seleccionando el valor producido más recientemente e incrementándolo. Esta técnica requiere un bloqueo durante la transacción y hace que varios usuarios esperen al siguiente valor de clave primaria, es decir, las transacciones se serializan. Las secuencias eliminan la serialización, lo que mejora la simultaneidad y la escalabilidad de la aplicación.
Consulte además:
-
Conceptos de Oracle Database para obtener información sobre el acceso simultáneo a las secuencias
Acerca de los cierres y la simultaneidad
Un aumento en los bloqueos internos significa más esperas basadas en simultaneidad y, por lo tanto, una disminución en la escalabilidad.
Un bloqueo es un mecanismo de serialización simple y de bajo nivel que coordina el acceso de varios usuarios a estructuras de datos compartidas. Los bloqueos internos protegen los recursos de memoria compartida de la corrupción cuando se accede mediante varios procesos.
Un aumento en los bloqueos internos significa más esperas basadas en simultaneidad y, por lo tanto, una disminución en la escalabilidad. Si puede utilizar un enfoque que se ejecute ligeramente más rápido durante el desarrollo o uno que utilice menos bloqueos internos, utilice el último.
Consulte además:
-
Conceptos de Oracle Database para obtener información sobre bloqueos internos
-
Conceptos de Oracle Database para obtener información sobre mutex, que son como bloqueos internos para objetos únicos
Acerca de las lecturas y escrituras no bloqueantes y la simultaneidad
En Oracle Database, las lecturas y escrituras sin bloqueo permiten que las consultas se ejecuten simultáneamente con los cambios en los datos que están leyendo, sin bloquear ni parar. Las lecturas y escrituras que no bloquean permiten a una sesión leer datos mientras otra sesión cambia esos datos.
Acerca de SQL Compartido y Simultaneidad
Oracle Database compila una sentencia SQL en un objeto ejecutable una vez y, a continuación, otras sesiones pueden reutilizar el objeto durante el tiempo que exista. Esta función de Oracle Database, denominada SQL compartido, permite a la base de datos realizar operaciones de compilación y optimización de sentencias SQL muy intensivas en recursos solo una vez, en lugar de cada vez que una sesión utiliza la misma sentencia SQL.
Consulte también: Conceptos de Oracle Database para obtener más información sobre SQL compartido
Limitación del Número de Sesiones Simultáneas
Cuantas más sesiones simultáneas tenga, más esperas basadas en simultaneidad tendrá y más lento será el tiempo de respuesta.
Si el equipo tiene n núcleos de CPU, como máximo n sesiones pueden estar activas de forma simultánea. Cada sesión "concurrente" adicional debe esperar a que un núcleo de CPU esté disponible antes de que se pueda activar. Si algunas sesiones en espera solo esperan E/S, el aumento del número de sesiones simultáneas a un poco más de n puede mejorar ligeramente el rendimiento en tiempo de ejecución. Sin embargo, el aumento excesivo del número de sesiones simultáneas reducirá significativamente el rendimiento en tiempo de ejecución.
El parámetro de inicialización SESSIONS determina el número máximo de usuarios simultáneos en el sistema. Para obtener más información, consulte Referencia de Oracle Database.
Consulte también: http://www.youtube.com/watch?v=xNDnVOCdvQ0 para ver un vídeo que muestra el efecto de reducir el número de sesiones simultáneas en un equipo con 12 núcleos de CPU de miles a 96.
Comparación de Técnicas de Programación con Runstats
La herramienta Runstats le permite comparar el rendimiento de dos técnicas de programación para ver cuál es mejor.
Acerca de Runstats
La herramienta Runstats le permite comparar el rendimiento de dos técnicas de programación para ver cuál es mejor.
Runstats mide los siguientes valores:
-
Tiempo transcurrido para cada técnica en centésimas de segundos (hseg)
-
Tiempo transcurrido para la primera técnica como porcentaje del de la segunda técnica
-
Estadísticas del sistema para las dos técnicas (por ejemplo, llamadas de análisis)
-
Cierre para las dos técnicas
De las mediciones anteriores, lo más importante es el bloqueo interno (consulte "Acerca de los Bloqueos Internos y la Simultaneidad").
Consulte también: Ejemplo 8-1, que utiliza Runstats
Definición de estadísticas de ejecución
La herramienta Runstats se implementa como un paquete que utiliza una vista y una tabla temporal.
Nota: Para el paso 1 del siguiente procedimiento, necesita el privilegio SELECT en las vistas de rendimiento dinámico V$STATNAME, V$MYSTAT y V$LATCH. Si no puede obtener este privilegio, haga que alguien que tenga el privilegio cree la vista en el paso 1 y le otorgue el privilegio SELECT.
Pasos para configurar la herramienta Runstats:
-
Cree la vista que Runstats utiliza:
CREATE OR REPLACE VIEW stats AS SELECT 'STAT...' || a.name name, b.value FROM V$STATNAME a, V$MYSTAT b WHERE a.statistic# = b.statistic# UNION ALL SELECT 'LATCH.' || name, gets FROM V$LATCH; -
Cree la tabla temporal que Runstats utiliza:
DROP TABLE run_stats; CREATE GLOBAL TEMPORARY TABLE run_stats ( runid VARCHAR2(15), name VARCHAR2(80), value INT ) ON COMMIT PRESERVE ROWS; -
Cree esta especificación del paquete:
CREATE OR REPLACE PACKAGE runstats_pkg AS PROCEDURE rs_start; PROCEDURE rs_middle; PROCEDURE rs_stop( p_difference_threshold IN NUMBER DEFAULT 0 ); end; /El parámetro
p_difference_thresholdcontrola la cantidad de estadísticas y datos de bloqueo interno que muestra Runstats. Runstats muestra datos solo cuando la diferencia para las dos técnicas es mayor quep_difference_threshold. Por defecto, Runstats muestra todos los datos. -
Cree el siguiente cuerpo del paquete:
CREATE OR REPLACE PACKAGE BODY runstats_pkg AS g_start NUMBER; g_run1 NUMBER; g_run2 NUMBER; PROCEDURE rs_start IS BEGIN DELETE FROM run_stats; INSERT INTO run_stats SELECT 'before', stats.* FROM stats; g_start := DBMS_UTILITY.GET_TIME; END rs_start; PROCEDURE rs_middle IS BEGIN g_run1 := (DBMS_UTILITY.GET_TIME - g_start); INSERT INTO run_stats SELECT 'after 1', stats.* FROM stats; g_start := DBMS_UTILITY.GET_TIME; END rs_middle; PROCEDURE rs_stop( p_difference_threshold IN NUMBER DEFAULT 0 ) IS BEGIN g_run2 := (DBMS_UTILITY.GET_TIME - g_start); DBMS_OUTPUT.PUT_LINE ('Run 1 ran in ' || g_run1 || ' hsec'); DBMS_OUTPUT.PUT_LINE ('Run 2 ran in ' || g_run2 || ' hsec'); DBMS_OUTPUT.PUT_LINE ('Run 1 ran in ' || round(g_run1/g_run2*100, 2) || '% of the time of run 2'); DBMS_OUTPUT.PUT_LINE( CHR(9) ); INSERT INTO run_stats SELECT 'after 2', stats.* FROM stats; DBMS_OUTPUT.PUT_LINE ( RPAD( 'Name', 30 ) || LPAD( 'Run 1', 14) || LPAD( 'Run 2', 14) || LPAD( 'Difference', 14) ); FOR x IN ( SELECT RPAD( a.name, 30 ) || TO_CHAR( b.value - a.value, '9,999,999,999' ) || TO_CHAR( c.value - b.value, '9,999,999,999' ) || TO_CHAR( ( (c.value - b.value) - (b.value - a.value)), '9,999,999,999' ) data FROM run_stats a, run_stats b, run_stats c WHERE a.name = b.name AND b.name = c.name AND a.runid = 'before' AND b.runid = 'after 1' AND c.runid = 'after 2' AND (c.value - a.value) > 0 AND abs((c.value - b.value) - (b.value - a.value)) > p_difference_threshold ORDER BY ABS((c.value - b.value) - (b.value - a.value)) ) LOOP DBMS_OUTPUT.PUT_LINE( x.data ); END LOOP; DBMS_OUTPUT.PUT_LINE( CHR(9) ); DBMS_OUTPUT.PUT_LINE( 'Run 1 latches total compared to run 2 -- difference and percentage' ); DBMS_OUTPUT.PUT_LINE ( LPAD( 'Run 1', 14) || LPAD( 'Run 2', 14) || LPAD( 'Diff', 14) || LPAD( 'Pct', 10) ); FOR x IN ( SELECT TO_CHAR( run1, '9,999,999,999' ) || TO_CHAR( run2, '9,999,999,999' ) || TO_CHAR( diff, '9,999,999,999' ) || TO_CHAR( ROUND( g_run1/g_run2*100, 2), '99,999.99' ) || '%' data FROM ( SELECT SUM (b.value - a.value) run1, SUM (c.value - b.value) run2, SUM ( (c.value - b.value) - (b.value - a.value)) diff FROM run_stats a, run_stats b, run_stats c WHERE a.name = b.name AND b.name = c.name AND a.runid = 'before' AND b.runid = 'after 1' AND c.runid = 'after 2' AND a.name like 'LATCH%' ) ) LOOP DBMS_OUTPUT.PUT_LINE( x.data ); END LOOP; END rs_stop; END; /
Consulte además:
-
Referencia de Oracle Database para obtener información sobre las vistas de rendimiento dinámico
Uso de Runstats
En este tema se proporciona la sintaxis para utilizar la herramienta Runstats.
Para utilizar Runstats para comparar dos técnicas de programación, llame a los procedimientos runstats_pkg desde un bloque anónimo, utilizando esta sintaxis:
[ DECLARE local_declarations ]
BEGIN
runstats_pkg.rs_start;
code_for_first_technique
runstats_pkg.rs_middle;
code_for_second_technique
runstats_pkg.rs_stop(n);
END;
/
Consulte también: Ejemplo 8-1, que utiliza Runstats
Técnicas de procesamiento de datos y rendimiento en el mundo real
Una tarea común en las aplicaciones de base de datos en un entorno de almacén de datos es consultar o modificar un gran juego de datos. El problema para los desarrolladores de aplicaciones es cómo lograr un alto rendimiento al procesar grandes juegos de datos.
Las técnicas de procesamiento se dividen en dos categorías: iterativas y basadas en conjuntos. A lo largo de años de pruebas, el grupo Real-World Performance ha descubierto que las técnicas de procesamiento basadas en conjuntos realizan órdenes de magnitud mejores para las aplicaciones de base de datos que procesan grandes conjuntos de datos.
En este tema se incluyen los siguientes subtemas principales:
Acerca del procesamiento de datos iterativos
En el procesamiento iterativo, las aplicaciones utilizan la lógica condicional para realizar bucles en un juego de filas.
Normalmente, aunque no necesariamente, el procesamiento iterativo utiliza un modelo de cliente/servidor de la siguiente manera:
-
Transferir un grupo de filas del servidor de base de datos a la aplicación cliente.
-
Procese el grupo dentro de la aplicación cliente.
-
Vuelva a transferir el grupo procesado al servidor de base de datos.
Se pueden implantar algoritmos iterativos mediante tres técnicas principales: procesamiento fila por fila, procesamiento de matriz y paralelismo manual.
Procesamiento iterativo: fila por fila
En el proceso fila por fila, un único proceso realiza bucles a través de un juego de datos y funciona en una sola fila a la vez. En una implantación típica, la aplicación recupera cada fila de la base de datos, la procesa en la capa media y, a continuación, envía la fila de nuevo a la base de datos, que ejecuta DML y confirmaciones.
Supongamos que su requisito funcional es consultar una tabla externa denominada ext_scan_events y, a continuación, insertar sus filas en una tabla temporal organizada por pilas denominada stage1_scan_events. El siguiente bloque PL/SQL utiliza una técnica de fila por fila para cumplir este requisito:
declare
cursor c is select s.* from ext_scan_events s;
r c%rowtype;
begin
open c;
loop
fetch c into r;
exit when c%notfound;
insert into stage1_scan_events d values r;
commit;
end loop;
close c;
end;
La técnica fila por fila tiene las siguientes ventajas:
-
Funciona bien en pequeños conjuntos de datos.
-
El algoritmo de bucle es familiar para todos los desarrolladores profesionales, fácil de escribir rápidamente y fácil de entender.
La técnica fila por fila tiene las siguientes desventajas:
-
El tiempo de procesamiento puede ser inaceptablemente largo para grandes conjuntos de datos.
-
La aplicación se ejecuta en serie y, por lo tanto, no puede aprovechar las funciones de procesamiento paralelo nativas de Oracle Database que se ejecutan en hardware moderno.
Véase también: Procesamiento basado en conjuntos RWP #7
Procesamiento iterativo: matrices
El proceso de matriz es idéntico al proceso fila por fila, excepto que procesa un grupo de filas en cada iteración en lugar de una sola fila.
Supongamos que el requisito funcional es el mismo que en el ejemplo X-X: consulte una tabla externa denominada ext_scan_events y, a continuación, inserte sus filas en una tabla temporal organizada por pilas denominada stage1_scan_events. El siguiente bloque PL/SQL utiliza una técnica de matriz para cumplir este requisito:
declare
cursor c is select s.* from ext_scan_events s;
type t is table of c%rowtype index by binary_integer;
a t;
rows binary_integer := 0;
begin
open c;
loop
fetch c bulk collect into a limit array_size;
exit when a.count = 0;
forall i in 1..a.count
insert into stage1_scan_events d values a(i);
commit;
end loop;
close c;
end;
El código anterior difiere del código fila por fila equivalente en el uso de un operador BULK COLLECT en FETCH STATEMENT, que está limitado por el valor array_size del tipo PLS_INTEGER. Por ejemplo, si array_size se define en 100, la aplicación recupera filas en grupos de 100.
La técnica de matriz tiene las siguientes ventajas con respecto a la técnica fila por fila:
-
La matriz permite a la aplicación procesar un grupo de filas al mismo tiempo, lo que significa que reduce los recorridos de ida y vuelta de la red, el tiempo de COMMIT y la ruta de código en el cliente y el servidor.
-
La base de datos es más eficaz porque el proceso de servidor realiza por lotes las inserciones y confirma después de cada grupo de inserciones en lugar de después de cada inserción.
Las desventajas de esta técnica son las mismas que para el procesamiento fila por fila. El tiempo de procesamiento puede ser inaceptable para grandes juegos de datos. Además, la aplicación debe ejecutarse en serie en un único núcleo de CPU y, por lo tanto, no puede aprovechar el paralelismo nativo de Oracle Database.
Procesamiento iterativo: paralelismo manual
El paralelismo manual utiliza el mismo algoritmo iterativo que el procesamiento fila por fila y matriz, pero permite que varios procesos de servidor dividan el trabajo y se ejecuten en paralelo.
Supongamos que el requisito funcional es el mismo que el de los ejemplos de fila por fila y matriz. Las principales diferencias son las siguientes:
-
Los registros de eventos de análisis se almacenan en una masa de archivos planos.
-
32 procesos de servidor deben ejecutarse en paralelo, con cada proceso de servidor consultando una tabla externa diferente.
-
Utilice PL/SQL para lograr el paralelismo mediante la ejecución de 32 threads del mismo programa PL/SQL, con cada thread ejecutándose simultáneamente como un trabajo independiente gestionado por Oracle Scheduler. Un trabajo es la combinación de un programa y un programa.
El siguiente código PL/SQL utiliza el paralelismo manual:
declare
sqlstmt varchar2(1024) := q'[
-- BEGIN embedded anonymous block
cursor c is select s.* from ext_scan_events_${thr} s;
type t is table of c%rowtype index by binary_integer;
a t;
rows binary_integer := 0;
begin
for r in (select ext_file_name from ext_scan_events_dets where ora_hash(file_seq_nbr,${thrs}) = ${thr})
loop
execute immediate
'alter table ext_scan_events_${thr} location' || '(' || r.ext_file_name || ')';
open c;
loop
fetch c bulk collect into a limit ${array_size};
exit when a.count = 0;
forall i in 1..a.count
insert into stage1_scan_events d values a(i);
commit;
-- demo instrumentation
rows := rows + a.count; if rows > 1e3 then exit when not sd_control.p_progress('loading','userdefined',rows); rows := 0; end if;
end loop;
close c;
end loop;
end;
-- END embedded anonymous block
]';
begin
sqlstmt := replace(sqlstmt, '${array_size}', to_char(array_size));
sqlstmt := replace(sqlstmt, '${thr}', thr);
sqlstmt := replace(sqlstmt, '${thrs}', thrs);
execute immediate sqlstmt;
end;
La función ORA_HASH divide la tabla ext_scan_events_dets en 32 cubos distribuidos uniformemente y, a continuación, la sentencia SELECT recupera los nombres de archivo para el cubo 0. Para cada nombre de archivo del cubo, el programa define la ubicación de la tabla externa en este nombre de archivo. A continuación, el programa utiliza el proceso en batch para consultar la tabla externa, insertarla en la tabla temporal y, a continuación, confirmarla.
Mientras se ejecuta el trabajo 1, los otros 31 trabajos de Oracle Scheduler se ejecutan en paralelo. De esta forma, cada trabajo lee simultáneamente un subjuego diferente de los archivos de eventos de exploración e inserta los registros de su subjuego en la misma tabla temporal.
La técnica de paralelismo manual tiene las siguientes ventajas sobre las técnicas iterativas alternativas:
-
Se desempeña mucho mejor en juegos de datos grandes porque los procesos de servidor funcionan en paralelo.
-
Cuando la aplicación utiliza ORA_HASH para distribuir la carga de trabajo, cada thread de ejecución puede acceder a la misma cantidad de datos, lo que significa que los procesos paralelos pueden terminar al mismo tiempo.
La técnica de paralelismo manual tiene las siguientes desventajas:
-
El código es relativamente largo, complicado y difícil de entender.
-
La aplicación debe realizar una cierta cantidad de trabajo preparatorio antes de que la base de datos pueda iniciar el trabajo principal, que es el procesamiento de las filas en paralelo.
-
Si varios threads realizan las mismas operaciones en un juego común de objetos de base de datos, es posible la contención de bloqueo y bloqueo interno.
-
El procesamiento paralelo consume importantes recursos de CPU en comparación con las técnicas iterativas de la competencia.
Véase también: RWP #8: Procesamiento paralelo basado en conjuntos
Acerca del procesamiento basado en juegos
El procesamiento basado en juegos es una técnica SQL que procesa un juego de datos dentro de la base de datos.
En un modelo basado en juegos, la sentencia SQL define el resultado y permite a la base de datos determinar la forma más eficaz de obtenerlo. Por el contrario, los algoritmos iterativos utilizan la lógica condicional para extraer cada fila o grupo de filas de la base de datos a la aplicación cliente, procesar los datos en el cliente y, a continuación, enviar los datos a la base de datos. El procesamiento basado en juegos elimina la sobrecarga de la API de la base de datos y el recorrido de ida y vuelta de la red porque los datos nunca salen de la base de datos.
Supongamos el mismo requisito funcional que en los ejemplos anteriores. Las siguientes sentencias SQL cumplen este requisito mediante un algoritmo basado en juegos:
alter session enable parallel dml;
insert /*+ APPEND */ into stage1_scan_events d
select s.* from ext_scan_events s;
commit;
Debido a que la sentencia INSERT contiene una subconsulta de la tabla ext_scan_events, una sentencia SQL única lee y escribe todas las filas. Además, la aplicación ejecuta una única confirmación después de que la base de datos haya insertado todas las filas. En cambio, las aplicaciones iterativas ejecutan un COMMIT después de insertar cada fila o cada grupo de filas.
La técnica basada en conjuntos tiene ventajas significativas sobre las técnicas iterativas:
-
Como se demuestra en las demostraciones y clases de Real-World Performance, el rendimiento en grandes conjuntos de datos es órdenes de magnitud más rápido. No es inusual que el tiempo de ejecución de un programa disminuya de varias horas a varios segundos.
-
Un efecto secundario del aumento de los órdenes de magnitud en la velocidad de procesamiento es que los administradores de bases de datos pueden eliminar los trabajos por lotes de larga ejecución y propensos a errores, e invocar procesos de negocio en tiempo real.
-
La longitud del código es significativamente más corta, una abreviatura de dos o tres líneas de código, porque SQL define el resultado y no el método de acceso.
-
A diferencia del paralelismo manual, el DML paralelo se optimiza para el rendimiento porque la base de datos, en lugar de la aplicación, gestiona los procesos.
-
Al unir juegos de datos, la base de datos utiliza automáticamente uniones hash de alta eficacia en lugar de bucles de nivel de aplicación relativamente ineficaces.
-
La indicación APPEND fuerza una carga de ruta de acceso directa, lo que significa que la base de datos no crea redo ni deshacer, evitando así el desperdicio de E/S y CPU.
El procesamiento basado en juegos tiene algunas desventajas potenciales:
-
Las técnicas no son familiares para muchos desarrolladores de bases de datos, por lo que pueden ser más difíciles.
-
Debido a que un modelo basado en conjuntos es completamente diferente de un modelo iterativo, cambiarlo requiere reescribir completamente el código fuente.
Véase también: RWP #7 Set-Based Processing, RWP #8: Set-Based Parallel Processing, RWP #9: Set-Based Processing–Data Deduplication, RWP #10: Set-Based Processing–Data Transformations, y RWP #11: Set-Based Processing–Data Aggregation