为什么需要事务处理?
我们前面接触到的增删改查几乎都是单独的进行的操作,但是在实际的开发过程中,我们可能会涉及到一系列的连续对数据库的操作时,例如:某人要从自己的A银行卡往B银行卡转账1000元。正常的流程:
但是,假设在这个过程中,当A银行从账户中扣除了1000元,但是B银行因为某种原因未能收到这1000元的转账?如果当B银行收到了这笔转账,但是往用户账户增加1000元时,发生了故障导致未能增加1000元?我们应该怎么做?肯定有人想,把A银行已经扣除的钱回滚回去,然后等待下一次执行不就行了嘛?所以,事实上的流程应该是这样的:
这样,我们就能保证,当在转账这个流程中出现了任何问题,我们都能让数据回滚到最开始的状态,这种处理方式我们就称为事务处理。也就是说:使用事务处理,就能保证所有的处理要么都成功,要么都失败。在进行多个联合数据的处理时,事务处理是必不可少的。(PS:现实生活中关于转账实际上要比这个复杂很多,我们仅仅是将转账的流程作为一个例子进行举例说明)
使用事务处理需要准备什么?
其实,我们在上一篇文章:就已经有过说明,想要使用处理,我们必须将我们的数据表的存储引擎设置为【InnoDB】
事务处理的特性
事务其实就是并发控制的基本单位;相信我们都知道,事务是一个序列操作,是指作为单个逻辑工作单元执行的一系列操作,其中的操作要么都执行,要么都不执行,它是一个不可分割的工作单位;数据库事务的 ACID 四大特性是事务的基础,了解了 ACID 是如何实现的,我们也就清除了事务的实现,接下来我们将依次介绍数据库是如何实现这四个特性的。
原子性(Atomicity)
一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
一致性(Consistency)
一致性是指事务必须使数据库从一个一致的状态变到另外一个一致的状态,也就是执行事务之前和之后的状态都必须处于一致的状态。
隔离性(Isolation)
隔离性是指当多个用户并发访问数据库时,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)
持久性(Durability)
持久性是指一个事务一旦被提交了,那么对于数据库中的数据改变就是永久性的,即便是在数据库系统遭遇到故障的情况下也不会丢失提交事务的操作。
事务处理
自动提交
在 MySQL 命令行的默认设置下,事务都是自动提交的,也就说用户不用意识此事,执行 SQL 语句后就会马上执行 【COMMIT 】操作。特别是当存储引擎为MyISAM的情况下,因为它本身就是不支持事务处理的。只要执行了命令,所有的命令就会被提交。因此要显式地开启一个事务务须使用命令 【BEGIN】 或 【START TRANSACTION】后,才不会自动提交,只有明确执行了【COMMIT】命令后才会被提交,再次之前也就可以执行【ROLLBACK】这样的命令了。
查看自动提交模式
我们想要查看当前提交的模式可以使用【SELECT】语句,语法如下:
SELECT@@AUTOCOMMIT;
运行结果如下:
mysql> SELECT@@AUTOCOMMIT;
+————–+ | @@AUTOCOMMIT | +————–+ | 1 | +————–+ 1 row in set (0.00 sec)
我们可以看到我们目前自动提交的模式是开启的(0:为开启;1:开启)
修改自动提交模式
修改自动提交模式使用的是【SET】语句:
- 将自动提交模式设置为OFF的语法是:
SET AUTOCOMMIT =0;
- 将自动提交模式设置为ON的语法是:
SET AUTOCOMMIT =1;
简单事务回滚
使用事务回滚主要是使用三个命令,他们的语法如下:
命令 | 解释 |
---|---|
BEGIN(或:START TRANSACTION); | 开启事务 |
COMMIT; | 提交整个事务 |
ROLLBACK; | 回滚到事务开始的状态 |
下面我们就演示一下简单的事务回滚,具体流程如下:
运行流程和结果如下:
mysql> ALTER TABLE user ENGINE = InnoDB;
Query OK, 0 rows affected (0.59 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> SHOW CREATE TABLE user;//修改存储引擎
//查看是否修改成功
+——-+—————————————————————————————————————————————————————————————————————————-+ | Table | Create Table | +——-+—————————————————————————————————————————————————————————————————————————-+ | user | CREATE TABLEuser
(userId
char(5) CHARACTER SET latin1 NOT NULL,name
varchar(30) DEFAULT NULL,sex
char(1) CHARACTER SET latin1 DEFAULT ‘0’, PRIMARY KEY (userId
) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 | +——-+—————————————————————————————————————————————————————————————————————————-+ 1 row in set (0.00 sec) mysql> BEGIN;//开启事务
Query OK, 0 rows affected (0.00 sec) mysql> SELECT * FROM user;//确认数据
+——–+——–+——+ | userId | name | sex | +——–+——–+——+ | 1 | 张三 | 0 | | 2 | 李四 | 1 | | 3 | 李四 | 0 | | 4 | 王五 | 0 | | 5 | 小张 | 1 | | 6 | 张三丰 | 1 | | 7 | NULL | 0 | +——–+——–+——+ 7 rows in set (0.00 sec) mysql> DELETE FROM user;//删除数据
Query OK, 7 rows affected (0.00 sec) mysql> SELECT * FROM user;//查看是否删除成功
Empty set (0.00 sec) mysql> ROLLBACK;//回滚
Query OK, 0 rows affected (0.05 sec) mysql> SELECT * FROM user;//查看是否回滚成功
+——–+——–+——+ | userId | name | sex | +——–+——–+——+ | 1 | 张三 | 0 | | 2 | 李四 | 1 | | 3 | 李四 | 0 | | 4 | 王五 | 0 | | 5 | 小张 | 1 | | 6 | 张三丰 | 1 | | 7 | NULL | 0 | +——–+——–+——+ 7 rows in set (0.00 sec)
可以看到,我们原本已经删除掉的数据就被我们回滚成功了。如果我们将【ROLLBACK】换成【COMMIT】,那么数据就被删除,再也不可恢复。
部分回滚
我们上面介绍的简单事务回滚是回滚到事务最开始的状态,其实在事务的处理过程中,我们可以通过保存节点(SAVEPOINT),来进行事务回滚。流程如下:
运行结果如下:
mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)//开启事务
//插入一条新的数据
mysql> INSERT INTO user VALUE(8,”WANG”,1); Query OK, 1 row affected (0.00 sec) mysql> SELECT * FROM user;//查看是否插入成功
+——–+——–+——+ | userId | name | sex | +——–+——–+——+ | 1 | 张三 | 0 | | 2 | 李四 | 1 | | 3 | 李四 | 0 | | 4 | 王五 | 0 | | 5 | 小张 | 1 | | 6 | 张三丰 | 1 | | 7 | NULL | 0 | | 8 | WANG | 1 | +——–+——–+——+ 8 rows in set (0.00 sec) mysql> SAVEPOINT sk;//保存节点
Query OK, 0 rows affected (0.00 sec) mysql> INSERT INTO user VALUE(9,”HONG”,0);//插入第二条数据
Query OK, 1 row affected (0.00 sec) mysql> SELECT * FROM user;//查看是否插入成功
+——–+——–+——+ | userId | name | sex | +——–+——–+——+ | 1 | 张三 | 0 | | 2 | 李四 | 1 | | 3 | 李四 | 0 | | 4 | 王五 | 0 | | 5 | 小张 | 1 | | 6 | 张三丰 | 1 | | 7 | NULL | 0 | | 8 | WANG | 1 | | 9 | HONG | 0 | +——–+——–+——+ 9 rows in set (0.00 sec) mysql> ROLLBACK TO SAVEPOINT sk;//回到保存节点
Query OK, 0 rows affected (0.00 sec) mysql> SELECT * FROM user;//查看是否回退成功
+——–+——–+——+ | userId | name | sex | +——–+——–+——+ | 1 | 张三 | 0 | | 2 | 李四 | 1 | | 3 | 李四 | 0 | | 4 | 王五 | 0 | | 5 | 小张 | 1 | | 6 | 张三丰 | 1 | | 7 | NULL | 0 | | 8 | WANG | 1 | +——–+——–+——+ 8 rows in set (0.00 sec)
我们可以看到,我们就成功的进行了部分事务的回滚。
注意事项
下面的四条语句在事务处理中是不可进行事务回滚的,也就是这四条语句一旦执行,就立刻被提交
- DROP DATABASE;
- DROP TABLE;
- DROP;
- ALTER TABLE;