彻底搞懂数据库的并发问题以及事务的隔离级别(通俗易懂)

14 篇文章 4 订阅
10 篇文章 1 订阅
本文详细介绍了数据库事务的四大特性(ACID)以及并发操作中可能出现的问题,包括脏读、不可重复读和幻读。讨论了四种不同的事务隔离级别,分析了它们如何解决并发问题,并通过实例演示了MySQL中不同隔离级别的行为。最后,文章展示了如何查看和设置事务的隔离级别,帮助读者深入理解数据库事务的管理和控制。
摘要由CSDN通过智能技术生成

1. 前言

1.1 什么是数据库事务?

  • 数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。
  • 举例说明
    在这里插入图片描述
    即:事务在提交之前,可以回滚。一旦提交,就不能撤销,提交成功后其他用户才可以通过查询浏览数据的变化。

1.2 概述

  • 数据库中的事务有四大特性(ACID),分别是原子性、一致性、隔离性和持久性。
  • 针对隔离性,有四个等级的隔离级别:读未提交、读已提交、可重复读、串行化。设置事务的隔离级别是为了解决数据库并发产生的脏读、不可重复读、幻读问题。
  • 具体的我们往下看……

2. 事务的四大特性(ACID)

  • ① 原子性( Atomicity )

    事务中的全部操作在数据库中是不可分割的,要么全部完成,要么全部不执行(即:要么全部执行成功,要么全部执行失败)。
  • ② 一致性( Consistency )

    几个并行执行的事务,其执行结果必须与按某一顺序串行执行的结果相一致,即:一个事务执行之前和执行之后都必须处于一致性状态。
    意思就是:事务执行后,数据库状态与其业务规则保持一致。用上述转账的例子来说就是:无论事务执行成功与否,也不管互相转账多少次,但是参与转账的两个账户余额之和是保持不变的
  • ③ 隔离性( Isolation )

    一个事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须是透明的。
    即:在并发操作中,不同事务之间应该隔离开来,使每个并发中的事务不会相互干扰。(类似于多线程中的锁)
  • ④ 持久性( Durability )

    对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障。
    即:一个事务一旦提交,事务中对数据库的所有操作都被持久化到数据库中,即永久性的改变,即使提交后的下一秒遇到数据库崩溃,也不会对数据造成影响,在数据库重启后依然能通过某种机制恢复数据。

3. 数据库并发产生的问题

  • ① 脏读

    • 脏读就是读了未提交的新事务
    • 意思就是:对于 table1 表中的某条记录的某个字段 filed ,现在有两个事务,一个正在读的事务A 读到了另一个事务B 正在 update 但未提交的数据。
    • 解释就是:A事务正在 查询字段 filed 的值,B事务正在修改 字段 filed的值,B事务修改之后还未提交,但是A事务读到了这个修改了但未提交的内容,这种情况就是脏读。因为A不应该读到B未提交的数据,因为B可能会回滚,那么对A来说未提交的数据就是脏数据,不应该读到。
  • ② 不可重复读——针对update

    • 读取了提交的新事务(指更新操作)
    • 意思就是:对于 table1 表中的某条记录的某个字段 filed ,现在有两个事务 A 和 B,A 事务正在读取这条记录的字段 filed 内容,B 事务正在 update 这条记录的字段 filed 内容,现在可能发生的情况是:第①步:A 事务第一次读取的时候 filed 内容为XXXX111(此时B事务未修改),第②步:B 事务将filed 内容 update为XXXX1222,并提交了事务,第③步:A 事务再次读取的时候读到了filed 内容为XXXX1222,即 A 事务如果多次读取数据,读取到的数据可能会不一致,不能重复读到 B 事务修改前的内容
    • 解释就是:站在数据库的存储原理以及开发的角度来看,这种情况是正常的,B 修改并且已经提交了,A 第二次读就应该读到新的数据,这很合理。但是站在某个业务的角度来看,我这是同一个事务,却发生第一次读是XXXX111,第二次读变成XXXX1222,或许第一次的内容正要记还没来得及数据变了,怎么不可重复读呢?对他们来说这就是不可重复读问题。这样解释的话应该就很明白什么是不可重复读问题了吧。
  • ③ 幻读——针对插入、删除

    • 读取了提交的新事务,指增删操作
      关于幻读的解释,看个人理解了,我这里列举了两种情况,第一种解释是最为常见的。也最好理解,但是我个人觉得第二种解释更为合理,都行吧,都算挺好理解的。

      无论哪种解释,需要了解的是:幻读都是可以接受的,所以一般不解决这种问题。

    (1) 幻读的第一种解释

    • 用两种场景来解释一下就是:
      ① 事务A正在根据条件筛选读取table1里符合条件的数据,然后事务B往表里批量插入很多新的数据,之后事务A第二次读取的时候,数据突然变多了,以为产生了幻觉。
      ② 事务A正在批量更新table1的某个字段,比如正在更新dog表里的【品种】字段,将品种都跟新为”边牧“,此时事务B也在操作dog表,只是事务B在批量插入品种为”金毛“的数据,然后事务A更新之后再次查的时候,发现居然不都是”边牧“还有”金毛“,以为出现了幻觉。

    (2) 幻读的第二种解释

    • 事务 A 根据条件查询得到了 N 条数据,但此时事务 B 新增 或者删除了 M 条符合事务 A 查询条件的数据,这样当事务 A 再次进行查询的时候真实的数据集已经发生了变化,但是A却查询不出来这种变化,因此产生了幻读。

