Los deadlocks son un problema clásico de las bases de datos transaccionales, pero no son peligrosos a menos que sean tan frecuentes que no se puedan ejecutar en absoluto ciertas transacciones. Normalmente, las aplicaciones deben ser escritas de modo que esten preparadas para emitir nuevamente una transacción si ésta es cancelada debido a un deadlock.
InnoDB
emplea bloqueos automáticos a nivel
de fila. Se pueden producir deadlocks aún en el caso de
transacciones que solamente insertan o eliminan una fila
individual. Esto se debe a que estas operaciones no son
realmente “atómicas”; sino que establecen
automáticamente bloqueos enlos (posiblemente varios) registros
de índice de la fila insertada o eliminada.
Con las siguientes técnicas se puede estar a cubierto de los deadlocks y reducir la probabilidad de que ocurran:
Emplear SHOW INNODB STATUS
para
determinar la causa del último deadlock. Puede ayudar a
afinar la aplicación para evitar que ocurran otros.
Siempre hay que estar preparado para emitir nuevamente una transacción que haya fallado por un deadlock. Los deadlocks no revisten peligro, simplemente hay que intentar de nuevo.
Confirmar las transacciones frecuentemente. Las transacciones pequeñas son menos propensas a originar conflictos.
Si se están usando lecturas que establecen bloqueos
(SELECT ... FOR UPDATE
o ...
LOCK IN SHARE MODE
), hay que intentar utilizar un
nivel de aislamiento bajo, como READ
COMMITTED
.
Acceder a las tablas y filas en un orden fijo. Entonces, las transacciones forman secuencias bien definidas y no originan deadlocks.
Agregar a las tablas índices adecuadamente elegidos.
Entonces las consultas necesitarán examinar menos registros
de índice y en consecuencia establecerán menos bloqueos.
Utilizar EXPLAIN SELECT
para determinar
los índices que MySQL considera más apropiados para las
consultas.
Utilizar menos el bloqueo. Si es aceptable que
SELECT
devuelva datos de una captura de
la base de datos que no sea la más actualizada, no hay que
agregarle las cláusulas FOR UPDATE
o
LOCK IN SHARE MODE
. En este caso es
adecuado utilizar el nivel de aislamiento READ
COMMITTED
, porque cada lectura consistente dentro
de la misma transacción leerá de su propia captura más
reciente.
Si nada de esto ayuda, habrá que serializar las
transacciones con bloqueos a nivel de tabla. La forma
correcta de emplear LOCK TABLES
con
tablas transaccionales, como InnoDB, es establecer
AUTOCOMMIT = 0
y no invocar a
UNLOCK TABLES
hasta que se haya
confirmado explícitamente la transacción. Por ejemplo, si
se necesitara escribir en una tabla t1
y
leer desde una tabla t2
, se puede hacer
esto:
SET AUTOCOMMIT=0; LOCK TABLES t1 WRITE, t2 READ, ...; [aquí se hace algo con las tablas t1 y t2]; COMMIT; UNLOCK TABLES;
Los bloqueos a nivel de tabla favorecen el funcionamiento de la cola de transacciones, y evitan los deadlocks.
Otra manera de serializar transacciones es crear una tabla
“semáforo” auxiliar que contenga sólo una
fila. Hay que hacer que cada transacción actualice esa fila
antes de acceder otras tablas. De ese modo, todas las
transacciones se producirán en serie. Nótese que el
algoritmo de detección instantánea de deadlocks de
InnoDB
también funciona en este caso,
porque el bloqueo de serialización es un bloqueo a nivel de
fila. Con los bloqueos a nivel de tabla de MySQL, debe
emplearse el método de timeout para solucionar deadlocks.
En aquellas aplicaciones que emplean el comando de MySQL
LOCK TABLES
, MySQL no establece bloqueos
de tabla si AUTOCOMMIT=1
.
Ésta es una traducción del manual de referencia de MySQL, que puede encontrarse en dev.mysql.com. El manual de referencia original de MySQL está escrito en inglés, y esta traducción no necesariamente está tan actualizada como la versión original. Para cualquier sugerencia sobre la traducción y para señalar errores de cualquier tipo, no dude en dirigirse a mysql-es@vespito.com.