Sunteți pe pagina 1din 19

1

3.5 SQL incrustado (embedded SQL)


Introducción
Hasta este momento, se ha utilizado SQL de forma interactiva, sin embargo, SQL tiene
tanto la función de lenguaje interactivo como de lenguaje de programación de base de
datos.
El principio fundamental del SQL incrustado, al cual se le conoce como el principio
“modo-dual”, es que cualquier sentencia de SQL que puede ser utilizada en una
Terminal puede también ser utilizada en un programa de aplicación. Por supuesto,
existen diferencias entre una sentencia SQL interactiva y su contraparte incrustada, por
ejemplo, la sentencia SELECT, en particular, requiere un tratamiento significantemente
mayor en su entorno de programación; sin embargo, el principio se cumple
generalmente (Existe un número de sentencias de SQL que son sentencias de
programación y no pueden ser utilizadas interactivamente).
Se debe tomar en cuenta que el principio “modo-dual” se aplica al lenguaje SQL
completo y no solo a las operaciones de manipulación de datos (DML). Ciertamente,
las operaciones de manipulación de datos son las más frecuentemente utilizadas en el
contexto de programación, pero es posible incrustar una sentencia CREATE TABLE
(por ejemplo) en un programa.

Ejemplo (utilizando PL/I):

DCL GivenS# CHAR(5);


DCL Rank FIXED BIN(15);
DCL City CHAR(15);
DCL Alpha …;
DCL Beta ..;

EXEC SQL DECLARE s TABLE (


S# CHAR(5) NOT NULL,
SName CHAR(20),
Status SMALLINT,
City CHAR(15) ) ;

EXEC SQL INCLUDE SQLCA ;



IF Alpha > Beta THEN
GETSTC:
EXEC SQL SELECT Status, City
INTO :rank, :city
FROM s
WHERE s# = :GivneS#;

PUT SKIP LIST (Rank, City);

Se debe tomar en cuenta los siguientes aspectos:


1. Las sentencias de SQL incrustado, se inician con “EXEC SQL”, de tal forma
que sean fácilmente distinguidas de las sentencias del lenguaje anfitrión, y
terminan por un símbolo especial (en el ejemplo punto y coma).
2

2. Una sentencia ejecutable de SQL incrustado puede aparecer en cualquier parte


donde pueda aparecer una sentencia ejecutable del lenguaje anfitrión. El
calificativo “ejecutable” se refiere: A diferencia del SQL interactivo, el SQL
incrustado incluye algunas sentencias que son declarativas, no ejecutables. Por
ejemplo DECLARE TABLE no es una sentencia ejecutable, como tampoco lo es
DECLARE CURSOR.
3. Las sentencias de SQL pueden incluir referencias a variables del lenguaje
anfitrión; tales referencias inician con dos puntos (:) para distinguir estas
referencias de los nombres de campos en SQL. Las variables del lenguaje
anfitrión pueden aparecer en SQL incrustado (sentencias DML únicamente) de
la misma forma que las variables aparecen en el SQL interactivo. También
pueden aparecer en las cláusulas INTO, SELECT o FETCH para designar áreas
de destino para la recuperación, y en ciertas sentencias que se utilizan solamente
en SQL incrustado.
4. Cualquier tabla (tabla base o vista) utilizada en el programa debe ser declarada
utilizando la sentencia DECLARE TABLE, para hacer el programa mejor
documentado y habilitar al pre-compilador para realizar ciertos chequeos
sintácticos sobre las sentencias manipuladas.
5. Después de que cualquier sentencia SQL ha sido ejecutada, información de
retroalimentación es colocada en el área del programa llamada área de
comunicación SQL (SQLCA). En particular, un indicador numérico de estado
es retornado en el campo del SQLCA llamado SQLCODE. Un SQLCODE con
valor cero significa que la sentencia se ejecutó correctamente; un valor positivo
significa que la sentencia se ejecutó, pero generó una advertencia (warning) que
indica que cierta condición de excepción ocurrió (por ejemplo, un valor de +100
significa que no se encontró datos que satisficieran el requerimiento); y un valor
negativo significa que un error ocurrió y la sentencia no se completó
exitosamente. En principio, cada sentencia de SQL incrustada en el programa
debe ser seguida por una validación del SQLCODE, y se debe tomar la acción
apropiada si el valor retornado no es el esperado. El área de comunicación SQL
es incluida en el programa utilizando la sentencia EXEC SQL INCLUDE
SQLCA.
6. Las variables del lenguaje anfitrión deben tener un tipo de dato compatible con
los tipos de dato de los campos de SQL con que son comparados o asignados.
La compatibilidad del tipo de datos se define como sigue: a) el tipo de datos
“string” de SQL es compatible con el tipo de dato “string” del lenguaje anfitrión
(debe considerarse la longitud de las variables), b) el tipo de datos “numérico”
de SQL es compatible con los tipos de dato numéricos del lenguaje anfitrión,
debemos considerar la base (decimal o binaria), la escala (fija o flotante), y la
precisión (número de dígitos); c) Los tipos de dato “date” y “time” de SQL son
compatibles con el tipo de dato “string” del lenguaje anfitrión. Si se pierde
información importante al realizar la asignación, se debe a que el campo que
recibe es muy pequeño, o SQLCA ha retornado algún indicador de error.
3

Operaciones que no involucran cursores


