Objetivos:
o Transacciones
o Gestión de concurrencia
Preparación de la práctica: si en el boletín 04, ejercicio 6, se llegaron a crear las tablas de marcas y
departamentos así como los usuarios y roles, hay que borrarlo todo, si no se sabe como, se puede
ejecutar el script "b05_bdborrar.sql" proporcionado, posteriormente hay que ejecutar el script
"b05_bdcrear.sql" desde el psql. Este último es un ejemplo de script que invoca a otros.
Cuando se producen errores de consistencia, las transacciones se quedan latentes hasta que se termina la
transacción (con commit o rollback), si introducimos un valor repetido:
BEGIN;
INSERT INTO clientes VALUES ('0010','JUAN','PALOMO','VALENCIA');
responde que hay valores duplicados, si intentamos otra operación, en este caso correcta:
INSERT INTO clientes VALUES ('0012','JUAN12','PALOMO12','ALICANTE');
responde que esta transacción está abortada y que se ignora todo hasta el fin de la transacción, debemos marcar el
fin de la transacción para poder seguir:
BEGIN;
INSERT INTO clientes VALUES ('0012','JUAN12','PALOMO12','ALICANTE');
COMMIT;
ahora ya están los datos.
se tratará de trabajar con dos sesiones, en una se inicia una transacción donde insertamos datos pero no
confirmamos la transacción, en la otra, miramos los datos y vemos que no están, luego confirmamos la transacción y
consultamos los datos
Sesión 1:
BEGIN;
INSERT INTO clientes VALUES ('0020','NOMBRE20','APELLIDO20','CIUDAD2');
INSERT INTO clientes VALUES ('0030','NOMBRE30','APELLIDO30','CIUDAD3');
Sesión 2:
y no salen datos
Sesión 1:
COMMIT;
Sesión 2:
BEGIN;
INSERT INTO clientes VALUES ('0040','NOMBRE40','APELLIDO40','CIUDAD4');
INSERT INTO clientes VALUES ('0050','NOMBRE50','APELLIDO50','CIUDAD5');
\q
a. sesión 1: iniciamos una transacción, vemos los datos que hay, actualizamos los datos y
no confirmamos la transacción.
b. sesión 2: iniciamos la transacción y consultamos la información de distribución.
c. sesión 2: iniciar una transacción e intentar sumar 5 a la cantidad.
d. sesión 1: confirmamos los cambios y vemos qué pasa con los datos vistos desde esta
sesión y luego vamos a ver los datos vistos desde la sesión 2.
e. sesión 2: confirmamos los cambios
Antes de empezar, si nos conectamos con pgAdmin, vamos a ver las sesiones, nos fijamos sobre todo en las sesiones
de los usuarios que hemos conectado:
vemos que hay 2 sesiones por usuario, la propia al conectar, y la que se ha abierto al abrir el SQL interactivo.
Paso a)
Sesión 1:
CIFC;CODCOCHE;CANTIDAD
"0001";"001";3
"0001";"005";7
"0001";"006";7
"0002";"006";5
"0002";"008";10
"0002";"009";10
"0003";"010";5
"0003";"011";3
"0003";"012";5
"0004";"013";10
"0004";"014";5
"0005";"015";10
"0005";"016";20
"0005";"017";8
"0006";"019";3
Paso b)
Sesión 2:
ROLLBACK;
SELECT * FROM distribucion;
y vemos los datos antiguos, lo cual es correcto, no hay bloqueo y se han preservado los cambios. Vamos a ver las
sesiones:
Paso c)
Sesión 2:
BEGIN;
UPDATE distribucion SET cantidad = cantidad + 5;
parece que no da error, pero si nos fijamos, se está ejecutando la consulta, esto es debido a que hay un bloqueo
porque intentamos modificar los mismos registros que en la sesión 1 que no están confirmados.
Paso d)
Sesión 1: confirmamos los cambios
COMMIT;
si miramos los datos de esta sesión, son los esperados, la cantidad multiplicada por dos, y si nos fijamos, nada más
confirmar esta transacción, se ha terminado la que había en la sesión 2, si vamos a ver los datos de esta sesión,
veremos que nos sale los datos de la sesión 1 sumados con 5, es decir, se han ejecutado las 2 transacciones, una
detrás de la otra, como si hubiéramos hecho
Paso e)
Sesion 2: confirmar
COMMIT;
Los datos quedan así y las sesiones y bloqueos quedan como al principio, no hay bloqueos ni sesiones con esperas.
CIFC;CODCOCHE;CANTIDAD
"0001";"001";11
"0001";"005";19
"0001";"006";19
"0002";"006";15
"0002";"008";25
"0002";"009";25
"0003";"010";15
"0003";"011";11
"0003";"012";15
"0004";"013";25
"0004";"014";15
"0005";"015";25
"0005";"016";45
"0005";"017";21
"0006";"019";11
Este comportamiento es el que soporta por defecto PostgreSQL, nivel de aislamiento de lecturas confirmadas,
“read_commited”. El valor por defecto de este nivel se ve en el parámetro “default_transaction_isolation”, aunque
se puede cambiar en una sesión ejecutando:
6. Vamos a probar la Serialización. Vamos a trabajar sobre la tabla clientes, para el cliente cuyo
dni es ‘0001’. Intentaremos cambiar su nombre de ‘LUIS’ a ‘LUISA’ en ambas sesiones, el
proceso sería, antes de empezar las transacciones, activar el modo de transacciones a
‘serializable’:
Paso a)
Sesión 1:
devuelve ‘LUIS’
devuelve ‘LUIS’
ahora son las dos sesiones las que están esperando a terminar la transacción, ambas realizan un bloqueo
compartido.
paso e) la sesión 2 se queda en espera y ya tenemos ¡3 bloqueos de fila exclusivos! estamos en peligro de que haya
un bloqueo mortal (deadlock).
Confirmamos en la sesión 1:
COMMIT;
COMMIT;
7. Ahora pasamos al esquema “empresa”, donde los departamentos pueden tener un jefe que es un
empleado, y los empleados tienen un jefe que también es un empleado, así como pertenecen a
un departamento. Normalmente, los jefes de los departamentos, son empleados que ya existen,
pero imaginemos que demos de alta un departamento que tiene un empleado nuevo, con lo que
hay que darlo de alta también, realizar esta operación para estos datos:
¿Es posible realizar esto en una transacción, dando de alta primero el departamento y luego el
empleado? ¿Qué tendríamos que hacer para poder realizar la transacción?.
BEGIN;
INSERT INTO departamentos VALUES ('50','INFORMATICA','VALENCIA','5000');
luego, con la estructura de las tablas no se puede hacer, la transacción falla y se deshace. Si no iniciamos la
transacción, ejecutamos el INSERT directamente, también nos da el mensaje de error.
Para arreglar el tema, debemos de redifinir las constraints de departamentos para que sean “DEFERRABLE”:
ahora nos ha dejado hacer la inserción e incluso podemos consultar que está en la tabla, seguimos:
INSERT INTO empleados (numemp, nomemp, trabajo, jefe, f_alta, salario, comision, numdept, grado)
VALUES (5000, 'DARIO ROIG', 'RESPONSABLE', NULL, '2006-10-26', 35000.00, NULL, 50, 1);
COMMIT;
Es muy simple, como se puede cambiar dinámicamente si es “Deferrable”, ejecutamos, antes de hacer los cambios y
después de empezar la transacción:
BEGIN;
SET CONSTRAINTS fk_jefedepartamentos DEFERRED;