MySQL45讲笔记-事务隔离级别,为什么你改了数据我看不见

简单来说,事务就是要保证一组数据库操作,要么全部成功,要么全部失败。在MySQL中,事务至此是在引擎层实现的,但并不是所有的MySQL引擎都支持事务,这也是MyISAM被InnoDB取代的原因之一。

隔离性与隔离级别

提到事务,想到的是ACID(原子性,一致性,隔离性,持久性)

  • 原子性:原子性操作就是这个事物执行要么成功,要么失败。将整个过程看作是一个不可分割的整体。

  • 一致性:一致性指的是一个事务在执行前后其状态一致。比如你和小明各有100元,无论你俩之间相互借还多少钱加起来都是200.这就是事务的一致性。

  • 持久性:对于事务来讲,事务一旦提交他对数据库所做的改变是永久的,即使数据库故障也不会产生干扰。

  • 隔离性。

当数据库上有多个事务同时执行的时候,就可能出现脏读、不可重复读、幻读的问题,为了解决这些问题,就有了事务的隔离级别的概念。

  • 脏读:事务 A 读取了事务 B 更新后的数据,但是事务 B 没有提交,然后事务 B 执行回滚操作,那么事务 A 读到的数据就是脏数据
  • 不可重复读::事务 A 进行多次读取操作,事务 B 在事务 A 多次读取的过程中执行更新操作并提交,提交后事务 A 读到的数据不一致。
  • 幻读:事务 A 将数据库中所有学生的成绩由 A -> B,此时事务 B 手动插入了一条成绩为 A 的记录,在事务 A 更改完毕后,发现还有一条记录没有修改,那么这种情况就叫做出现了幻读。

在说隔离级别之前你首先要知道,隔离的越狠,效率就越低。

SQL标准的事务隔离级别包括

  • 读未提交(read uncommitted)
  • 读提交( read commited)
  • 可重复读(repeatable read)
  • 串行化(Serializable)
  1. 读未提交是指,一个事务还没提交时,他做的变更就能被别的事务看到。
  2. 读提交是指:一个事务提交之后,他做的变更才会被其他事物看到。
  3. 可重复读:一个事务执行过程中看到的数据总是跟这个事务启动时看到的数据是一致的,当然在这种隔离级别下,未提交的事务是不可见的。
  4. 串行化:顾名思义就是同一行记录,写的时候加锁,读也会加锁,当出现读写锁冲突时,后访问的事务必须等前一个事务执行完毕,才能继续执行。

隔离级别由小到大:读未提交<读已提交<可重复读<串行化
在这里插入图片描述

  • 如果隔离级别是“读未提交” ,则V1的值是2,这时候事务B虽然没有提交但是结果已经被A看到了。因此V2和V3都是2.
  • 若隔离级别是“读提交”则V1是1,V2和V3是2.
  • 若隔离级别是“可重复读”则V1,V2是1,V3是2。因为可重复读要求就是事务在执行期间看到的数据前后必须是一致的。
  • 若隔离级别是“串行化”,则在事务B执行“将1改成2”的时候,会被锁住。直到事务A提交后,事务B才可以继续执行。所以A的角度看,V1,V2是1,V3是2.

在实现上,数据库里面会创建一个视图,访问的时候以视图的逻辑结果为准。在“可重复读”隔离级别下。这个视图是在事务启动时才创建的,整个事务存在期间都用这个视图。在“读提交”隔离级别下,这个视图是在每个SQL语句开始执行的时候创建的。“读未提交”下直接返回记录上的新值,没有视图概念;而“串行化”隔离级别下直接用加锁的方式来避免并行访问。

事务隔离级别各自能解决的问题是

事务隔离级别脏读不可重复读幻读
读未提交允许允许允许
读已提交不允许允许允许
可重复读不允许不允许允许
串行化不允许不允许不允许

MySQL的默认隔离级别是repeatable-read(可重复读), Oracle数据库的默认隔离级别是“读已提交”,因此对于一些从Oracle迁移搭配MySQL的应用,为保证数据库隔离级别的一致,你一定记得将MySQL的隔离级别设置为“读未提交”。

配置的参数就是将

transaction-isolation的值设置成READ-COMMITED
show variable 来查看当前的值

事务隔离的实现

在MySQL中实际上每条记录在更新的时候都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到前一个状态的值。

例如将num从1改成2,再从2改成3过程中。mysql会保存这几个回滚段,将2改回1,将3改回2. read-viewA,read-viewB.

回滚日志什么时候删除?
再不需要的时候才删除,系统会自动判定当没有事务在需要使用这些回滚日志时,回滚日志会被删除。

在MySQL5.5及以前版本,回滚日志是和数据字典在一起放在ibdata文件里的,即使尝试无最终提交,回滚段被清理,文件也不会变小。长事务除了会堆积大量事务视图占据系统资源,他还占用锁资源,也可能拖垮整个库。

事务的启动方式

  1. 显示启动事务语句,begin / start transaction。配套的提交是commit,回滚是rollback。
  2. set autocommit=0,这个命令会将这个线程的自动提交关闭掉。意味着如果你只执行一个select语句,这个事务就启动了,而且并不会自动提交。这个事务持续存在直到你主动执行commit或rollback,或者断开连接。

一般建议使用set autocomiit =1,通过显示语句的方式来启动事务。

commit work and chain,这是提交事务并自动启动下一个事务,这样省去了begin语句的开销。同时带来的好处是从程序开发的角度明确知道每个语句是否处于事务中。

原文地址:https://www.cnblogs.com/itjiangpo/p/14181401.html