极客时间已完结课程限时免费阅读

14丨什么是事务处理,如何使用COMMIT和ROLLBACK进行操作?

14丨什么是事务处理,如何使用COMMIT和ROLLBACK进行操作?-极客时间

14丨什么是事务处理,如何使用COMMIT和ROLLBACK进行操作?

讲述:陈旸

时长10:05大小13.86M

我们知道在 MySQL 5.5 版本之前,默认的存储引擎是 MyISAM,在 5.5 版本之后默认存储引擎是 InnoDB。InnoDB 和 MyISAM 区别之一就是 InnoDB 支持事务,也可以说这是 InnoDB 取代 MyISAM 的重要原因。那么什么是事务呢?事务的英文是 transaction,从英文中你也能看出来它是进行一次处理的基本单元,要么完全执行,要么都不执行。
这么讲,你可能觉得有些抽象,我换一种方式讲。
不知道你是否遇到过这样的情况,你去家门口的小卖铺买东西,已经交了钱,但是老板比较忙接了个电话,忘记你是否交过钱,然后让你重新付款,这时你还要找之前的付款记录证明你已经完成了付款。
实际上如果我们线下的交易也能支持事务(满足事务的特性),就不会出现交了钱却拿不到商品的烦恼了,同样,对于小卖铺的老板来说,也不存在给出了商品但没有收到款的风险。总之,事务保证了一次处理的完整性,也保证了数据库中的数据一致性。它是一种高级的数据处理方式,如果我们在增加、删除、修改的时候某一个环节出了错,它允许我们回滚还原。正是因为这个特点,事务非常适合应用在安全性高的场景里,比如金融行业等。
我们今天就来学习下 SQL 中的事务。今天的课程你将重点掌握以下的内容:
事务的特性是什么?如何理解它们?
如何对事务进行控制?控制的命令都有哪些?
为什么我们执行 COMMIT、ROLLBACK 这些命令的时候,有时会成功,有时会失败?

事务的特性:ACID

我刚才提到了事务的特性:要么完全执行,要么都不执行。不过要对事务进行更深一步的理解,还要从事务的 4 个特性说起,这 4 个特性用英文字母来表达就是 ACID。
A,也就是原子性(Atomicity)。原子的概念就是不可分割,你可以把它理解为组成物质的基本单位,也是我们进行数据处理操作的基本单位。
C,就是一致性(Consistency)。一致性指的就是数据库在进行事务操作后,会由原来的一致状态,变成另一种一致的状态。也就是说当事务提交后,或者当事务发生回滚后,数据库的完整性约束不能被破坏。
I,就是隔离性(Isolation)。它指的是每个事务都是彼此独立的,不会受到其他事务的执行影响。也就是说一个事务在提交之前,对其他事务都是不可见的。
最后一个 D,指的是持久性(Durability)。事务提交之后对数据的修改是持久性的,即使在系统出故障的情况下,比如系统崩溃或者存储介质发生故障,数据的修改依然是有效的。因为当事务完成,数据库的日志就会被更新,这时可以通过日志,让系统恢复到最后一次成功的更新状态。
ACID 可以说是事务的四大特性,在这四个特性中,原子性是基础,隔离性是手段,一致性是约束条件,而持久性是我们的目的。原子性和隔离性比较好理解,这里我讲下对一致性的理解(国内很多网站上对一致性的阐述有误,具体你可以参考 Wikipedia 对Consistency的阐述)。
我之前讲到过数据表的 7 种常见约束(对应 04 篇)。这里指的一致性本身是由具体的业务定义的,也就是说,任何写入数据库中的数据都需要满足我们事先定义的约束规则。
比如说,在数据表中我们将姓名字段设置为唯一性约束,这时当事务进行提交或者事务发生回滚的时候,如果数据表中的姓名非唯一,就破坏了事务的一致性要求。所以说,事务操作会让数据表的状态变成另一种一致的状态,如果事务中的某个操作失败了,系统就会自动撤销当前正在执行的事务,返回到事务操作之前的状态。
事务的另一个特点就是持久性,持久性是通过事务日志来保证的。日志包括了回滚日志和重做日志。当我们通过事务对数据进行修改的时候,首先会将数据库的变化信息记录到重做日志中,然后再对数据库中对应的行进行修改。这样做的好处是,即使数据库系统崩溃,数据库重启后也能找到没有更新到数据库系统中的重做日志,重新执行,从而使事务具有持久性。

