事务隔离级别(图文详解)

该文已加入笔主的开源项目——JavaGuide(一份涵盖大部分Java程序员所须要掌握的核心知识的文档类项目),地址:github.com/Snailclimb/…。以为不错的话,记得点个Star。mysql

本文由 SnailClimbBugSpeak 共同完成。git

事务隔离级别(图文详解)

什么是事务?

事务是逻辑上的一组操做,要么都执行,要么都不执行。程序员

事务最经典也常常被拿出来讲例子就是转帐了。假如小明要给小红转帐1000元,这个转帐会涉及到两个关键操做就是:将小明的余额减小1000元,将小红的余额增长1000元。万一在这两个操做之间忽然出现错误好比银行系统崩溃,致使小明余额减小而小红的余额没有增长,这样就不对了。事务就是保证这两个关键操做要么都成功,要么都要失败。github

事物的特性(ACID)

  1. 原子性: 事务是最小的执行单位,不容许分割。事务的原子性确保动做要么所有完成,要么彻底不起做用;
  2. 一致性: 执行事务先后,数据保持一致;
  3. 隔离性: 并发访问数据库时,一个用户的事物不被其余事物所干扰,各并发事务之间数据库是独立的;
  4. 持久性: 一个事务被提交以后。它对数据库中数据的改变是持久的,即便数据库发生故障也不该该对其有任何影响。

并发事务带来的问题

在典型的应用程序中,多个事务并发运行,常常会操做相同的数据来完成各自的任务(多个用户对统一数据进行操做)。并发虽然是必须的,但可能会致使一下的问题。面试

  • 脏读(Dirty read): 当一个事务正在访问数据而且对数据进行了修改,而这种修改尚未提交到数据库中,这时另一个事务也访问了这个数据,而后使用了这个数据。由于这个数据是尚未提交的数据,那么另一个事务读到的这个数据是“脏数据”,依据“脏数据”所作的操做多是不正确的。
  • 丢失修改(Lost to modify): 指在一个事务读取一个数据时,另一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,所以称为丢失修改。 例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。
  • 不可重复读(Unrepeatableread): 指在一个事务内屡次读同一数据。在这个事务尚未结束时,另外一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,因为第二个事务的修改致使第一个事务两次读取的数据可能不太同样。这就发生了在一个事务内两次读到的数据是不同的状况,所以称为不可重复读。
  • 幻读(Phantom read): 幻读与不可重复读相似。它发生在一个事务(T1)读取了几行数据,接着另外一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些本来不存在的记录,就好像发生了幻觉同样,因此称为幻读。

不可重复度和幻读区别:算法

不可重复读的重点是修改,幻读的重点在于新增或者删除。sql

例1(一样的条件, 你读取过的数据, 再次读取出来发现值不同了 ):事务1中的A先生读取本身的工资为 1000的操做还没完成,事务2中的B先生就修改了A的工资为2000,导 致A再读本身的工资时工资变为 2000;这就是不可重复读。数据库

例2(一样的条件, 第1次和第2次读出来的记录数不同 ):假某工资单表中工资大于3000的有4人,事务1读取了全部工资大于3000的人,共查到4条记录,这时事务2 又插入了一条工资大于3000的记录,事务1再次读取时查到的记录就变为了5条,这样就致使了幻读。多线程

事务隔离级别

SQL 标准定义了四个隔离级别:并发

  • READ-UNCOMMITTED(读取未提交): 最低的隔离级别,容许读取还没有提交的数据变动,可能会致使脏读、幻读或不可重复读
  • READ-COMMITTED(读取已提交): 容许读取并发事务已经提交的数据,能够阻止脏读,可是幻读或不可重复读仍有可能发生
  • REPEATABLE-READ(可重读): 对同一字段的屡次读取结果都是一致的,除非数据是被自己事务本身所修改,能够阻止脏读和不可重复读,但幻读仍有可能发生。
  • SERIALIZABLE(可串行化): 最高的隔离级别,彻底服从ACID的隔离级别。全部的事务依次逐个执行,这样事务之间就彻底不可能产生干扰,也就是说,该级别能够防止脏读、不可重复读以及幻读

MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)。咱们能够经过SELECT @@tx_isolation;命令来查看

mysql> SELECT @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
复制代码

这里须要注意的是:与 SQL 标准不一样的地方在于InnoDB 存储引擎在 **REPEATABLE-READ(可重读)事务隔离级别下使用的是Next-Key Lock 锁算法,所以能够避免幻读的产生,这与其余数据库系统(如 SQL Server)是不一样的。因此说InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读) 已经能够彻底保证事务的隔离性要求,即达到了 SQL标准的SERIALIZABLE(可串行化)**隔离级别。

由于隔离级别越低,事务请求的锁越少,因此大部分数据库系统的隔离级别都是READ-COMMITTED(读取提交内容):,可是你要知道的是InnoDB 存储引擎默认使用 **REPEATABLE-READ(可重读)**并不会有任何性能损失。

InnoDB 存储引擎在 分布式事务 的状况下通常会用到**SERIALIZABLE(可串行化)**隔离级别。

实际状况演示

在下面我会使用 2 个命令行mysql ,模拟多线程(多事务)对同一份数据的脏读问题。

MySQL 命令行的默认配置中事务都是自动提交的,即执行SQL语句后就会立刻执行 COMMIT 操做。若是要显式地开启一个事务须要使用命令:START TARNSACTION

咱们能够经过下面的命令来设置隔离级别。

SET [SESSION|GLOBAL] TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]
复制代码

咱们再来看一下咱们在下面实际操做中使用到的一些并发控制语句:

  • START TARNSACTION |BEGIN:显式地开启一个事务。
  • COMMIT:提交事务,使得对数据库作的全部修改为为永久性。
  • ROLLBACK 回滚会结束用户的事务,并撤销正在进行的全部未提交的修改。

脏读(读未提交)

避免脏读(读已提交)

不可重复读

仍是刚才上面的读已提交的图,虽然避免了读未提交,可是却出现了,一个事务尚未结束,就发生了 不可重复读问题。

可重复读

防止幻读(可重复读)

一个事务对数据库进行操做,这种操做的范围是数据库的所有行,而后第二个事务也在对这个数据库操做,这种操做能够是插入一行记录或删除一行记录,那么第一个是事务就会以为本身出现了幻觉,怎么还有没有处理的记录呢? 或者 怎么多处理了一行记录呢?

幻读和不可重复读有些类似之处 ,可是不可重复读的重点是修改,幻读的重点在于新增或者删除。

参考

专一Java知识和面试技能分享!我已经整理好了一份Java 学习必备的书籍+视频+文档汇总,内容比较多,你能够在公众号后台回复关键“1”,我会免费无套路把这些都给你。

个人公众号
相关文章
相关标签/搜索