事务详解


Read Uncommitted

Read Uncommitted是隔离级别最低的一种事务级别。在这种隔离级别下,一个事务会读到另一个事务更新后但未提交的数据,如果另一个事务回滚, 那么当前事务读到的数据就是脏数据,这就是脏读(Dirty Read)。

首先,我们准备好student表的数据,该表仅一行记录:

                    mysql> select * from students;
                    +----+-------+
                    | id | name  |
                    +----+-------+
                    |  1 | Alice |
                    +----+-------+
                    1 row in set (0.00 sec)
                

然后,分别开启两个MySQL客户端连接,按顺序依次执行事务A和事务B:

时刻事务A事务B
1SET SESSION transaction_isolation = "READ-UNCOMMITTED";SET SESSION transaction_isolation = "READ-UNCOMMITTED";
2BEGIN;BEGIN;
3UPDATE students SET name = 'Bob' WHERE id = 1;
4SELECT * FROM students WHERE id = 1;
5ROLLBACK;
6SELECT * FROM students WHERE id = 1;
7COMMIT;

通过上述实验,可以验证Read Uncommitted的作用机制。

Read Committed

在Read Committed隔离级别下,一个事务可能会遇到不可重复读(Non Repeatable Read)的问题。

不可重复读是指,在一个事务内,多次读同一数据,在这个事务还没有结束时,如果另一个事务恰好修改了这个数据,那么,在第一个事务中,两次读取的数据就可能不一致。

我们仍然先准备好students表的数据:

                    mysql> select * from students;
                    +----+-------+
                    | id | name  |
                    +----+-------+
                    |  1 | Alice |
                    +----+-------+
                    1 row in set (0.00 sec)
                

然后,分别开启两个MySQL客户端连接,按顺序依次执行事务A和事务B:

时刻事务A事务B
1SET SESSION transaction_isolation = "READ-COMMITTED";SET SESSION transaction_isolation = "READ-COMMITTED";
2BEGIN;BEGIN;
3SELECT * FROM students WHERE id = 1; -- Alice
4UPDATE students SET name = 'Bob' WHERE id = 1;
5COMMIT;
6SELECT * FROM students WHERE id = 1; -- Bob
7COMMIT;

通过上述实验,可以验证Read Committed的作用机制。

Repeatable Read

在Repeatable Read隔离级别下,一个事务可能会遇到幻读(Phantom Read)的问题。

幻读是指,在一个事务中,第一次查询某条记录,发现没有,但是,当试图更新这条不存在的记录时,竟然能成功,并且,再次读取同一条记录,它就神奇地出现了。

我们仍然先准备好students表的数据:

                    mysql> select * from students;
                    +----+-------+
                    | id | name  |
                    +----+-------+
                    |  1 | Alice |
                    +----+-------+
                    1 row in set (0.00 sec)
                

然后,分别开启两个MySQL客户端连接,按顺序依次执行事务A和事务B:

时刻事务A事务B
1SET SESSION transaction_isolation = "REPEATABLE-READ";SET SESSION transaction_isolation = "REPEATABLE-READ";
2BEGIN;BEGIN;
3SELECT * FROM students WHERE id = 99; -- empty
4INSERT INTO students (id, name) VALUES (99, 'Bob');
5COMMIT;
6SELECT * FROM students WHERE id = 99; -- empty
7COMMIT;
8SELECT * FROM students WHERE id = 99; -- Alice
9COMMIT;

通过上述实验,可以验证Repeatable Read的作用机制。

Serializable

Serializable是最严格的隔离级别。在Serializable隔离级别下,所有事务按照次序依次执行,因此,脏读、不可重复读、幻读都不会出现。

虽然Serializable隔离级别下的事务具有最高的安全性,但是,由于事务是串行执行,所以效率会大大下降,应用程序的性能会急剧降低。如果没有特别重要的情景, 一般都不会使用Serializable隔离级别。

默认隔离级别

如果没有指定隔离级别,数据库就会使用默认的隔离级别。在MySQL中,如果使用InnoDB,默认的隔离级别是Repeatable Read。