Concurrencia y transacciones - script

From Ibbddunq

Contents

Intro a problemática de concurrencia

Operaciones que se ejecutan en forma concurrente

Ejemplo 1: un tablero con llaves maestras, cada una abre varias cerraduras. Yo tengo que abrir varias cerraduras, ¿qué pasa si por las dudas me llevo todo el tablero? ¿Qué pasa si otra persona tiene que abrir una cerradura para la que sirve solamente una llave que me llevé yo?

Ver que estos problemas sólo se plantean si hay operaciones concurrentes.

Ejemplo 2: un negocio de la era pre-informática con varios vendedores, cada cliente tiene una ficha con su límite de crédito, lo que lleva, y lo que paga, los clientes van llevando mercadería en cuenta y van haciendo pagos. También hay una ficha por cada producto, donde dice la cantidad, y se anota lo que sale por ventas (que después el cliente retirará) y lo que entra por entregas de proveedores.

Preguntarles si se les ocurren problemas relacionados con operaciones concurrentes, p.ej. concentrándose en ventas.


Recurso

Concepto de recurso. En nuestros ejemplos está claro, los recursos son las llaves (ej 1) y las fichas (ej 2).

Ver que los problemas derivados de operaciones concurrentes están relacionados con el manejo de recursos, necesidad de garantizar el acceso exclusivo a un recurso y de liberarlo luego.

En el ej. 1 el acceso es naturalmente exclusivo, en el 2 no necesariamente, dos personas pueden mirar la misma ficha al mismo tiempo. Garantizar acceso exclusivo sería llevarme una ficha, los que quieran hacer algo con el cliente o producto tienen que esperar que yo termine. Esto podría servir si el permitir dos operaciones concurrentes sobre el mismo cliente implicara riesgo de problemas, como ya veremos.

Concepto de cola de espera: gente que está esperando por una llave (o por el acceso al tablero), o por una ficha.


Schedule

Schedule, schedules "buenos" y "malos".

Armar un schedule bueno y uno malo. Para esto creo que es mejor usar el ejemplo 2

  1. Los vendedores Nora y Roque están atendiendo a dos personas de la misma empresa, que es un cliente del negocio.
  2. El cliente tiene un límite de crédito de 100 pesos, y debe 60. O sea, puede comprar por 40.
  3. A Nora le piden productos por 30 pesos. Se fija en la ficha del cliente, le da el saldo. Va armando la factura.
  4. A Roque le piden productos por 20 pesos. Idem.
  5. Nora termina de despachar a la persona, vuelve al fichero, anota que el cliente debe 30 pesos más.
  6. Idem Roque. ¡Eh! el cliente compró por más de lo que podía, todo mal.

Acá puede venir bien anotar el schedule en castellano, pero ya con los andariveles "Nora" y "Roque" y la línea de tiempo.

Mostrar que si Roque empieza después que Nora termina, la anomalía no se produce, Roque ve que al cliente no le da el crédito, y no le vende. Todavía no vemos cómo garantizar que los schedules sean buenos, estamos reconociendo cuáles son buenos y cuáles no.


Deadlock

Si una persona necesita varios recursos, debe esperar que le lleguen todos. De acá llegar al concepto de deadlock.

Un ejemplo de deadlock de las llaves

  1. José se lleva la llave 1 que abre las cerraduras A y B. Se va.
  2. Ana se lleva la llave 2 que abre las cerraduras C y D. Se va.
  3. José se da cuenta que también necesita abrir la cerradura C, la \'unica llave que la abre es la que se llevó Ana. Vuelve, la llave no está, se va a tomar una cerveza sin largar la llave 1, esperando que la persona que tiene la llave 2 la devuelva.
  4. Ana se da cuenta que también necesita abrir la cerradura A, la \'unica llave que la abre es la que se llevó José. Vuelve, la llave no está, se va a tomar una cerveza sin largar la llave 2, esperando que la persona que tiene la llave 1 la devuelva.

Ooops cada una de las personas involucradas tiene un recurso que necesita otra. En vez de dos podrían ser más.

