Code前端首页关于Code前端联系我们

MySQL不支持嵌套事务? ThinkPHP和Laravel嵌套设计支持

terry 2年前 (2023-09-25) 阅读数 49 #后端开发

MySQL官方文档明确指出不支持嵌套事务:

事务不能嵌套。这是当您发出 START TRANSACTION 命令或其同义词之一时对任何当前事务执行隐式提交的结果。因此,我们需要在系统架构层面支持事务嵌套。

幸运的是,一些成熟的ORM框架都支持嵌套,例如ThinkPHP和Laravel。

如果有多个嵌套级别,则显示transTimes。如果嵌套,则只有最外层的事务有效。这种嵌套事务的方法与laravel完全相同。

ThinkPHP 6.x 嵌套事务逻辑代码

文件位置:vendor/topthink/think-orm/src/db/PDOConnection.php

    /**
     * 启动事务
     * @access public
     * @return void
     * @throws \PDOException
     * @throws \Exception
     */
    public function startTrans(): void
    {
        try {
            $this->initConnect(true);

            ++$this->transTimes;

            if (1 == $this->transTimes) {
                $this->linkID->beginTransaction();
            } elseif ($this->transTimes > 1 && $this->supportSavepoint()) {
                $this->linkID->exec(
                    $this->parseSavepoint(‘trans‘ . $this->transTimes)
                );
            }
            $this->reConnectTimes = 0;
        } catch (\Exception $e) {
            if ($this->reConnectTimes < 4 && $this->isBreak($e)) {
                --$this->transTimes;
                ++$this->reConnectTimes;
                $this->close()->startTrans();
            } else {
                throw $e;
            }
        }
    }

    /**
     * 用于非自动提交状态下面的查询提交
     * @access public
     * @return void
     * @throws PDOException
     */
    public function commit(): void
    {
        $this->initConnect(true);

        if (1 == $this->transTimes) {
            $this->linkID->commit();
        }

        --$this->transTimes;
    }

    /**
     * 事务回滚
     * @access public
     * @return void
     * @throws PDOException
     */
    public function rollback(): void
    {
        $this->initConnect(true);

        if (1 == $this->transTimes) {
            $this->linkID->rollBack();
        } elseif ($this->transTimes > 1 && $this->supportSavepoint()) {
            $this->linkID->exec(
                $this->parseSavepointRollBack(‘trans‘ . $this->transTimes)
            );
        }

        $this->transTimes = max(0, $this->transTimes - 1);
    }

Laravel 8.x 嵌套事务逻辑代码文件位置 : / laravel/ Framework/blob/8.x/src/Illuminate/Database/DatabaseTransactionsManager.php
    /**
     * Start a new database transaction.
     *
     * @param  string  $connection
     * @param  int  $level
     * @return void
     */
    public function begin($connection, $level)
    {
        $this->transactions->push(
            new DatabaseTransactionRecord($connection, $level)
        );
    }

    /**
     * Rollback the active database transaction.
     *
     * @param  string  $connection
     * @param  int  $level
     * @return void
     */
    public function rollback($connection, $level)
    {
        $this->transactions = $this->transactions->reject(function ($transaction) use ($connection, $level) {
            return $transaction->connection == $connection &&
                   $transaction->level > $level;
        })->values();
    }

    /**
     * Commit the active database transaction.
     *
     * @param  string  $connection
     * @return void
     */
    public function commit($connection)
    {
        $this->transactions = $this->transactions->reject(function ($transaction) use ($connection) {
            if ($transaction->connection == $connection) {
                $transaction->executeCallbacks();

                return true;
            }

            return false;
        })->values();
    }

文件位置:/laravel/framework/blob/8.x/src/Illuminate/Database/DatabaseTransactionRecord.php php+mysql 如何实现 multi级事务嵌套

msyql本身不支持事务嵌套,但是它可以伪装成嵌套事务的思想来实现多级事务嵌套。
启动事务时,先标记flag,每次嵌套时该值加1,但只有mark=1时才执行事务启动操作,其他只是累加。
发送时必须从最内层开始。每次提交时,grade都会减1,直到grade=1时才真正执行提交。
退货也是如此。 ?操作 1中的数据是实际写入的,仅回滚操作 2中的数据。当第一个事务没有提交或回滚时,当第二个事务开始时,第一个事务会自动提交。

这显然不符合心理预期,并且无法撤消部分操作。那么问题来了,MySQL支持事务嵌套吗?

这个问题支持不支持很难准确回答!

首先,多次调用begin肯定不允许MySQL中嵌套事务。经过群里的朋友提醒我才知道MySQL中有一个命令叫savepoint和rollback to。

示例代码:

DROP TABLE IF EXISTS `test`;
CREATE TABLE `test` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

begin;
insert into `test`(`name`) values(‘111‘);
SAVEPOINT p1;
insert into `test`(`name`) values(‘222‘);
ROLLBACK TO p1;
commit;

最终执行结果,测试表中只有数据111,实现了返回部分操作的操作。同理,也避免了多个事务发起,导致前一个事务提交的问题。

可能的savepoint回滚到命令不能称为嵌套事务,也不能说MySQL是否支持嵌套事务。简而言之,通过保存点回滚到可以用来实现一些事务嵌套功能。

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门