2009-09-29 10 views
40

В PHP-скрипте, работающем с базой данных mysql, мне недавно пришлось использовать транзакцию в точке, которая оказалась внутри другой транзакции. Все мои тесты, похоже, указывают, что это хорошо работает, но я не могу найти никакой документации по этому использованию.Mysql транзакции в транзакции

Я хочу быть уверенным - это операции внутри транзакций, действительные в mysql? Если да, есть ли способ узнать, сколько уровней вы находитесь во вложенных транзакциях? (Т. Е сколько откатов потребуется, чтобы вернуться к нормальной жизни)

Спасибо заранее, Брайану

ответ

46

Эта страница руководства может заинтересовать: 12.3.3. Statements That Cause an Implicit Commit; процитировать несколько предложений:

Заявления, перечисленное в этом разделе (и любые синонимы для них) неявно конца транзакции, как если бы вы сделали COMMIT перед выполнением заявления.


И немного дальше на странице:

Transaction-контроль и блокировка заявления. BEGIN, LOCK TABLES, SET autocommit = 1 (если значение уже не 1), START TRANSACTION, UNLOCK TABLES.


Смотрите также этот paragrah:

Сделки не могут быть вложенными.
Это следствие неявного commit выполняется для любой текущей транзакции , когда вы выдаете заявление START TRANSACTION или один из его синонимов.


Надеюсь, это поможет!

+0

'Сделки не могут быть вложены' в том же соединении, или также в разных местах? –

+3

Общая логика говорит, что разные связи ничего не знают друг о друге. Таким образом, транзакция в одном соединении не знает о другой транзакции в другом соединении. Не проверено, но должно быть правдой. –

12

Я хочу быть уверенным - это операции внутри сделок, действительных в mysql?

No.

1

Вы можете проверить тестирования methadology. Вне MaxDB MySQL не поддерживает ничего удаленного, как вложенные транзакции.

+2

«MySQL не поддерживает ничего удаленно, как вложенные транзакции.«Не могли бы вы сказать, что точки сохранения - это что-то вроде вложенных транзакций? –

7

MySql не поддерживает вложенные транзакции. Есть несколько способов, которыми вы можете его эмулировать. Во-первых, вы можете использовать точки сохранения как форму транзакции, так что вы получаете два уровня транзакций; Я использовал это для тестирования, но я не уверен в ограничениях, если вы используете его в производственном коде. Более простым решением является игнорировать второй begin transaction и вместо этого увеличивать счетчик. Для каждого commit вы уменьшаете его. Как только вы достигнете нуля, вы делаете фактический commit. Есть очевидные ограничения этого; Например.откат откатит все операции, но для случая, когда вы используете транзакции для обработки ошибок, что может быть приемлемым.

46

В отличие от всех остальных ответ, вы можете эффективно создавать транзакции в рамках операций, и это очень легко. Вы просто создаете местоположения SAVEPOINT и используете ROLLBACK TO savepoint для отката части транзакции, где savepoint - это то, что вы даете точке сохранения. Ссылка на документацию MySQL: http://dev.mysql.com/doc/refman/5.0/en/savepoint.html И, конечно, ни один из запросов в любой транзакции не должен иметь тип, который неявно совершает, или вся транзакция будет совершена.

Примеры:

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 

В PHP я написал код, как это, и это работает прекрасно:

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

Не тестировал, но я чувствовал необходимость прокомментировать, насколько это красиво. –

+2

@ MarcoAurélioDeleu спасибо Я не думаю, что когда-либо получал такой комплимент для любого из моих кодов. Он отлично работает в моем приложении, и, похоже, некоторые другие люди тоже его работают, судя по upvotes. –

+0

Правильно, можно установить точку сохранения и откат к этой точке сохранения внутри одной транзакции. Но дело здесь в том, что вложение транзакций невозможно, потому что начало внутренней транзакции будет неявно совершать предыдущие внешние транзакции, и это, вероятно, то, чего вы не хотите. Это может быть проблематично : например, если у кого-то есть класс House и еще один класс, каждый из которых имеет свою собственную транзакцию для создания базы данных DB, здесь создается R Объект oom внутри объекта дома нарушит транзакцию внешнего дома !!! – sbrbot

5

Есть некоторые большие ответы в этой теме, однако, если вы используете InnoDB, как ваш механизм хранения MySQL и используете MySQL 5.0.3 или новее, вы получаете вложенные транзакции прямо из коробки без необходимости какой-либо дополнительной работы с вашей стороны или любого из причудливых методов, описанных другими в этом потоке.

Из документов MySQL на XA сделок:

MySQL 5.0.3 и выше обеспечивает поддержку на стороне сервера для XA транзакций. В настоящее время эта поддержка доступна для механизма хранения InnoDB. Реализация MySQL XA основана на документе X/Open CAE. Обработка распределенных транзакций: спецификация XA. Этот документ публикуется The Open Group и доступен по адресу http://www.opengroup.org/public/pubs/catalog/c193.htm. Ограничения текущей реализации XA описаны в разделе E.5 «Ограничения на транзакции XA».

Мой XA Пример сделки Just For You:

# 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 Документация для XA сделок:

I < 3 XA Сделки 4 Eva!

+0

Пробовал на MySQL 5.6. Для команд «XA» требуется строка xid, и попытка запустить вложенную команду дает следующее: «ERROR 1399 (XAE07): XAER_RMFAIL: команда не может быть выполнена, когда глобальная транзакция находится в состоянии ACTIVE» – HectorJ

Смежные вопросы