事务的4种隔离级别

  • Read Uncommitted

    是隔离级别最低的一种事务级别。

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

    举例:准备好students表的数据,该表仅一行记录:

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

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

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

    当事务A执行完第3步时,它更新了id=1的记录,但并未提交,而事务B在第4步读取到的数据就是未提交的数据。

    随后,事务A在第5步进行了回滚,事务B再次读取id=1的记录,发现和上一次读取到的数据不一致,这就是脏读。

    可见,在Read Uncommitted隔离级别下,一个事务可能读取到另一个事务更新但未提交的数据,这个数据有可能是脏数据。

  • Read Committed

    不可重复读(Non Repeatable Read):在这种隔离级别下,一个事务可能会遇到不可重复读(Non Repeatable Read)的问题。即在一个事务内,多次读同一数据,在这个事务还没有结束时,如果另一个事务恰好修改了这个数据,那么,在第一个事务中,两次读取的数据就可能不一致

    举例:准备好students表的数据:

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

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

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

    当事务B第一次执行第3步的查询时,得到的结果是Alice,随后,由于事务A在第4步更新了这条记录并提交,所以,事务B在第6步再次执行同样的查询时,得到的结果就变成了Bob,因此,在Read Committed隔离级别下,事务不可重复读同一条记录,因为很可能读到的结果不一致。

  • Repeatable Read

    幻读(Phantom Read):在这种隔离级别下,一个事务可能会遇到幻读(Phantom Read)的问题。即在一个事务中,第一次查询某条记录,发现没有,但是,当试图更新这条不存在的记录时,竟然能成功,并且,再次读取同一条记录,它就神奇地出现了。

    举例:准备好students表的数据:

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

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

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

    事务B在第3步第一次读取id=99的记录时,读到的记录为空,说明不存在id=99的记录。随后,事务A在第4步插入了一条id=99的记录并提交。事务B在第6步再次读取id=99的记录时,读到的记录仍然为空,但是,事务B在第7步试图更新这条不存在的记录时,竟然成功了,并且,事务B在第8步再次读取id=99的记录时,记录出现了。

    可见,幻读就是没有读到的记录,以为不存在,但其实是可以更新成功的,并且,更新成功后,再次读取,就出现了

  • Serializable

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

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

  • 默认隔离级别

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

posted @ 2022-06-27 16:37  大于昨天  阅读(1871)  评论(0编辑  收藏  举报