MySQL 事务的隔离级别(详解)



什么是事务

事务是一组操作,包含许多单一的逻辑。只要有一个逻辑没有执行成功,那么所有的逻辑都算操作失败。并且所有的数据都会回到最初的状态(回滚)。


事务的隔离级别

事务隔离的目的是解决事务并发出现的问题,所以这里先介绍事务并发问题。

1-并发事务导致的问题

我们假设现在有A,B两个事务,这两个事务都可以对数据库进行读取和修改。那么,排列组合后可以分为三种情况(都是以操作同一资源为前提):

  1. A,B都采取读操作:不会出现任何问题。

  2. 其中一个事务进行修改,另一个则进行读取:执行修改的那个事务不会出错,执行读操作的事务读取的数据会出现问题(称为读问题,下面会详细介绍)。

  3. A,B都对数据库进行修改:在对同一数据进行修改时,先修改的数据会被后修改的数据覆盖(称为更新问题,下面会详细介绍)。

1.1读问题

读问题分为三类(我们假定执行读操作的事务为A,执行修改的事务为B):

  1. 脏读:读到了脏数据。即A在B还未提交数据时读取数据。因为你不知道B之后执行的是回滚还是提交,所以你不能保证此数据的可靠性。

  2. 不可重复读:A在B提交数据前读取一次,然后在B提交数据后再读取一次。造成A事务内多次读取的结果不一样。

  3. 幻读:A 在B插入数据前读取一次,然后在B插入数据后再读取一次。造成A前后两次查询结果不一致。

1.2更新问题

丢失更新(具体分类如下图所示):以更改表中的数据为例。A修改好表中的某一数据准备提交时,B立即修改此表中的其它项数据。A正常提交数据,然后B也正常提交数据。

时间点 事务A 事务B
1 开启事务
2 开启事务
3 update t_sutdent ts set ts.sname=“张三” where ts.sid=“10”
4 update t_sutdent t set t.sname=“李四”,t.age=18 where ts.sid=“10”
5 提交
6 提交
问题 事务A认为数据库中的数据已成功改为:sid=10的学生的姓名为“张三”。(实际上是“李四”) 事务B认为数据库已修改成功。(确实修改成功,但却覆盖了A的数据,且A并不知晓

解决方法:
  • 使用排它锁(首句添加“for update”): 把写写并行改为写写串行(如下表)。简单地说就是A,B在执行数据修改时要排队。而且当某一进程正在修改时,其它进程不能查询。

    时间点 事务A 事务B
    1 开启事务
    2 修改数据 不可开启事务
    3 提交数据 不可开启事务
    4 开启事务
    5 不可开启事务 修改数据
    6 不可开启事务 提交数据
  • 使用乐观锁(添加version或时间戳): 在写写并行发生冲突时报异常1,交给程序员处理。



2-事务的隔离级别

事务的四种隔离级别:

种类 处理的问题 解释
串行化 三个读问题都能处理 顾名思义,此方法下,对同一数据的访问是串行的。没有并发,就不会出现任何并发问题。此级别基本不使用,一是效率底;二是容易出现死锁。
可重复读 不能处理幻读 mysql有两种机制能达到此级别的隔离效果。一、加读锁(读读共享) + 加写锁(读写串行):实现简单,但因为读写无法并行,所以效率低;二、读不加锁 + 加写锁 + MVCC(有兴趣的可以自行百度):实现复杂,但读写可以并行2,效率高。
读已提交数据 只能处理脏读 操作:写时加排它锁,读时不加操作。 引用大佬的话(原文找不到了):在mysql的MVCC机制和该隔离级别的共同作用下,每次select的时候数据库会新生成一个版本号,所以每次select的时候读的不是一个副本 而是不同的副本。每次select之间有其他事务更新了,而我们读取数据并提交了,那就出现了不可重复读。所以虽然此级别效率高,但是mysql不能使用。
读未提交数据 即,不做任何处理。





  1. 假如我们规定,数据库原始的version为0,在某个事务对其进行修改后要把version + 1后才能存入数据库。那么,只要 存入的version < 当前数据库的version,就把它判定为丢失更新。通常遇到这种问题时,更新数据库版本就行(需要程序员自己实现)。 ↩︎

  2. 当原数据被写锁占用时,读请求可以去读取数据库的副本,又因为并行的写操作加了写锁(写的这段时间数据库不能给出副本)所以得到的副本只有两种情况:一、改写之前的副本;二、改写后的副本。这样便解决了更新问题。 ↩︎

发布了10 篇原创文章 · 获赞 13 · 访问量 426

猜你喜欢

转载自blog.csdn.net/hufuzhi1146231094/article/details/105092695