30 SQL 事务2
1. 前言
在上一小节中,我们介绍了事务的概念和基本使用,探讨了事务的四大特性,本小节我们将更加深入的学习事务。
在实际的生产环境中,偶尔会遇到大量并发访问的情况;大量的并发会导致数据的竞争,从而引起一系列的并发问题。
本小节,我们将一起学习 SQL 的4
种事务隔离机制,以及与之对应的3
种并发异常。
本小节测试数据如下,请先在数据库中执行,本小节的所有操作若无特殊说明默认在 MySQL 中执行。
DROP TABLE IF EXISTS imooc_user;
CREATE TABLE imooc_user
(
id int PRIMARY KEY,
username varchar(20),
age int
);
INSERT INTO imooc_user(id,username,age)
VALUES (1,'peter',18),(2,'pedro',24),(3,'jerry',22);
2. 并发异常
SQL 标准共定义了 3 种并发异常,这三种异常分别是脏读(Dirty Read)、不可重复读(Nnrepeatable Read)和幻读(Phantom Read)。
这3种异常比较抽象,我们直接以一个小例子来讲解。
2.1 脏读
某一天,小王正在访问数据库,开启了一个事务,且向 imooc_user 表中插入名为tom
的用户,如下:
BEGIN;
INSERT INTO imooc_user(id,username,age) VALUES (NULL,'tom',27);
此时,小王还未提交事务,但小李却在此时访问了数据库,并且查询了imooc_user 表,如下:
SELECT * FROM imooc_user;
小李看到了如下结果:
+----+----------+-----+
| id | username | age |
+----+----------+-----+
| 4 | tom | 27 |
+----+----------+-----+
小王明明还没有提交事务,但小李却已经看到了小王操作的结果。试想一下,如果此时小王回滚了事务,那么对于小王来说,他就看到了错误的数据,我们称之为脏读
。
流程图如下:
2.2 不可重复读
第二天,小王又在访问数据库了,他查看了 imooc_user 表中 id 为1
的用户,如下:
SELECT * FROM imooc_user WHERE id = 1;
小王看到的结果是这样的:
+----+----------+-----+
| id | username | age |
+----+----------+-----+
| 1 | peter | 18 |
+----+----------+-----+
此时,小李也来访问数据库,他开启了一个事务,并且修改了 id 为1
的用户,如下:
BEGIN;
UPDATE imooc_user SET age = 100 WHERE id = 1;
这个时候,小王又查看了一次 id 为1
的用户。
SELECT * FROM imooc_user WHERE id = 1;
但是他看到的结果却是这样的:
+----+----------+-----+
| id | username | age |
+----+----------+-----+
| 1 | peter | 100 |
+----+----------+-----+
小王发现自己两次查询的数据不一样,可是小李的事务还未提交。像小王这样,两次查询结果不同的情况,我们称之为不可重复读
。
流程图如下:
2.3 幻读
第三天,小王开始了一个事务,并向 imooc_user 表中插入了一个名为mary
的用户,如下:
BEGIN;
INSERT INTO imooc_user(id,username,age) VALUES (NULL,'mary',17);
此时,搞事的小李也来访问数据库,小李查询了一下 imooc_user 表,如下:
SELECT * FROM imooc_user;
小李看到了如下结果:
+----+----------+-----+
| id | username | age |
+----+----------+-----+
| 1 | peter | 100 |
| 2 | pedro | 24 |
| 3 | jerry | 22 |
| 4 | tom | 27 |
| 5 | mary | 17 |
+----+----------+-----+
小李此时已经看到了新增的用户 mary 了,但是小王后悔了,他不想创建 mary 了,于是小王回滚了事务:
ROLLBACK;
但小李却看到了 mary,他一度以为自己出现了幻觉,我们把这种情况称之为幻读
。
流程图如下:
我们总结一下这三种异常的特点:
- 脏读:读到了其它事务还未提交的数据;
- 不可重复读:两次读到了同一数据的不同结果;
- 幻读:读到了其它事务新增但未提交的数据,而且新增数据并未成功。
3. 隔离级别
介绍了常见的 3 种并发异常后,我们再来介绍 4 种隔离机制。
SQL事务的四种隔离机制主要是为了解决上述的三种并发异常,它们之间的关系如下表所示:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(READ UNCOMMITTED ) | 允许 | 允许 | 允许 |
读已提交(READ COMMITTED) | 禁止 | 允许 | 允许 |
可重复读(REPEATABLE READ) | 禁止 | 禁止 | 允许 |
可串行化(SERIALIZABLE) | 禁止 | 禁止 | 禁止 |
上面的隔离级别由上往下,级别依次会提高,但消耗的性能也会依次提高。我们总结一下四种隔离级别:
- 读未提交:允许读未提交数据,可能会发生脏读、不可重复读和幻读异常;
- 读已提交:只能读已经提交的数据,避免了脏读,但可能会出现不可重复读和幻读;
- 可重复读:即能保证在一个事务中多次读取,数据一致,但可能会出现幻读;
- 可串行化:最高的隔离级别,串行的执行事务,可以避免 3 种异常,但性能耗损最高。
提示: SQL Server 和 Oracle 的默认隔离级别是
读已提交
,而 MySQL 的默认隔离级别是可重复读
。
鱼和熊掌不可而得兼!因此 SQL 提供了 4 种事务隔离级别,在数据吞吐能力和数据安全中,你需要作出相应的选择。
通过如下语句你可以设置事务隔离级别:
SET SESSION TRANSACTION ISOLATION LEVEL [level];
其中level
表示隔离级别,如:READ UNCOMMITTED。
4. 小结
事务隔离级别
以及并发异常
是数据库面试中的重点,请熟练掌握并总结。- 在实际开发中,
事务
的使用十分频繁,当然你不会手写这些 SQL 语句,会有相应的框架来帮你处理,但你仍然需要弄懂它们的原理,当出现问题时,你才可以迅速解决。