2009-09-29 19 views
40

En una secuencia de comandos PHP que trabaja con una base de datos mysql, recientemente tuve la necesidad de usar una transacción en un punto que sucedía dentro de otra transacción. Todas mis pruebas parecen indicar que esto está funcionando bien, pero no puedo encontrar ninguna documentación sobre este uso.Transacciones de Mysql dentro de transacciones

Quiero estar seguro - son transacciones dentro de transacciones válidas en mysql? Si es así, ¿hay alguna forma de averiguar cuántos niveles profundos tiene en las transacciones anidadas? (. Es decir, el número de reversiones que se tardaría en volver a la normalidad)

Gracias de antemano, Brian

Respuesta

46

Esta página del manual podrían interesarle: 12.3.3. Statements That Cause an Implicit Commit; citando un par de frases:

Las declaraciones que aparecen en esta sección (y cualquier sinónimo de los mismos) implícitamente finalizar una transacción, como si se hubiera hecho un COMMIT antes de ejecutar la instrucción .


Y, un poco más lejos en la página:

Transacción-control y bloqueo declaraciones. BEGIN, LOCK TABLES, SET autocommit = 1 (si el valor es no ya 1), START TRANSACTION, UNLOCK TABLES.


Ver también este PÁRRAFO:

Las transacciones no se pueden anidar.
Esta es consecuencia de lo implícito commit realizado por cualquier transacción actual cuando se emite una declaración START TRANSACTION o uno de sus sinónimos.


Hope this helps!

+0

'¿Las transacciones no se pueden anidar' en la misma conexión, o también en diferentes? –

+3

La lógica general dice que las diferentes conexiones no saben nada el uno del otro. Entonces, la transacción en una conexión no sabe acerca de otra transacción en una conexión diferente. No probado, pero debe ser cierto. –

12

Quiero estar seguro - son transacciones dentro de las transacciones válidas en mysql?

1

Es posible que desee revisar su methadology pruebas. Fuera de MaxDB, MySQL no admite nada de forma remota, como las transacciones anidadas.

+2

"MySQL no admite nada de forma remota como las transacciones anidadas."¿No dirías que los puntos de rescate son algo así como las transacciones anidadas? –

7

MySql no admite transacciones anidadas. Sin embargo, hay algunas formas en que puedes emularlo. En primer lugar, puede usar puntos de rescate como una forma de transacción, por lo que le da dos niveles de transacciones; Lo he usado para probar, pero no estoy seguro de las limitaciones, si lo usas en el código de producción. Una solución más simple es ignorar el segundo begin transaction y en su lugar aumentar un contador. Para cada commit, lo disminuye. Una vez que llegue a cero, realiza un commit real. Hay limitaciones obvias de esto; P.ej.una reversión hará rodar todas las transacciones, pero para un caso en el que solo usa transacciones para el manejo de errores, eso puede ser aceptable.

46

Contrariamente a la respuesta de todos los demás, se pueden crear de manera efectiva las transacciones dentro de las transacciones y es muy fácil. Simplemente crea ubicaciones SAVEPOINT y usa ROLLBACK TO savepoint para deshacer parte de la transacción, donde savepoint es el nombre que le das al punto de rescate. Enlace a la documentación de MySQL: http://dev.mysql.com/doc/refman/5.0/en/savepoint.html Y, por supuesto, ninguna de las consultas en ninguna parte de la transacción debe ser del tipo que se confirma implícitamente, o se comprometerá toda la transacción.

Ejemplos:

START TRANSACTION; 

# queries that don't implicitly commit 

SAVEPOINT savepoint1; 

# queries that don't implicitly commit 

# now you can either ROLLBACK TO savepoint1, or just ROLLBACK to reverse the entire transaction. 

SAVEPOINT savepoint2; 

# queries that don't implicitly commit 

# now you can ROLLBACK TO savepoint1 OR savepoint2, or ROLLBACK all the way. 
# e.g. 

ROLLBACK TO savepoint1; 
COMMIT; # results in committing only the part of the transaction up to savepoint1 

En PHP me han escrito código como este, y funciona perfectamente:

