事务

# 什么是事务

一个事务其实就是一个完整的业务逻辑。一个事务是一个最小的工作单元。

转账业务:从 A 账户向 B 账户转账 10000,需要将 A 账户的钱减去 10000 ,将 B 账户的钱加上 10000,这就是一个完整的业务逻辑。

只有 insert update delete 这三个与事务才有关系,其他的与事务都没有关系。如果只有一句 DML 语句就没必要使用事务。

说到底,一个事务其实就是多条 DML 语句同时成功,或者同时失败。事务就是批量的 DML 语句。

# 事务是如何处理 DML 同时成功或者失败

事务开启了
...
insert 
update
delete
...
事务结束了
1
2
3
4
5
6
7

在事务的执行过程中,每一条 DML 的操作都会记录到 事务性活动的日志文件中。在事务的执行过程中,我们可以提交事务,也可以回滚事务。

提交事务:清空事务性活动的日志文件,将数据全部彻底持久化到数据库表中。提交事务标志着事务的结束,并且是一种全部成功的结束。

回滚事务:将之前所有的 DML 操作全部撤销,并且清空事务性活动的日志文件。回滚事务标志着事务的结束,并且是一种全部失败的结束。

# 怎么提交事务,怎么回滚事务

提交事务:commit 语句。

回滚事务:rollback 语句。回滚永远只能回滚到上一次的提交点。

mysql 默认情况下是支持自动提交事务的。每执行一次 DML 语句就自动提交一次事务。

start transaction 手动开启事务,从而关闭默认事务。

mysql> desc test;
+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id    | int(11)      | NO   | PRI | NULL    |       |
| name  | varchar(255) | NO   |     | NULL    |       |
+-------+--------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test values(1,'sss');
Query OK, 1 row affected (0.00 sec)

mysql> insert into test values(2,'sss');
Query OK, 1 row affected (0.00 sec)

mysql> insert into test values(3,'sss');
Query OK, 1 row affected (0.00 sec)

mysql> insert into test values(4,'sss');
Query OK, 1 row affected (0.00 sec)

mysql> select * from test;
+----+------+
| id | name |
+----+------+
|  1 | sss  |
|  2 | sss  |
|  3 | sss  |
|  4 | sss  |
+----+------+
4 rows in set (0.00 sec)

mysql> rollback;
Query OK, 0 rows affected (0.01 sec)

mysql> select * from test;
Empty set (0.00 sec)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

上面例子未执行提交操作,回滚就成功了。

一旦 commit 后 rollback 就无法回滚了。

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test values(1,'sss');
Query OK, 1 row affected (0.00 sec)

mysql> insert into test values(2,'sss');
Query OK, 1 row affected (0.00 sec)

mysql> insert into test values(3,'sss');
Query OK, 1 row affected (0.00 sec)

mysql> insert into test values(4,'sss');
Query OK, 1 row affected (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.01 sec)

mysql> select * from test;
+----+------+
| id | name |
+----+------+
|  1 | sss  |
|  2 | sss  |
|  3 | sss  |
|  4 | sss  |
+----+------+
4 rows in set (0.00 sec)

mysql> rollback;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test;
+----+------+
| id | name |
+----+------+
|  1 | sss  |
|  2 | sss  |
|  3 | sss  |
|  4 | sss  |
+----+------+
4 rows in set (0.00 sec)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

上面例子 commit 后 rollback 就无法回滚了。

# 事务包括四个特性

  • 原子性 A:说明事务是最小的工作单元,不可再分。
  • 一致性 C:所有事务要求,在同一个事务当中,所有操作必须同时成功或者同时失败,以保证数据的一致性。
  • 隔离性 I:A事务和B事务之间具有一定的隔离。相当于多线程并发访问处理一张表。
  • 持久性 D:事务最终结束的一个保障,事务提交,就相当于将没有保存到硬盘上的数据保存操硬盘上。

# 事务的隔离性

A 教室和 B 教室中间有一道墙,这道墙可以薄也可以厚,这就是隔离级别,墙越厚隔离级别就越高。

事务之间的隔离级别有 4 级:

  • 读未提交:read uncommitted 最低级别。这种隔离级别一般都是理论上的。大多数数据库隔离级别都高于这个级别。

    存在问题:事务 A 可以读取到事务 B 未提交的数据。这样存在脏读现象。

  • 读已提交:read committed 事务 A 只能读取到事务 B 提交之后的数据。oracle 数据库默认的级别。

    存在问题:每次读到的数据是绝对的真实,存在不可重复读取数据问题。

    解决问题:解决了数据脏读现象。

  • 可重复读:repeatable read 事务 A 开启之后,不管是多久,每一次在事务 A 中读取到的数据读取到的数据还是没有发生改变,这就是可重复读取。mysql 默认的隔离级别。

    解决问题:解决了不可重复读取数据的问题。

    存在问题:可能出现幻影现象。每次读取到的数据可能不是真实数据。

  • 序列化/串行化:serializable 最高级别。

    存在问题:效率最低。表示事务排队,不能并发。

    解决问题:每次读取的是最真实的数据。

# 验证 read uncommitted 这种隔离级别

开启两个 cmd 终端。
都开启事务。
一个终端给表插入数据后不执行提交操作,另一个终端使用查询语句即可查到前面终端插入的数据。
最终得出结论:存在脏读现象。
1
2
3
4

# 验证 read committed 这种隔离级别

开启两个 cmd 终端。cmd1 和 cmd2。
cmd1 和 cmd2 都开启事务。
cmd1 先执行查询操作,cmd2 再执行插入数据操作,cmd1 先执行查询操作,cmd2 再执行 commit 操作,cmd1 先执行查询操作,
最终得出结论:只有提交过的事务才能被另一个终端查询到数据。
1
2
3
4

# 验证 repeatable read 这种隔离级别

开启两个 cmd 终端。cmd1 和 cmd2。
cmd1 和 cmd2 都开启事务。
cmd1 先执行查询操作,cmd2 连续执行多次插入数据操作,cmd2 执行 commit 操作,cmd2 再执行查询操作,cmd1 先执行查询操作,
最终得出结论:cmd1查询到的数据永远是第一次查询的数据。
1
2
3
4

# 验证 serializable 这种隔离级别

开启两个 cmd 终端。cmd1 和 cmd2。
cmd1 和 cmd2 都开启事务。
cmd1 先执行查询操作,cmd1 执行插入数据操作, cmd2 执行查询操作会发现光标一闪一闪的卡住了,其实是在等待 cmd1 执行 commit 操作。当 cmd1 执行 commit 后,cmd2 窗口立即显示出了查询结果。
最终得出结论:所有终端的操作是排队的,也就是串行执行。
1
2
3
4
上次更新: 2025/02/10, 20:20:37
最近更新
01
vue3尚硅谷课件
04-26
02
Git问题集合
01-29
03
安装 Nginx 服务器
01-25
更多文章>
×
×