目录
- 1、背景
- 2、事务的特性
- 3、事务之间的几种错误
- 【1】脏读
- 【2】不可重复读
- 【3】幻读
- 4、事务中的隔离级别
- 5、总结
1、背景
事务是存储引擎层面实现的,有的引擎支持事务,有的引擎不支持事务,我们常用的引擎InnoDB就支持事务,本文大概讲解一下事务的特性,后续再讲解事务的MVCC(多版本并发控制)如何实现的。
2、事务的特性
事务的特性满足ACID,A(Atomicity):原子性、C(Consistency):一致性、I(Isolation):隔离性、D(Durability):持久性。
1、原子性:一组操作要么全部成功要么全部失败。
2、一致性:数据库从一个有效的状态变为另一个有效的状态。
3、隔离性:事务间的影响程度,四种级别分别为读未提交、读已提交、可重复读、串行化。
4、持久性:事务提交之后就会永久生效,也就是将数据写入磁盘发生异常能恢复。
3、事务之间的几种错误
假设有张表如下:
CREATE TABLE student
(id INT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(255) NOT NULL DEFAULT '' COMMENT '姓名'
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
向其中插入一条数据,后面讲的几种错误都以这条记录作为初始条件:
mysql [xxx]> INSERT INTO student (name) VALUES ('张三');
Query OK, 1 row affected (0.001 sec)mysql [xxx]> SELECT * from student;
+----+--------+
| id | name |
+----+--------+
| 1 | 张三 |
+----+--------+
1 row in set (0.000 sec)
【1】脏读
事务A读取到了事务B未提交修改之后的数据,事务B发生了回滚,此时事务A读取到的数据就是"脏数据",这就叫脏读,举个例子理解:
时间顺序 | 事务A | 事务B |
---|---|---|
t1 | 开始事务 | 开始事务 |
t2 | UPDATE student SET name = ‘李四’ WHERE id = 1; [将"张三"更新为"李四"] | |
t3 | SELECT name FROM student WHRER id =1; [此时读取到的name为"李四"] | |
t4 | 提交事务 | |
t5 | 回滚事务 [此时name的值变为"张三"] |
数据库中原本的name值为"张三",t2时刻事务B将name更新为"李四",t3时刻事务A读取到name为"李四",t4时刻事务A提交,t5时刻事务B发送回滚,此时name的值变为"张三",但是事务A读取到的name还是为"李四",这就是读到了一个无效的数据,叫做"脏读"。
【2】不可重复读
事务A只能读取已提交事务的结果,多个已提交事务结果的不同,事务A就可能读到不同的结果,这就叫不可重复读,示例如下:
时间顺序 | 事务A | 事务B | 事务C |
---|---|---|---|
t1 | 开始事务 | 开始事务 | 开始事务 |
t2 | SELECT name FROM student WHERE id = 1; [此时读取到的name为"张三"] | ||
t3 | UPDATE student SET name = ‘李四’ WHERE id =1; [将"张三"更新为"李四"] | ||
t4 | 提交事务 [此时name为"李四"] | ||
t5 | SELECT name FROM student WHERE id = 1; [此时读取到的name为"李四"] | ||
t6 | UPDATE student SET name= ‘王五’ WHERE id =1; [将"李四"更新为"王五"] | ||
t7 | 提交事务 [此时name为"王五"] | ||
t8 | SELECT name FROM student WHERE id = 1; [此时读取到的name为"王五"] | ||
t9 | 提交事务 |
上面t2、t5、t8时刻事务A读取的name分别为:“张三”、“李四”、“王五”。一个事务三次读取结果都不一样,这就叫"不可重复读"。
【3】幻读
事务A两次根据某个条件查询两次的结果由于中途事务B进行了插入导致两次查询结果不一致,这就叫幻读。示例如下:
时间顺序 | 事务A | 事务B |
---|---|---|
t1 | 开始事务 | 开始事务 |
t2 | SELECT name FROM student WHERE id < 10; [查出来的只有"张三"] | |
t3 | INSERT INTO student (name) VALUES (‘李四’); [新插入一条name为"李四"的记录] | |
t4 | 提交事务 [此时表中有name为"张三"、"李四"两条记录] | |
t5 | SELECT name FROM student WHERE id < 10; [查出来的有"张三"、“李四”] | |
t6 | 提交事务 |
事务A种t2时刻和t3时刻查询条件相同,但t3时刻多出来一条记录,这就叫做"幻读"。
4、事务中的隔离级别
事务中有4种隔离级别
READ UNCOMMITTED | 未提交读 |
READ COMMITTED | 已提交读 |
REPEATABLE READ | 可重复读 |
SERIALIZABLE | 可串行化 |
查看mysql中设置的隔离级别如下:
mysql [xxx]> SELECT @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+
1 row in set (0.016 sec)
每个隔离级别对应事务之间的问题如下:
脏读 | 不可重复读 | 幻读 | |
---|---|---|---|
未提交读 | yes | yes | yes |
已提交读 | no | yes | yes |
可重复读 | no | no | yes |
可串行化 | no | no | no |
未提交读每次都会读取最新的结果;已提交读每次读取的都是已提交事务的结果;可重复读会对所读的行进行加锁,保证读取行的时候不会被其它事务修改;虽然可串行化事务之间不会有并发问题,但是执行效率也是最低的,因为要给每一行去加锁。
5、总结
本文对事务的特性和事务之间的问题有了个大概的认知,后面会再细节讲解事务的回滚机制和MVCC实现原理。