mysql事务和阶段性回滚点

栏目: IT技术 · 发布时间: 4年前

内容简介:手册地址默认的, 如果不修改mysql配置文件, 会启用 自动提交功能auto commit, 即隐式提交你的每一条SQL语句;有些语句,在执行之前,会隐式地发出commit。这些语句包括:

手册地址 https://dev.mysql.com/doc/refman/8.0/en/savepoint.html

默认的, 如果不修改 mysql 配置文件, 会启用 自动提交功能auto commit, 即隐式提交你的每一条 SQL 语句;

有些语句,在执行之前,会隐式地发出commit。这些语句包括:

  1. DDL语句, 比如 create database/drop database, create/drop/alter table/routine
  2. 隐式地使用或者修改mysql数据库的语句,比如 ALTER USER, CREATE USER, DROP USER, GRANT, RENAME USER, REVOKE, SET PASSWORD
  3. 事务控制和锁语句,比如BEGIN,START TRANSACTION,  LOCK/UNLOCK TABLES, SET autocommit = 1(原来不是1)
  4. LOAD数据语句, LOAD DATA INFILE
  5. 管理语句,比如 ANALYZE TABLE, CACHE INDEX, CHECK TABLE等等
  6. 复制控制语句,比如 START/STOP/RESET SLAVE, CHANGE MASTER TO

示例:

mysql> set autocommit=1;

mysql> BEGIN;
mysql> CREATE TABLE T2(ID INT);
mysql> INSERT INTO T2 VALUES(100);
mysql> ROLLBACK;

mysql> SELECT * FROM T2;
+------+
| ID   |
+------+
|  100 |
+------+
1 row in set (0.00 sec)
————————————————

事务的基础操作:

开启事务(进入事务模式,不会自动提交) START TRANSACTION; 或者简写 BEGIN;

提交事务 COMMIT;

回滚事务 ROLLBACK;

关闭提交之后, 无需开启事务, 等价于"已经在事务模式下", 需要手动COMMIT或ROLLBACK才能使SQL发生作用;

当前TCP session级别, 关闭自动提交 SET autocommit=0;

全局, 关闭自动提交 SET GLOBAL autocommit=0;

重要提示:DDL语句无法被回滚, 上面说了"有些语句会提供commit提交",比如 create database/drop database, create/drop/alter table/routine。

在需要COMMIT/ROLLBACK的场景下, 可以阶段性的回滚和提交, 即 "打点计时器" 一样, 留下锚点(保存点, save point);

  1. 开启事务后,在事务内声明存档点 SAVEPOINT $name

    批处理也是事务, 比如: (1)BEGIN;(2)巴拉巴拉多条SQL;;(3)COMMIT;或ROLLBACK;
  2. 提交事务之前,释放存档点 RELEASE SAVEPOINT $name

    只会移除这一个存档点,不影响其他存档点, 也不会自动触发commit或rollback;
    
    如果释放的存档点不存在,则报错 ERROR 1305 (42000): SAVEPOINT identifier does not exist
  3. 回滚至某个存档点 ROLLBACK TO SAVEPOINT $name

    如果回滚的存档点不存在,则报错 ERROR 1305 (42000): SAVEPOINT identifier does not exist
  4. 执行COMMIT或ROLLBACK后, 自动删除 事务内的所有存档点;
  5. 开启事务之前, 可以设置隔离级别:

    SET TRANSACTION ISOLATION LEVEL $level;

四种级别情况,

第一种情况:

READ COMMITTED

作用是:

指定在读取数据时控制共享锁以避免脏读,但数据可在事务结束前更改,从而产生不可重复读取或幻像数据。该选项是 SQL Server的默认值。

第二种情况:

READ UNCOMMITTED

作用是:

执行脏读或0级隔离锁定,这表示不发出共享锁,也不接受排它锁。当设置该选项时,可以对数据执行未提交读或脏读;在事务结束前可以更改数据内的数值,行也可以出现在数据集中或从数据集消失。该选项的作用与在事务内所有语句中的所有表上设置 NOLOCK 相同。这是四个隔离级别中限制最小的级别。

第三种情况:

REPEATABLE READ

作用是:

锁定查询中使用的所有数据以防止其他用户更新数据,但是其他用户可以将新的幻像行插入数据集,且幻像行包括在当前事务的后续读取中。因为并发低于默认隔离级别,所以应只在必要时才使用该选项。

第四种情况:

SERIALIZABLE

作用是:

在数据集上放置一个范围锁,以防止其他用户在事务完成之前更新数据集或将行插入数据集内。这是四个隔离级别中限制最大的级别。因为并发级别较低,所以应只在必要时才使用该选项。该选项的作用与在事务内所有 SELECT 语句中的所有表上设置 HOLDLOCK相同。

SAVEPOINT语法如下:
SAVEPOINT identifier
ROLLBACK [WORK] TO [SAVEPOINT] identifier
RELEASE SAVEPOINT identifier

