Está en la página 1de 10

Salvador Jesús Romero Castellano

Métodos de Control de Concurrencia en Bases de Datos


Salvador Jesús Romero Castellano
14 de Enero de 2005

Resumen
El objetivo de los métodos de control de concurrencia es garantizar la no inferencia o la
propiedad de aislamiento de transacciones que se ejecutan de manera concurrente. Los distintos
objetivos atacan el problema garantizando que las transacciones se ejecuten en un plan que sea
serializable, es decir, que el resultado sea equivalente a el resultante de ejecutar un plan en serie.
Este documento analiza los tres grandes grupos de métodos y protocolos (de bloqueo, de
marcas de tiempo y optimistas), haciendo especial énfasis en las ventajas e inconvenientes de usar
unos u otros.

Contenidos
1. Protocolos basados en técnicas de bloqueo
1. Bloqueos binarios
1. Usando bloqueos binarios
2. Bloqueos de lectura/escritura
3. Protocolo de bloqueo en dos fases
1. Bloqueo en dos fases básico, conservador, estricto y riguroso
1. Conservador o estático
2. Estricto
3. Riguroso
2. Problemas del bloqueo en dos fases: Interbloqueo y espera indefinida
1. Protocolos de prevención de interbloqueo
2. Detección del interbloqueo
3. Espera indefinida

2. Protocolos de ordenación por marcas de tiempos


1. Ordenación básica
2. Regla de escritura de Thomas
3. Ordenamiento estricto o conservador

3. Protocolos de control multiversión


1. Protocolo de ordenación por marcas de tiempo multiversión
2. Protocolo de bloqueo en dos fases multiversión

4. Protocolos de control optimistas

1
Métodos de control de concurrencia

Protocolos basados en técnicas de bloqueo

Cabe destacar antes de comenzar el estudio de los protocolos basados en bloqueos que son
los más utilizados por los SGBD comerciales. Los demás tienen una alcance más teórico que
práctico.

Un bloqueo es una variable asociada a un elemento de datos de la base de datos, usada para
restringir las operaciones que se pueden aplicar sobre él. Existen varios tipos de bloqueo: binarios
(de propiedades limitadas), compartidos, exclusivos (usados en la práctica), y bloqueos de
certificación.
Las operaciones sobre bloqueos se deben implementar como secciones críticas, es decir, de
forma indivisible; el SGBD no deberá alternar sus instrucciones con otras.

Bloqueos binarios
Se caracterizan por tener dos valores posibles; bloqueado y desbloqueado. Cada elemento de
la base de datos tiene un bloqueo distinto. El bloqueo señala si una transacción está operando sobre
el elemento o está libre para que se pueda operar con él. De esta manera se impide que dos o más
transacciones estén operando sobre un mismo elemento al mismo tiempo.
La implementación de un bloqueo binario es simple; basta con un vector de la siguiente
forma: <referencia al dato bloqueado, booleano, referencia a la transacción que lo bloquea> donde
el booleano es en sí el indicador del bloqueo.

Usando bloqueos binarios


Cuando se usan bloqueos, una transacción ha de usarlos mediante las funciones
bloquear_elemento y desbloquear_elemento, cumpliendo las siguientes reglas:

1. Una transacción T debe emitir la operación bloquear_elemento(x) antes de que se realice


cualquier operación leer_elemento(X) i escribir_elemento(x)
1. Una transacción T debe emitir la operación desbloquear_elemento(X) después de haber
completado todas las operaciones leer_elemento(X) y escribir_elemento(X) en T.
1. Una transacción T no emitirá una operación bloquear_elemento(X) si ya posee el bloqueo del
elemento X.
1. Una transacción T no emitirá una operación desbloquear_elemento(X) a menos que ya posea
el bloqueo del elemento X.

Bloqueos de lectura/escritura
Son una ampliación de los bloqueos binarios. Tenemos que el bloqueo puede tener tres
posibles posiciones: libre, bloqueado para lectura, y bloqueado para escritura. De esta forma, más de
una transacción puede tener un mismo elemento de datos bloqueado para lectura, pero sólo una para
escritura. Si una transacción quiere escribir en ese elemento, habrá de esperar a que el bloqueo
quede libre (cualquiera que sea el tipo de bloqueo), y a continuación, bloquearlo para escritura. Si
quiere leer, sólo tendrá que esperar si el elemento está bloqueado para escritura. Se dice por tanto,
que el bloqueo de lectura es compartido y el de escritura exclusivo. Tendremos por tanto tres
operaciones; bloquear_escritura(X), bloquear_lectura(X) y desbloquear(X).

