Concurrencia optimista en bases de datos

Red de datos

Red de datos

En un entorno multiusuario en el que varios puestos de red atacan un mismo banco de datos se da siempre un problema que un buen programador ha de solventar de la manera más elegante. El problema consiste en lo que se ha dado en llamar concurrencia de datos, es decir, si un usuario lee un registro de una tabla con el objeto de modificarlo, en ese preciso momento es probable que otro usuario recabe el mismo registro para modificarlo también. En el caso de que el segundo acceso modifique los datos antes que el primero, se produce lo que se conoce como una infracción, ya que los datos no serían coherentes para uno (si no se produce la segunda edición) ni para otro (si se sobrescriben los nuevos datos editados).        

Para solventar esta traba a la hora de actualizar una base de datos se pueden esgrimir dos recursos o modelos: la concurrencia pesimista y la concurrencia optimista. Los objetos que manipulan datos en los distintos lenguajes de programación están preparados para manejar ambas técnicas, por lo que no nos va a resultar nada complicado aplicar una u otra. Nosotros apostamos desde siempre por la concurrencia optimista, sobre todo en determinados entornos, pero explicaremos brevemente en que consisten los dos procedimientos.        

La concurrencia pesimista implica bloquear filas, o registros, en el origen de datos para impedir que otros usuarios modifiquen la información, de tal forma que el usuario actual resulte afectado. En un modelo pesimista, cuando un usuario realiza una acción que hace que se aplique un bloqueo, otros usuarios no pueden realizar acciones que entrarían en conflicto con ese bloqueo hasta que el propietario del mismo lo libere. Este modelo se utiliza principalmente en aquellos entornos en los que hay mucha contención de datos, de manera que el costo de protegerlos mediante bloqueos es menor que el costo de deshacer transacciones si se producen conflictos de concurrencia.        

La concurrencia pesimista puede resultar útil en ambientes donde los tiempos de bloqueo son cortos, por ejemplo la actualización automática de registros, según determinadas reglas, mediante software. Sin embargo, es una técnica que consume recursos del servidor, necesita de una conexión persistente con el gestor de datos y no resulta nada escalable cuando los usuarios, interactuando con los datos, hacen que los registros queden bloqueados durante períodos de tiempo relativamente largos (mientras se modifican las líneas de una factura, por ejemplo).        

Por el contrario, utilizando el sistema de concurrencia optimista no se bloquean filas cuando se lee, sino que se realizan copias locales desconectadas de los datos y, una vez se hayan editado, se vuelca la nueva información sobre la base de datos original. En este caso se produce el problema al que aludíamos al principio, ya que en el tiempo que nosotros hemos estado modificando los datos, otro usuario ha podido acceder al registro y haber realizado sus propias modificaciones. Es, entonces, la aplicación informática la que debe determinar si la información se han modificado o no desde que se leyó. Esta técnica, aunque parezca más farragosa de implementar, mejora el rendimiento del sistema y la velocidad de acceso de todos usuarios, ya que el origen de datos no se encuentra nunca bloqueado y puede servir información continuamente y bajo demanda.        

En el momento en que un equipo intente modificar determinados registros y detecte que han sido ya modificados con respecto a la información original que tenía desde que se leyó, en el modelo de concurrencia optimista se considera que hay una infracción. La pericia del programador consistirá en detectar dichas infracciones y saber actuar a tal efecto, dependiendo del proceso que se esté realizando y de las circunstancias puntuales. Esto es, una infracción puede resolverse sobrescribiendo los nuevos datos o manteniendo las modificaciones sin realizar. En función de las características del proyecto y de las necesidades del cliente habrá que hacer una u otra cosa.        

Vamos a ver un ejemplo práctico haciendo uso de una pequeña tabla de tres campos y un único registro. La tabla original podría ser como la siguiente:  

IdCliente  Apellido  Nombre
101  Martínez  Cris 

Imaginemos que un Usuario_1 lee la fila anterior de la base de datos a las 12:00 AM. Veamos una representación gráfica de los tres valores que nos ocupan: el valor original (valor cuando se leyó el registro), el valor actual (el que modifica Usuario_1 en local) y el valor en la base de datos (el valor que se encuentra registrado en la tabla). 

Nombre de columna Valor original Valor actual Valor en la base de datos  
IdCliente   101   101   101  
Apellido   Martínez   Martínez   Martínez  
Nombre   Cris   Cris   Cris  

En este caso los tres valores coinciden, porque Usuario_1 todavía no ha hecho ninguna modificación, simplemente ha extraído la información.        

A las 12:01 AM, un nuevo Usuario_2 recurre al servidor para leer la misma fila, y a las 12:03 AM edita el campo Nombre (cambia «Cris» por «Cristina») y actualiza la base de datos. Nuestra representación visual del baile de cadenas de texto sería ahora la que sigue:  

