事务的 4 个隔离级别

一、事务的 4 个隔离级别

  • 未提交读(Read Uncommitted):事务可以读取未提交的数据,也称作脏读(Dirty Read)。一般很少使用。

  • 提交读(Read Committed):是大都是 DBMS (如:Oracle, SQLServer)默认事务隔离。执行两次同意的查询却有不同的结果,也叫不可重复读。

  • 可重复读(Repeable Read):是 MySQL 默认事务隔离级别。能确保同一事务多次读取同一数据的结果是一致的。可以解决脏读的问题,但理论上无法解决幻读(Phantom Read)的问题。

  • 可串行化(Serializable):是最高的隔离级别。强制事务串行执行,会在读取的每一行数据上加锁,这样虽然能避免幻读的问题,但也可能导致大量的超时和锁争用的问题。很少会应用到这种级别,只有在非常需要确保数据的一致性且可以接受没有并发的应用场景下才会考虑。

    solation Level 脏读可能性(Dirty Read) 不可重复读可能性(Non Repeatable Read) 幻读可能性(Phantom Read)
    ead Uncommitted Yes Yes Yes
    ead Committed - Yes Yes
    epeatable Read - - Yes
    erializable - - -

二、测试:模拟异常发生

我们通过两个客户端(A 和 B)测试模拟一下这三种异常(脏读、不可重复读和幻读)。

  • 准备工作

    1、首先我们操作客户端 A 查看当前隔离级别。

    事务的 4 个隔离级别

    2、可以看到当前的隔离级别是 REPEATABLE-READ,也就是可重复读。那么接下来我们要把它切换为未提交读(READ UNCOMMITTED)方便测试。

    事务的 4 个隔离级别

    3、切换完成后,我们还需要设置 autocommit 参数为 0 ,因为 MySQL 是默认自动提交事务的。

    事务的 4 个隔离级别

    4、然后我们操作客户端 B 以刚才客户端 A 同样的操作实现切换隔离级别为 READ UNCOMMITTED 和 autocommit 为 0。

    5、最后准备一个数据表测试数据就完成了准备工作,我这里新建了个students数据表用来测试。

    事务的 4 个隔离级别

  • 脏读

    事务 A 往测试表新增了一条数据,而事务 B 查询测试表时,读到事务 A 新增了却还未提交的数据。如果事务 A 回滚,那么事务 B 读到的数据就是脏数据。

    步骤 事务 A 事务 B 注释
    1 BEGIN; BEGIN; 开启事务。
    2 INSERT INTO students VALUES (5, ‘赵六’); 事务 A 新增一条数据。
    3 SELECT * FROM students; 事务 B 读到了事务 A 新增却还没提交的数据。
    4 ROLLBACK; 要是这时事务 A 回滚操作。
    5 SELECT * FROM students; 事务 B 再次查询发现已经没有了「赵六」的数据。
    6 COMMIT; 提交事务。
    • 事务 A 回滚前,事务 B 查询到的结果。

      事务的 4 个隔离级别

    • 事务 A 回滚后,事务 B 查到的结果。

      事务的 4 个隔离级别

  • 不可重复读

    同一事务两次查询读取同一数据,得到的结果不一致。

    步骤 事务 A 事务 B 注释
    1 BEGIN; BEGIN; 开启事务。
    2 SELECT name FROM WHERE id = 1; 事务 B 查询 id = 1 的是「张三」。
    3 UPDATE students SET name = ‘张三三’ WHERE id = 1; 事务 A 修改 id = 1 为「张三三」。
    4 COMMIT; 提交事务 A。
    5 SELECT name FROM WHERE id = 1; 事务 B 再次查询 id = 1 的已经改成「张三三」。
    COMMIT; 提交事务 B。
    • 事务 A 修改数据前,事务 B 查到的结果。

      事务的 4 个隔离级别

    • 事务 A 修改数据后,事务 B 查到的结果。

      事务的 4 个隔离级别

  • 幻读

    同一事务两次查询同一范围数据,得到的结果不一致。幻读是不可重复读的一种特殊情景,事务 A 查询某条件范围的数据,而事务 B 正在修改此条件范围的数据。

    步骤 事务 A 事务 B 注释
    1 BEGIN; BEGIN; 开始事务。
    2 SELECT * FROM students WHERE id BETWEEN 1 AND 5; 事务 B 查询某范围内的数据。
    3 INSERT INTO students VALUES (4, ‘赵六’); 事务 A 新增一条数据。
    4 COMMIT; 提交事务 A
    5 SELECT * FROM students WHERE id BETWEEN 1 AND 5; 事务 B 再次查询发现数据发生更改。
    • 事务 A 添加数据前,事务 B 查到的结果。

      事务的 4 个隔离级别

    • 事务 A 添加数据后,事务 B 查到的结果。

      事务的 4 个隔离级别

三、异常小结

  • 脏读:读取到了其他事务还没提交的数据。

  • 不可重复读:两次查询同一条数据有不同的结果,因为其它事务可能在UPDATE操作。

  • 幻读:事务 B 根据条件查询到了 N 条数据,但这时事务 A INSERTDELETE了 M 条符合事务 B 查询条件的数据。事务 B 再次查询结果就和上一次不一致了,得到了 N+M 条数据。

    不可重复读和幻读的区别

    • 不可重复读:是同一数据内容被修改了,是 UPDATE操作。
    • 幻读:某一个范围内的数据行数变多了或变少了,是 INSERTDELETE 操作。