当前位置:首页 > SQL > 正文内容

MYSQL事务嵌套,PHP事务嵌套,THINKPHP事务嵌套

高老师3年前 (2021-09-24)SQL2708

最近有在追踪一个tp3的事务问题,正好看到事务嵌套的问题,于是整理了出来,本来想等待同事整理,白嫖他,结果等了个寂寞。

(1).参考事务嵌套的错误SQL:

### 事务1开启
BEGIN;

 ## 事务1修改数据
 UPDATE hqjf_job_num  SET wx_uname='蒋琦1024' where id = 602;
 
 ### 事务2开启
 BEGIN;
 
 ### 事务2提交
 COMMIT; 
 
### 事务1回滚
ROLLBACK;

我们期望的结果:update语句不会执行成功,实际执行成功了

出现问题的原因:BEGIN语句会隐式的执行事务提交,相当于第二个BEGIN执行的时候提交了第一个事务.

(2).哪些SQL语法会隐式事务提交事务呢?

参考Mysql官方文档:

https://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html

(2.1).DDL 

ALTER DATABASE ... UPGRADE DATA DIRECTORY NAME, ALTER EVENT, ALTER PROCEDURE, ALTER SERVER, ALTER TABLE, ALTER TABLESPACE, ALTER VIEW, CREATE DATABASE, CREATE EVENT, CREATE INDEX, CREATE PROCEDURE, CREATE SERVER, CREATE TABLE, CREATE TABLESPACE, CREATE TRIGGER, CREATE VIEW, DROP DATABASE, DROP EVENT, DROP INDEX, DROP PROCEDURE, DROP SERVER, DROP TABLE, DROP TABLESPACE, DROP TRIGGER, DROP VIEW, INSTALL PLUGIN, RENAME TABLE, TRUNCATE TABLE, UNINSTALL PLUGIN.

(2.2).USER|MODIFY TABLES

ALTER USER, CREATE USER, DROP USER, GRANT, RENAME USER, REVOKE, SET PASSWORD.

(2.3).TRANSACTION|LOCK TABLES

BEGIN, LOCK TABLES, (if the value is not already 1), START TRANSACTION, UNLOCK TABLES. SET autocommit = 1,UNLOCK TABLES

(2.4).DATA LOADING STATEMENTS

LOAD DATA

(2.5).ADMINISTRATIVE STATEMENTS

ANALYZE TABLE, CACHE INDEX, CHECK TABLE, FLUSH, LOAD INDEX INTO CACHE, OPTIMIZE TABLE, REPAIR TABLE, RESET

(2.6).REPLICATION CONTROL STATEMENTS

START SLAVE, STOP SLAVE, RESET SLAVE, CHANGE MASTER TO

好家伙原来这多SQL操作都会隐式提交事务

同时上面的文档中提到:

Transactions cannot be nested. This is a consequence of the implicit commit performed for any current transaction when you issue a START TRANSACTION statement or one of its synonyms.

事务不能嵌套。 这是当您发出 START TRANSACTION 语句或其同义词之一时对任何当前事务执行的隐式提交的结果。

(3).设置autocommit并不能解决上面的事务嵌套问题,好好了解下autocommit到底是啥

首先要知道什么叫自动提交。就是自动提交事务啦。瓜娃子。

(3.1).假设开启事务自动提交的时候,你执行一个SQL如下:

UPDATE hqjf_job_num  SET wx_uname='蒋琦104' where id = 6956;

实际上已经等价于执行了如下SQL:

BEGIN;
UPDATE hqjf_job_num  SET wx_uname='蒋琦104' where id = 6956;
COMMIT;

只不过是MYSQL帮你的SQL自动加了事务并且提交了。

如果你开启了事务自动提交且自己使用了事务操作,MYSQL就会乖乖听你的,不会乱自动提交,除非你自己隐式提交。

(3.2).假设关闭事务自动提交的时候,你执行一个SQL如下:

UPDATE hqjf_job_num  SET wx_uname='蒋琦104' where id = 6956;

实际上根本不会执行成功,关闭事务自动提交后MYSQL要求你必须自己手动提交事务,否则SQL没有提交也就不会更新咯

老高你的内容我看不懂,给我推荐1个详细的autocomit的文章,好的,给你。直达地址:https://blog.csdn.net/wx145/article/details/82740737

上面我们得出的结论是MYSQL是不支持事务嵌套的,特别注意是不支持的,不支持的,不支持的!别看其他文章瞎说,看官方文档。

(4).MYSQL不支持事务嵌套,如果模拟事务嵌套的效果

(4.1).例子SQL:

### 开启事务
BEGIN;

### 建立事务保存点a
SAVEPOINT a;

### 更新数据名称为1024
UPDATE hqjf_job_num  SET wx_uname='1024'  WHERE id=7638;

### 建立事务保存点b
SAVEPOINT b;

### 更新数据名称为2048
UPDATE hqjf_job_num  SET wx_uname='2048'  WHERE id=7638;

### 建立事务保存点d
SAVEPOINT c;