2
Salvador Jesús Romero Castellano

El sistema debe hacer cumplir las siguientes reglas para usar los bloqueos de
lectura/escritura:

1. Una transacción T debe emitir la operación bloquear_lectura(x) o bloquear_escritura(X) antes


de que se realice cualquier operación leer_elemento(X) de T.
1. Una transacción T debe emitir la operación bloquear_escritura(X) antes de que realice
cualquier operación escribir_elemento(X) de T.
1. Una transacción T debe emitir la operación desbloquear(X) una vez que se hayan completado
todas las operaciones leer_elemento(X) y escribir_elemento(X) de T.
1. Una transacción T no emitirá una operación bloquear_lectura(X) si ya posee un bloqueo de
lectura (compartido) o de escritura (exclusivo) para el elemento X.
1. Una transacción T no emitirá una operación bloquear_escritura(X) si ya posee un bloqueo de
lectura (compartido) o de escritura (exclusivo) para el elemento X.
1. Una transacción T no emitirá una operación desbloquear(X) a menos que ya posea algún tipo
de bloqueo sobre el elemento X.

Las reglas 4 y 5 se pueden relajar, admitiendo que una transacción que tenga bloqueado un
elemento en modo lectura, lo pueda bloquear en modo escritura, si no existe ninguna otra
transacción que lo tenga bloqueado. Así mismo, si lo tiene bloqueado en escritura, puede relajar el
bloqueo a lectura.

Protocolo de bloqueo en dos fases


En él, todas las operaciones de bloqueo preceden a la primera operación de desbloqueo de la
transacción. El proceso se puede así dividir en dos fases; la fase de bloqueo y de desbloqueo. Si se
permite la conversión entre bloqueos, las conversiones de bloqueos de lectura a bloqueos de
escritura son en la primera fase y las de bloqueos de escritura a lectura en la segunda.

Su principal ventaja es que garantiza la seriabilidad, lo que no se consigue usando


simplemente bloqueos.

Como inconvenientes podemos citar varios:


• Bloquea los elementos que podrían ser desbloqueados tras su uso ocupados hasta la segunda fase,
impidiendo que otras transacciones que los necesiten los utilicen. Esto hace que el rendimiento
de este protocolo se degrade conforme aumenta el grado de concurrencia;
• No permite todos los planes serializables posibles.
• La implementación de este este bloqueo depende del programador, que puede no realizar su tarea
convenientemente.

Bloqueo en dos fases básico, conservador, estricto y riguroso


Son variaciones del bloqueo en dos fases. Se detallan a continuación:

Conservador o estático
Requiere que una transacción bloquee todos los elementos a los que tendrá acceso antes de

3
Métodos de control de concurrencia

comenzar a ejecutarse. Una vez bloqueados, no habrá conversión de bloqueos de lectura a escritura.
Si no es posible bloquearlos todos, la transacción no bloqueará nada y esperará a poder bloquear
todos los elementos necesarios en su totalidad.
Su principal ventaja es que no sólo garantiza la seriabilidad, sino que evita el interbloqueo
de transacciones.
Como principal inconveniente señalaremos que, en la práctica, es muy difícil saber qué
elementos serán necesarios durante la transacción antes de que esta comience, si no imposible.
También es interesante destacar que, al tener que esperar a poder bloquear todos los
elementos que la transacción necesite, este protocolo reduce la concurrencia.

Estricto
La transacción no libera ninguno de sus bloqueos de escritura antes de confirmarse o
abortar.
Este tipo de bloqueo garantiza planes estrictos en cuanto a recuperabilidad (recuperable es
un plan que, una vez confirmada la transacción, no será necesaria deshacerla). Sin embargo, puede
sufrir interbloqueos.

Riguroso
Es una versión más restrictiva del estricto. Similar al anterior, pero además tampoco libera
los bloqueos de lectura. Es más fácil de implementar.

