在一个与mysql数据库交互的PHP脚本中,我最近需要在一个已有事务中使用另一个事务。目前我所有的测试结果都表明这样做是可行的,但我无法找到相关文档证实这种用法的正确性。
我希望确认一下:在mysql中,嵌套事务是有效的吗?如果是,有没有方法可以查找嵌套事务的层数?(例如,需要回滚多少次才能返回正常状态)
提前致谢, Brian
在一个与mysql数据库交互的PHP脚本中,我最近需要在一个已有事务中使用另一个事务。目前我所有的测试结果都表明这样做是可行的,但我无法找到相关文档证实这种用法的正确性。
我希望确认一下:在mysql中,嵌套事务是有效的吗?如果是,有没有方法可以查找嵌套事务的层数?(例如,需要回滚多少次才能返回正常状态)
提前致谢, Brian
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
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
}
这份手册中的内容可能会让你感兴趣:12.3.3. 导致隐式提交的语句,其中摘录了几句话:
本节列出的语句(以及它们的任何同义词)隐式地结束一个事务,就好像在执行该语句之前已经执行了
COMMIT
一样。
而且,在页面的稍远处:
事务控制和锁定 语句。
BEGIN
,LOCK TABLES
,SET autocommit = 1
(如果值不是 已经是1),START TRANSACTION
,UNLOCK TABLES
。
还可以参考以下段落:
事务不能嵌套。
这是在发出START TRANSACTION
语句或其同义词之一时为任何当前事务执行的隐式commit
的结果。
MySql 不支持嵌套事务,但是可以通过几种方法来模拟。首先,您可以使用保存点作为一种事务形式,这样就可以实现两个级别的事务;我已经在测试中使用过它,但不确定如果在生产代码中使用时是否有限制。另一种更简单的解决方案是忽略第二个 begin transaction
,而是增加一个计数器。对于每个commit
,您将其减少。一旦计数器为零,您就会执行实际的commit
。这种方法显然存在一些限制;例如,回滚将回滚所有事务,但对于仅将事务用于错误处理的情况,这可能是可接受的。
# 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!
XA 事务的 MySQL 文档:
我 <3 XA 事务永远!
SAVEPOINT
解决方案有什么问题吗?对我来说完美地运作。 - Buttle Butkus你可能需要检查你的测试方法。除了MaxDB之外,MySQL不支持任何类似嵌套事务的功能。
START TRANSACTION
语句,但我在我的答案中粗略概述的SAVEPOINT
方法正好实现了嵌套事务的功能。假设你有一个room
对象数组,你想将它们添加到一个house
对象中。你实际上可以使用数据库约束来决定何时房子“满了”。你可以尝试添加每个房间,并在房子满了后返回一个house full
异常,这可以与数据库模型返回错误并回滚到“savepointX”相吻合。“无法添加第5个浴室。满屋了。” - Buttle Butkus