Ejemplo de deadlock del negocio: como vieron que la política de fijarse al principio y anotar al final tiene agujeros, hacen al revés, cuando miran una ficha de cliente o de producto se la llevan y no la devuelven hasta que terminaron. El que necesita una ficha se queda esperando que el que la tiene la devuelva. Las fichas de los productos A y B están en ficheros distintos. Tanto Roque como Nora tienen que procesar una venta que incluye a los artículos A y B, Roque empieza por el fichero del A, al mismo tiempo Ana empieza por el fichero del B. Listo. Plantear un schedule bueno: primero Roque agarra todo, después Ana agarra todo.

Diferencia entre los procesos y las personas: les es más difícil intercambiarse cosas. P.ej. Roque y Nora pueden hablar entre ellos y coordinarse, en lo que vamos a ver no se trabaja así.

En SO van a ver mucho más sobre el tema de recursos y deadlock, en IBD se ve solamente qué quiere decir, y un poquitito así de cómo evitar deadlock.


Carga

Es la medida de concurrencia de cualquier situación concurrente. P.ej. cuanto más carga, más posibilidad de espera, y a menos que lo manejemos hábilmente, mayor probabilidad de deadlock.



Concepto de transacción

en las BD y en la vida

Ejemplos de la vida: pago con tarjeta, intercambio de rehenes.

Ejemplos de BD: si voy a cargar una factura (vg ej de productos clientes y facturas (1) guía SQL avanzado), tengo que cargar los ítems. Si en una BD de una biblioteca (ej. Biblioteca (4) guía SQL avanzado) una persona en el mismo acto se lleva dos libros y devuelve tres, eso hay que registrarlo transaccionalmente. Entender qué quiere decir "transaccionalmente".

Hablar de interacción entre un SI y una BD.


propiedades ACID

  • Atomicidad: la garantiza el motor. Las aplicaciones deben la responsabilidad de demarcar transacciones (ya veremos qué es eso).
  • Consistencia: la debe garantizar uno (el que envía las sentencias SQL al motor) haciendo operaciones correctas en cada transacción. Las cuestiones de consistencia más semántica no pueden resolverse mediante restricciones, hay que hacer las cosas bien. P.ej.
    • si tengo un sistema de stock en distintos depósitos y muevo mercadería de uno a otro, tengo que hacer dos operaciones, si hago una sola está mal.
    • si agrego una factura por un importe, debo agregar ítems que justifiquen ese importe.
  • Durabilidad: la garantiza el motor.
  • Aislamiento: necesita un tratamiento particular, de eso vamos a hablar bastante.


demarcación

Contar la idea de demarcación, se puede hacer pensando desde el motor, me llegan sentencias SQL de un usuario, cómo sé cuándo termina una transaccion y empieza otra. P.ej. en el sistema de la biblioteca, un usuario se lleva dos libros, después otro se lleva tres y devuelve uno, después se agrega un nuevo socio que retira un libro, etc.


costo de la serialización

Costo del aislamiento si lo llevo a serialización, necesidad de manejarlo a mano.

Pensar en el sistema de carga on-line de declaraciones de Ganancias de la AFIP el día que se vence. Ahí es muy difícil que tengas problemas de concurrencia, porque mi declaración no se pisa con la tuya. Entonces la serialización no trae ventajas y sí hace impracticable a un sistema con una BD relacional atrás.

Referencia: pág 573 Silberschatz


Concurrencia y tx en una BD, concretamente

¿De dónde le llegan a una BD las transacciones que tiene que procesar?

  • De programas externos que le demarcan transacciones, o sea, le dicen "acá empieza transacción", después sentencias (en nuestro caso en SQL), después "acá termina transacción"
  • De ejecutar stored procedures.

¿Cuáles son los recursos? Los objetos a nivel instancia, nosotros nos vamos a manejar pensando en filas (también se podría trabajar usando las tablas como recursos, pero es más raro). En un rato vamos a ver qué quiere decir tener acceso a una fila.

¿Cuáles son las operaciones? Select, insert, update, delete.

Está claro que a un SGBD le pueden llegar concurrentemente pedidos de tx.

Entonces en nuestro análisis de tx concurrentes, vamos a tener un andarivel para cada tx, y lo que vamos a anotar son operaciones de (instancia de) BD.

Como se nos complica tener programas externos que se comuniquen con la BD, y también ejecutar stored procedures paso por paso, vamos a simular este comportamiento simulando a mano en el Query Browser la ejecución de las tx, incluyendo comienzo y fin.