### 回滚到事务保存点
ROLLBACK TO SAVEPOINT a;

### 提交事务
COMMIT;

假设以上数据的原始wx_uname的原始值为空

ROLLBACK TO SAVEPOINT a;则数据不会修改
ROLLBACK TO SAVEPOINT b;则数据会被修改为1024
ROLLBACK TO SAVEPOINT c;则数据会被修改为2048

看看上面的SQL代码的执行顺序吧:

上面的SQL在执行到ROLLBACK TO SAVEPOINT a的时候回跳到建立事务保存点a的位置,然后执行剩下的COMIT语句,因此示例的SQL不会修改任何数据

(5).PHP框架中解决MYSQL事务嵌套的方案(TP6)

开启事务示例:

/**
 * 启动事务
 * @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 (\Throwable | \Exception $e) {
        if ($this->transTimes === 1 && $this->reConnectTimes < 4 && $this->isBreak($e)) {
            --$this->transTimes;
            ++$this->reConnectTimes;
            $this->close()->startTrans();
        } else {
            if ($this->isBreak($e)) {
                // 尝试对事务计数进行重置
                $this->transTimes = 0;
            }
            throw $e;
        }
    }
}

开启事务时统一递增事务次数

第一次开启事务则真正调用MYSQL开启事务

第二次或以上开启事务分情况:支持savepoint时调用MYSQL创建事务保存点,不支持时则相当于啥也不干,


执行事务回滚示例:

/**
 * 事务回滚
 * @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);
}

执行事务回滚时事务次数统一减1

如果事务次数为1则真正提交MYSQL让事务回滚

如果事务次数大于1并且支持savepoint则回滚事务到事务保存点


执行事务提交的示例:

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

只有事务次数为1的时候才会真正提交MYSQL事务

通过框架层的支持,你虽然包含了多层事务,但是本质上你只会真正开启1次事务,提交1次事务,配合savepoint实现事务嵌套的效果。和上面我们模拟事务嵌套的效果一致。

我看到很多PHP事务嵌套没有使用savepoint的实现,严格来说不算是事务嵌套,比如下面的问题:

// 开启主事务
Db::startTrans();
// 开启子事务
Db::startTrans();
// 执行UPDATE语句
// 回滚子事务
Db::rollback();
// 提交主事务
Db::rollback();

最终结果导致主事务提交后子事务的SQL也执行了,因为子事务开启和回滚是虚拟的,什么也没做。当然部分实现中只要子事务回滚强制让主事务也回滚,这样失去的嵌套的意义。

所以支持saveponit才能实现真正的框架层事务嵌套。

扫描二维码推送至手机访问。

版权声明:本文由高久峰个人博客发布,如需转载请注明出处。

本文链接:https://blog.5b1.cn/post/356.html

分享给朋友:

“MYSQL事务嵌套,PHP事务嵌套,THINKPHP事务嵌套” 的相关文章

MySql基本的建表原则

MySql基本的建表原则

1.定长和变长的分离 如int,char,time所占字节是固定的字段放在一张表 如varchar,text所占字节不确定的字段放在一张表中2.常用字段和不常用字段进行分离,根据查询频率来设计3.一对多的关联表可以添加冗余字段,如商品分类表 和商品表 ,在首页中需要显示每个分类商...

mysql group分组排序

mysql group分组排序

下面的是商品数据表,id是主键,catename是分类,titile是标题,price是价格,现在要求计算每种分类下的商品平均价格.id     catename       &n...

Left join,​Right join,​Inner join的区别实例讲解

Left join,​Right join,​Inner join的区别实例讲解

Left join:即左连接,是以左表为基础,根据ON后给出的两表的条件将两表连接起来。结果会将左表所有的查询信息列出,而右表只列出ON后条件与左表满足的部分。左连接全称为左外连接,是外连接的一种。Right join:即右连接,是以右表为基础,根据ON后给出的两表的条件将两表连接起来。结果会将右表...

mysql没有快捷方式,mysql快捷方式,mysql启动快捷方式

mysql没有快捷方式,mysql快捷方式,mysql启动快捷方式

有很多集成环境安装完成之后是没有快捷方式的,例如西部数码的网站管理助手4.0,、 更或者是护卫神PHP套件都是一样的。安装完成最多给你安装一个PhPmyadmin让你管理Mysql,但是对于经常使用命令行的我们来说是非常不方面的,而且还必须安装PhPmyadmin来管理。下面就让我们自己手...

mysql常用函数

mysql常用函数

1.floor(x)返回小于x的整数,向下取整,用法,商品的价格是浮点型的,需要向下取整 eg:select id,title,floor(price)  from  shopgoods2.rand()返回0-1之间的随机数 select rand() select rand()...

 mysql count 性能优化,mysql count(*)优化

mysql count 性能优化,mysql count(*)优化

1.很多人认为count查询非常快,但是在加上筛选条件那就是未必的了!测试:user表中4000w数据(1).SELECT  count(*)   from user;   用时0.00s (2).SELECT...