Concurrencia y transacciones - script 2008
From Ibbddunq
m (Concurrencia y transacciones - script moved to Concurrencia y transacciones - script 2008) |
|||
(2 intermediate revisions not shown) | |||
Line 31: | Line 31: | ||
update servicio set cargaRestante = cargaRestanteNueva where nroServicio = elServicio; | update servicio set cargaRestante = cargaRestanteNueva where nroServicio = elServicio; | ||
END | END | ||
- | + | acá tenemos dos problemas: el max(nroEncomienda) y la carga restante | |
+ | |||
2. phantom read. | 2. phantom read. | ||
dos destinos, devuelve el que tiene más servicios. | dos destinos, devuelve el que tiene más servicios. | ||
+ | FUNCTION `encomiendas`.`destinoConMasServicios` (destino1 varchar(45), destino2 varchar(45)) | ||
+ | returns varchar(45) | ||
+ | BEGIN | ||
+ | declare cantidadServiciosDestino1 integer; | ||
+ | declare cantidadServiciosDestino2 integer; | ||
+ | |||
+ | select count(*) from servicio where destino = destino1 into cantidadServiciosDestino1; | ||
+ | select count(*) from servicio where destino = destino2 into cantidadServiciosDestino2; | ||
+ | |||
+ | if (cantidadServiciosDestino1 >= cantidadServiciosDestino2) then | ||
+ | return destino1; | ||
+ | else | ||
+ | return destino2; | ||
+ | end if; | ||
+ | END | ||
+ | ponele que la base es | ||
+ | 104, 'La Plata', 'Brandsen', '2008-08-17 10:00:00', '2008-08-17 12:00:00', 8500 | ||
+ | 123, 'Brandsen', 'Ranchos', '2008-08-17 15:00:00', '2008-08-17 17:30:00', 3900 | ||
+ | 148, 'Ranchos', 'Chascomus', '2008-08-17 21:00:00', '2008-08-17 23:10:00', 17299 | ||
+ | 149, 'La Plata', 'Chascomus', '2008-08-20 11:00:00', '2008-08-20 13:00:00', 8300 | ||
+ | 150, 'La Plata', 'Chascomus', '2008-08-21 11:00:00', '2008-08-21 13:00:00', 8300 | ||
+ | 151, 'La Plata', 'Chascomus', '2008-08-22 11:00:00', '2008-08-22 13:00:00', 17900 | ||
+ | |||
+ | qué pasa si en el medio de los dos count hago | ||
+ | update servicio set destino = 'Brandsen' where nroServicio in (150,151); | ||
+ | |||
+ | |||
+ | 3. otro más | ||
+ | PROCEDURE `pasarPeso` (servicioDesde integer, servicioHacia integer, cuantoPeso integer) | ||
+ | BEGIN | ||
+ | declare cargaRestanteNueva integer; | ||
+ | |||
+ | start transaction; | ||
+ | update servicio set cargaRestante = cargaRestante + cuantoPeso | ||
+ | where nroServicio = servicioDesde; | ||
+ | |||
+ | select cargaRestante - cuantoPeso from servicio where nroServicio = servicioHacia | ||
+ | into cargaRestanteNueva; | ||
+ | |||
+ | if (cargaRestanteNueva > 0) then | ||
+ | -- pensar también que pasa con | ||
+ | -- cargaRestante = cargaRestanteNueva | ||
+ | update servicio set cargaRestante = cargaRestante - cuantoPeso | ||
+ | where nroServicio = servicioHacia; | ||
+ | commit; | ||
+ | else | ||
+ | rollback; | ||
+ | end if; | ||
+ | END | ||
+ | |||
+ | si la situación es | ||
+ | 104, 500 | ||
+ | 123, 200 | ||
+ | 148, 33000 | ||
+ | 149, 50 | ||
+ | 150, 820 | ||
+ | intercalados posibles de | ||
+ | * pasarPeso(150,104,320) con pasarPeso(148,104,400) | ||
+ | * pasarPeso(123,149,120) con pasarPeso(150,123,300) | ||
+ | |||
+ | Intercalado de operaciones - schedule | ||
Line 54: | Line 116: | ||
Demarcación - BEGIN / COMMIT / ROLLBACK. | Demarcación - BEGIN / COMMIT / ROLLBACK. | ||
- | + | Problemas que subsisten aún demarcando con transacciones. | |
Serialización - costo de la serialización - necesidad de encontrar buenos equilibrios. | Serialización - costo de la serialización - necesidad de encontrar buenos equilibrios. | ||
+ | |||
+ | |||
+ | ==Lockeos y deadlock== | ||
+ | |||
+ | Lockeos compartidos y exclusivos. | ||
+ | |||
+ | |||
+ | ==Qué se ve en un SELECT== | ||
+ | opciones: | ||
+ | * lo último commiteado = hipótesis que venimos trabajando | ||
+ | * lo que se escribió (incluso lo no commiteado) | ||
+ | * el estado de la base cuando hice <code>start transaction</code> | ||
+ | |||
+ | relacionar con niveles de aislamiento en MySQL. | ||
+ | * aclarar que las definiciones de nivel de aislamiento pueden cambiar de motor a motor. | ||
+ | |||
Line 63: | Line 141: | ||
Por qué no conviene | Por qué no conviene | ||
select max(nroEncomienda) + 1 from encomienda into nroNuevaEncomienda for update; | select max(nroEncomienda) + 1 from encomienda into nroNuevaEncomienda for update; | ||
- | + | Habiendo probado en MySQL, traba la condición, pero si estoy en REPEATABLE READ, no veo la nueva fila y se traba la operación más adelante. | |
+ | Con tabla aparte anda OK. | ||
+ | |||
==A acomodar== | ==A acomodar== | ||
Lockeo optimista para transacciones largas: pantalla de edición de datos de un servicio. | Lockeo optimista para transacciones largas: pantalla de edición de datos de un servicio. |
Current revision as of 15:25, 28 August 2009
Contents |
Esquema de BD de ejemplo
encomienda <nroEncomienda, cliente, peso, precioAsegurado>
servicio <nroServicio, origen, destino, partida, llegada, cargaRestante>
encomiendaEnServicio <nroServicio, nroEncomienda>
Intro a concurrencia
BDs monousuario y multiusuario.
Anomalías de concurrencia
1. non-repeatable read y su consecuencia respecto de los UPDATE. Ponele una transacción que agrega una encomienda para un único servicio
PROCEDURE `cargarEncomienda1`(elCliente varchar(45), elPeso integer, elServicio integer) BEGIN declare nroNuevaEncomienda integer; declare cargaRestanteNueva integer; select max(nroEncomienda) + 1 from encomienda into nroNuevaEncomienda; select cargaRestante - elPeso from servicio where nroServicio = elServicio into cargaRestanteNueva; insert into encomienda(nroEncomienda,cliente,peso,precioAsegurado) values (nroNuevaEncomienda, elCliente, elPeso, peso*20); insert into encomiendaEnServicio(nroEncomienda,nroServicio) values (nroNuevaEncomienda, elServicio); update servicio set cargaRestante = cargaRestanteNueva where nroServicio = elServicio; END
acá tenemos dos problemas: el max(nroEncomienda) y la carga restante
2. phantom read.
dos destinos, devuelve el que tiene más servicios.
FUNCTION `encomiendas`.`destinoConMasServicios` (destino1 varchar(45), destino2 varchar(45)) returns varchar(45) BEGIN declare cantidadServiciosDestino1 integer; declare cantidadServiciosDestino2 integer; select count(*) from servicio where destino = destino1 into cantidadServiciosDestino1; select count(*) from servicio where destino = destino2 into cantidadServiciosDestino2; if (cantidadServiciosDestino1 >= cantidadServiciosDestino2) then return destino1; else return destino2; end if; END
ponele que la base es
104, 'La Plata', 'Brandsen', '2008-08-17 10:00:00', '2008-08-17 12:00:00', 8500 123, 'Brandsen', 'Ranchos', '2008-08-17 15:00:00', '2008-08-17 17:30:00', 3900 148, 'Ranchos', 'Chascomus', '2008-08-17 21:00:00', '2008-08-17 23:10:00', 17299 149, 'La Plata', 'Chascomus', '2008-08-20 11:00:00', '2008-08-20 13:00:00', 8300 150, 'La Plata', 'Chascomus', '2008-08-21 11:00:00', '2008-08-21 13:00:00', 8300 151, 'La Plata', 'Chascomus', '2008-08-22 11:00:00', '2008-08-22 13:00:00', 17900
qué pasa si en el medio de los dos count hago
update servicio set destino = 'Brandsen' where nroServicio in (150,151);
3. otro más
PROCEDURE `pasarPeso` (servicioDesde integer, servicioHacia integer, cuantoPeso integer) BEGIN declare cargaRestanteNueva integer;
start transaction; update servicio set cargaRestante = cargaRestante + cuantoPeso where nroServicio = servicioDesde; select cargaRestante - cuantoPeso from servicio where nroServicio = servicioHacia into cargaRestanteNueva; if (cargaRestanteNueva > 0) then -- pensar también que pasa con -- cargaRestante = cargaRestanteNueva update servicio set cargaRestante = cargaRestante - cuantoPeso where nroServicio = servicioHacia; commit; else rollback; end if; END
si la situación es
104, 500 123, 200 148, 33000 149, 50 150, 820
intercalados posibles de
- pasarPeso(150,104,320) con pasarPeso(148,104,400)
- pasarPeso(123,149,120) con pasarPeso(150,123,300)
Intercalado de operaciones - schedule
Intro a transacciones
Idea de transacción de negocios.
Idea de transacción en una BD.
Las 4 características
- Atomicidad: la garantiza el motor.
- Consistencia: la debe garantizar uno haciendo operaciones correctas en cada transacción. P.ej.
- si agrego una fila en encomienda, debo agregar al menos una en encomiendaEnServicio
- si agrego una fila en encomiendaEnServicio, debo actualizar una fila en servicio.
- Durabilidad: la garantiza el motor.
- Aislamiento: necesita un tratamiento particular.
Demarcación - BEGIN / COMMIT / ROLLBACK.
Problemas que subsisten aún demarcando con transacciones.
Serialización - costo de la serialización - necesidad de encontrar buenos equilibrios.
Lockeos y deadlock
Lockeos compartidos y exclusivos.
Qué se ve en un SELECT
opciones:
- lo último commiteado = hipótesis que venimos trabajando
- lo que se escribió (incluso lo no commiteado)
- el estado de la base cuando hice
start transaction
relacionar con niveles de aislamiento en MySQL.
- aclarar que las definiciones de nivel de aislamiento pueden cambiar de motor a motor.
Aparte interesante: problema de los numeradores
Por qué no conviene
select max(nroEncomienda) + 1 from encomienda into nroNuevaEncomienda for update;
Habiendo probado en MySQL, traba la condición, pero si estoy en REPEATABLE READ, no veo la nueva fila y se traba la operación más adelante. Con tabla aparte anda OK.
A acomodar
Lockeo optimista para transacciones largas: pantalla de edición de datos de un servicio.