Anomalías de concurrencia - y un ejemplo ya en el dominio de las BD

(acá borramos todo el pizarrón, salvo tal vez una listita con palabras: recurso / schedule / deadlock / tx. Necesitamos lugar para escribir los dos stored procedures, y que nos quede un cacho libre. OJO que los pizarrones no son muy grandes)

Se llama anomalía de concurrencia a cualquier situación en la que se produce un error relacionado con la ejecución concurrente de transacciones en una BD, que no ocurriría si se ejecutaran primero una y después la otra (o sea, en forma serializada)

Las tres manifestaciones más comunes de anomalías son:

  1. o bien se permite hacer una operación que no debería hacerse. P.ej. cuando un cliente en el negocio compró por más de lo que podía.
  2. o bien en la base quedan datos erróneos. Ejemplo: en el negocio cada vendedor al principio cuando se fija el disponible del cliente lo anota, hace la cuenta para obtener el nuevo disponible, y cuando termina borra el número que había y pone el suyo. Si en el medio otra persona actualizó el crédito disponible, fuimos.
  3. o bien se obtiene un resultado incorrecto de una serie de consultas, porque hubo modificaciones concurrentes. Esto tal vez es más raro, igual vamos a ver un ejemplo.

En este punto se puede pasar al sp agregarEncomienda, ver schedules buenos y malos. De esta parte debería

  • mostrarse que los schedules "buenos" son los que dan el mismo resultado de uno serial, y los "malos" son los que no. Prepárense uno bueno pero no serial, y otro malo, o varios.
  • con qué tiene que ver la posibilidad de anomalías: con las operaciones (p.ej. si son todos select no pasa nada) y muy importante, con los datos involucrados. Ningún schedule de agregado simultáneo de dos encomiendas en dos servicios distintos es malo.

si ven que cambiando el ejemplo esto se muestra mejor, escucho propuestas.

Finalmente contar que no vamos a corregir los schedules "malos" para que se porten bien, sino que vamos a evitar que pasen. O sea, vamos a agregar cosas para que sólo puedan ocurrir los schedules "buenos" y evitar la posibilidad de que dos tx concurrentes corran con un schedule "malo".


Cómo demarcamos transacciones

start transaction - commit - rollback.

se puede ver con el ejemplo del stored procedure pasarPeso.

¿y si hago un start pero después no cierro ni por commit ni por rollback? ¡¡está todo mal!!


Lockeos

Ya vimos que los recursos en los que vamos a pensar son las filas.

Un lockeo es una declaración que hace una transacción, en la que le pide a la BD que limite lo que pueden hacer otras tx que se están ejecutando en forma concurrente con una o varias filas.

Hay dos tipos de lockeo: compartido y exclusivo.

  • en el compartido, sí se permite que otras tx puedan leer, pero no se permite que otras tx puedan escribir ... ni poner lock exclusivo (ya veremos esto).
  • en el exclusivo ni escribir, ni poner lock compartido, ni poner lock exclusivo.

Qué operaciones de BD generan lockeos

   SELECT ... LOCK IN SHARE MODE (lock compartido)
   SELECT ... FOR UPDATE (lock exclusivo)
   UPDATE (lock exclusivo)

Qué operaciones de BD liberan filas lockeadas

   COMMIT
   ROLLBACK

¿Y si una tx T1 quiere hacer una operación que no se puede porque otra tx T2 puso lock? T1 debe esperar que T2 termine.

Acá hacer ejemplo de tx concurrentes, probar qué pasa si a tal SELECT le agrego una de las dos formas de lockeo si el otro quiere hacer SELECT / UPDATE, cuándo se para, cuándo se libera. Bien fácil, escribir un

   START TRANSACTION
   SELECT f1
   UPDATE f1
   SELECT f2
   COMMIT (o ROLLBACK)

y un

   START TRANSACTION
   SELECT f2
   UPDATE f1
   SELECT f1
   COMMIT (o ROLLBACK)

plantear dos ejecuciones concurrentes. Repasar la diferencia entre COMMIT y ROLLBACK. Y preguntar qué pasa si las dos tx trabajan sobre distintas filas. Se puede jugar haciendo que el UPDATE sea sobre el mismo o sobre distinto atributo. Jugar siempre con las operaciones sobre f1.

