SQL事务隔离级别的关键词和实例全在这

事务的ACID属性

  1. 原子性(Atomicity)

    原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

  2. 一致性(Consistency)

    事务必须使数据库从一个一致性状态变换到另一个一致性状态。

  3. 隔离性(Isolation)

    事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

  4. 持久性(Durability)

    持久性是指一个事务一旦被提交,它对数据库中的改变就是永久的,接下来的其他操作和数据库故障不应该对其有任何影响。.

数据库的并发问题

  • 对于同时运行的多个事务,当这些事务访问数据库中相同的数据时,如果没有采取必要的隔离机制,就会导致各种并发问题:

    • 脏读:对于两个事务T1和T2, T1 读取了已经被 T2更新但还没有被提交的字段。之后,若T2回滚,T1读取的内容就是临时且无效的。

    • 不可重复读:对于两个事务T1和T2, T1读取了一个字段,然后 T2 更新 了该字段。之后,T1 再次读取同一个字段,值就不同了。

    • 幻读:对于两个事务T1和T2, T1 从表中读取了一个字段,然后 T2 在该表中 插入 了一些新的行。之后,如果 T1 再次读取同一个表,就会多出几行。

  • 数据库事务的隔离性:数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题。

  • 一个事务与其他事务隔离的程度称为隔离级别。数据库规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性就越弱。

四种隔离级别

  • READ UNCOMMITTED(读未提交数据)

    允许事务读取未被其他事务提交的变更

    脏读,不可重复读和幻读 都会出现

  • READ COMMITED(读已提交数据)

    只允许事务读取已经被其他事务提交的变更

    可以避免 脏读 ,但 不可重复读和幻读 仍可能出现

  • REPEATABLE READ(可重复读)

    确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其他事务对这个字段进行更新

    可以避免 脏读和不可重复读,但仍可能出现 幻读

  • SERIALIZABLE(串行化)

    确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入,更新和删除操作

    所有并发问题 都可以避免,但性能十分低下


下面通过代码验证一下四种隔离级别

首先我们通过sql语句查询一下mysql默认的隔离级别

#查询mysql当前的隔离级别 
select @@tx_isolation;  #mysql5.7版本的
select @@transcation_isolation; #mysql8.0版本后的

SQL事务隔离级别的关键词和实例全在这

通过查询得知,mysql默认的隔离级别是 可重复读,也就是说 mysql 默认解决了 脏读 和 不可重复读


那我们接下来将事务隔离级别切换

#将事务隔离级别切换为所写隔离级别
set global transaction isolation level 隔离级别;

首先切换为 READ UNCOMMITTED(读未提交)

SQL事务隔离级别的关键词和实例全在这

然而此时当我们查看当前隔离级别,却并未发生改变

SQL事务隔离级别的关键词和实例全在这

在修改隔离级别后,我们需要重新登录mysql数据库

SQL事务隔离级别的关键词和实例全在这

此时数据库的隔离级别就切换过来了

特别注意,在测试事务隔离级别前要先将 自动提交 设为 false

#将事务自动提交关闭
set autocommit= false;

SQL事务隔离级别的关键词和实例全在这

此时我们登录两个用户,分别对同一个表进行查询,读取到数据

SQL事务隔离级别的关键词和实例全在这

我们对左边数据库表的数据进行修改,然后再进行查询

SQL事务隔离级别的关键词和实例全在这

此时还未进行提交事务,然后我们再对左边进行查询

SQL事务隔离级别的关键词和实例全在这

发现右边数据库表的数据也发生了改变

两边对照

SQL事务隔离级别的关键词和实例全在这

然而当左边进行回滚,右边也变回了之前的数,代表刚刚读取的数据是无效的

SQL事务隔离级别的关键词和实例全在这

这种情况是不应该出现的

接下来,我们再切换到 READ COMMITED(读已提交数据)

SQL事务隔离级别的关键词和实例全在这

切换完记得 重新登录

同样还是查询同一个表的同一行数据

SQL事务隔离级别的关键词和实例全在这

此时先对左边数据库表的数据进行修改,同时对右边表的数据进行查询

SQL事务隔离级别的关键词和实例全在这

可以看到,这次左边未进行事务提交,右边并没有查询出修改的数据,证明脏读问题已经被避免

当左边进行事务提交,右边再次进行查询

SQL事务隔离级别的关键词和实例全在这

在左边提交事务过后,右边便能查询到修改后的数据,然而此时右边还处在其事务中,也就是说出现了不可重复读的问题

不过相比脏读,这种问题已经是可以接受的了,

因而在Oracle中的默认事务隔离级别就是 READ COMMITTED(读已提交)

接下来再看REPEATABLE READ(可重复读)

SQL事务隔离级别的关键词和实例全在这

首先还是先查询一下两边同一表的同一行数据

SQL事务隔离级别的关键词和实例全在这

然后对左边表的数据进行修改,随后查询右边表的数据

SQL事务隔离级别的关键词和实例全在这

可以看到,这次不管左边是否进行事务提交,右边查询数据都不会受到影响

只有当右边 也进行 事务提交,右边才会查询到更新的数据

SQL事务隔离级别的关键词和实例全在这

也就是说 可重复读 隔离级别避免了 脏读 和 不可重复读 两个问题

接下来两边都对表中所有数据进行查询

SQL事务隔离级别的关键词和实例全在这

此时左边新添加一条数据并提交事务,然后右边再次进行查询

SQL事务隔离级别的关键词和实例全在这

可以看到,右边在自己的 事务提交前 也是不会查询到新插入的事务的,只有在 提交事务后 才能查询到

也就是说,可重复读 隔离级别除了 脏读 和 不可重复读 问题外,还尽可能避免了 幻读,但也只是尽可能,仍有出现幻读的风险,比如在新插入的数据的主键重复时。

SQL事务隔离级别的关键词和实例全在这

比如说这样,当我们插入重复主键的数据时,mysql就会报错,告诉我们主键重复。也就是说虽然我们在当前事务查询不到新添加的数据,但其实该数据已经存在于表中了,因为该数据看不见却摸得着,像虚幻的一样,所以称为幻读

也就是说实际上可重复读隔离界别还是没有避免幻读

至于 SERIALIZABLE(串行化),在可重复读的基础上成功避免了幻读,原理为额外增添了锁,使得一次只进行一个事务,该事务提交后才进行其他事务,在提交前,其他事务会被阻塞

虽然隔离级别越高,数据的一致性就越好,但其并发度也就越弱,因而并不是隔离级别越高越好,要根据实际开发需求进行取舍