Problemas del bloqueo en dos fases: interbloqueo y espera indefinida


El interbloqueo se produce cuando cada transacción T en un conjunto de dos o más
transacciones está esperando a algún elemento que está bloqueado por alguna otra transacción T' de
dicho conjunto. En este estado, cada transacción está parada en espera a que otra transacción libere
el recurso. Las condiciones para que se produzca en interbloqueo son las siguientes:

1. Exclusión mutua. Cada elemento está bloqueado por una transacción, o está libre.
2. Retención y espera: Una transacción que ya tiene elementos bloqueados puede solicitar un
elemento adicional, y esperar que se le asigne, sin devolver previamente ninguno de los
anteriores.
3. No apropiación: Sólo puede liberar un elemento la transacción que lo tiene asignado; no se lo
puede quitar otra transacción que tenga mayor prioridad, ni el SGBD.
4. Espera circular: Existe una cadena circular, compuesta por dos transacciones o más, y otros
tantos elementos intercalados, de manera que cada proceso está esperando que se le asigne un
elemento, el cual, a su vez, está asignado al siguiente proceso de la cadena.

El tratamiento del interbloqueo está orientado bien a prevenirlo, bien a detectarlo y evitarlo.

Protocolos de prevención de interbloqueo


Estos protocolos no se emplean en la práctica, ya sea debido a suposiciones poco realistas, o
una posible sobrecarga del sistema. Esto no sólo es válido para las bases de datos; en los sistemas
operativos el coste de la detección en los casos de los sistemas para PC o similares y el de la

4
Salvador Jesús Romero Castellano

prevención en casi todos, hace que no se haga nada para tratarlo, circunstancia con la que tenemos
que enfrentarnos a menudo, y que solemos resolver matando un programa.

El primer protocolo, el bloqueo en dos fases conservador, ya lo hemos visto y comentado.


Un segundo protocolo, que también limita la concurrencia, consiste en ordenar todos los elementos
de la base de datos y asegurarse de que una transacción que necesite varios elementos los bloqueará
según ese orden. Su inconveniente es que obliga al programador a conocer la manera en que están
ordenados estos elementos.

Otros métodos para evitar el interbloqueo se basan en una 'marca de tiempo de


transacción', que es un identificador único asignado a la transacción que ayuda a ordenar las
transacciones temporalmente, según el orden en que han sido iniciadas. Mediante estas marcas se
decide si una transacción que se encuentre en posible situación de interbloqueo debe esperar,
abortar, o hacer que otra transacción aborte.
Hay dos enfoques, conocidos como esperar-morir y herir-esperar. Suponiendo que una
transacción A intenta bloquear una elemento X bloqueado por una transacción B, en el primer
enfoque, si A es más antigua que B espera a que B libere X. En caso contrario, A aborta y vuelve a
reiniciar con la misma marca de tiempo. En el enfoque herir-esperar si A es más antigua que B
entonces B aborta. En caso contrario, A espera a que B acabe. Estos algoritmos tienen la virtud de
evitar la espera indefinida (comentada más adelante).

El algoritmo de no-espera consiste en que si una transacción no consigue un bloqueo, aborta


y se reinicia tras un lapso de tiempo, aunque no fuera a haber interbloqueo. Las desventajas de este
método son evidentes. Para mejorarlo se propone la espera cautelosa, que reduce el número de
abortos innecesarios. Supongamos como antes que A trata de bloquear un elemento X ya bloqueado
por B. Si B no está en espera de otro bloqueo, A espera a que B libere X. En caso contrario se aborta
A. Este método trata de evitar el interbloqueo atacando a su condición de espera circular.

Otros algoritmos diseñados para evitar bloqueos en espera de recursos compartidos


múltiples, como el algoritmo del banquero, pueden ser aplicados también en este contexto.

Detección del interbloqueo


Este enfoque es más práctico, y más interesante si esperamos que haya poca interferencia
entre transacciones. La forma más sencilla y conocida es mediante el grafo de espera de
transacciones/elementos. Se tiene que existe interbloqueo si el grafo de espera tiene un ciclo. El
sistema revisa periódicamente este grafo, y si encuentra un ciclo, elige una transacción víctima para
abortarla y así romper el anterior. El inconveniente de este sistema es decidir una política adecuada
para la frecuencia de la comprobación y la selección de víctimas.