Después de esto sí le ponemos lockeo a las operaciones de f2, para que salte un deadlock.

Acá también se puede ver qué pasa si la primera hace un SELECT f1 LOCK IN SHARE MODE y después un UPDATE f1: upgrade de lock.


Uso de lockeos para resolver anomalías de concurrencia

Ver en el ejemplito de recién que la existencia de lock provoca que ciertos schedules no sean posibles. Ese va a ser el truco.

pasarPeso

  • ver dónde está el problema, cuál es la condición de "mal schedule": 2do select antes del 1er update.
  • ¿cómo lo solucionamos con lockeos? ¿alcanza compartido, o hace falta exclusivo?
  • hablar sobre non repeatable read



Hasta acá es donde debería entrar seguro. OJO que si se corta antes del final, la moraleja hay que decirla




agregarEncomienda ... donde ya volamos el SELECT del servicio ¡¡ojo!! no olvidarse de hacer esto, si no se pierde el chiste.

  • ver condición de "mal schedule".
  • pensar cuál es el recurso que conviene lockear ... la fila de servicio, cuyo SELECT en el estado actual del sp no necesitamos.

Moraleja: OJO que a veces el recurso a reservar = la fila a lockear, no es ninguna de las que ya estamos trabajando.

  • ¿cómo lo solucionamos con lockeos?

(acá creo que conviene borrar agregarEncomienda, pasarPeso viene bien más adelante) cantidadServiciosADestinos este es lindo porque se ve que con un compartido alcanza. OJO que hay que demarcar, si no, los lockeos no tienen sentido. Buen ejemplo para aclararlo.


Moraleja: cuando armo una tx en un entorno en el que preveo concurrencia, conviene pensar si conviene reservar ciertos recursos al ppio. Más fino (no sé si contarlo) para evitar deadlock, o reservar todo junto, o establecer órdenes y ser consecuentes ... u otras cosas que verán en otras materias.

Qué se ve en un SELECT / niveles de aislamiento

... volvamos a cantidadServiciosADestino ... (schedule con el UPDATE en el medio) ... acá plantear las tres opciones de qué puede leer un SELECT - lo actual-actual - lo último commiteado - el estado de la base en el start de la tx donde está el SELECT.

volvamos ahora a pasarPeso, el escenario

  pasarPeso(desde 123, hacia 149, 120) con pasarPeso(desde 150, hacia 123, 240)

con el schedule como está en el doc de ejemplos. Analizar lo que se obtiene en el select de la 2da tx según lo que se ve.

Introducir la idea de nivel de aislamiento, que controla esto y tal vez otras cosas.

  • El conjunto de niveles de aislamiento y su comportamiento varían de motor en motor, no hay un standard.
  • Por eso hay que entender en forma independiente las opciones de qué se ve en un SELECT, y después asociar cada nivel de aislamiento disponible en mi entorno con qué opción corresponde.
  • Hablar de los niveles de aislamiento en MySQL, eso lo encuentran en el manual de MySQL 5.1.

Comentario aparte pero que es un buen momento para decirlo: pero ... ¿cómo es que hace el rollback, cómo se acuerda? Contar que en algunos motores (en particular de READ COMMITTED para arriba) lo que pasa es que en realidad los cambios van "a la base posta" recién en el COMMIT. Moraleja: no hacer superenormes tx, o por lo menos revisar antes que el motor se las banque ¡¡en las condiciones que va a tener la base en producción!!


Moraleja

Un SGBD hace otras cosas además de acordarse de datos, ya vimos dos

  • puede almacenar y ejecutar stored procedures / stored functions
  • maneja el concepto de transacción "todo o nada"
  • da soporte al control de aislamiento de transacciones concurrentes

En esas otras cosas es donde se ve todo lo que te puede ofrecer un SGBD a la hora de armar un SI.


Para que lean ellos

phantom reads / non-repeatable reads / dirty reads, en el artículo sobre Isolation (database systems) de Wikipedia está bien explicado. OJO que los isolation level que dice en esa página nooooo son los de MySQL.

autocommit, que googleen

Personal tools