Las sentencias de manipulación de datos que no requieren cursores son las siguientes:
Select simple
EXEC SQL SELECT status, city
INTO :rank, :city
FROM s
WHERE s# = :givenS#;
Utilizaremos el término “Select simple” para las sentencias SELECT para las cuales la
tabla resultante contiene un único registro. En el ejemplo, si existe exactamente un
registro en la tabla S que satisfaga la condición del WHERE, entonces los valores de
“status” y “city” serán almacenados en las variables anfitrionas “rank” y “city”, y
SQLCODE tendrá un valor CERO. Si ningún registro de S satisface la condición
WHERE, entonces SQLCODE tomará el valor +100 (y las variables anfitrionas “rank”
y “city” no cambiarán); y si más de un registro cumple la condición, el programa
reportará un error, y SQLCODE tomará un valor negativo (las variables anfitrionas
“rank” y “city” tomarán un valor impredecible en este caso).
Si se intenta almacenar un valor NULL en una variable anfitriona, pero el lenguaje
anfitrión no soporta este tipo de dato, entonces, SQLCODE tomará un valor negativo
reportando esta situación. Para evitarla se puede utilizar una variable indicadora para la
variable dentro de la cláusula INTO como se ve a continuación:
EXEC SQL SELECT status, city
INTO :rank:rankind, :city
FROM s
WHERE s# = :givenS#;
IF :ranking < 0 THEN /* el campo status recuperado fue NULL */ …
En este caso, si se da el valor NULL, la variable indicadora toma un valor negativo y la
variable anfitriona que tomaría el valor de status (en este caso “rank”) no es modificada.
Update (excepto cuando se usa con la cláusula CURRENT)
EXEC SQL UPDATE s
SET status = status + :raise
WHERE city = ‘LONDON’;
Esta instrucción incrementa el estado de todos los proveedores de localizados en la
ciudad de Londres en una cantidad dada por la variable anfitriona “raise”. Si ningún
registro de la tabla S satisface la condición WHERE entonces SQLCODE tomará el
valor +100.
Delete
Borrar todos los pedidos para proveedores cuya ciudad es igual al valor almacenado en
la variable anfitriona “city”.
EXEC SQL DELETE
FROM sp
WHERE :city = (
SELECT city
FROM s
WHERE s.s# = sp.s# );
Si ningún registro satisface la condición WHERE, entonces SQLCODE tomara el valor
+100.
4

Insert
Insertar una nueva pieza donde las variables anfitrionas “pno”, “pname” y “pwt”
contienen el valor para los campos “p#”, “pname” y “weight” respectivamente; los
datos del color y la ciudad son desconocidos.
EXEC SQL INSERT
INTO p (p#, pname, weight)
VALUES (:pno, :pname, :pwt);

Operaciones que involucran cursores


Ahora se analizará el caso de las sentencias SELECT que recuperan conjuntos de
registros. En este caso, se necesita un mecanismo que permita acceder a los registros de
uno en uno; y los cursores proveen tales mecanismos:
EXEC SQL DECLARE x CURSOR FOR /* definición del cursor */
SELECT s#, sname, status
FROM s
WHERE city = :Y;

EXEC SQL OPEN x ; /* ejecutar el query */



EXEC SQL EXIT WHEN NO_DATA_FOUND; /* si no más registros salir */
EXEC SQL FETCH x INTO :s#, :sname, :status; /* leer proveedor */

END;
EXEC SQL CLOSE x ; /* desactivar el cursor */
Normalmente, las instrucciones EXIT WHEN y FETCH aparecen dentro de una
sentencia cíclica del lenguaje anfitrión.

Sintaxis de una instrucción DECLARE CURSOR


EXEC SQL DECLARE cursor_name CURSOR
FOR select-statement
[ FOR UPDATE OF columna [, column] …
| ORDER BY column [, column] … ] ;

Existen dos sentencias que pueden incluir referencias a cursors. Estas son las formas
CURREN del UPDATE y DELETE. Si un cursor “x”, por ejemplo, esta posicionado
sobre un registro particular en la base de datos, entonces es posible actualizarlo o borrar
el registro elegido del cursor “x”. Sintaxis:

EXEC SQL UPDATE table


SET field = scalar-expression
[, field = scalar-expression ] …
WHERE CURRENT OF cursor;

EXEC SQL DELETE


FROM TABLE
WHERE CURRENT OF cursor;

Las variantes UPDATE CURRENT y DELETE CURRENT no son permitidas si la


sentencia SELECT en la declaración del cursor involucra las sentencias UNION,
ORDER BY o si se utiliza una vista no actualizable en la cláusula SELECT. En el caso
5

de la variante UPDATE CURRENT, la declaración del cursor debe incluir la cláusula


FOR UPDATE identificando todos los campos que aparecen como modificados en la
cláusula SET en alguna sentencia UPDATE CURRENT para este cursor.

SQL dinámico
Consiste en un conjunto de instrucciones de SQL inmerso que son provistas
específicamente para permitir la construcción de aplicaciones generales, en línea y
posiblemente interactivas.
Las acciones que normalmente realiza una aplicación en línea son:
• Aceptar un comando desde una Terminal
• Analizar este comando
• Elegir la sentencia SQL apropiada para la base de datos.
• Retornar un mensaje y/o el resultado a la Terminal
Si el conjunto de comandos que el programa acepta es muy pequeño, como podría ser el
caso de un programa que administra las reservaciones de líneas aéreas, entonces el
conjunto de posibles sentencias SQL a ser utilizada puede ser pequeño y además estar
escrito en “hard code” dentro del programa. En este caso, sería conveniente construir
las sentencias SQL dinámicamente, y ejecutar estas sentencias construidas
dinámicamente. Las características de SQL dinámico son provistas para asistir este tipo
de procesos.
Las dos sentencias dinámicas principales son PREPARE y EXECUTE.

Ejemplo:
sqlSource char(256);
EXEC SQL DECLARE sqlObj STATEMENT ;
sqlSource = ‘DELETE FROM SP WHERE QTY < 100’;
EXEC SQL PREPARE sqlObj FROM :sqlSource;
EXEC SQL EXECUTE sqlObj;

SqlSource es una variable del lenguaje anfitrión de tipo hilera en la cual el programa
construirá la forma origen (una construcción de texto) de alguna sentencia SQL (en el
ejemplo una sentencia DELETE). sqlObj, por contraste, es una variable SQL, no una
variable del lenguaje anfitrión que se utilizará para almacenar el objeto de la sentencia
SQL cuya forma origen está dada en sqlSource. La instrucción de asignación
“sqlSource = …;” asigna a sqlSource la forma original de una sentencia DELETE de
SQL. La sentencia PREPARE entonces toma aquellas sentencias origen, las precompila
y genera la versión ejecutable, la cual es almacenada en sqlObj. Finalmente, la
sentencia EXECUTE eejcuta la versión ejecutable y entonces ocurre la instrucción
DELETE. Información de retroalimentación de esta sentencia DELETE será retornada
en la variable SQLCA de la forma usual.
6

Unidad 4
Entorno de base de datos

Recuperación y concurrencia
Introducción
El problema de la recuperación y la concurrencia en un sistema de bases de datos está
ampliamente cubierto con el concepto de “procesamiento transaccional”. En esta
sección se tratará el significado de lo que es una transacción, el significado de un
proceso transaccional (o manejo transaccional), y lo que exactamente hacen las
instrucciones COMMIT y ROLLBACK. Finalmente, se discutirán los problemas de
concurrencia y recuperación que el concepto “transacción” intenta resolver.

Recuperación transaccional
Una transacción es una unidad lógica de trabajo. Por ejemplo, si la tabla P (tabla de
partes), se le agregara un campo adicional “TOTQTY” el cual representa la cantidad
total de pedidos para la parte en cuestión; en otras palabras, el valor de “TOTQTY” para
una parte dada es igual a la suma de todos los valores SP.QTY, tomados de todos los
registros de la tabla SP para esta parte. Ahora, si se considera la siguiente secuencia de
operaciones:
EXEC SQL WHENEVER SQLERROR GO TO UNDO ;
EXEC SQL INSERT INTO SP (S#, P#, QTY)
VALUES (‘S5’, ‘P1’, 1000) ;
EXEC SQL UPDATE P SET TOTQTY = TOTQTY + 1000
WHERE P# = ‘P1’;
EXEC SQL COMMIT;
GO TO FINISH

UNDO:
EXEC SQL ROLLBACK;
FINISH:
RETURN;
El ejemplo anterior agrega un Nuevo pedido a la tabla SP, luego, la instrucción
UPDATE actualiza el campo “TOTQTY” para la parte “P1” apropiadamente.
E punto en este ejemplo consiste en ver que presumiblemente se trata de una operación
atómica – agregar un nuevo pedido – lo cual en efecto involucra dos actualizaciones a la
base de datos. Lo que es más, la base de datos no es consistente entre las dos
actualizaciones; temporalmente, viola el requerimiento de que el valor en el campo
“TOTQTY” para la parte P1 sea igual a la suma de todos los valores SP.QTY para la
parte P1. En conclusión, una unidad lógica de trabajo (una transacción) no
necesariamente es una operación simple en la base de datos; sino, es una secuencia de
tales operaciones, en general, esto transforma un estado consistente de la base de datos,
en otro estado consistente de la base de datos, sin preservar necesariamente la
consistencia en todos sus puntos intermedios.
Ahora, es claro que no se debe permitir en el ejemplo que sólo uno de las dos
actualizaciones se ejecuten, es decir, o se ejecutan las dos actualizaciones o no se
ejecuta ninguna (porque finalmente la base de datos debe quedar en un estado
7

consistente). Idealmente, es necesario garantizar que ambas actualizaciones se ejecuten,


desafortunadamente, es imposible garantizar que esto suceda – siempre existe la
posibilidad de que ocurra un error, y lo más seguro es que el error ocurra en el peor
momento. Por ejemplo, una falla del sistema puede ocurrir entre ambas actualizaciones,
o una sobrecarga aritmética puede ocurrir en la segunda actualización, etc.
Específicamente, garantizar que si la transacción ejecuta alguna actualización y luego
ocurre alguna falla (por cualquier razón) antes de que la transacción llegue a su
finalización normal, entonces estas actualizaciones ya realizadas serán revertidas. En
consecuencia, la transacción o se ejecuta completa o se cancela completa (como si no se
hubiese ejecutado nunca ninguna actualización). De esta forma una secuencia de
operaciones que fundamentalmente no son atómicas puede realizarse como si realmente
fuera atómica desde un punto de vista externo.
El componente del sistema que provee esta atomicidad es llamado “manejador
transaccional” y las operaciones COMMIT y ROLLBACK son la clave para que esto
funcione.
• La operación COMMIT señala el final de una transacción exitosa: le indica al
manejador transaccional que una unidad lógica de trabajo ha sido completada
exitosamente, es decir, la base de datos está en un estado consistente de nuevo, y
todas las actualizaciones se realizaron como una unidad de trabajo y por lo tanto
pueden ser “grabadas” o quedar permanentemente registradas.
• La operación ROLLBACK, por contraste, señala el final NO exitoso de una
transacción: le indica al manejador transaccional que algo ha salido mal, por lo
tanto, la base de datos está en un estado inconsistente, y todas las actualizaciones
hechas porla unidad lógica de trabajo deben ser revertidas.
En el ejemplo anterior, se utiliza la operación COMMI si ambas actualizaciones se
realizaron exitosamente, lo cual hace permanentes los cambios en la base de datos. Si
algo sale mal, se levanta una condición de SQLERROR y se ejecuta una operación
ROLLBACK, para deshacer cualquier cambio que pudiese haber sido realizado.
Para hacer posible la reversión de las actualizaciones, el sistema mantiene un log o
bitácora (journal) en almacenamiento secundario, en la cual se detallan todas las
operaciones de actualización – en particular, los valores antes y después de que se
actualizara el objeto – Esto es necesario para deshacer una actualización particular, el
sistema puede utilizar las entradas de un LOG particular para restaurar los objetos
modificados a su valor anterior.
Puntos de sincronización
Lo que se ejecuta entre una operación COMMIT o ROLLBACK establece lo
que se llama un punto de sincronización (abreviadamente synchpoint). Un
SYNCHPOINT representa la frontera entre dos transacciones consecutivas; es
decir, corresponde al final de una unidad lógica de trabajo, y por lo tanto a un
punto en el cual la base de datos llega a un estado consistente. Las únicas
operaciones que establecen un SYNCHPOINT son el COMMIT y el
ROLLBACK, y la iniciación del programa. Cuando un SYNCHPOINT es
establecido:
• Todas las actualizaciones hechas por un programa desde el
SYNCHPOINT previo son grabadas (COMMIT) o revertidas
(ROLLBACK)
• Todos los cursores abiertos son cerrados y todos los punteros a la base de
datos son liberados (esto no ocurre en todos los sistemas administradores
de base de datos)
• Todos los registros bloqueados son liberados.
8

Es importante resaltar que las operaciones COMMIT y ROLLBACK terminan la


transacción, no el programa. En general la ejecución de un programa consistirá
de una secuencia de transacciones, corriendo una luego de otra, cada una con
una operación COMMIT o ROLLBACK terminando una transacción y
arrancando la siguiente. Sin embargo, también es cierto que frecuentemente la
ejecución de un programa corresponderá a una sola transacción; y si esto ocurre,
entonces será posible codificar este programa sin una sentencia COMMIT o
ROLLBACK explícita.
En conclusión, las transacciones no son solamente la unidad de trabajo, sino también la
unidad de recuperación. Si una transacción se ejecuta exitosamente, entonces el sistema
debe garantizar que sus actualizaciones queden almacenadas permanentemente en la
base de datos, inclusive si el sistema cae un instante después. Existe la posibilidad, por
un instante, de que el sistema colapse después de que se invoque una operación
COMMIT pero antes de que las actualizaciones sean físicamente escritos a la base de
datos (los cuales todavía esperaban en el buffer del almacenamiento principal y por lo
tanto se pierden en el momento del colapso). Si esto ocurre, los procedimientos de
reinicialización del sistema todavía instalaran estos cambios en la base de datos. es
posible descubrir los valores que deben ser escritos en la base de datos si se examinan
las entradas relevantes en el LOG. (Esto implica que el LOG debe ser físicamente
almacenado antes de que el procesamiento de un COMMIT sea completado. Esta regla
se conoce como Write-Ahead Log Protocol) Entonces el procedimiento de
reinicialización recuperará cualquier unidad de trabajo (transacción) que haya sido
completada en forma exitosa, pero que no fue manejada para que sus actualizaciones
fueran almacenadas físicamente antes de que se diera el colapso del sistema, esto es,
como un estado inicial, las transacciones son también las unidades de recuperación.
Más adelante se verá que también son las unidades de concurrencia.
Recuperación del sistema y de la media física
El sistema debe prepararse para la recuperación, no solamente de fallas locales como las
que ocurren en una condición de sobrecarga dentro de una transacción, sino también de
fallas globales tales como una falla en la fuente de poder del CPU. Una falla local, por
definición, afecta solo las transacciones en las cuales la falla ha ocurrido. Una falla
global, por contraste, afecta grandes cantidades de transacciones que se encuentran en
progreso en el momento de la falla, y por lo tanto tendrá grandes implicaciones en el
funcionamiento de la aplicación. A continuación se analizará lo que está envuelto en la
recuperación de una falla global, tales fallas pueden clasificarse de la siguiente forma:
Fallas del sistema (Ej. Fallas de suministro de corriente), los cuales afectan
todas las transacciones actualmente en progreso, pero no daña físicamente la
base de datos. Una falla del sistema es conocida como una falla suave (soft
chash)
Fallas de la media física (Ej. La cabeza del disco daña la superficie del disco),
las cuales causan daño a la base de datos, o a una parte de la misma, y afecta
aquellas transacciones que utilizan esta porción del disco. Una falla de la media
física es llamada una falla fuerte (hard crash).
Fallas del sistema
El punto crítico de las fallas del sistema es que el contenido del almacenamiento
principal se pierde (en particular, los buffers de la base de dato se pierden). El estado
preciso de cualquier transacción que estaba en progreso en el momento de la falla no se
conoce; tal como si una transacción no fue completamente exitosamente, y por lo tanto
deben ser revertidas (ROLLED BACK) cuando el sistema reinicie. Más adelante, se
verá que es necesario en el momento de reiniciar el sistema rehacer ciertas transacciones
9

que fueron completadas exitosamente antes de que la falla ocurriera, pero no fueron
manejadas para obtener la transferencia de sus actualizaciones de los buffers de la base
de datos física.
El punto es: ¿Cómo sabe el sistema al momento de re-iniciar qué transacciones deshacer
y cuales rehacer? Para responder a esta pregunta, se analizará el siguiente
procedimiento: En ciertos intervalos prescritos (típicamente siempre que algún número
específico de entradas han sido escritas al log) el sistema automáticamente toma un
“checkpoint”. Tomar un checkpoint involucra:
a. Escritura física del contenido de los buffers de la base de datos hacia la base de
datos física y
b. Escritura física de un registro especial “checkpoint” hacia el log físico. El
registro checkpoint da una lista de todas las transacciones que estaban en
progreso en el momento en que el checkpoint se ejecuta. Para ver cómo esta
información es utilizada, vea la figura 4.1, la cual se debe leer como sigue:
• Una falla del sistema ocurre en el tiempo tf..
• El más reciente checkpoint, antes del tiempo tf se ejecutó en el tiempo tc
• Transacciones del tipo T1 fueron completadas antes del tiempo tc.
• Transacciones del tipo T2 empezaron antes del tiempo tc y se completaron
después del tiempo tc y antes del tiempo tf
• Transacciones del tipo T3 también empezaron antes del tiempo tc pero no
fueron completadas en el tiempo tf.
• Transacciones del tipo T4 iniciaron después del tiempo tc y se completaron
antes del tiempo tf.
• Transacciones del tipo 5 también empezaron después del tiempo tc, pero
no se habían completado en el tiempo tf.

Tiempo tc tf

T
R T1
A
N T2
S
A T3
C
C T4
I
O T5
N
E
S Fig. 4.1

Es claro, que cuando el sistema se reinicie, transacciones del tipo T3 y T5 deben ser
revertidas, y transacciones del tipo T2 y T4 deben volver a realizarse. Se debe notar, sin
embargo, que las transacciones del tipo T1 no se contemplan en el proceso de reinicio,
debido a que sus actualizaciones fueron escritas físicamente a la base de datos en el
tiempo tc como parte de el proceso checkpoint.
10

Al momento de reinicio, el sistema ejecuta el siguiente procedimiento para identificar


todas las transacciones del tipo T2 – T5.
1. Iniciar con dos listas de transacciones, la lista UNDO y la lista REDO. La lista
UNDO debe contener todas las transacciones del registro checkpoint, la lista
REDO debe estar vacía.
2. Buscar hacia delante dentro del LOG, empezando en el registro checkpoint.
3. Si una entrada de “transacción de inicio” se encuentra en el log para la
transacción T, agregar T a la lista UNDO.
4. Si una entrada “COMMIT” es encontrada para la transacción T, mover T de la
lista UNDO hacia la lista REDO.
5. Cuando se llegue al final del log, las listas UNDO y REDO identificadas,
representan respectivamente transacciones de tipo T3 y T5; y transacciones de
tipo T2 y T4.
El sistema ahora trabaja hacia atrás dentro del log, revirtiendo las transacciones en la
lista UNDO; y luego, trabaja hacia delante de nuevo, rehaciendo las transacciones en la
lista REDO. Finalmente, cuando todas las actividades de recuperación están completas,
entonces el sistema esta listo para aceptar nuevo trabajo.

Fallas de la media física


Estas son fallas en las cuales una parte de la base de datos es físicamente destruida o
dañada. La recuperación de este tipo de fallas básicamente involucra restaurar la base
de datos desde un backup, y entonces usando el log deshacer todas las transacciones que
todavía estaban en progreso al momento de la falla, por definición todas las
actualizaciones de tales transacciones han sido destruidas de todos modos.
Nota: La necesidad de ser capaces de realizar una recuperación de media física implica
la necesidad de una herramienta para almacenar/recuperar los datos de la base de datos.
El proceso de almacenar de la herramienta en cuestión es utilizada para hacer backups
de la base de datos en demanda (estas copias pueden ser almacenadas en cintas
magnéticas u otro tipo de almacenamiento secundario; no es necesario que se almacenen
en dispositivos de almacenamiento directo). El proceso de recuperación de la
herramienta en cuestión es utilizado para recrear la base de datos después de una falla
de media física desde una copia específica de backup.

Commit de dos fases


El commit de dos fases es importante siempre que una transacción dada pueda
interactuar con múltiples e independientes “manejadores de recursos”, por ejemplo,
considere una transacción que actualiza tanto una base de datos IMS como una base de
datos DB2. Si la transacción se completa exitosamente, entonces todas las
actualizaciones, en ambas bases de datos deben ser grabadas permanentemente; por otro
lado, si falla, entonces todas las actualizaciones deben ser revertidas (en otras palabras
existe la posibilidad de que las actualizaciones en IMS sean grabadas y las de DB2 sean
revertidas o viceversa; para entonces, la transacción no ha sido atómica).
No existe una razón para justificar que en una base de datos se ejecute una instrucción
COMMIT y en la otra una instrucción ROLLBACK; sin embargo, esto ocurre porque
enviando la misma instrucción en ambas bases de datos, el sistema podría fallar en
alguna de las dos, dando un resultado no deseado. En consecuencia, la transacción
utiliza un COMMIT extendido del sistema. Este COMMIT o ROLLBACK es
manejado por un componente del sistema llamado “Coordinador”, cuya tarea es
garantizar que ambos manejadores de recursos (en el ejemplo IMS y DB2) ejecuten un
COMMIT o un ROLLBACK a las actualizaciones que ambos tienen en responsabilidad
11

en forma unísona y además garantizarlo aún si el sistema falla en medio del proceso. El
protocolo commit de dos fases es el que habilita al coordinador para proveer esta
garantía.
A continuación, se muestra el funcionamiento del commit de dos fases. Por simplicidad
se asume que la transacción se completa exitosamente, esto es, la operación COMMIT
extendida del sistema se ejecuta (no el ROLLBACK). El coordinador recibe una
solicitud de COMMIT, entonces realiza el siguiente proceso de dos fases:
1. Primero, instruye todos los manejadores de recursos para estar listos para actuar
sobre la transacción. En la práctica, esto significa que cada manejador de
recursos debe forzar la escritura de todas las entradas del log local utilizadas por
la transacción hacia su propio log físico (por ejemplo, grabarla a un
almacenamiento no volátil, de esta forma, el manejador de recursos tendrá ahora
un registro permanente de lo que hizo en función a dicha transacción, y será
capaz de grabar permanentemente las actualizaciones o revertirlas si es
necesario). Asumiendo que la escritura forzada tiene éxito, el manejador de
recursos responde “OK” al coordinador, de lo contrario responde “NOT OK”.
2. Cuando el coordinador ha recibido las respuestas de todos los manejadores de
recursos, forza la escritura de una entrada en su propio log físico, la decisión de
almacenarlo depende de la transacción. Si todas las respuestas fueron “OK”, la
decisión es “COMMIT”; si alguna respuesta fue “NOT OK”, la decisión es
ROLLBACK. De cualquier forma, el coordinador informa a cada manejador de
recursos de su decisión, y cada manejador de recursos debe realizar un
COMMIT o ROLLBACK a la transacción localmente, como se instruyó. Debe
de tomarse en cuenta que cada manejador de recursos debe hacer lo que el
coordinador indica en la fase 2 – esto es el protocolo. (Note también, que la
aparición del registro de decisión en el log físico del coordinador marca la
transición de la fase 1 a la fase 2).
Ahora, si el sistema falla en algún punto durante el proceso, el proceso de reinicio
buscará por el registro de decisión en el log del coordinador. Si lo encuentra, entonces,
asumirá que la decisión fue ROLLBACK, y de nuevo el proceso habrá concluido
apropiadamente.
El manejador de comunicación de datos (DC manager) puede ser considerado como un
manejador de recursos según el sentido anterior. Esto es, los mensajes pueden ser
considerados como un recurso recuperable, como los registros de la base de datos, y el
DC manager necesita ser capaz de participar en el proceso de dos fases del COMMIT.

Tres problemas de concurrencia


La mayoría de DBMS son sistemas multi-usuario; esto es, son sistemas que permiten
cualquier número de transacciones accediendo la misma base de datos al mismo tiempo.
En un sistema como estos, algún tipo de mecanismo de control de concurrencia es
necesario para asegurar que las transacciones concurrentes no interfieren con otras
operaciones. Si no se tiene un mecanismo para control de concurrencia, se da una gran
cantidad de problemas que pueden ocurrir; inicialmente se analizarán estos problemas y
luego se analizará como estos problemas pueden ser resueltos utilizando un mecanismo
de control de concurrencia conocido como “locking”. (Nota. “locking” no es el único
método para el problema de control de concurrencia, pero es el más comúnmente
utilizado en la práctica)
Esencialmente, existen tres caminos en los cuales las cosas pueden salir mal, es decir,
tres formas en que las transacciones, siendo correctas en sí mismas, podría producir un
12

resultado incorrecto debido a la interferencia con una parte de otra transacción (en la
ausencia de un mecanismo de control). Los tres problemas son:
El problema de la pérdida de actualización
El problema de la dependencia a lo no confirmado (no grabado)
El problema del análisis inconsistente

El problema de la pérdida de actualización


Considere la situación ilustrada en la figura 4.2. Esta figura se explica a continuación:
La transacción A recupera un registro R en el tiempo t1; la transacción B recupera el
mismo registro R en el tiempo t2; la transacción A actualiza el registro (basado en los
valores obtenidos en el tiempo t1) en el tiempo t3; y la transacción B actualiza el mismo
registro (pero basado en los valores obtenidos en el tiempo t2, que eran los mismo del
tiempo t1) en el tiempo t4. La actualización ejecutada por la transacción A se pierde en
el tiempo t4, porque la transacción B la sobrescribe como se aprecia en la figura.

Transacción A Tiempo Transacción B

FETCH R t1

t2

UPDATE R t3

UPDATE R
Fig. 4.2 t4

El problema de la dependencia a lo no confirmado (no grabado)


Este problema se da cuando una transacción permite recuperar (o, peor aún, actualizar)
un registro que ha sido actualizado por otra transacción, pero que no ha sido grabada
permanentemente (COMMIT) por parte de la otra transacción. Si no ha sido grabada,
siempre existe la probabilidad de que sea revertida (ROLLED BACK) – en cuyo caso la
primera transacción estará viendo los mismos datos que ahora no existen (y nunca
existirán). Considere las figuras 3.3 y 3.4.

En la figura 3.3, la transacción A ve una actualización no grabada (también conocida


como uncommited change) en el tiempo t2, esta actualización es revertida en el tiempo
t3. Ahora, la transacción A está operando sobre una información falsa , es decir, asume
que el registro R tiene el valor visto en el tiempo t2, el cual corresponde al valorque
tenía el registro en el tiempo t1. Como resultado, la transacción A producirá un
resultado incorrecto. Casualmente, el ROLLBACK de la transacción B debe ser
causado por una falta en dicha transacción, y la transacción A no ha terminado en este
momento, en cuyo caso la falta no causará un ROLLBACK en la transacción A.
13

En la figura 3.4, es un poco peor. La transacción A no sólo depende de un cambio no


grabado en el timepo t2, sino que además, pierde una actualización en el tiempo t3
(porque el ROLLBACK en el tiempo t3 causa que el registro R hace que su valor se
restaure a su valor anterior al tiempo t1). Esta es otra versión del problema de la
pérdida de la actualización.

Transacción A Tiempo Transacción B

t1 UPDATE R

FETCH R t2

t3 ROLLBACK R

Fig. 4.3

Transacción A Tiempo Transacción B

t1 UPDATE R

UPDATE R t2

ROLLBACK R
t3

Fig. 4.4

El problema del análisis inconsistente


Considere la figura 4.5, la cual muestra dos transacciones A y B operando sobre un
registro de cuenta (ACC): La transacción A está sumando los balances de las cuentas, la
transacción B está transfiriendo una cantidad 10 desde una cuenta 3 hacia una cuenta 1.
El resultado producido por A (110) es incorrecto; si A escribiera en la base de datos,
causaría que la base de datos quedara en un estado inconsistente y que resultaría en un
análisis inconsistente. La diferencia entre este ejemplo y el previo radica en que aquí A
depende de un cambio no almacenado, hasta que B actualiza (COMMIT) todas sus
actualizaciones antes de que A vea ACC 3.
14

ACC 1 ACC 2 ACC 3

40 50 30

Transacción A Tiempo Transacción B


FETCH ACC 1 (40) t1 …
Sum = 40 …
…. …
t2 …
FETCH ACC2 (50)
Sum = 50 …
… t3 FETCH ACC3 (30)
… …
… UPDATE ACC3

t4 30 Æ 20
… …
… FETCH ACC1 (40)
t5
… …
… …
… t6 UPDATE ACC1
… 40 Æ 50
… ….
t7 COMMIT

FETCH ACC3 (20)
Sum = 110 – no 120 t8

Fig. 4.5

Bloqueos
La idea básica del bloqueo es muy simple: Cuando una transacción necesita asegurar
que un objeto será utilizado por ella – normalmente un registro de la base de datos – no
lo cambiará de una manera impredecible mientras no tenga cubiertas las espaldas, es
decir, realizará primero un bloqueo sobre el objeto. El efecto del bloqueo es “bloquear
otras transacciones externas” al objeto, y esto logra prevenir un cambio en el objeto. La
primera transacción será capaz de realizar todo su proceso con la certeza de que el
objeto en cuestión se mantendrá en un estado estable por el tiempo que la transacción lo
necesite.
Ahora se muestra en detalle como funciona el bloqueo:
1. Primero, se asume la existencia de dos tipos de bloqueo, llamados bloqueos
exclusivos (X locks) y bloqueos compartidos (S locks), los cuales se definen en
los incisos 2 y 3.
2. Si la transacción A provoca un bloqueo exclusivo (X) sobre el registro R,
entonces un requerimiento desde otra transacción B para bloquear de cualquier
15

forma el registro R causara que B quede en un estado de espera. B esperará


hasta que la transacción A libere el bloqueo sobre el registro R.
3. Si una transacción A provoca un bloqueo compartido (S) sobre el registro R,
entonces:
a. Un requerimiento de un bloqueo X desde una transacción B causará que
B quede en un estado de espera, y B esperará hasta que la transacción A
libere el bloqueo sobre el registro R.
b. Un requerimiento de un bloqueo S desde una transacción B sobre el
registro R será permitido (esto es, B también provocará un bloqueo
compartido sobre el registro R).

Estos tres párrafos pueden convertirse en una matriz resumida conocida como “matriz
de compatibilidad” (Ver Fig. 4.6). Esta matriz se interpreta como sigue: Considere
algún registro R; suponga una transacción A que provoca un bloqueo sobre el registro R
como se indica en los encabezados de las columnas (el guión representa que no existe
bloqueo); si otra transacción B solicita un bloqueo sobre el registro R como se indica
por las entradas de abajo, leyendo de izquierda a derecha. Una letra “N” indica un
conflicto (la solicitud de B no puede ser satisfecha y B cae en un estado de espera), y
una letra “Y” indica compatibilidad (la solicitud de la transacción B es satisfecha). La
matriz es simétrica.

X S -

X N N Y

S N Y Y

- Y Y Y

Fig. 4.6 Matriz de


compatibilidad
4. Las solicitudes de las transacciones para bloquear registros normalmente son
implícitas. Cuando una transacción recupera un registro en forma exitosa,
automáticamente provoca un bloqueo S sobre el registro. Cuando una
transacción actualiza exitosamente un registro, automáticamente provoca un
bloqueo X sobre el registro. (Si la transacción ya provocó un bloqueo S sobre el
registro, como ocurre en una secuencia de SQL FETCH/update, entonces la
instrucción “update” promueve el bloqueo S hacia un nivel X.)
5. Un bloqueo X se mantiene hasta el próximo Synchpoint.

Ahora se puede entender la forma en que el bloqueo resuelve los tres problemas
descritos en las secciones previas.

El problema de la pérdida de actualización


La figura 4.7 muestra lo que ocurre en el ejemplo de la figura 4.2 utilizando el esquema
de bloqueos descrito anteriormente.
16

Transacción A Tiempo Transacción B

FETCH R t1
S-lock sobre R

t2 FETCH R
S-lock sobre R
UPDATE R t3
Requiere X-lock sobre R
Esperar
Esperar UPDATE R
t4 Requiere X-lock sobre R

Esperar
Esperar
Fig. 4.7

La instrucción UPDATE de la transacción A en el tiempo t3 debe esperar, debido a que
requiere un X-lock sobre el registro R, pero existe un conflicto debido al S-lock que la
transacción B obtuvo también sobre R; por esa razón la transacción A queda en un
estado de espera. Por la misma razón, la transacción B queda en estado de espera en el
tiempo t4. Ahora ambas transacciones están bloqueadas, lo cual sin duda es otro
problema. La técnica de bloqueo resuelve el problema de la pérdida de actualización,
pero crea un nuevo problema llamado “deadlock”.

El problema de la dependencia a lo no confirmado


Las figuras 4.8 y 4.9, muestran respectivamente, la aplicación de las reglas de la técnica
de bloqueo en los ejemplos mostrados en las figuras 4.3 y 4.4.

Transacción A Tiempo Transacción B

t1 UPDATE R
(obtiene X-lock
Sobre R)
FETCH R (solicita S- t2
lock)
Esperar
Esperar t3 ROLLBACK R
Obtiene registro R (Synchpoint)
S-lock sobre R Libera X-lock sobre R

Fig. 4.8
17

Transacción A Tiempo Transacción B

t1 UPDATE R
Obtiene X-lock sobre R

UPDATE R (requiere S-lock t2


sobre R)
esperar ROLLBACK R (synchpoint)
(obener registro R) t3 (libera X-lock sobre R)
Obtiene x-Llock sobre R
Fig. 4.9

La operación de la transacción A en el tiempo t2 (FETCH en la figura 4.8 y UPDATE


en la figura 4.9) no son aceptadas en ambos casos, porque requieren un bloqueo
implícito sobre el registro R, y como tal requerimiento está en conflicto con el x-lock ya
obtenido por la transacción B en el tiempo t1, implique que la transacción A queda en
un estado de espera. El estado de espera se mantiene hasta que la transacción B llega a
un synchpoint (es decir, cuando ejecuta una operación COMMIT o ROLLBACK),
cuando el bloqueo de B termina, y la transacción A puede continuar; entonces, ve un
valor actualizado (en los ejemplos el valor original del valor del registro R ya que
terminan con una instrucción ROLLBACK). Dicho de otra forma, la transacción A no
depende de un cambio no confirmado.

El problema del análisis de inconsistencia


La figura 4.10 muestra lo que pasaría en el ejemplo de la figura 4.5 si se aplica el
método de bloqueo que se explicó anteriormente. El UPDATE de la transacción B en el
tiempo t6 no es aceptado, porque es un requerimiento implícito de un X-lock sobre
ACC1, y tal requerimiento tiene conflicto con el S-Lock ya obtenido por A; por lo tanto
B va a un estado de espera. Asimismo, el FETCH de la transacción A en el tiempo t7
no es aceptado, porque el requerimiento implícito por un S-Lock sobre ACC3 tiene
conflicto con el X-Lock ya obtenido por B; por lo tanto la transacción A también queda
en estado de espera. De nuevo, la técnica del bloqueo resuelve el problema original,
pero genera un deadlock.
18

ACC 1 ACC 2 ACC 3

40 50 30

Transacción A Tiempo Transacción B


FETCH ACC1 (40) t1 …
(obtiene S-Lock sobre ACC1) …
Sum = 40 …
t2 …
….
FETCH ACC2 (50) FETCH ACC3 (30)
(obtiene S-Lock sobre ACC2) t3 (obtiene S-Lock sobre ACC3)
Sum = 50 …
… UPDATE ACC3
t4 (obtiene X-Lock sobre ACC3)

… 30 Æ 20
… …
t5
… FETCH ACC1 (40)
… (Obtiene S-Lock sobre ACC1)
… t6 …
… UPDATE ACC1
… (requiere X-Lock sobre ACC1)
t7 Esperar
FETCH ACC3 (20)
(requiere S-Lock sobre ACC3) Esperar
Esperar t8 ….
Esperar

Fig. 4.10

Deadlock
Ya se ha visto como el bloqueo puede resolver los tres problemas básicos de
concurrencia. Desafortunadamente, también introduce problemas, principalmente el
problema del “deadlock”. Anteriormente se vieron dos problemas de “deadlock”. La
figura 4.11 muestra una versión más generalizada de este problema. R1 y R2 en esta
figura representan algún objeto que puede ser bloqueado (por ejemplo registros de la
base de datos).
El “deadlock” es una situación en la cual dos o más transacciones están
simultáneamente en un estado de espera, cada una de las cuales espera a que alguna de
las otras libere el bloqueo antes de proceder. La figura 4.11 muestra un “deadlock” que
envuelve dos transacciones, pero los deadlocks normalmente involucran tres, cuatro,
etc., transacciones.
19

Transacción A Tiempo Transacción B


Requiere un X-Lock sobre R1 t1 …
… …
… …
… t2 Requiere un X-Lock sobre R2
… …
… …
… …
t3
Requiere X-Lock sobre R2 …
Esperar …
Esperar Requiere X-Lock sobre R1
Esperar t4
Esperar
Esperar Esperar

Figura 4.11

Si un “deadlock” ocurre, sería deseable que el sistema lo detectara y lo eliminara.


Detectar un “deadlock” involucra detectar un ciclo en la gráfica de “espera por” (por
ejemplo, la gráfica de “quién espera por alguien más”. Romper un “deadlock”
involucra elegir una de las transacciones que participan en el “deadlock” y revertirla
(ejecutar un ROLLBACK), de esta forma se liberaría el “deadlock” y el resto de las
transacciones continuaría su proceso normal.
Es de notar que al resolver un “deadlock” la víctima elegida “falla” y se revierte
(ROLLBACK), aunque no existió un falla en sí misma. Algunos sistemas
automáticamente reanudan la transacción víctima desde el inicio, sobre el supuesto de
que las condiciones que originaron el “deadlock” en la primera ejecución
probablemente no se volverán a repetir. Otros sistemas simplemente envían un código a
la aplicación, para que sepa que su transacción fue víctima en un “deadlock” y que por
lo tanto fue cancelada, de esta forma la aplicación tendrá que buscar una solución a esta
situación. El primero de los métodos anteriores es el preferido por los programadores
de aplicaciones.

S-ar putea să vă placă și