【问题标题】:Atomic transactions and logging原子事务和日志记录
【发布时间】:2020-11-17 06:12:29
【问题描述】:

假设我有一个修改数据库的函数。这可以是从一个查询到事务中的多个查询的任何地方。在任何这些修改中,我们都希望确保所有查询都成功。感谢交易,如果其中一个失败,我有办法确保没有任何更改是永久性的。

现在假设我还需要写入一个日志以记录更改。我怎样才能确保进行了修改并写入了日志?

我总是可以在其中任何一个上执行try/catch,但例如说我的查询成功但我的日志不成功。然后我怎样才能返回并撤消对数据库的所有修改?

这样的做法是否有效和/或可取?

try {
    $db->connect();
    $db->mysqli->begin_transaction();

    // series of queries
    ...
    // log to file

    $db->mysqli->commit();
} catch (exception $e) {
    $db->mysqli->rollback();
    $db->mysqli->close();
}

想记录下我在测试时观察到的一些行为。如果将commit()放在日志之前,即使在记录时捕获到异常,数据库更改也不会回滚。

【问题讨论】:

  • 只要您的日志记录函数在/如果它在// log to file 位失败时抛出异常,这应该可以正常工作。使用它我不会看到任何问题。

标签: php exception mysqli transactions


【解决方案1】:

一旦您调用commit() 或触发隐式提交,数据将存储在数据库中。例如,这不会对数据库进行任何更改,因为没有提交:

$mysqli->begin_transaction();
$mysqli->query('INSERT INTO test1(val) VALUES(2)');

// end of script's execution

在您的小示例中,只要您的日志记录功能停止执行或引发异常,事务就会回滚。

$db->connect();
try {
    $db->mysqli->begin_transaction();

    // series of queries
    ...
    // log to file
    throw new \Exception(); // <-- This will prevent commit from executing. 

    $db->mysqli->commit();
} catch (\Exception $e) {
    // An exception? That's ok, we'll try something different instead.
    $db->mysqli->rollback();
}

只有当您想从异常中恢复并清除未保存的缓冲区时,您才需要调用rollback。唯一一次你应该捕获异常是如果你想以某种方式从中恢复并做不同的事情。否则,您的代码可以通过完全删除 try-catch 和回滚来简化。

但是,最好在事务中捕获异常并尽快显式回滚,即使您不想恢复也是如此。如果您在代码中的其他地方捕获异常并尝试执行其他数据库操作,它可以防止出现问题。未保存的数据仍在缓冲区中,直到您关闭会话或回滚!这就是为什么你会经常看到这样的代码:

$db->connect();
try {
    $db->mysqli->begin_transaction();

    throw new \Exception(); // <-- Something throws an exception

    $db->mysqli->commit();
} catch (\Exception $e) {
    // rollback unsaved data, but rethrow the exception as we do not know how to recover from it here
    $db->mysqli->rollback();
    throw $e;
}

【讨论】:

    猜你喜欢
    • 2011-05-10
    • 1970-01-01
    • 2011-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-17
    • 2016-09-16
    • 2011-02-27
    相关资源
    最近更新 更多