Entendiendo el Aislamiento en base de datos

Entendiendo el Aislamiento en base de datos

Cuando comenzamos a desarrollar aplicaciones que usan bases de datos nos suele parecer lejano los problemas de concurrencia que se estudian en programación en paralelo en la universidad, sin embargo, estamos muy muy cerca de aquellas complejidades. Cuando al menos dos procesos intentan usar un mismo recurso hay que decidir como se gestiona ese recurso. Si esos recursos son registros de una base de datos, tienen una naturaleza ligeramente distinta a los recursos “físicos”; por ejemplo una impresora imprime solo un folio a la vez, mientras un registro puede ser accedido por cientos de consultas a la vez sin que eso le resulte un problema.

Mientras que los accesos son de lectura, el problema no parece muy importante, ¡que todos lean y ya esta! podríamos decir, sin embargo si algunos escriben, es decir insertan o modifican ese registro (ese dato si queremos ser más puristas), el problema cambia, se complica.

Desde los años 70 cuando las bases de datos estaban naciendo y además de las relacionales  habían otro tipo de estructuras para almacenar información, ya se hablaba de “ACID” .

No, no es nada oscuro ni extraño ni ilegal, se trata del acrónimo de Atomicidad, Consistencia, Aislamiento (Isolation en ingles) y Durabilidad. Como veis tenéis un link a la Wikipedia donde explica que significan esas cuatro palabras de forma más académica y clara que lo haría yo, ¿para que reescribir lo que ya está perfecto? :), las implicaciones para cumplir todo esto si que son importantes.

Siguiendo desde un punto de vista teórico y como cuentan en la Wikipedia, pero de una forma “más clara” -en mi opinión- voy a intentar explicar cual es el problema del aislamiento.

La atomicidad, que consiste en que una operación por compleja que sea o se hace entera o no se hace nada, es implementada mediante el uso de transacciones -vamos a hablar de ellas pero creo que se entiende fácil-, la consistencia mediante índices y constraints, la durabilidad la garantiza en SQL Server el log de transacciones, nos falta ver que es eso del Aislamiento. El aislamiento lo que pretende decir es que una operación no pueda afectar a otras, o dicho de otra forma (sin pretender ser exacto), que todos los usuarios estén en la base de datos, como si solo ellos pudieran cambiar algo, veamos los problemas que esto puede traer

Imaginemos solo dos usuarios leyendo de una base de datos, Luis y María. A Luis le encargan la tarea de modificar las tarifas de productos y determina que tiene que subir los 10.000 productos del catálogo un 4%. María, está registrando ofertas a clientes al mismo tiempo. Si maría lee la tarifa mientras Luis está modificándola, puede tomar algunos productos con el precio antiguo…. y otras con el precio nuevo. Esto podría ser un desastre y es lo que el concepto de aislamiento pretende evitar. Ahora bien… ¿Cómo hacemos para que esto sea posible? Si maría comienza a leer los datos antes de que Luis los modifique, parecería que lo correcto es que pueda leer todos los precios antiguos, si comienza después que haya terminado Luis, debería leer los nuevos precios. Hay dos formas distintas de hacer esto

  1. Cuando Luis comienza a modificar precios, bloquea la tarifa y hasta que no termine de escribir nadie la lee. Por tanto si María llega cuando Luis está modificando… se espera a que Luis termine. (esto es generar bloqueos)
  2. Luis comienza a modificar precios, pero antes, hace una copia de todos los precios anteriores, así si en el periodo que el tarda en cambiar los precios alguien (María) los necesita puede consultar la ultima versión buena de los datos.

Aunque hay más factores mas complejos que intervienen, grosso modo vemos las dos formas en las que las bases de datos implementan el aislamiento, con bloqueos o con versiones de filas. Pueden imaginar que aunque para el ejemplo hemos hablado de toda la tarifa, hay cambios en productos que van pasando en el tiempo mientras otros consultan los datos. Estudiando lo que esto quiere decir al final lo que vemos es que hay dos roles en datos, los lectores y los escritores, y que cuando varios interactúan hay que ver que pasa, podemos así crear estas dos tablas distintas