事务的控制

当我们了解了事务的特性后,再来看下如何使用事务。我们知道 Oracle 是支持事务的,而在 MySQL 中,则需要选择适合的存储引擎才可以支持事务。如果你使用的是 MySQL,可以通过 SHOW ENGINES 命令来查看当前 MySQL 支持的存储引擎都有哪些,以及这些存储引擎是否支持事务。
你能看出在 MySQL 中,InnoDB 是支持事务的,而 MyISAM 存储引擎不支持事务。
看到这里,我们已经对事务有了一定的了解,现在我们再来看下事务的常用控制语句都有哪些。
START TRANSACTION 或者 BEGIN,作用是显式开启一个事务。
COMMIT:提交事务。当提交事务后,对数据库的修改是永久性的。
ROLLBACK 或者 ROLLBACK TO [SAVEPOINT],意为回滚事务。意思是撤销正在进行的所有没有提交的修改,或者将事务回滚到某个保存点。
SAVEPOINT:在事务中创建保存点,方便后续针对保存点进行回滚。一个事务中可以存在多个保存点。
RELEASE SAVEPOINT:删除某个保存点。
SET TRANSACTION,设置事务的隔离级别。
需要说明的是,使用事务有两种方式,分别为隐式事务和显式事务。隐式事务实际上就是自动提交,Oracle 默认不自动提交,需要手写 COMMIT 命令,而 MySQL 默认自动提交,当然我们可以配置 MySQL 的参数:
mysql> set autocommit =0; //关闭自动提交
mysql> set autocommit =1; //开启自动提交
我们看下在 MySQL 的默认状态下,下面这个事务最后的处理结果是什么:
CREATE TABLE test(name varchar(255), PRIMARY KEY (name)) ENGINE=InnoDB;
BEGIN;
INSERT INTO test SELECT '关羽';
COMMIT;
BEGIN;
INSERT INTO test SELECT '张飞';
INSERT INTO test SELECT '张飞';
ROLLBACK;
SELECT * FROM test;
运行结果(1 行数据):
在这个事务中,整个 SQL 一共执行了 2 个事务,第一个是插入“关羽”,提交后执行成功,第二个是插入两次“张飞”,这里需要注意的是,我们将 name 设置为了主键,也就是说主键的值是唯一的,那么第二次插入“张飞”时就会产生错误,然后执行 ROLLBACK 相当于对事务进行了回滚,所以我们看到最终结果只有一行数据,也就是第一个事务执行之后的结果,即“关羽”。
那么如果我们进行下面的操作又会怎样呢?
CREATE TABLE test(name varchar(255), PRIMARY KEY (name)) ENGINE=InnoDB;
BEGIN;
INSERT INTO test SELECT '关羽';
COMMIT;
INSERT INTO test SELECT '张飞';
INSERT INTO test SELECT '张飞';
ROLLBACK;
SELECT * FROM test;
运行结果(2 行数据):
你能看到这次数据是 2 行,上一次操作我把两次插入“张飞”放到一个事务里,而这次操作它们不在同一个事务里,那么对于 MySQL 来说,默认情况下这实际上就是两个事务,因为在 autocommit=1 的情况下,MySQL 会进行隐式事务,也就是自动提交,因此在进行第一次插入“张飞”后,数据表里就存在了两行数据,而第二次插入“张飞”就会报错:1062 - Duplicate entry '张飞' for key 'PRIMARY'
最后我们在执行 ROLLBACK 的时候,实际上事务已经自动提交了,就没法进行回滚了。
同样的我们再来看下这段代码,你又能发现什么不同呢?
CREATE TABLE test(name varchar(255), PRIMARY KEY (name)) ENGINE=InnoDB;
SET @@completion_type = 1;
BEGIN;
INSERT INTO test SELECT '关羽';
COMMIT;
INSERT INTO test SELECT '张飞';
INSERT INTO test SELECT '张飞';
ROLLBACK;
SELECT * FROM test;
运行结果(1 行数据):
你能看到还是相同的 SQL 代码,只是我在事务开始之前设置了SET @@completion_type = 1;,结果就和我们第一次处理的一样,只有一个“关羽”。这是为什么呢?
这里我讲解下 MySQL 中 completion_type 参数的作用,实际上这个参数有 3 种可能:
completion=0,这是默认情况。也就是说当我们执行 COMMIT 的时候会提交事务,在执行下一个事务时,还需要我们使用 START TRANSACTION 或者 BEGIN 来开启。
completion=1,这种情况下,当我们提交事务后,相当于执行了 COMMIT AND CHAIN,也就是开启一个链式事务,即当我们提交事务之后会开启一个相同隔离级别的事务(隔离级别会在下一节中进行介绍)。
completion=2,这种情况下 COMMIT=COMMIT AND RELEASE,也就是当我们提交后,会自动与服务器断开连接。
在上面这段代码里,我使用了 completion=1,也就是说当我提交之后,相当于在下一行写了一个 START TRANSACTION 或 BEGIN。这时两次插入“张飞”会被认为是在同一个事务之内的操作,那么第二次插入“张飞”就会导致事务失败,而回滚也将这次事务进行了撤销,所以你能看到的结果就只有一个“关羽”。
通过这样简单的练习,你应该能体会到事务提交和回滚的操作。
当我们设置 autocommit=0 时,不论是否采用 START TRANSACTION 或者 BEGIN 的方式来开启事务,都需要用 COMMIT 进行提交,让事务生效,使用 ROLLBACK 对事务进行回滚。
当我们设置 autocommit=1 时,每条 SQL 语句都会自动进行提交。
不过这时,如果你采用 START TRANSACTION 或者 BEGIN 的方式来显式地开启事务,那么这个事务只有在 COMMIT 时才会生效,在 ROLLBACK 时才会回滚。

