提问人:user21266319 提问时间:2/22/2023 最后编辑:user21266319 更新时间:2/23/2023 访问量:126
如何让MySQL的启动事务查询失败?
How to make MySQL's start transaction query fail?
问:
我可以在PHP文档中看到begin_transaction可能会失败。我怎样才能让它失败?(我所有的表都使用 InnoDB 引擎。MySQL 版本 5.7.38-log。
我为什么在乎?我正在调查系统中的编程错误,该错误不检查启动事务的失败。添加一些日志记录后,我发现系统认为---我有主键 ID ---发生了许多插入,但这些记录从未进入数据库,因此系统的扣除不会继续进行。由于系统不会检查启动事务语句中的失败,这可以解释我所拥有的日志记录证据。
更多背景信息。尽管系统不会检查 start-transaction 语句是否可能失败,但会检查提交结果。我有兴趣发现当 start-transaction 语句不成功时会发生什么,但是(使用相同的连接)我继续进行插入和提交。(这个插入和提交应该成功吗?由于我不知道如何使 start-transaction 语句失败,因此我无法尝试查看结果。
这是一个以 Apache 为前端---的 PHP 7.4 Web 系统。
我尝试了 Apache 程序,希望我的 Web 系统用请求压倒我,我可以得到一个 start-transaction 语句失败。我以为他们会失败。他们没有失败。ab
(*)日志
以下日志证明已运行一系列 SQL INSERT 语句,但未进入数据库。(我假设没有人在我背上删除东西---我是数据库的唯一所有者。代码(生成这些日志的位置)如下。
1675716327901,"{""message"":""New Order for user ID: 21473"",""AWN"":""some-aws-tag"",""log_level"":6}"
1675716327942,"{""message"":""Order created: 4328077174"",""AWN"":""some-aws-tag"",""log_level"":6}"
1675716327971,"{""message"":""Attendee created: 3156"",""AWN"":""some-aws-tag"",""log_level"":6}"
1675716327988,"{""message"":""Invoice created: 336845"",""AWN"":""some-aws-tag"",""log_level"":6}"
1675716331883,"{""message"":""Committed fine"",""AWN"":""some-aws-tag"",""log_level"":6}"
(*)代码
public function create() {
$this->sendCloudWatchLog("New Order for user ID: $this->userId");
$oSql->start_transaction();
foreach ($this->orders as $aCourseOrder) {
$oRegistration = new ProgramRegistration($aCourseOrder->iProgramID, $aCourseOrder->iCourseID);
$iRegID = $oRegistration->createRegistration('unpaid', $iRegistrarUserId);
if ($iRegID > 0) {
$this->sendCloudWatchLog("Order created: {$oRegistration->registrationNumber}");
} else {
$error = 'Registration order creation failed';
break;
}
foreach ($this->attendes as $aAttendeeInfo) {
$newAttendeeId = $oRegistration->createAttendee($aAttendeeData);
if ($newAttendeeId > 0) {
$this->sendCloudWatchLog("Attendee created: {$newAttendeeId}");
} else {
$error = "Attendee creation failed for user id: $this->userId";
break;
}
}
}
if ($error != '') {
$oSql->rollback_transaction();
$this->sendCloudWatchLog("Order error");
throw new \Exception("Order Error" . $error);
}
try {
$this->sendCloudWatchLog("Invoice created: {$this->getInvoiceId()}");
$statementID = $this->createInvoiceStatement();
if (intval($statementID) <= 0) {
throw new \Exception('Invoice Error - Statement failed to be created');
}
/* credit card charge, possible exception thrown */
$this->processPayment();
} catch (\Exception $e) {
$error = handleError($e->getMessage());
}
if ($error == '') {
$bCommit = $oSql->commit_transaction();
if ($bCommit === false) {
$error = 'Commit transaction failed.';
} else {
$this->sendCloudWatchLog("Committed fine");
}
}
if ($error !== '') {
$oSql->rollback_transaction();
$this->sendCloudWatchLog("Rollback due to non-empty: {$error}");
}
return true;
}```
答:
mysqli_begin_transaction()
只是一个包装器,所以它可能会因为失败的相同原因而失败。但总的来说,它永远不会失败。潜在的故障仅限于一些一般错误,例如连接断开。mysqli_query('START TRANSACTION')
mysqli_query()
如果由于任何原因开始事务失败,那么 PHP 将抛出异常,就像任何其他 mysqli 错误一样(除非出于某种原因,您将 mysqli 错误报告设置为静默)。因此,只要启用了错误报告并且不捕获错误,就不必担心不一致的事务。如果忽略该错误,但该错误不是由于连接断开造成的,则以下查询将执行,就好像事务从未打开过一样。这可能会导致数据库中的结果不一致(一个查询失败,另一个查询成功)。这就是为什么始终完全启用错误报告很重要的原因!mysqli_begin_transaction()
正确的方法是在尝试捕获之外。mysqli_begin_transaction()
$mysqli->begin_transaction();
try {
// All your prepared statements go here
$mysqli->commit();
} catch (mysqli_sql_exception $exception) {
$mysqli->rollback();
throw $exception;
}
但是,将其放在 try-catch 中不会有什么区别。当它失败时,它只会不必要地调用 ,这将回滚任何内容。rollback()
只要确保你不使用语句和错误检查。这非常容易出错!if
mysqli_error()
评论
mysqli_begin_transaction
评论
start transaction