4. 事务的隔离级别

4.1 数据库的4种隔离级别

  • 事务的隔离级别就是为解决上面并发产生的脏读、不可重复读、幻读 问题存在的。
 脏读不可重复读幻读描述
读未提交(Read uncommitted))问题存在问题存在问题存在允许一个事务可以读取另一个未提交事务的数据
读已提交(Read committed)×问题存在问题存在一个事务修改的数据必须提交后才能被其他事务读取到
可重复读(Repeatable red)××问题存在在事务结束前,都可以反复读取到事务刚开始时看到的数据
串行化(Serializable)×××最高的事务隔离级别,在该级别下,事务串行化顺序执行
  • 我们再对上面表格简单说明一下:上面表格的 × 说明当前的隔离性解决了对应的并发问题。

    ① 读未提交(Read uncommitted)):隔离级别最低,脏读、不可重复读、幻读都没解决。所以,一般开发中不会用这个隔离级别。

    读已提交(Read committed):可以理解为只允许事务A读取已被事务B提交后的数据,这样就避免了脏读,可重复读可幻读问题还存在。

    可重复读(Repeatable red):在事务结束前,都可以反复读取到事务刚开始时看到的数据。就是即便其他事务有修改操作,也允许事务A在多次读取数据的过程中,保证读取到的数据是一致的。解决了脏读、不可重复读问题这个是 MySQL 默认的事务隔离级别。

    ④ 串行化(Serializable):事务的最高隔离级别,在一个事务在读取一张表的数据时,禁止其他事务对该表进行增删改查操作,解决了所有并发问题,但是性能十分低下,一般开发中也不会用这个隔离级别。

4.2 数据库的默认隔离级别

  • Oracle 支持两种隔离级别:读已提交(Read committed)、串行化(Serializable)
    默认的隔离级别是:读已提交(Read committed)
  • MySQL 四种隔离级别都支持。
    默认的隔离级别是:可重复读(Repeatable red)

5. 解释说明事务的隔离级别

  • 看完上面的一般应该都明白的差不多了,为了更形象的说明,我们用命令结合数据库数据再简单说一下,我下面用的是 MySQL 数据库来解释说明的。首先先看几个命令。

5.1 查看事务的隔离级别与设置事务自动提交模式

5.1.1 常用的命令

  • ① 查看事务的隔离级别:
    select @@tx_isolation;
    select @@global.tx_isolation,@@tx_isolation;(系统全局的隔离级别)
    在这里插入图片描述
    在这里插入图片描述
    MySQL 的默认隔离级别:可重复读。
  • ② 查看当前事务自动提交模式:
    SHOW VARIABLES LIKE 'autocommit';
    在这里插入图片描述
    如果结果显示 autocommit 的值是 ON,表示系统开启自动提交模式;如果 autocommit 的值是 OFF,则表示未开启自动提交模式。
  • ③ 修改事务自动提交的模式:
    语法:
    SET autocommit = 0 | 1 | true | false | ON | OFF;
    在这里插入图片描述
  • ④ 修改当前MySQL连接的隔离级别:
set session transaction isolation level serializable;

可选的参数为:read uncommitted, read committed, repetable read, serializable