总结

关于 SQL 中的事务处理,内容相对比较多,因此我会采用两节来进行讲解。今天我们对事务的概念进行了理解,并进行了简单的事务操作。我们在做数据库操作的时候,可能会失败,但正是因为有事务的存在,即使在数据库操作失败的情况下,也能保证数据的一致性。同样,多个应用程序访问数据库的时候,事务可以提供隔离,保证事务之间不被干扰。最后,事务一旦提交,结果就会是永久性的,这就意味着,即使系统崩溃了,数据库也可以对数据进行恢复。
在使用事务的过程中,我们会采用控制流语句对事务进行操作,不过在实际操作中,不一定每次使用 COMMIT 或 ROLLBACK 都会成功,你还需要知道当前系统的事务执行方式,也就是一些常用的参数情况,比如 MySQL 中的 autocommit 和 completion_type 等。
事务是数据库区别于文件系统的重要特性之一,当我们有了事务就会让数据库始终保持一致性,同时我们还能通过事务的机制恢复到某个时间点,这样可以保证已提交到数据库的修改不会因为系统崩溃而丢失。
今天的内容到这里就结束了,你能说一下 MySQL 中都有哪些存储引擎支持事务,通过什么命令可以查看它们吗?另外,你是如何理解事务的特性的?
欢迎你在评论区写下你的答案,也欢迎把这篇文章分享给你的朋友或者同事,一起来交流。
分享给需要的人,Ta购买本课程,你将得20
生成海报并分享

赞 22

提建议

上一篇
13丨什么是存储过程,在实际项目中用得多么?
下一篇
15丨初识事务隔离:隔离的级别有哪些,它们都解决了哪些异常问题?
unpreview
 写留言