foreach($some_data as $key => $sub_array) { 
    $result = mysql_query('START TRANSACTION'); // note mysql_query is deprecated in favor of PDO 
    $rollback_all = false; // set to true to undo whole transaction 
    for($i=0;$i<sizeof($sub_array);$i++) { 
    if($sub_array['set_save'] === true) { 
     $savepoint = 'savepoint' . $i; 
     $result = mysql_query("SAVEPOINT $savepoint"); 
    } 
    $sql = 'UPDATE `my_table` SET `x` = `y` WHERE `z` < `n`'; // some query/queries 
    $result = mysql_query($sql); // run the update query/queries 

    $more_sql = 'SELECT `x` FROM `my_table`'; // get data for checking 
    $result = mysql_query($more_sql); 

    $rollback_to_save = false; // set to true to undo to last savepoint 
    while($row = mysql_fetch_array($result)) { 
     // run some checks on the data 
     // if some check says to go back to savepoint: 
     $rollback_to_save = true; // or just do the rollback here. 
     // if some check says to rollback entire transaction: 
     $rollback_all = true; 
    } 
    if($rollback_all === true) { 
     mysql_query('ROLLBACK'); // rollback entire transaction 
     break; // break out of for loop, into next foreach 
    } 
    if($rollback_to_save = true) { 
     mysql_query("ROLLBACK TO $savepoint"); // undo just this part of for loop 
    } 
    } // end of for loop 
    mysql_query('COMMIT'); // if you don't do this, the whole transaction will rollback 
} 
+4

No lo probé, pero sentí la necesidad de comentar sobre lo hermoso que es esto. –

+2

@ MarcoAurélioDeleu gracias ! No creo que haya recibido tal elogio por ninguno de mis códigos. Funciona perfectamente en mi aplicación, y parece que algunas otras personas también lo han hecho funcionar, a juzgar por los votos positivos. –

+0

Así es, uno puede establecer punto de rescate y retrotracción a este punto de rescate dentro de una transacción. Pero el punto aquí es que la anidación de transacciones no es posible porque iniciar una transacción interna implícitamente confirmará las declaraciones de transacciones externas anteriores y eso probablemente sea algo que no desea. Esto puede ser problemático : por ejemplo, si uno tiene la clase House y otra clase Room, cada una de las cuales tiene su propia transacción para DB create statement; aquí crea R ¡Un objeto dentro del objeto de la Casa romperá la transacción exterior de la Casa! – sbrbot

5

Hay algunos grandes respuestas en este tema, sin embargo, si se utiliza InnoDB su motor de almacenamiento MySQL y está utilizando MySQL 5.0.3 o superior, obtendrá transacciones anidadas desde el primer momento sin la necesidad de ningún trabajo adicional de su parte o cualquiera de las técnicas sofisticadas descritas por otros en este hilo.

De la documentación de MySQL en transacciones XA:

MySQL 5.0.3 y proporciona soporte de servidor para transacciones XA. Actualmente, este soporte está disponible para el motor de almacenamiento InnoDB. La implementación de MySQL XA se basa en el documento X/Open CAE Procesamiento distribuido de transacciones: la especificación XA. Este documento es publicado por The Open Group y está disponible al http://www.opengroup.org/public/pubs/catalog/c193.htm. Las limitaciones de la implementación XA actual se describen en la Sección E.5, "Restricciones en transacciones XA". Ejemplo de transacción

Mi XA Sólo Para Ti: Documentación

# Start a new XA transaction 
XA START; 

    # update my bank account balance, they will never know! 
    UPDATE `bank_accounts` SET `balance` = 100000 WHERE `id` = 'mine'; 

    # $100,000.00 is a bit low, I'm going to consider adding more, but I'm not sure so 
    # I will start a NESTED transaction and debate it... 
    XA START; 

     # max int money! woo hoo! 
     UPDATE `bank_accounts` SET `balance` = 2147483647 WHERE `id` = 'mine'; 

    # maybe thats too conspicuous, better roll back 
    XA ROLLBACK; 


# The $100,000 UPDATE still applies here, but the max int money does not, going for it! 
XA COMMIT; 

# Oh No! Sirens! It's the popo's!!! run!! 
# What the hell are they using ints for money columns anyway! Ahhhh! 

MySQL para transacciones XA:

I < 3 XA Transacciones 4 eva!

+0

probado en MySQL 5.6. Los comandos "XA" requieren una cadena xid, e intentar iniciar una anidada le da a esto: "ERROR 1399 (XAE07): XAER_RMFAIL: El comando no se puede ejecutar cuando la transacción global está en el estado ACTIVO" – HectorJ