Concurrencia y transacciones - script 2008

From Ibbddunq

(Difference between revisions)
m (Concurrencia y transacciones - script moved to Concurrencia y transacciones - script 2008)
 
(3 intermediate revisions not shown)
Line 15: Line 15:
1. non-repeatable read y su consecuencia respecto de los UPDATE.
1. non-repeatable read y su consecuencia respecto de los UPDATE.
Ponele una transacción que agrega una encomienda para un único servicio
Ponele una transacción que agrega una encomienda para un único servicio
-
<code>
+
  PROCEDURE `cargarEncomienda1`(elCliente varchar(45), elPeso integer, elServicio integer)
-
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
-
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);
 
-
  update servicio set cargaRestante = cargaRestante - elPeso where nroServicio = elServicio;
 
-
  insert into encomiendaEnServicio(nroEncomienda,nroServicio) values
 
-
      (nroNuevaEncomienda, elServicio);
 
-
END
 
-
</code>
 
-
 
 
2. phantom read.
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
Line 44: Line 108:
Las 4 características
Las 4 características
* Atomicidad: la garantiza el motor.
* Atomicidad: la garantiza el motor.
-
* Consistencia: la debe garantizar uno haciendo operaciones correctas en cada transacción.
+
* 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.
* Durabilidad: la garantiza el motor.
* Aislamiento: necesita un tratamiento particular.
* Aislamiento: necesita un tratamiento particular.
Line 50: Line 116:
Demarcación - BEGIN / COMMIT / ROLLBACK.
Demarcación - BEGIN / COMMIT / ROLLBACK.
-
Intercalado de transacciones - schedule
+
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.
-
==Intro a transacciones==
+
==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.
 +
 
 +
 
 +
 
 +
==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.

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.

Personal tools