精选留言(68)

  • 柔软的胖
    2019-07-13
    set autocommit=0; BEGIN; INSERT INTO test1 VALUES ('a') ; BEGIN; INSERT INTO test1 VALUES ('b'); INSERT INTO test1 VALUES ('b'); 在上面代码中,第一个BEGIN没有显示提交。在执行第二个BEGIN时,自动把第一个事务提交了。请问这是MYSQL中默认的行为吗?
    展开

    作者回复: 对的,在MySQL中,如果是连续BEGIN,开启了第一个事务,还没有进行COMMIT提交,而直接进行第二个事务的BEGIN,数据库会隐式的帮助COMMIT第一个事务,然后进入到第二个事务

    60
  • 马以
    2019-07-12
    老师能不能穿插着一些工作环境中的实例来介绍呢?比如说事务的自动提交,生产当中我们要设置成自动提交还是不自动提交,是基于什么样的情况下才这样设计,这样就更容易理解了~,一个小建议

    作者回复: 感谢Ant同学

    16
  • 挠头侠
    2019-08-12
    老师,在第一个例子中,在第二个begin中插入了两个 ‘张飞’ ,但是执行时发生错误,不是说错误会回滚吗?为什么我们还需要指定 ROLLBACK? 如果将第一个例子中的ROLLBACK 改成COMMIT 为什么会有一条 ‘张飞’ 插入成功呢?这两个insert不应该是一个事务内吗?不应该都进行回滚吗?
    共 2 条评论
    11
  • 夜路破晓
    2019-07-12
    事务是个有理想、有个性、讲爱憎的耿直BOY。跟这样的人打交道会很放心。

    作者回复: 可以这么理解,要么全部执行,要么全部不执行。不过实际操作的时候,还需要使用COMMIT和ROLLBACK来管理我们的事务

    共 2 条评论
    10
  • 半瓶醋
    2019-10-26
    MySQL中InnoDB是支持事务的,MyISAM不支持事务的,可以通过SHOW ENGINES命令查看~ 事务,简单来说就是:要么成功,要么失败。具体:四个特性(ACID) A:原子性,不可分割; C:一致性,无论事务是否提交成功,数据库的完整性约束不会改变,即会由原来的一致性状态变为另一种一致性状态; I:隔离性,各个事务是独立的,不会互相影响; D:持久性,一旦事务提交,对数据的修改是持久的,即使系统故障,修改依然有效。 ----分割线--- 记一下,加深印象~
    展开

    作者回复: 加油 总结的不错

    9
  • 森鱼
    2019-09-08
    你好,在总结部分之前,音频中有一段“DELIMITER”操作方式,我看文本中并没有相应的段落,可否有空让工作人员补上?谢谢!
    9
  • 庞鑫华
    2019-07-25
    老师,什么时候能讲一下redo,undo log

    作者回复: 我记得专栏后面章节里有讲到过,可以看看

    8
  • 张小倔
    2019-09-04
    文章中提到的保存点 savepoint,实用场景有哪些呢

    作者回复: 对于复杂的事务来说,可以采用savepoint,可以回滚到指定的保留点,操作起来更灵活

    6
  • 毛豆
    2019-07-12
    为什么一直重点在讲Mysql呢,oracle只是随口一提,而且oracle是有自动提交的,例如像create语句,oracle是自动提交

    作者回复: 目前是围绕一个RDBMS来做举例,顺便举例下Oracle和SQL Server的不同,这些不同我回来再做个详细的整理,放到进阶篇中

    共 4 条评论
    6
  • Tyrant
    2019-09-10
    我发现在navicat里,rollback完全没用,不知道为啥
    共 1 条评论
    4
  • 2019-07-17
    CREATE TABLE test(name varchar(255), PRIMARY KEY (name)) ENGINE=InnoDB; BEGIN; INSERT INTO test SELECT '关羽'; COMMIT; INSERT INTO test SELECT '张飞'; INSERT INTO test SELECT '张飞'; ROLLBACK; SELECT * FROM test; 老师 后面两次插入张飞不在一个事务里是什么意思?这两次插入前面也没有begin
    展开
    共 5 条评论
    3
  • 时间是最真的答案
    2019-07-12
    不知道作者在什么样的环境下操作的,我实验结果和你的不一样; 我的 MySQL8.0,使用 Navicat 12 操作的,实验结果都不一样 第一个实验,数据库中插入两条数据:关羽,张飞 第二个实验,数据库中插入两条数据:关羽,张飞 第三个实验,数据库中插入一条数据:关羽 麻烦老师回复一下
    展开

    作者回复: 采用默认的autocommit=1; 第一个实验后面那个应该是ROLLBACK,和后面的实验都一样,最后一个操作应该是ROLLBACK。所以结果: 实验1:关羽 实验2:关羽,张飞 实验3:关羽

    共 4 条评论
    3
  • JustDoDT
    2019-07-12
    看着文章听一遍,然后在读文章实践一下。然后消化消化,然后1个小时学完一篇。 我是不是很菜。

    作者回复: 不会的,逐渐会有感觉,尤其是跟着代码跑一遍的时候,效果更佳

    共 2 条评论
    3
  • 知行合一
    2019-10-23
    为什么在navicat中,我和老师的代码一致。但是,实验结果,总是不同。 张飞,每次都被存进去了。很不理解!能说说吗? 谢谢
    共 3 条评论
    3
  • linus
    2019-07-15
    DROP TABLE test; CREATE TABLE test(name varchar(255), PRIMARY KEY (name)) ENGINE=InnoDB; set autocommit=0; BEGIN; INSERT INTO test SELECT 'aaa'; COMMIT; BEGIN; INSERT INTO test SELECT 'bbb'; INSERT INTO test SELECT 'bbb'; ROLLBACK; SELECT * FROM test; 为什么我的结果还是 aaa bbb
    展开
    共 3 条评论
    3
  • 柔软的胖
    2019-07-13
    如果“INSERT INTO test SELECT '关羽';”之后没有执行COMMIT,结果应该是空。 但是我执行出来的结果是'关羽',为什么ROLLBACK没有全部回退。 代码如下 CREATE TABLE test(name varchar(255), PRIMARY KEY (name)) ENGINE=InnoDB; BEGIN; INSERT INTO test SELECT '关羽'; BEGIN; INSERT INTO test SELECT '张飞'; INSERT INTO test SELECT '张飞'; ROLLBACK; SELECT * FROM test;
    展开

    作者回复: 在MySQL中BEGIN用于开启事务,对于连续BEGIN的情况,开启了第一个事务,还没有进行COMMIT提交,而直接进行第二个事务的BEGIN,这时数据库会隐式的帮助COMMIT第一个事务,然后进入到第二个事务

    2
  • Demon.Lee
    2019-07-12
    ACID 可以说是事务的四大特性,在这四个特性中,原子性是基础,隔离性是手段,一致性是约束条件,而持久性是我们的目的。 ------以后面试就说这一句,如果问ACID

    作者回复: 可以的 随着我们对事务理解的深入,对ACID这四个特性也会了解的越来越全面

    2
  • 我不是矿长
    2019-07-12
    学习了

    作者回复: 谢谢 自己可以跑下代码 多加深理解

    2
  • hlz-123
    2019-07-12
    1.在MySQL中,只有InnoDB引擎支持事务,可以通过命令SHOW ENGINES查看 2.事务的特性,可以理解为完整性,在一个事务中的所有指令要么全部有效,要么全部失效, 如果有一部分语句成功,有一部分语句失败,数据库也会撤销成功的语句效果, 回滚到该事务执行前的状态。

    作者回复: 对的 看你的MySQL都支持哪些存储引擎,一般是InnoDB和BDB支持事务

    2
  • tttttttttttt_SL
    2019-12-17
    看了四五遍懂了

    作者回复: 加油~

    1