Bloqueando Lectores Escritores Versionando Lectores Escritores
Lectores

O

X

Lectores

O

O

Escritores

X

X

Escritores

O

X

¿Que quiere decir esta tabla?

Bloqueando En el ejemplo de María y Luis, si aplicamos la idea de bloqueos, si maría comienza a hacer presupuestos, Luis no debe cambiar precios hasta que María termine. Es decir la lectura de la tarifa de Maria impide que Luis cambie los precios (la bloquea). De ahí que los lectores bloqueen a los escritores. Si por el contrario Luis comienza a cambiar la tarifa, hasta que no termine maría no podrá leerla, de ahí que los escritores bloquen a los lectores.

Versionando María se pone a leer la tarifa, y Luis decide comenzar a cambiarla, se guarda la tarifa anterior y maría sigue consultándola mientras Luis la cambia. Por tanto María(lectores) no bloquea a Luis (escritores). Si Luis comienza a cambiar primero la tarifa, hace una copia, cuando llega maría lee directamente la copia, por tanto, Luis (escritores) no bloquea a maria (lectores). Solamente habrá bloqueo en el caso de que ambos quieran modificarla a la vez.

En resumen el mismo problema se puede resolver de dos formas distintas, cada una tiene ventajas e inconvenientes y cada fabricante de bases de datos elige si da ambas opciones, o una sola de ellas.

SQL Server desde la versión 2005 implementa ambas aproximaciones, Oracle por ejemplo siempre (que yo sepa) funciona con la opción de versionado.

Pareciera que lo mejor siempre es funcionar con versionado, porque genera muchos menos bloqueos, pero como siempre pasa en estos escenarios, siempre hay un coste, guardar la versión anterior de 10.000 filas modificadas tiene un coste, muy superior en recursos a simplemente hacer que un hilo espere.

Por tanto no hay un modelo mejor ni peor, sino hay un modelo que se adapta mejor o peor a los requisitos que tenga nuestra aplicación.

Lo siguiente que creo que debemos entender son los niveles de aislamiento que hay definidos en el estándar y sus efectos en las lecturas.

Los niveles de aislamiento que existen son 4 fundamentalmente, Serializable, Lecturas repetibles, Lecturas validadas, y lecturas no validadas. En inglés que es más común usarlas son Serializable, repeatable reads, Read Commited y Read Uncommited.

 

En el link que teneis a la Wikipedia explica también parte de lo que van a tratar estas series de artículos.

Los problemas que pueden presentar las lecturas son 3

  • Problema de lecturas sucias
  • Problema de Lecturas repetibles
  • Problema de lecturas fantasma

Los problemas de lecturas sucias se producirían si maría pudiera leer los precios mientras Luis los modifica, de esta forma podría leer la tarifa antigua del producto A y después la nueva del producto B. Imaginemos que Luis luego se arrepiente de la modificación de tarifa, María habría hecho presupuestos a precios que nunca Luis validó. Eso es el problema de lecturas sucias.

Las lecturas repetibles, maría está haciendo presupuestos solo de un artículo, del artículo A, mira el precio antes de que luis cambie precios y vale 10€, toma un café maria, y vuelve a leer, entre tanto, luis ha cambiado los precios y los ha confirmado, no es que esté cambiándolos sino que ya lo ha hecho, por tanto lee de precio 14€, ¿de dónde sacó los 10€ antes?

Las lecturas fantasma se producen, cuando maría lee la tarifa entera, y cotiza el producto Z, luis, sabe que el producto Z no tiene calidad, y lo borra del catálogo. María vuelve a leer la tarifa para comprobar precios… y el producto Z desapareció como los fantasmas.

 

En los próximos artículos primero veremos que pasa y como se comporta SQL server con lecturas sucias, lecturas confirmadas, lecturas repetibles y serializable tanto en la implementación basada en bloqueos como en la implementación basada en versiones de filas.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *