MySQL Server (versiones 3.23-max y todas las versiones 4.0 y
posteriores) soportan transacciones con los motores
trasaccionales InnoDB
y
BDB
. InnoDB
proporciona
completa compatibilidad
ACID
. Consulte
Capítulo 14, Motores de almacenamiento de MySQL y tipos de tablas.
Los otros motores no transaccionales en MySQL Server (como
MyISAM
) siguen un paradigma diferente para
integridad de datos llamado "operaciones atómicas". En
términos transaccionales, tablas MyISAM
operan en modo AUTOCOMMIT=1
. Operaciones
atómicas a menudo ofrecen integridad comparable con mejor
rendimiento.
MySQL Server soporta ambos paradigmas, puede decidir si su aplicación necesita la velocidad de operaciones atómicas o el uso de características transaccionales. Esta elección puede hacerse para cada tabla.
Como se ha dicho, el compromiso entre tipos de tablas
transaccionales y no transaccionales reside principalmente en
el rendimiento. Tablas transaccionales tienen requerimientos
significativamente mayores para memoria y espacio de disco, y
mayor carga de CPU. Por otra parte, tipos de tablas
transaccionales como InnoDB
también ofrece
muchas características significativas. El diseño modular de
MySQL Server permite el uso concurrente de distintos motores
de almacenamiento para cumplir distintos requerimientos y
mostrarse óptimo en todas las situaciones.
Pero, ¿cómo usar las características de MySQL Server para
mantener integridad de forma rigurosa incluso en tablas no
transaccionales como MyISAM
, y cómo se
comparan estas características con los tipos de tablas
transaccionales?
Si su aplicación está escrita de forma que dependa en
que pueda llamar a ROLLBACK
en lugar de
COMMIT
en situaciones críticas, es
preferible usar transacciones. Transacciones aseguran que
actualizaciones no acabadas o actividades corruptas no se
ejectuen en la base de datos; el servidor tiene la
oportunidad de hacer un rollback automático para mantener
la base de datos a salvo.
Si usa tablas no transaccionales, MySQL Server le permite solucionar problemas potenciales en prácticamente todos los casos simplemente incluyendo chequeos antes de las actualizaciones y ejecutando scripts sencillos que comprueban que la consistencia de la base de datos, dando una advertencia o reparando automáticamente cualquier incosistencia. Simplemente usando el log de MySQL o añadiendo un log extra, normalmente puede arreglar tablas sin pérdida de integridad en los datos.
Normalmente, las actualizaciones transaccionales críticas
pueden reescribirse como atómicas.Generalmente hablando,
todos los problemas de integridad que resuelven las
transacciones pueden resolverse con LOCK
TABLES
o actualizaciones atómicas, asegurando
que no se aborten automáticamente desde el servidor, el
cuál es un problema habitual en sistemas de bases de
datos transaccionales.
Para tener un entorno fiable de MySQL, usando tablas transaccionales o no, sólo necesita tener copias de seguridad y el log binario activado. Con ello, puede recuperarse de cualquier situación de la que pueda hacerlo con cualquier otro sistema transaccional. Siempre es bueno tener copias de seguridad, independientemente del sistema de bases de datos usado.
El paradigma transaccional tiene sus ventajas y desventajas. Muchos usuarios y desarrolladores de aplicaciones dependen en la facilidad con la que pueden solucionar problemas donde un aborto parece ser o es necesario. Sin embargo, incluso si el paradigma de operaciones atómicas le es desconocido o está más familiarizado con las transacciones, considere el beneficio de la velocidad que pueden ofrecer las tablas no transaccionales, que puede ser de tres a cinco veces más rápido que las más optimizadas tablas transaccionales.
En las situaciones en las que la integridad es de máxima
importancia, MySQL Server ofrece integridad a nivel de
transacción incluso para tablas no transaccionales. Si
bloquea tablas con LOCK TABLES
, todas las
actualizaciones se bloquean hasta que se hacen las
comprobaciones necesarias. Si obtiene un bloqueo READ
LOCAL
(el contrario a un bloqueo de escritura) para
una tabla que permita inserciones concurrentes al final de la
tabla, las lecturas están permitidas, así como las
inserciones de otros clientes. Los registros insertados no
puede verlos el cliente que tenga el bloqueo hasta que lo
libere. Con INSERT DELAYED
, puede encolar
inserciones en una cola local, hasta que los bloqueos se
liberan, sin tener que esperar el cliente a que acabe la
inserción. Consulte Sección 13.2.4.2, “Sintaxis de INSERT DELAYED
”.
"Atómico", en el sentido en que nos referimos, no es nada mágico. Se trata que puede asegurar que mientras cada actualización específica está ejecutándose, ningún otro usuario puede interferir con ellas, y que nunca puede haber un rollback automático (lo que puede ocurrir con tablas transaccionales si no se es muy cuidadoso). MySQL Server garantiza que no hay dirty reads (lecturas sucias).
A continación se presentan algunas técnicas para trabajar con tablas no transaccionales:
Los bucles que necesiten transacciones normalmente pueden
codificarse con la ayuda de LOCK
TABLES
, y no necesita cursores para actualizar
registros en tiempo real.
Para evitar usar ROLLBACK
, puede usar
la siguiente estrategia:
Use LOCK TABLES
para bloquear todas
las tablas a las que quiere acceder.
Compruebe las condiciones que deben darse antes de ejecutar la actualización.
Actualice si todo es correcto.
Use UNLOCK TABLES
para liberar los
bloqueos.
Este es un método mucho más rápido que usar transacciones con posibles rollbacks, aunque no siempre. La única situación en que esta situación no funciona es cuando alguien mata el thread durante una actualización. En ese caso, todos los bloqueos se liberan pero algunas actualizaciones pueden no ejecutarse.
Puede usar funciones para actualizar registros en una única operación. Puede obtener una aplicación muy eficiente usando las siguientes técnicas:
Modifique columnas con su valor actual.
Actualice sólo aquéllas que hayan cambiado.
Por ejemplo, cuando estamos actualizando la información
de un cliente, sólo actualizamos los datos del cliente
que han cambiado y comprobamos que los datos cambiados o
datos que dependen de los datos cambiados, han cambiado
respecto a los datos originales. El test para datos
cambiados se hace con la cláusula
WHERE
en el comando
UPDATE
. Si el registro no se ha
actualizado, mostramos un mensaje al cliente: "Algunos de
los datos actualizados han sido cambiados por otro
usuario". A continuación mostramos los registros viejos
junto a los nuevos en una ventana para que el usuario
pueda decidir qué versión del registro de usuario usar.
Esto nos da algo que es similar a bloqueo de columnas pero
es incluso mejor ya que sólo actualizamos algunas de las
columnas, usando valores que son relativos a sus valores
actuales. Eso significa que el típico comando
UPDATE
será algo así:
UPDATE tablename SET pay_back=pay_back+125; UPDATE customer SET customer_date='current_date', address='new address', phone='new phone', money_owed_to_us=money_owed_to_us-125 WHERE customer_id=id AND address='old address' AND phone='old phone';
Esto es muy eficiente y funciona incluso si otro cliente
ha cambiado los valores en las columnas
pay_back
o
money_owed_to_us
.
En muchos casos, los usuarios han querido usar
LOCK TABLES
y/o
ROLLBACK
con la intención de
administrar identificadores únicos. Se puede tratar de
forma mucho más eficiente sin bloquear o rolling back
usando columnas AUTO_INCREMENT
y la
función SQL LAST_INSERT_ID()
o la
función de la API C mysql_insert_id()
.
Consulte Sección 12.9.3, “Funciones de información”. Consulte
Sección 24.2.3.34, “mysql_insert_id()
”.
Normalmente puede codificar la necesidad de bloqueo a
nivel de registro. Algunas situaciones realmente lo
necesitan, y las tablas InnoDB
lo
soportan. Con tablas MyISAM
, puede usar
una columna flag en la tabla y hacer algo como lo
siguiente:
UPDATE tbl_name
SET row_flag=1 WHERE id=ID;
MySQL retorna 1
para el número de
registros afectados si la fila ha sido encontrada y
row_flag
no era 1
en
el registro original.
Puede imaginarlo como si MySQL Server cambiase la consulta anterior a:
UPDATE tbl_name
SET row_flag=1 WHERE id=ID AND row_flag <> 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.