22 diciembre 2016

¿Hay vida más allá de Oracle? Malebolge el lenguaje infernal


Malbolge es un lenguaje de programación esotérico de dominio público desarrollado por Ben Olmstead en 1998. Se llamó así por el octavo círculo del infierno en La Divina Comedia, escrito por Dante.

Malbolge es peculiar porque se diseñó para ser el lenguaje más difícil. Sin embargo, varios de los trucos utilizados para hacerlo difícil de entender pueden ser evitados.


Este lenguaje tiene una utilidad muy buena, que al ser el más difícil de entender y al conocer este lenguaje tan poca gente, si te roban el código una empresa, tardarán muchos años y mucho dinero en personal en entender qué hace. Sin embargo, para cosas más complejas es imposible usarlo. Como muestra gráfica de ello, decir que Hisashi Iizawa, programador de Malbolge, tardó 7 años en realizar un programa que muestre por pantalla la canción completa de 99 Bottles of Beer.


Este es el Hola Mundo en Malebolge:

 (=<`:9876Z4321UT.-Q+*)M'&%$H"!~}|Bzy?=|{z]KwZY44Eq0/{mlk**
 hKs_dG5[m_BA{?-Y;;Vb'rR5431M}/.zHGwEDCBA@98\6543W10/.R,+O<

Si quieres saber mas:

  • https://esolangs.org/wiki/Talk:Malbolge
  • http://www.lscheffer.com/malbolge.shtml

28 noviembre 2016

V$SESSION : Sácale partido a esta vista


¿Qué es V$SESSION?

La vista V$SESSION es la base de toda la información relacionada con el estado actual del sistema:
  • ¿Cuántas sesiones hay actualmente corriendo en la base de datos?
  • ¿Qué consulta se está ejecutando?
  • ¿Cuánto tardan en ejecutarse estas consultas?
 V$SESSION es el primer lugar cuando DBA comienza a buscar información relacionada con el rendimiento  e información de la ejecución de las consultas, un DBA puede llegar a consultar un centenar de veces al día esta vista, así que arrojemos un poco de luz sobre esta vista.



A efectos prácticos este artículo está basado en una base de datos Oracle 11g, usaremos:
  • V$session: para una estancia sencilla.
  • GV$session: para una instalación en cluster (RAC).

Listado de las sesiones ejecutándose


Lo primero que un DBA busca en nuestra vista favorita es la lista de sesiones de usuario de bases de datos. Hay dos tipos de sesiones de base de datos, principalmente background y sesiones de usuario. La sesión background se utiliza para la funcionalidad básica de la base de datos como dbwr, arch, smon, pmon etc. La sesión del usuario es la sesión que realiza las operaciones del usuario.

Cada vez que se conecta a los datos se crea una sesión en la base de datos para realizar sus operaciones. Un DBA puede ver fácilmente esto consultando la vista de sistema V$SESSION.

Actualmente sólo hay una sesión de usuario en ejecución. 
SQL> select count(*),type from v$session group by type;
  COUNT(*) TYPE
---------- ----------
         1 USER
        49 BACKGROUND
En el caso del entorno RAC, DBA debe utilizar GV$SESSION en lugar de V$SESSION.
   
SQL> select count(*),type,INST_ID from gv$session group by type,inst_id;
 COUNT(*) TYPE              INST_ID
----------  ---------- ----------
        21  USER            1
        48  BACKGROUND       1
        49  BACKGROUND       2
        17  USER         2

Supongamos que tenemos dos nodos RAC, la instancia 1 tiene 21 usuarios y 48 sesiones en background, mientras que la instancia 2 tiene 17 usuarios y 49 sesiones en background. Esta información es útil cuando se produce el siguiente error "ORA-00020: maximum number of processes (%s) exceeded" error. Puede utilizar la consulta siguiente para identificar qué usuario está creando un número alto de sesiones.
SQL> select SID,USERNAME,COMMAND,PROCESS,TERMINAL,PROGRAM from gv$session where type='USER';
       SID USERNAME                          COMMAND PROCESS                  TERMINAL                       PROGRAM
---------- ------------------------------ ---------- ------------------------ ------------------------------ --------
        16 SYS                                    47 17978                                                   oraagent.bin@dbarm2 (TNS V1-V3)
        19 DBSNMP                                  0 1234                     unknown                        JDBC Thin Client
        25 SYSMAN                                  0 1234                     unknown                        OMS
        26 DBSNMP                                  0 1234                     unknown                        JDBC Thin Client
  • SID es la ID de la sesión, USERNAME es el nombre del usuario de la base de datos.
  • Process es el número de proceso. 
  • Terminal es el nombre del sistema que esta ejecutando la consulta. 
  • Program muestra el nombre del programa que esta usando la consulta.

Terminal y Program son los campos más importantes para encontrar las sesiones culpables de los errores ORA-00020.

Es una lista del uso de la vista V$SESSION, he enumerado algunos de ellos aquí. Por favor, comparte en los comentarios si sabéis más.

Encontrar sesiones bloqueantes

Una queja común al administrador de la base de datos por el usuario es "mi conexión de la base de datos es muy lenta". En este caso DBA debe comprobar dos cosas O toda la base de datos se está ejecutando lento o Sólo una sesión de usuario es lenta. Para comprobar el estado completo de la base de datos, DBA puede utilizar Top Command, Watcher de OS e Informe AWR.
Si está seguro de que sólo una sesión única se está ejecutando lento entonces V$session o gv$session es el lugar perfecto para empezar. He visto muchos casos en los que una sesión está bloqueando otra sesión debido a transacciones no confirmadas.

SQL> select sid, username,command,status,program,sql_id,BLOCKING_INSTANCE,BLOCKING_SESSION,WAIT_CLASS from gv$session where BLOCKING_SESSION is not null;
       SID USERNAME      COMMAND STATUS   PROGRAM                                          SQL_ID        BLOCKING_INSTANCE BLOCKING_SESSION WAIT_CLASS
---------- ---------- ---------- -------- ------------------------------------------------ ------------- ----------------- ---------------- ---------------
        47 SCOTT               6 ACTIVE   sqlplus@database.example.com (TNS V1-V3)         2rbwgj3zdsnus                 1               34 Application

En el caso anterior, el usuario scott no recibía ninguna respuesta de su consulta. Comprobé utilizando la consulta anterior en la que "BLOCKING_SESSION" muestra el detalle de la sesión que está bloqueando la sesión de usuario SCOTT
Los campos: 
  • SID, 
  • USERNAME,
  • COMMAND, 
  • STATUS, 
  • PROGRAM y 
  • SQL_ID 
son detalles del usuario SCOTT, BLOCKING_INSTANCE significa en qué instancia se está ejecutando la sesión de bloqueo, BLOCKING_SESSION es el ID de sesión de la sesión de bloqueo.
Ahora el siguiente plan de acción para DBA es comprobar lo que está causando que la sesión 34 bloquee la sesión de scott. El DBA puede utilizar la consulta siguiente para averiguar los detalles de la sesión 34.
SQL> select sid,username,command,status,program,sql_id,BLOCKING_INSTANCE,BLOCKING_SESSION,WAIT_CLASS from gv$session where sid=34;
SID USERNAME      COMMAND STATUS   PROGRAM  SQL_ID BLOCKING_INSTANCE             BLOCKING_SESSION     WAIT_CLASS
--- ---------- ---------- -------- ------------------------------------------------ ------------- ------------
34 SCOTT               3 INACTIVE sqlplus@database.example.com (TNS V1-V3)         17d40vwcct4g6       Idle
Sin embargo, podría haber muchas razones para este tipo de problema. Uno de ellos lo he discutido aquí.

23 noviembre 2016

Archivos redo log


En Oracle RDBMS, los redo logs comprenden archivos en un formato propietario que registra un historial de todos los cambios realizados en la base de datos. Cuando algo se cambia en un datafile, Oracle registra los cambios a la base de datos.

¿Qué es Online Redo Log?


El Online Redo log, es una estructura física que consiste de mínimo de dos archivos, estos a su vez pueden estar multiplexados en dos o más copias idénticas, que a estos se le conocen como miembros de un grupo de Redo log. Como mencionamos, el Online Redo Log consiste de mínimo dos archivos, esto permite que Oracle escriba en un archivo de Online Redo Log mientras el otro se archiva (si la base de datos se encuentra en modo ARCHIVELOG). 
En los Online Redo logs se almacenan registros de Redo, los cuales están conformados por vectores de cambio (change vectors), cada uno de estos vectores describe los cambios a un bloque de datos.


Todos los registros de tipo redo tiene metadatos relevantes, incluyendo:
  • SCN y la estampa de tiempo del cambio
  • El ID de la transacción que ha generado el cambio
  • SCN y la estampa de tiempo cuando la transacción fue cometida (commit)
  • Tipo de operación que efectuó el cambio
  • Nombre y tipo del segmento de dato modificado.

Los Online Redo Log son usados únicamente en el proceso de la recuperación de la base de datos. Básicamente, lo que hay que entender como principio, es que cuando algún DML (insert, update o delete) o un DDL (alter, create, drop) sucede en nuestra base de datos, Oracle registra los cambios en memoria, en un buffer llamado Redo Log Buffer, que con este buffer hay un proceso asociado llamado LGWR.




El proceso LGWR de lo que se encarga es de escribir de la estructura de memoria (Redo) Log Buffer a los Online Redo Logs, y muy importante es saber cuáles son las circunstancias que hacen que el LGWR escriba al Online Redo Log:

  • Cuando un usuario hace un commit a la transacción
  • Cuando sucede un cambio (log switch) de archivo de Redo Log
  • Cuando han pasado tres segundos desde la ultima escritura del LGWR hacia el Online Redo Log
  • Cuando el Redo Log Buffer esta 1/3 lleno o contiene mas de 1Mb de datos en el buffer.
  • Cuando el proceso DBWn necesita escribir datos del Database Buffer Cache hacia disco.
El proceso LGWR escribe a los archivos de Online Redo Log de manera circular, cuando el LGWR escribe en el ultimo archivo de Online Redo Log disponible, el LGWR se regresa a escribir al primer archivo de Online Redo Log.

-- Consultamos las vistas del diccionario de datos relativas a los redo logs
select * from v$logfile;
select * from v$log;
-- Rotamos los logs
alter system switch logfile;
-- Comprobamos los logs
select * from v$log;


21 noviembre 2016

Tareas de un DBA (primera parte)

Esta es una compilación de tareas que un administrador de base de datos ha de ejecutar  de forma diaria, semanal, mensual y otras sin un periodo definido.

Actividad diaria 

En esta entrada nos centraremos en las tareas diarias.

Comprueba si la instancia Oracle está funcionando o no.

1.     Sistema operativo Windows: mirar el programa services.msc
2.     Sistema operativo Unix: ps -ef | grep pmon
3.     SQL: SQL> select status from v$instance;

Comprueba si los listeners Oracle están funcionando o no

Para que desde fuera del servidor donde está instalada la base de datos Oracle se pueda acceder a la misma el servicio denominado listener ha de estar activado, o como se suele decir, el listener de Oracle ha de estar escuchando.
Puede pasar que la base de datos esté correctamente levantada y no se pueda conectar desde otros servidores, que también están correctamente configurados (TNSNAMES correcto, etc.). En estos casos puede ser que el listener tenga algún problema, o simplemente que no haya sido iniciado. En ese caso tan sólo habría que arrancar el listener.
Consultar el estado del mismo, arrancarlo o pararlo es muy sencillo. Sólo hay que abrir una sesión de línea de comandos (consola, terminal, etc. ) con el usuario con el que se ha instalado la base de datos, y ejecutar el comando lsnrctl con los siguientes parámetros para cada caso:
·         Comprobar su estado: > lsnrctl status
·         Parar el listener:          > lsnrctl stop
·         Levantar el listener:     > lsnrctl start

Comprueba si hay sesiones que bloquean otras sesiones 

En Oracle hay una vista v$lock que nos indica los objetos que se encuentran en bloqueo, el identificador de usuario y sesión y el tipo de bloqueo.
Una “join” con la tabla dba_objects nos proporciona además el nombre y tipo de los objetos bloqueados:
Existen principalmente dos tipos de bloqueo:
·         Bloqueos de tablas (TM) 
·         Bloqueos a nivel de fila (TX)
Los bloqueos a nivel de tabla son creados cuando se ejecuta una sentencia DML del tipo: update, insert, delete, select ..for update sobre la tabla entera. 
Los bloqueos a nivel de fila se crean cuando se ejecutan sentencias DML contra un conjunto de registros específicos.
Una consulta sobre esta vista nos permite rápidamente saber que procesos están bloqueados y si además hacemos un join con v$open_cursor podemos ver que consulta es la que se encuentra parada a la espera de que se produzca el desbloqueo para poder ejecutarse. En la consulta siguiente podemos ver las sentencias paradas y el id de proceso que las está bloqueando.

Esta consulta permite ver los objetos que están esperando a que termine un bloqueo y la sentencia que quieren ejecutar. el id de proceso nos da la pista de quien esta bloqueando:
select /*+ ordered
no_merge(L_WAITER)
no_merge(L_LOCKER) use_hash(L_LOCKER)
no_merge(S_WAITER) use_hash(S_WAITER)
no_merge(S_LOCKER) use_hash(S_LOCKER)
use_nl(O)
use_nl(U)
*/
/* first the table-level locks (TM) and mixed TM/TX TX/TM */
S_LOCKER.OSUSER OS_LOCKER,
S_LOCKER.USERNAME LOCKER_SCHEMA,
S_LOCKER.PROCESS LOCKER_PID,
S_WAITER.OSUSER OS_WAITER,
S_WAITER.USERNAME WAITER_SCHEMA,
S_WAITER.PROCESS WAITER_PID,
'Table lock (TM): '||U.NAME||'.'||O.NAME||
' - Mode held: '||
decode(L_LOCKER.LMODE,
0, 'None', /* same as Monitor */
1, 'Null', /* N */
2, 'Row-S (SS)', /* L */
3, 'Row-X (SX)', /* R */
4, 'Share', /* S */
5, 'S/Row-X (SSX)', /* C */
6, 'Exclusive', /* X */
'???: '||to_char(L_LOCKER.LMODE))||
' / Mode requested: '||
decode(L_WAITER.REQUEST,
0, 'None', /* same as Monitor */
1, 'Null', /* N */
2, 'Row-S (SS)', /* L */
3, 'Row-X (SX)', /* R */
4, 'Share', /* S */
5, 'S/Row-X (SSX)', /* C */
6, 'Exclusive', /* X */
'???: '||to_char(L_WAITER.REQUEST))
SQL_TEXT_WAITER
from
V$LOCK L_WAITER,
V$LOCK L_LOCKER,
V$SESSION S_WAITER,
V$SESSION S_LOCKER,
sys.OBJ$ O,
sys.USER$ U
where S_WAITER.SID = L_WAITER.SID
and L_WAITER.TYPE IN ('TM')
and S_LOCKER.sid = L_LOCKER.sid
and L_LOCKER.ID1 = L_WAITER.ID1
and L_WAITER.REQUEST > 0
and L_LOCKER.LMODE > 0
and L_WAITER.ADDR != L_LOCKER.ADDR
and L_WAITER.ID1 = O.OBJ#
and U.USER# = O.OWNER#
union
select /*+ ordered
no_merge(L_WAITER)
no_merge(L_LOCKER) use_hash(L_LOCKER)
no_merge(S_WAITER) use_hash(S_WAITER)
no_merge(S_LOCKER) use_hash(S_LOCKER)
no_merge(L1_WAITER) use_hash(L1_WAITER)
no_merge(O) use_hash(O)
*/
/* now the (usual) row-locks TX */
S_LOCKER.OSUSER OS_LOCKER,
S_LOCKER.USERNAME LOCKER_SCHEMA,
S_LOCKER.PROCESS LOCK_PID,
S_WAITER.OSUSER OS_WAITER,
S_WAITER.USERNAME WAITER_SCHEMA,
S_WAITER.PROCESS WAITER_PID,
'TX: '||O.SQL_TEXT SQL_TEXT_WAITER
from
V$LOCK L_WAITER,
V$LOCK L_LOCKER,
V$SESSION S_WAITER,
V$SESSION S_LOCKER,
V$_LOCK L1_WAITER,
V$OPEN_CURSOR O
where S_WAITER.SID = L_WAITER.SID
and L_WAITER.TYPE IN ('TX')
and S_LOCKER.sid = L_LOCKER.sid
and L_LOCKER.ID1 = L_WAITER.ID1
and L_WAITER.REQUEST > 0
and L_LOCKER.LMODE > 0
and L_WAITER.ADDR != L_LOCKER.ADDR
and L1_WAITER.LADDR = L_WAITER.ADDR
and L1_WAITER.KADDR = L_WAITER.KADDR
and L1_WAITER.SADDR = O.SADDR
and O.HASH_VALUE = S_WAITER.SQL_HASH_VALUE

Comprueba el “alert log” si hay errores

El registro de alertas es un registro cronológico de mensajes y errores, e incluye los siguientes elementos:

  • Todos los errores internos (ORA-600),
  • Errores de corrupción de bloques (ORA-1578)
  • Errores de bloqueo (ORA-60)
  • Operaciones administrativas, como sentencias CREATE, ALTER y DROP y instrucciones STARTUP, SHUTDOWN y ARCHIVELOG.
  • Mensajes y errores relacionados con las funciones de los procesos de servidor compartido y despachador.
  • Errores que ocurren durante la actualización automática de una vista materializada.
  • Los valores de todos los parámetros de inicialización que tenían valores no predeterminados al iniciarse la base de datos y la instancia.

Los archivos de seguimiento se escriben en nombre de los procesos del servidor siempre que se produzcan errores críticos. Además, al establecer el parámetro de inicialización SQL_TRACE = TRUE, el recurso de rastreo SQL genera estadísticas de rendimiento para el procesamiento de todas las sentencias SQL de una instancia y las escribe en el repositorio de diagnóstico automático. 
Opcionalmente, puede solicitar que se generen archivos de seguimiento para los procesos del servidor. Independientemente del valor actual del parámetro de inicialización SQL_TRACE, cada sesión puede habilitar o deshabilitar el registro de trazas en nombre del proceso del servidor asociado mediante la instrucción SQL ALTER SESSION SET SQL_TRACE

Este ejemplo habilita el recurso de rastreo de SQL para una sesión específica:
ALTER SESSION SET SQL_TRACE TRUE;
Puede ver y cambiar las configuraciones de umbral para las métricas de alertas del servidor mediante los procedimientos SET_THRESHOLD y GET_THRESHOLD del paquete de PL / SQL, DBMS_SERVER_ALERTS.
SELECT
    metrics_name,
    warning_value,
    critical_value,
    consecutive_occurrences
FROM
    dba_thresholds
WHERE
    metrics_name LIKE '%CPU Time%';

Vista
Descripción
DBA_THRESHOLDS
Enumera los valores de umbral definidos para la instancia
DBA_OUTSTANDING_ALERTS
Describe las alertas pendientes de la base de datos
DBA_ALERT_HISTORY
Enumera un historial de alertas que se han borrado
V$ALERT_TYPES
Proporciona información como grupo y tipo para cada alerta
V$METRICNAME
Contiene los nombres, identificadores y otra información sobre las métricas del sistema
V$METRIC
Contiene valores de métrica a nivel de sistema
V$METRIC_HISTORY
Contiene un historial de valores de métrica a nivel de sistema

Utilice los paquetes DBMS_SESSION o DBMS_MONITOR si desea controlar el seguimiento de SQL para una sesión.

El paquete DBMS_MONITOR le permite utilizar PL / SQL para controlar el rastreo adicional y la recopilación de estadísticas.


Proceso PLSQL
Descripción
CLIENT_ID_STAT_DISABLE
Inhabilita la recopilación estadística habilitada previamente para un identificador de cliente determinado
CLIENT_ID_STAT_ENABLE
Permite la recopilación estadística de un determinado identificador de cliente
CLIENT_ID_TRACE_DISABLE
Deshabilita la traza activada previamente para un identificador de cliente determinado globalmente para la base de datos
CLIENT_ID_TRACE_ENABLE
Habilita la traza de un identificador de cliente determinado globalmente para la base de datos
DATABASE_TRACE_DISABLE
Deshabilita la traza de SQL para toda la base de datos o una instancia específica
DATABASE_TRACE_ENABLE
Habilita la traza de SQL para toda la base de datos o una instancia específica
SERV_MOD_ACT_STAT_DISABLE
Inhabilita la recopilación estadística activada para una combinación dada de Nombre del servicio, MÓDULO y ACCIÓN
SERV_MOD_ACT_STAT_ENABLE
Permite la recopilación de estadísticas para una combinación dada de Nombre del servicio, MÓDULO y ACCIÓN
SERV_MOD_ACT_TRACE_DISABLE
Deshabilita la traza para TODAS las instancias habilitadas para una combinación o una combinación dada de Nombre de servicio, MÓDULO y ACCIÓN nombre globalmente
SERV_MOD_ACT_TRACE_ENABLE
Habilita el rastreo de SQL para una combinación dada de Nombre de servicio, MÓDULO y ACCIÓN globalmente a menos que se especifique un nombre_instancia
SESSION_TRACE_DISABLE
Deshabilita la traza previamente habilitada para un identificador de sesión de base de datos (SID) en la instancia local
SESSION_TRACE_ENABLE
Habilita la traza de un identificador de sesión de base de datos (SID) en la instancia local

Comprueba si hay dbms jobs ejecutándose y comprueba los estados del mismo.

ORACLE ofrece una cola para planificar operaciones rutinarias en una base de datos. La funcionalidad de los Jobs de Oracle es parecida al cron de UNIX en el cual se puede planificar una tarea, a una determinada hora y con una periodicidad concreta pero en base de datos.
El paquete encargado de hacer esta planificación es DBMS_JOB.

Para que cualquier Job de Oracle se pueda ejecutar tenemos que tener en cuenta el parámetro job_queue_processes no esté a cero, ya que es el que nos indica el número de colas que gestionarán nuestros jobs. Este parámetro debe de ser mayor del número de jobs que se desee ejecutar de forma simultánea (el máximo es 1000 para Oracle).
Las vistas que podemos usar para manejar los jobs son:
  • DBA_JOBS: Muestra la información de todos los jobs de la base de datos.
  • ALL_JOBS: Muestra la misma información que dba_jobs pero sólo los jobs a los cuales puede acceder el usuario actual con el que se está realizando la consulta.

Para consultar todos los jobs:
select * from all_scheduler_jobs;
Consultar log de ejecución de jobs:
select * from all_scheduler_job_log order by log_date desc;
Consultar los jobs que están en ejecución:
select * from all_scheduler_running_jobs;
Detener un job:
exec dbms_scheduler.stop_jop('USER.JOB');
Se puede forzar la parada de un job ejecutando la siguiente query como usuario system:
exec dbms_scheduler.stop_job('USER.JOB', true);
Deshabilitar un Job:
exec dbms_scheduler.disable('USER.JOB');
Se puede forzar la deshabilitación de un job ejecutando la siguiente query como system:
exec dbms_scheduler.disable('USER.JOB', true);
Habilitar un Job:
exec dbms_scheduler.enable('USER.JOB');
Arrancar un Job:
exec dbms_scheduler.run_job('USER.JOB');

15 noviembre 2016

PL / SQL: Evitar algoritmos innecesariamente complejos



Si tienes un equipo de  programadores nuevos en PL / SQL (y SQL), no es raro encontrar que complican demasiado las cosas, escribiendo más código de lo necesario y poniendo demasiada lógica en PL / SQL. Ese problema puede ser agravado por accidentalmente obtener la "respuesta correcta" basada en pruebas inadecuadas y datos de prueba.

Supongamos que tengo una tabla y un conjunto de datos como sigue:



# 1. Éxito accidental con datos de prueba incorrectos


CREATE TABLE undead_type
(
   undead_id     INTEGER PRIMARY KEY,
   undead_name   VARCHAR2 (100)
)
/

BEGIN
   INSERT INTO undead_type (undead_id, undead_name)
        VALUES (1, 'Skeleton');
   INSERT INTO undead_type (undead_id, undead_name)
        VALUES (2, 'Zombie');
   INSERT INTO undead_type (undead_id, undead_name)
        VALUES (3, 'Ghoul');
   INSERT INTO undead_type (undead_id, undead_name)
        VALUES (4, 'Wight');
   INSERT INTO undead_type (undead_id, undead_name)
        VALUES (5, 'Wraith');
   INSERT INTO undead_type (undead_id, undead_name)
        VALUES (6, 'Mohrg');
  INSERT INTO undead_type (undead_id, undead_name)
        VALUES (7, 'Ghast');
   INSERT INTO undead_type (undead_id, undead_name)
        VALUES (8, 'Mummy');
   INSERT INTO undead_type (undead_id, undead_name)
        VALUES (9, 'Lich');
   INSERT INTO undead_type (undead_id, undead_name)
        VALUES (10, 'Demilich');
   INSERT INTO undead_type (undead_id, undead_name)
        VALUES (11, 'Vampire Spawn');
   INSERT INTO undead_type (undead_id, undead_name)
        VALUES (12, 'Vampire');
   INSERT INTO undead_type (undead_id, undead_name)
        VALUES (13, 'Allip');
   COMMIT;
END;
/
Y tengo que escribir un programa que produce la siguiente salida: 
Undead en Orden Alfabético: 
  • Allip 
  • Demilich 
  • Ghast
  • ...
DECLARE
   l_count   INTEGER;
   l_name    plch_animals.animal_name%TYPE;
BEGIN
   DBMS_OUTPUT.put_line ('Animals in Alphabetical Order');

   SELECT COUNT (*) INTO l_count FROM plch_animals;

   FOR indx IN 1 .. l_count
   LOOP
      SELECT animal_name
        INTO l_name
        FROM plch_animals
       WHERE animal_id = indx;

      DBMS_OUTPUT.put_line (l_name);
   END LOOP;
END;
/

# 2. Demasiado SQL, datos insuficientes

DECLARE
   TYPE undead_ids_t IS TABLE OF undead_type.undead_id%TYPE;

   l_undead_ids   undead_ids_t;
   l_name         undead_type.undead_name%TYPE;
BEGIN
   DBMS_OUTPUT.put_line ('undead in Alphabetical Order');

   SELECT undead_id
     BULK COLLECT INTO l_undead_ids
     FROM undead_type;

   FOR indx IN 1 .. l_undead_ids.COUNT
   LOOP
      SELECT undead_name
        INTO l_name
        FROM undead_type
       WHERE undead_id = indx;

      DBMS_OUTPUT.put_line (l_name);
   END LOOP;
END;
/

# 3. Vamos a pasarlo bien con las conexiones PL/SQL con colecciones

DECLARE
   TYPE undead_ids_t IS TABLE OF undead_type.undead_id%TYPE
      INDEX BY undead_type.undead_name%TYPE;

   l_undead_ids   undead_ids_t;
   l_index        undead_type.undead_name%TYPE;
   l_name         undead_type.undead_name%TYPE;
BEGIN
   DBMS_OUTPUT.put_line ('undeads in Alphabetical Order');

   FOR rec IN (  SELECT *
                   FROM undead_type
               ORDER BY undead_name DESC)
   LOOP
      l_undead_ids (rec.undead_name) := rec.undead_id;
   END LOOP;

   l_index := l_undead_ids.FIRST;

   WHILE l_index IS NOT NULL
   LOOP
      DBMS_OUTPUT.put_line (l_index);

      l_index := l_undead_ids.NEXT (l_index);
   END LOOP;
END;
/

#4. SQL Simple

SELECT undead_name FROM undead_type
 ORDER BY undead_name;

No es mucho más simple que eso. Y si lo necesita dentro de PL / SQL

 #5. PL/SQL Simple

 BEGIN
   DBMS_OUTPUT.put_line ('undead in Alphabetical Order');

   FOR rec IN (SELECT undead_name FROM undead_type
                ORDER BY undead_name)
   LOOP
      DBMS_OUTPUT.put_line (rec.undead_name);
   END LOOP;
END;


Lecciones aprendidas 


  •  Deje a SQL hacer el levantamiento de pesas, tanto como sea posible (no es que haya algo muy "pesado" para levantar en este ejercicio!) 
  • No sobre-complicar las cosas. Asegúrese de que sus datos de prueba tengan suficiente volumen y variedad para ejercer realmente su algoritmo. 
  • Si te encuentras pensando "¿tiene que ser tan complicado?", Casi con toda seguridad la respuesta es un resonante "No!" Y debe dar un paso atrás, desafiar sus suposiciones, y ver cómo se puede simplificar su código

13 noviembre 2016

Oracle RAC for not so dummies

¿Qué es un Cluster?

Un clúster está formado por dos o más servidores independientes pero interconectados. 

Algunos clústeres están configurados de modo tal que puedan proveer alta disponibilidad permitiendo que la carga de trabajo sea transferida a un nodo secundario  si el nodo principal deja de funcionar. Otros clústeres están diseñados para proveer escalabilidad permitiendo que los usuarios o carga se distribuya entre los nodos. Ambas configuraciones son consideradas clústeres.

Una característica importante que tienen los clústeres es que se presentan a las aplicaciones como si fueran un solo servidor. Es deseable que la administración de diversos nodos de un clúster sea lo más parecida posible a la administración de una configuración de un solo nodo. El software de administración del clúster debería proveer este nivel de transparencia.

Para que los nodos puedan actuar como si fueran un solo servidor, los archivos deben estar almacenados de modo tal que puedan ser accedidos por todos los nodos del clúster.

En resumen, un clúster es un grupo de servidores independientes que cooperan comportándose como si fueran un solo sistema.

¿Qué es Oracle Real Application Clusters?

Real Application Clusters es un software que permite utilizar un clúster de servidores ejecutando múltiples instancias sobre una misma base de datos. Los archivos de base de datos quedan almacenados en discos física o lógicamente conectados a cada nodo, de modo tal que todas las instancias activas pueden leerlos o escribirlos.

El software de RAC maneja el acceso a los datos, de modo tal que los cambios en los datos son coordinados entre las instancias y cada instancia ve imágenes consistentes de la base. El interconnect del cluster permite que las instancias se pasen entre ellas información de coordinación e imágenes de los datos.

Esta arquitectura permite que los usuarios y aplicaciones se beneficien de la potencia de procesamiento de múltiples máquinas. La arquitectura RAC también ofrece redundancia; por ejemplo, en el caso de que un nodo quede inutilizado, la aplicación continuará accediendo a los datos vía el resto de las instancias disponibles.

Coordinación de recursos globales en un entorno RAC

En ambientes de una sola instancia, los mecanismos de bloqueo coordinan el acceso a recursos comunes como ser una simple fila de una tabla. Los mecanismos de lockeo previenen la posibilidad de que dos procesos modifiquen un mismo recurso al mismo tiempo.

En entornos RAC, la sincronización “internodo” es crítica para mantener una adecuada coordinación entre los distintos procesos en diferentes nodos, previniendo que estos procesos modifiquen el mismo recurso al mismo tiempo. La sincronización internodo garantiza que cada instancia vea la versión más reciente de un bloque de la buffer cache.
RAC utiliza lo que se conoce como Global Resource Directory (GRD) para registrar información sobre cómo los recursos son utilizados dentro de una base de datos en clúster. Global Cache Services (GCS) y Global Enqueue Services (GES) administran la información del GRD.

Cada instancia mantiene una parte de la GRD en su propia SGA. GCS y GES nominan a una instancia para administrar la información particular de un recurso. Esta instancia es llamada Resource Master. De este modo cada instancia sabe en qué instancia está masterizado cada recurso.

Mantener una cache coherente es también una parte muy importante dentro de la actividad de un RAC. El algoritmo Cache fusión es el encargado de mantener una cache coherente utilizando técnicas que mantienen múltiples copias consistentes de un mismo bloque entre diferentes instancias Oracle. Este algoritmo es implementado por GCS.
GES administra todos los recursos inter instancia que no maneja Oracle Fusión:
  • Dictionary cache locks,
  • Llibrary cache locks,
  • Deadlock detection.

Coordinación en el uso de la cache global.

Analizaremos un breve ejemplo puntual de coordinación del uso de la cache global.

El escenario planteado en este ejemplo es el siguiente:

  • Dos instancias en RAC. Nodo 1 y Nodo 2.
  • Un bloque de datos ha sido modificado (dirtied) por el Nodo 1.
  • El Nodo 2 intentará modificar el mismo bloque.
Veamos qué ocurre para que el Nodo 2 modifique el bloque:
  • El Nodo 2, que intenta modificar el bloque, submite un requerimiento a GCS.
  • GCS transmite el requerimiento al “holder” del recurso. En este caso, el Nodo 1 es el quien tiene el recurso.
  • El Nodo 1 recibe el mensaje y envía el bloque a la segunda instancia. El Nodo 1 mantiene el bloque “sucio” (también llamado “past image”).
  • Cuando el Nodo 2 recibe el bloque, informa a GCS que ahora es el “holder” del bloque. 


Reconfiguración dinámica del GRD (Global Resource Directory)





RAC utiliza el Global Resource Directory (GRD) para registrar información sobre cómo son utilizados los recursos dentro de una base de datos en clúster. Cada instancia mantiene una porción del GRD en su propia SGA.

Cuando una instancia abandona el clúster, es necesario redistribuir la porción del GRD que administraba entre los nodos sobrevivientes. Algo análogo ocurre cuando una nueva instancia se suma al clúster, las porciones del GRD de cada instancia necesitan redistribuirse para crear la nueva porción de GRD correspondiente a la instancia que se suma. 

En vez de remasterizar todos los recursos a través de todos los nodos, RAC utiliza un algoritmo denominado lazy remastering que remasteriza una cantidad mínima de recursos durante una reconfiguración.

Afinidad de objetos y remasterización dinámica

Global Cache Services (GCS), el encargado de administrar el Global Resource Directory (GRD) junto a Global Enqueue Services (GES), implementa un algoritmo para migrar dinámicamente recursos del GRD. 
A este algoritmo se lo conoce como remasterización dinámica. La idea básica de la remasterización dinámica es mantener un recurso de la buffer cache en la instancia que mas lo accede. GCS lleva un registro de los requerimientos por instancia y por objeto de modo tal que dispone de la información necesaria para migrar en forma dinámica recursos de una instancia a otra que lo accede más.


Memoria en una instalación de Oracle RAC

La memoria específica para la administración del RAC se aloca mayoritariamente en la Shared pool. Como los bloques pueden ser cacheados a través de las instancias es necesario contar también con buffer caches más grandes.

Al migrar un base de datos Oracle single instance a RAC, si se pretende que cada nodo mantenga su rendimiento con los mismos niveles de carga que en single instance, habrá que asignar un 10% más de memoria a la buffer cache y un 15% más a la shared pool. Estos valores son heurísticos, basados en experiencias de RAC a través del tiempo.

Sin embargo, hay que considerar que generalmente los requerimientos por instancia se ven reducidos cuando la misma cantidad de usuarios y carga de trabajo es distribuido en múltiples nodos.

Ejecución paralelizada en RAC

El optimizador basado en costos incorpora consideraciones de ejecución en paralelo a fin de obtener planes de ejecución óptimos.

En entornos RAC las decisiones del optimizador se hacen tanto a nivel intranodo como internodo. 
Por ejemplo, si una consulta requiere de 6 procesos para completar  su tarea y existen 6 procesos esclavos ociosos en el nodo local (el nodo al que está conectado el usuario) entonces la consulta se resuelve utilizando solamente recursos locales. De este modo se logra un paralelismo intranodo eficiente y se elimina el overhead que generaría una resolución internodo. 

Sin embargo, si existieran sólo 2 procesos esclavos ociosos en el nodo local, entonces se usarán los 2 procesos locales mas 4 de otro para completar el query. En este último caso se utiliza paralelismo intranodo e internodo a fin de acelerar el procesamiento.

Otro aspecto a considerar es que frecuentemente las consultas son particionados de forma perfecta entre los procesos esclavos; de modo tal que no todos terminan al mismo tiempo. La tecnología de procesamiento paralelo de Oracle detecta en forma dinámica el proceso que están ociosos porque ya finalizaron y vuelve a asignarle trabajo tomado de las tablas de colas de los procesos sobrecargados. De este modo Oracle redistribuye el trabajo en forma dinámica y eficiente entre todos los procesos.  

Procesos background en RAC.



En una configuración RAC aparecen una serie de nuevos procesos background. La función primaria de estos procesos es mantener una administración coherente entre todos los nodos que conforman el clúster: 

  • LMON. Global Enqueue Service Monitor 
  • LMD 0. Global Enqueue Service Daemon 
  • LMS x. Global Cache Service (x puede ir de 0 a n) 
  • LCK. Proceso de lockeo DIAG. 

Proceso de diagnóstico También aparecen nuevos procesos a nivel de clúster para la administración de Oracle Clusterware: 

  • CRSD y RACGIMON motores para operaciones de alta disponibilidad 
  • OCSSD provee accesos de membrecía a nodos y grupos de servicios 
  • EVMD detecta eventos y reacciona invocando procesos. 
  • OPROCD monitor de procesos del clúster (no esta presente en Linux ni Windows)