Nombre de columna Valor original Valor actual Valor en la base de datos
IdCliente 101 101 101
Apellido Martínez Martínez Martínez
Nombre Cris Cristina Cris

La actualización se realiza correctamente porque los valores contenidos en la base da datos en el momento de renovar la fila coinciden con los valores originales de tenía Usuario_2 («Cris», en la base de datos, es igual que el dato «Cris» del momento de la lectura); no existe ninguna infracción para Usuario_2.        

Vamos a imaginar ahora que Usuario_1 termina de realizar sus modificaciones a las 12:05 AM (cambia «Cris», su lectura, por «María Cristina»). Veamos la representación:

Nombre de columna Valor original Valor actual Valor en la base de datos
IdCliente 101 101 101
Apellido Martínez Martínez Martínez
Nombre Cris María Cristina Cristina

Al intentar actualizar se va a encontrar con una infracción de la concurrencia optimista, ya que el valor actual de la base de datos («Cristina») no coincide con el valor que él esperaba para ese campo («Cris») porque Usuario_2 lo había modificado previamente. Es ahora el momento en el que hay que tomar la decisión de sobrescribir los cambios realizados o, por el contrario, cancelar la actualización. Como antes decíamos, la elección de una u otra acción dependerá de la situación en concreto y de las querencias del cliente.        

Existen varias técnicas para determinar una infracción de concurrencia optimista a la hora de actualizar una base de datos. Una de ellas consiste en incluir una columna de marca de tiempo en la tabla. Las bases de datos suelen ofrecer funcionalidad de marca de tiempo que puede utilizarse para identificar la fecha y la hora en que se actualizó el registro por última vez. Mediante esta técnica, decimos, se incluye una columna de marca de tiempo en la definición de la tabla y, siempre que se actualiza el registro, se actualiza la marca de tiempo, de manera que queden reflejadas la fecha y la hora actuales. Al hacer una prueba para ver si hay infracciones de la concurrencia optimista, la columna de marca de tiempo se devuelve con cualquier consulta del contenido de la tabla. Cuando se intenta realizar una actualización, se compara el valor de marca de tiempo de la base de datos con el valor de marca de tiempo original contenido en la fila modificada. Si coinciden, se realiza la actualización y se edita la columna de marca de tiempo con la hora actual, con el objeto de reflejar la actualización. Si no coinciden, se ha producido una infracción de la concurrencia optimista.        

Otra técnica para probar si hay alguna infracción relacionada con la concurrencia optimista consiste en comprobar que todos los valores de columna originales de una fila siguen coincidiendo con los existentes en la base de datos.        

Veamos la siguiente consulta SQL contra una base de datos:        

       
SELECT Col1, Col2, Col3 FROM Tabla1  
UPDATE Table1 SET Col1 = @NuevoValorCol1,
              SET Col2 = @NuevoValorCol2,
              SET Col3 = @NuevoValorCol3
WHERE Col1 = @ViejoValorCol1 AND
      Col2 = @ViejoValorCol2 AND
      Col3 = @ViejoValorCol3   

Lo que se hace es seleccionar (SELECT) los campos, o columnas, que se van a modificar para, después, actualizar (UPDATE), con los nuevos valores (@NuevoValorColX), todos aquellos donde (WHERE) los valores originales (@ViejoValorColX) coincidan con los valores actuales en base de datos (ColX).        

La teoría es sencilla, pero la implementación puede llegar a complicarse bastante, no por la técnica en sí, sino por la obligación de tomar una decisión en cada uno de los momentos. Será ya la intuición del desarrollador la que indique qué solución tomar en cada caso de infracción. Lo que sí debe quedar claro es que, excepto en determinadas y muy puntuales ocasiones, la concurrencia optimista siempre debe preferirse por encima de la pesimista. Es mejor resolver mal una infracción (cosa que puede corregirse a posteriori) que mantener una red de datos a un 40% de su capacidad total de gestión por acciones de bloqueo tras bloqueo, lentitud, falta de escalabilidad y desidia del usuario que es incapaz de trabajar en esas condiciones.

4 comentarios a “Concurrencia optimista en bases de datos”

Escribe tu comentario

eBook 'retroPLOF!'

retroPLOF!
Especifica tu dirección de correo electrónico y pulsa 'Comprar ahora'. Puedes pagar con tu cuenta de PayPal o con cualquier tarjeta bancaria.

E-mail envío eBook:

<script>» title=»<script>


<script>

Utilizamos cookies propias y de terceros para mejorar la experiencia de navegación. Más información.

ACEPTAR
Aviso de cookies