CategoryResourceRepost/极客时间专栏/SQL必知必会/第一章:SQL语法基础篇/14丨什么是事务处理,如何使用COMMIT和ROLLBACK进行操作?.md
louzefeng d3828a7aee mod
2024-07-11 05:50:32 +00:00

154 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<audio id="audio" title="14丨什么是事务处理如何使用COMMIT和ROLLBACK进行操作" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/27/bf/270a3a064d24b4473da95708fa70d5bf.mp3"></audio>
我们知道在MySQL 5.5版本之前默认的存储引擎是MyISAM在5.5版本之后默认存储引擎是InnoDB。InnoDB和MyISAM区别之一就是InnoDB支持事务也可以说这是InnoDB取代MyISAM的重要原因。那么什么是事务呢事务的英文是transaction从英文中你也能看出来它是进行一次处理的基本单元要么完全执行要么都不执行。
这么讲,你可能觉得有些抽象,我换一种方式讲。
不知道你是否遇到过这样的情况,你去家门口的小卖铺买东西,已经交了钱,但是老板比较忙接了个电话,忘记你是否交过钱,然后让你重新付款,这时你还要找之前的付款记录证明你已经完成了付款。
实际上如果我们线下的交易也能支持事务(满足事务的特性),就不会出现交了钱却拿不到商品的烦恼了,同样,对于小卖铺的老板来说,也不存在给出了商品但没有收到款的风险。总之,事务保证了一次处理的完整性,也保证了数据库中的数据一致性。它是一种高级的数据处理方式,如果我们在增加、删除、修改的时候某一个环节出了错,它允许我们回滚还原。正是因为这个特点,事务非常适合应用在安全性高的场景里,比如金融行业等。
我们今天就来学习下SQL中的事务。今天的课程你将重点掌握以下的内容
1. 事务的特性是什么?如何理解它们?
1. 如何对事务进行控制?控制的命令都有哪些?
1. 为什么我们执行COMMIT、ROLLBACK这些命令的时候有时会成功有时会失败
## 事务的特性ACID
我刚才提到了事务的特性要么完全执行要么都不执行。不过要对事务进行更深一步的理解还要从事务的4个特性说起这4个特性用英文字母来表达就是ACID。
1. A也就是原子性Atomicity。原子的概念就是不可分割你可以把它理解为组成物质的基本单位也是我们进行数据处理操作的基本单位。
1. C就是一致性Consistency。一致性指的就是数据库在进行事务操作后会由原来的一致状态变成另一种一致的状态。也就是说当事务提交后或者当事务发生回滚后数据库的完整性约束不能被破坏。
1. I就是隔离性Isolation。它指的是每个事务都是彼此独立的不会受到其他事务的执行影响。也就是说一个事务在提交之前对其他事务都是不可见的。
1. 最后一个D指的是持久性Durability。事务提交之后对数据的修改是持久性的即使在系统出故障的情况下比如系统崩溃或者存储介质发生故障数据的修改依然是有效的。因为当事务完成数据库的日志就会被更新这时可以通过日志让系统恢复到最后一次成功的更新状态。
ACID可以说是事务的四大特性在这四个特性中原子性是基础隔离性是手段一致性是约束条件而持久性是我们的目的。原子性和隔离性比较好理解这里我讲下对一致性的理解国内很多网站上对一致性的阐述有误具体你可以参考Wikipedia对[Consistency](https://en.wikipedia.org/wiki/ACID)的阐述)。
我之前讲到过数据表的7种常见约束[对应04篇](https://time.geekbang.org/column/article/101697))。这里指的一致性本身是由具体的业务定义的,也就是说,任何写入数据库中的数据都需要满足我们事先定义的约束规则。
比如说,在数据表中我们将姓名字段设置为唯一性约束,这时当事务进行提交或者事务发生回滚的时候,如果数据表中的姓名非唯一,就破坏了事务的一致性要求。所以说,事务操作会让数据表的状态变成另一种一致的状态,如果事务中的某个操作失败了,系统就会自动撤销当前正在执行的事务,返回到事务操作之前的状态。
事务的另一个特点就是持久性,持久性是通过事务日志来保证的。日志包括了回滚日志和重做日志。当我们通过事务对数据进行修改的时候,首先会将数据库的变化信息记录到重做日志中,然后再对数据库中对应的行进行修改。这样做的好处是,即使数据库系统崩溃,数据库重启后也能找到没有更新到数据库系统中的重做日志,重新执行,从而使事务具有持久性。
## 事务的控制
当我们了解了事务的特性后再来看下如何使用事务。我们知道Oracle是支持事务的而在MySQL中则需要选择适合的存储引擎才可以支持事务。如果你使用的是MySQL可以通过SHOW ENGINES命令来查看当前MySQL支持的存储引擎都有哪些以及这些存储引擎是否支持事务。
<img src="https://static001.geekbang.org/resource/image/dd/45/ddfbb2ec8a03dc4eb3c77ae3dea63945.png" alt=""><br>
你能看出在MySQL中InnoDB是支持事务的而MyISAM存储引擎不支持事务。
看到这里,我们已经对事务有了一定的了解,现在我们再来看下事务的常用控制语句都有哪些。
1. START TRANSACTION或者 BEGIN作用是显式开启一个事务。
1. COMMIT提交事务。当提交事务后对数据库的修改是永久性的。
1. ROLLBACK或者ROLLBACK TO [SAVEPOINT],意为回滚事务。意思是撤销正在进行的所有没有提交的修改,或者将事务回滚到某个保存点。
1. SAVEPOINT在事务中创建保存点方便后续针对保存点进行回滚。一个事务中可以存在多个保存点。
1. RELEASE SAVEPOINT删除某个保存点。
1. SET TRANSACTION设置事务的隔离级别。
需要说明的是使用事务有两种方式分别为隐式事务和显式事务。隐式事务实际上就是自动提交Oracle默认不自动提交需要手写COMMIT命令而MySQL默认自动提交当然我们可以配置MySQL的参数
```
mysql&gt; set autocommit =0; //关闭自动提交
```
```
mysql&gt; 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行数据
<img src="https://static001.geekbang.org/resource/image/a7/26/a7a49569e87dd5c6f5333a81d33c2826.png" alt="">
在这个事务中整个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行数据
<img src="https://static001.geekbang.org/resource/image/df/f7/df437e577ce75363f5eb22dc362dbff7.png" alt=""><br>
你能看到这次数据是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行数据
<img src="https://static001.geekbang.org/resource/image/26/80/26d1f5a4a534eb9b1415ce867f006b80.png" alt="">
你能看到还是相同的SQL代码只是我在事务开始之前设置了`SET @@completion_type = 1;`,结果就和我们第一次处理的一样,只有一个“关羽”。这是为什么呢?
这里我讲解下MySQL中completion_type参数的作用实际上这个参数有3种可能
1. completion=0这是默认情况。也就是说当我们执行COMMIT的时候会提交事务在执行下一个事务时还需要我们使用START TRANSACTION或者BEGIN来开启。
1. completion=1这种情况下当我们提交事务后相当于执行了COMMIT AND CHAIN也就是开启一个链式事务即当我们提交事务之后会开启一个相同隔离级别的事务隔离级别会在下一节中进行介绍
1. completion=2这种情况下COMMIT=COMMIT AND RELEASE也就是当我们提交后会自动与服务器断开连接。
在上面这段代码里我使用了completion=1也就是说当我提交之后相当于在下一行写了一个START TRANSACTION或BEGIN。这时两次插入“张飞”会被认为是在同一个事务之内的操作那么第二次插入“张飞”就会导致事务失败而回滚也将这次事务进行了撤销所以你能看到的结果就只有一个“关羽”。
通过这样简单的练习,你应该能体会到事务提交和回滚的操作。
当我们设置autocommit=0时不论是否采用START TRANSACTION或者BEGIN的方式来开启事务都需要用COMMIT进行提交让事务生效使用ROLLBACK对事务进行回滚。
当我们设置autocommit=1时每条SQL语句都会自动进行提交。<br>
不过这时如果你采用START TRANSACTION或者BEGIN的方式来显式地开启事务那么这个事务只有在COMMIT时才会生效在ROLLBACK时才会回滚。
## 总结
关于SQL中的事务处理内容相对比较多因此我会采用两节来进行讲解。今天我们对事务的概念进行了理解并进行了简单的事务操作。我们在做数据库操作的时候可能会失败但正是因为有事务的存在即使在数据库操作失败的情况下也能保证数据的一致性。同样多个应用程序访问数据库的时候事务可以提供隔离保证事务之间不被干扰。最后事务一旦提交结果就会是永久性的这就意味着即使系统崩溃了数据库也可以对数据进行恢复。
在使用事务的过程中我们会采用控制流语句对事务进行操作不过在实际操作中不一定每次使用COMMIT或ROLLBACK都会成功你还需要知道当前系统的事务执行方式也就是一些常用的参数情况比如MySQL中的autocommit和completion_type等。
事务是数据库区别于文件系统的重要特性之一,当我们有了事务就会让数据库始终保持一致性,同时我们还能通过事务的机制恢复到某个时间点,这样可以保证已提交到数据库的修改不会因为系统崩溃而丢失。
<img src="https://static001.geekbang.org/resource/image/6d/ae/6d9e3a6db17b2a4f52120ae67dece3ae.png" alt=""><br>
今天的内容到这里就结束了你能说一下MySQL中都有哪些存储引擎支持事务通过什么命令可以查看它们吗另外你是如何理解事务的特性的
欢迎你在评论区写下你的答案,也欢迎把这篇文章分享给你的朋友或者同事,一起来交流。