注意: 如果存档点的名称(标识符identifier)跟上方重复了, 则删除旧的存档点, 设置新的存档点, 相当于"游戏存档"被擦写覆盖了。

示例:

mysql> CREATE TABLE T2(ID INT);

mysql> BEGIN;
mysql> INSERT INTO T2 VALUES(100);
mysql> SAVEPOINT sp1;
mysql> INSERT INTO T2 VALUES(200);
mysql> ROLLBACK TO SAVEPOINT sp1;
mysql> RELEASE SAVEPOINT sp1;
mysql> COMMIT;

mysql> SELECT * FROM T2;
+------+
| ID   |
+------+
|  100 |
+------+
1 row in set (0.00 sec)
————————————————

存档点可以层叠嵌套, 很像"操作系统在切换进程上下文的时候,压栈/弹栈", 具体表现在:

  1. 当 "调用invoke"存储过程/函数(store function) 或 激活(activated)触发器(trigger) 时,将隐式创建"更高层 级别的"存档点。
  2. 低级别上的存档点将不可用(保留工作现场),因此不会与新级别上的存档点冲突(比如存档点的标识符名称)。
  3. 当函数或触发器执行结束时,它创建的任何存档点都将被释放,并还原上一个存档点级别(还原工作现场)。

值得一提的是, PHP的Yii2框架, 居然支持"多次开启事务", 其实mysql是不支持"多次开启事务"的, 而是"存档点的层叠嵌套"; 更有意思的是, 这也导致框架的使用者采坑: (1)多次"开启事务", (2)"提交事务"居然失败了?

$m = new AR();
$transaction = $m->getDb()->beginTransaction(); // 返回一个对象,挂载在db下
// 巴拉巴拉一顿CRUD操作猛如虎..
$transaction = $m->getDb()->beginTransaction(); // 该对象与前者是同一个实例(单例)
// 又是一顿CRUD操作猛如虎...
$m->getDb()->getTransaction()->commit();
// 或者 $transaction->commit();
exit;
 
// 等等,为什么MySQL没有找到刚才的"一顿操作"??!!
// 因为 第二次beginTransaction() 创建了新的savepoint层级,
// 第一次调用commit()函数, 只是 RELEASE SAVEPOINT $name
// 所以需要再多调用一次 commit() 函数...直到层级为0的时候,才会真正发送 commit;语句给MYSQL服务器;

详见代码 vendor/yiisoft/yii2/db/Transaction.php

public function begin($isolationLevel = null)
{
    $this->db->open();

    if ($this->_level === 0) {
        if ($isolationLevel !== null) {
            // 设置隔离级别SET TRANSACTION ISOLATION LEVEL $level;
            $this->db->getSchema()->setTransactionIsolationLevel($isolationLevel);
        }
        $this->db->pdo->beginTransaction();
        $this->_level = 1;
        return;
    }

    $schema = $this->db->getSchema();
    if ($schema->supportsSavepoint()) { // 如果是InnoDB存储引擎
        Yii::trace('Set savepoint ' . $this->_level, __METHOD__);
        $schema->createSavepoint('LEVEL' . $this->_level);
    } else {
        Yii::info('Transaction not started: nested transaction not supported', __METHOD__);
    }
    $this->_level++;
}
public function commit()
{
    if (!$this->getIsActive()) { // 没有开启事务则报错
        throw new Exception('Failed to commit transaction: transaction was inactive.');
    }

    $this->_level--;
    if ($this->_level === 0) {
        $this->db->pdo->commit();
        return;
    }

    $schema = $this->db->getSchema();
    if ($schema->supportsSavepoint()) { // InnoDb存储引擎
        Yii::trace('Release savepoint ' . $this->_level, __METHOD__);
        $schema->releaseSavepoint('LEVEL' . $this->_level);
    } else {
        Yii::info('Transaction not committed: nested transaction not supported', __METHOD__);
    }
}

YII2 的 rollback机制 也混合了savepoint, 要多次调用rollback函数才会真正rollback,很危险.


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

无懈可击的Web设计

无懈可击的Web设计

【美】Dan Cederholm / 马跃 / 清华大学出版社 / 2012-5 / 39.00元

本书将指导您采用标准设计策略来满足以各种方式浏览网页的各类用户的需要。每章首先列举一个沿用传统HTML技术的实例,然后指出该实例的局限性,并利用XHTML和CSS对其进行重构。从中您将学会如何用简洁高效的HTML标记和CSS来取代臃肿的代码,从而创建加载速度极快、能供所有用户使用的网站。本书最后将前面各章讨论的所有页面组件珠联璧合地结合在一起,制作了一个页面模板。这一版全面润色和更新了上一版本,介......一起来看看 《无懈可击的Web设计》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

html转js在线工具
html转js在线工具

html转js在线工具