在这里插入图片描述

  • ⑤ 设置数据库系统的全局的隔离级别:
    set global transaction isolation level serializable;

5.1.2 事务自动提交模式说明

  • 上面我们说 autocommit 的值是 ON 表示开启自动提交,否则没用开启。那么开启事务自动提交和关闭事务自动提交效果怎么体现的呢?

    ① 如果开启自动提交,则每执行一条 SQL 语句,事务都会提交一次。

    ② 如果关闭自动提交,用户将会一直处于某个事务中,只有提交或回滚后才会结束当前事务,重新开始一个新事务。

5.2 测试事务的隔离级别

5.2.1 MySQL 的默认隔离级别下——Repeatable red

  • 首先我们开启两个事务,在隔离级别为默认的隔离级别Repeatable red 下,设置事务不自动提交,设置之后检查一下:
    在这里插入图片描述
  • 然后,① 两个事务同时查询数据的某条数据,如图
    在这里插入图片描述
    ② 接下来,左边的事务要进行update操作,修改dog_age为6,修改之后在没有提交的情况下,两边事务再进行查询一下,可以看到右边的事务查询不到左边未提交的事务。这里就解释了我们说的避免了脏读
    在这里插入图片描述
    ③ 接下来,左边事务提交一下,两边再重查一下:可以看到右边的事务读取到的还是原先事务开始读的数据。这里就解释了我们说的解决了“不可重复读”的问题
    在这里插入图片描述
    这里,我们重新开启一个事务,即读取到左边事务提交后的数据
    在这里插入图片描述
    ④ 接下来,我们让左边事务再插入一条数据,插入之前先让右边事务查一下,插入之后再查询一下,对比看看右边事务查的数据有没有发生变化:
    在这里插入图片描述
    在这里插入图片描述
    我们看到,左边事务新插入了数据,已经改变了数据库的结果集,但是右边事务却没用查询出来(如果你用不可重复度解释,刚开始觉得有点意思,但是再想想觉得哪里不合适,因为是插入的数据没用读出来不只是影响你读的情况,而还会影响你接下来的插入操作),来看下图:
    在这里插入图片描述
    即:读不出来,又不让插入,导致右边的事务就产生了幻读

5.2.2 修改隔离级别为 serializable——解决幻读

  • 在上面的默认级别 Repeatable red 下,我们修改一下隔离级别再继续测试,请继续……
  • ① 我们用命令 set global transaction isolation level serializable; 将隔离级别修改为最高级。修改完之后重新连接一下数据库,查看一下隔离级别,同时别忘了关闭数据库事务自动提交模式:
    在这里插入图片描述
  • ② 我们再重新让左边事务插入数据,右边事务重新读取数据,根据测试可以看除,修改成最高隔离级别后,右边事务如果不结束,左边事务是不能进行插入(可以说是不能进行增删改查操作)操作的,因为事务是串行的他们是按照一定的先后顺序执行,也可理解为多线程的同步监视器。所以这种情况下,肯定可以解决脏读,不可重复读以及幻读,但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

5.2.3 测试隔离级别 read uncommitted

  • 都看到这里了,想必对隔离级别事务的隔离级别已经相当清楚了,怕你还有疑惑,再测试一个最低的隔离级别也不妨。
  • ① 将隔离级别修改为:read uncommitted
    set global transaction isolation level read uncommitted;
    在这里插入图片描述
  • ② 现在,我们让左边事务进行update操作,修改之前两个事务先进行查询,确保数据一致,且记得关闭自动提交事务
    在这里插入图片描述
  • 现在左边事务去修改这条数据对应的狗狗年龄,在未提交之前,让右边事务查询看看结果:
    在这里插入图片描述
    我们把这种情况就叫做脏读,所以可见,事务的最低隔离级别(read uncommitted)没用解决脏读问题,我们上面测试的如果隔离级别为可重复读(Repeatable red)的话,可以解决脏读问题,除 读未提交(Read uncommitted))外的其他3个隔离级别都能解决脏读问题,测试到这里了,其他的应该没必要测试了,该懂得早懂了,不能懂得,我只能说你加油哦!

好了,就说到这了吧,这篇文章应该足够弄明白事务的隔离级别了!

6. 参考:

https://baike.baidu.com/item/%E6%95%B0%E6%8D%AE%E5%BA%93%E4%BA%8B%E5%8A%A1/9744607?fr=aladdin.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@素素~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值