Otra solución más sencilla es el uso de tiempos limitados, que presupone que una transacción que
lleve en espera un tiempo superior a un tiempo predefinido se encuentra bloqueada, y se procede a
su eliminación.

Espera indefinida
Este problema tiene lugar cuando una transacción no puede continuar durante un periodo
indeterminado mientras otras transacciones del sistema continúan con normalidad. La solución a

5
Métodos de control de concurrencia

este problema pasa por tener una planificación justa, que impida que algunas de las transacciones
'mueran de inanición'.

Protocolos de ordenación por por marcas de tiempo


Una marca de tiempo es un identificador único que el SGBD crea para identificar una
transacción, basada en la mayoría de los casos en el momento en que se inician. Se pueden, por
tanto, ordenar las transacciones cronológicamente según su marca de tiempo.
Este método se basa en marcas de tiempo para ordenar las transacciones. El plan resultante
de esta ordenación será equivalente a un plan en serie con las transacciones ordenadas según sus
propias marcas de tiempo.

Esto es una diferencia fundamental conforme a los métodos anteriores, en los que los planes
serializables lo eran a algún plan serial que se pueda construir con los protocolos de bloqueo (¡que
no eran todos los planes seriales posibles!). En este caso sabemos a qué plan serial se
correspondería el plan serializable resultante de aplicar ordenación por marcas de tiempo.

Este sistema asocia a cada elemento un par de variables; marca_lectura y marca_escritura,


en las que se escribirá el valor de la marca de tiempo de una transacción que las consulte. De esta
manera, la marca_lectura(X) será igual a la marca de tiempo de la última transacción que haya leído
con éxito el elemento X. Ídem para marca_lectura(X).
El método consiste en dejar al sistema organizar las operaciones libremente (no las ordena
de forma activa, por tanto), pero al ejecutar una operación, verifica que esta no contradice el orden
de seriabilidad. Podríamos decir, por tanto, que es un método optimista.
Existen tres enfoques del método según qué hacer cuando al tratar de hacer una operación se
presenta el conflicto:

Ordenación básica
Consiste en lo siguiente:

Cuando la transacción T emite una operación escribir_elemento(X):


-- Si alguna de las marcas de X son mayores que la marca de tiempo de T, entonces
la operación no se lleva a cabo y se aborta T.
-- Si no, se procede con normalidad, y se asigna el valor de la marca de tiempo de T
a marca_lectura(X)

Si alguna de las marcas son mayores que la marca de T, significa que una transacción
posterior a T escribió o leyó de X, y por tanto, que T usara el elemento X estaría en contra del orden
por antigüedad de transacción; la transacción más vieja debería escribir en X antes de que la joven
usara este elemento.

Cuando la transacción T emite una operación leer_elemento(X)


Si la marca_escritura(X) es mayor que la marca de tiempo de T, se rechaza la
operación y se aborta y revierte T.
Si no, se procede con normalidad, y se asigna a marca_lectura(X) el mayor valor
entre la marca de tiempo de T y la marca_lectura(X) actual.

6
Salvador Jesús Romero Castellano

En esta ocasión, dado que lo que queremos hacer, leer, no afecta a otras transiciones, sólo
debe preocuparnos que una transacción más joven haya escrito en la variable en cuestión.

Al abortar una transacción, se vuelve a introducir en el sistema, con una nueva marca de
tiempo, para probar suerte como una transacción nueva.

Ventajas: Al ser un método semioptimista, da mejor rendimiento cuando la interacción entre


las transacciones es reducida. Con este método no se puede producir interbloqueo.

Inconvenientes:
• Una transacción puede verse sometida a un reinicio cíclico.
• Si existe mucha interacción, puede ser muy costoso, al tener que deshacer muchas veces.
• Se puede producir una restauración en cascada: Al deshacer una transacción T1, cualquier
transacción T2 que haya utilizado un valor escrito por T1 habrá de deshacerse también, y así
sucesivamente.

Regla de escritura de Thomas


Es una variante del protocolo anterior diseñada para rechazar menos operaciones de
escritura. Si se va a escribir un elemento que haya sido escrito por una transacción más joven,
simplemente se ignora la escritura, obteniendo el mismo efecto como ésta escribiese su valor justo
después de que la transacción que está intentando escribir hubiera escrito su valor, lo que sería
válido. El caso del intento de operación lectura(X) queda por tanto así:

-- Si marca_lectura(X) es mayor que la marca de T, abortar, y deshacer T.


-- Si marca_lectura(X) es mayor que la marca de T, ignorar esta operación y
continuar.
-- En otros casos se ejecuta la operación y se asigna a marca_lectura(X) el valor de
la marca de lectura de T.

Ordenamiento estricto o conservador


Otra variante del primero. El ordenamiento básico por marcas de tiempo trata de ejecutar
una operación tan pronto como se recibe ésta. Así, la ejecución de las operaciones es progresiva,
pero, como hemos como hemos comentado antes, se pueden producir muchos reinicios de
transacciones.
En esta variación, una transición que emita una operación de lectura o escritura tal que la
marca de la transacción es menor que marca_escritura(X), sufre un retraso de su operación hasta que
la transacción que escribió el valor de X haya abortado o se haya terminado.

Su principal ventaja, por tanto, es que evita el reinicio y la restauración en cascada.


Inconveniente: Al frenar una transacción se daña la concurrencia en general y el rendimiento
de dicha transacción en particular, a expensas de lo que haga la transacción a la que se espera.

7
Métodos de control de concurrencia

Protocolos de control multiversión


Se basan en conservan los valores antiguos de un elemento cuando es actualizado. El
mantener varios valores del mismo elemento es lo que da al nombre de estos protocolos.
Tener varias versiones de un elemento facilita que algunas operaciones de lectura que no se
llevarían a cabo por protocolos como los anteriores, pueden ser aceptadas si se hacen sobre una
versión más antigua del mismo. De esta manera, al escribir en un elemento, se escribe en una ueva
versión, conservando las antiguas o algunas de ellas para otras transacciones más antiguas que
pudieran necesitarlas más tarde, evitando así que reinicien.

Protocolo de ordenación por marcas de tiempo multiversión


Para cada elemento X, el sistema guarda varias versiones, X1, X2,...,Xk, teniendo para cada
versión una marca_lectura(Xi), y una marca_escritura(Xi), similares a las marcas utilizadas por
otros métodos ya comentados, pero referidos a la versión concreta Xi.

Para asegurar la seriabilidad, usamos dos reglas:


1. Si la transacción T emite una operación escribir_elemento(X), tomamos versión de X,
llamémosla Xi, con mayor marca_lectura(X). Si este valor es mayor que la marca de
tiempo de T, abortamos y deshacemos la transacción. Caso contrario, se crea una nueva
versión de X y se asigna la etiqueta de tiempo de T a sus valores marca_lectura(X) y
marca_escritura(X).
2. Si la transacción T emite una operación leer_elemento(X), se busca la versión Xi que tenga
su marca_escritura(Xi) mayor posible pero sin sobrepasar la marca de tiempo de T. Se
utiliza este elemento y después asignamos a marca_lectura(Xi) el mayor valor entre la
marca de tiempo de T y el valor que tuviera antes.

La principal ventaja de este método salta a la vista; no hay operación de lectura rechazada,
que provoque un reinicio de la transacción.
El principal inconveniente también cae por su propio peso. El mantener varias versiones de
un elemento puede llegar a resultar costoso. En el caso peor, la base de datos puede degenerar en
una base de datos temporal, que sigue la pista a todos los cambios y los momentos en los que
ocurrieron. Cabe destacar también que en este método, abortar una transacción puede provocar una
restauración en cascada, ya comentada antes.

Protocolo de bloqueo en dos fases multiversión


Es una ampliación del bloqueo en dos fases antes comentado. En esta versión se tienen tres
tipos de bloqueo; los dos anteriores, de escritura y lectura, y uno más, llamado de certificación. Al
contrario que en el bloqueo en dos fases simple, un bloqueo de escritura no impide que otras
transacciones bloqueen otros elementos para lectura. Lo que se hace, en caso de que transacción
bloquee un elemento para lectura, para poder conseguir esto es crear una nueva versión de X,
llamémosla X', en la que la transacción bloqueante podrá realizar cuantos cambios quiera, mientras
las demás transacciones seguirán viendo el valor anterior de X para lectura. Es importante señalar,
que si bien con este método se pueden realizar una lectura y una escritura simultáneas, sigue sin
poder realizarse dos escrituras al mismo tiempo.
Cuando una transacción que haya bloqueado un elemento para escritura esté lista para
confirmarse, debe obtener el bloqueo de certificación de X. Ahora sí, el bloqueo de certificación es

8
Salvador Jesús Romero Castellano

exclusivo respecto a los otros dos. Una vez lo obtiene, el valor de X pasa a ser el de X', con lo que a
partir de ese momento es visible para todas las transacciones.

La novedad de este método con respecto al bloqueo en dos fases ya visto es que permite la
lectura a mismo tiempo que la escritura. Sin embargo, una transacción deberá esperar a confirmarse
hasta tener los bloqueos de certificación de todos los elementos que ha modificado.
Este esquema evita el aborto en cascada, ya que siempre se leen versiones confirmadas de X.
Sin embargo, si permitimos que un bloqueo se convierta en un bloqueo de escritura, tal como ya
vimos anteriormente para la versión simple de este bloqueo, se puede producir un interbloqueo.

Protocolos de control optimistas


Los protocolos anteriores, que podemos clasificar como pesimistas, comprueban que la
operación que la transacción se dispone a hacer es válida antes de modificar el elemento en la base
de datos. Los protocolos de control optimistas, por el contrario, presuponen que todas las
operaciones se van a efectuar en orden correcto, y por tanto no controlan la validez de las
operaciones realizadas hasta que la transacción termina, en la llamada fase de validación o
certificación.
La forma de implementar estos protocolos para este contexto (transacciones que han de ser
confirmadas) es hacer que las transacciones lleven a cabo sus modificaciones en un espacio
particular, al que se han copiado previamente los datos que la transacción vaya a necesitar. Si al
final del proceso, los datos resultantes son válidos, se pasan del espacio particular a la base de datos.

Con esto tenemos que los protocolos de bloqueo optimistas tienen tres fases bien
diferenciadas:
• Fase de lectura/ejecución: En ella las transacciones leen cuantos elementos necesiten de la base
de datos, pero realizan las modificaciones en un espacio particular, como ya hemos señalado.
• Fase de certificación/validación: Se verifica que los resultados de la transacción no violan la
seriabilidad. De ser así (el resultado no es correcto), la transacción reinicia (como las
modificaciones eran en un espacio aparte de variables, no hace falta deshacer. Simplemente se
descartan estas variables).
• Fase de escritura/confirmación: Llegados a este punto, aplicamos las actualizaciones de la
transacción a la base de datos.

Este protocolo necesita de marcas de tiempo de transacciones, guardar las horas de inicio y
terminación de al menos una de las tres fases y además requiere que el sistema mantenga control
sobre los conjuntos de lectura y escritura de cada transacción.
En la fase de validación de la transacción T, el protocolo comprueba que que T no interfiere
en ninguna transacción confirmada ni en cualquier otra que actualmente esté en su fase de
validación. La fase de validación consiste por tanto en, para toda otra transacción T', comprobar que
se cumplan alguna de estas condiciones:

1. La transacción T' completa su fase de escritura antes de que T inicie su fase de lectura.
2. T inicia su fase de lectura después de que T' complete su fase de escritura y el conjunto de
lectura de T no tiene elementos en común con el conjunto de escritura de T'.
3. Ni el conjunto de lectura ni el conjunto de escritura de T tienen elementos en común con el
conjunto de lectura de T', y T' completa su fase de lectura antes de que T complete su fase de
lectura.

9
Métodos de control de concurrencia

El control optimista es más rápido y permite mayor grado de paralelismo, al menos, con
cargas de trabajo bajas o medias. Para cargas altas, aumenta la probabilidad de que se produzca un
conflicto, y las cancelaciones e intentos sucesivos causan un incremento apreciable del trabajo.

10

También podría gustarte