51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 2401|回复: 1
打印 上一主题 下一主题

MySQL进阶系列:锁与事务的隔离级别

[复制链接]
  • TA的每日心情
    擦汗
    6 小时前
  • 签到天数: 1047 天

    连续签到: 5 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2021-8-20 10:12:34 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    我们的数据库一般都会并发执行多个事务,多个事务可能会并发的对相同的一批数据进行增删改查操作,可能就会导致我们说的脏写、脏读、不可重复读、幻读这些问题。

      这些问题的本质都是数据库的多事务并发问题,为了解决多事务并发问题,数据库设计了事务隔离机制、锁机制、MVCC多版本并发控制隔离机制来解决多事务并发问题。

      首先,我们来了解一下事务,以及事务的ACID特性。

      那么什么是事务?事务是一组SQL语句要么全部执行成功,要么全部执行失败。

      事务的ACID特性

      原子性(Atomicity):事务是一个原子操作单元,其对数据的修改,要么全都执行,要么全都不执行。

      一致性(Consistent):在事务开始和完成时,数据都必须保持一致状态。这意味着所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性。

      隔离性(Isolation):数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的“独立”环境执行。这意味着事务处理过程中的中间状态对外部是不可见的,反之亦然。

      持久性(Durable):事务完成之后,它对于数据的修改是永久性的,即使出现系统故障也能够保持。


      并发事务带来的问题

      脏写:当多个事务对同一条数据进行操作时,最后的更新覆盖了由其他事务的更新,那么其他事务的更新数据即丢失,这种问题称为脏写。

      脏读:事务A读到了事务B已经修改但尚未提交的数据,事务A还在这个数据基础上做了操作。此时,如果事务B回滚,事务A读取的数据无效,不符合一致性要求。

      不可重复读:事务A内部的相同查询语句在不同时刻读出的结果不一致,不符合隔离性。

      幻读:事务A读取到了事务B提交的新增数据,不符合隔离性。

      那么MYSQL是怎么解决这些问题的呢?就是通过事务的隔离级别,有以下几种:

      读未提交:事务A能够读取到事务B没有提交的数据。

      读已提交:事务A只能读取到事务B已提交的数据。

      可重复读:事务A在当前事务中,相同SQL读取到的数据都是一样的。

      串行化:相当于加了事务级别的锁。事务A提交之后,其他事务才能往下执行。

      下面有一张图记录了这四种隔离级别的问题:

    常看当前数据库的事务隔离级别: show variables like '%isolation%';
      设置事务隔离级别:set transaction_isolation='REPEATABLE-READ'。
      Mysql默认的事务隔离级别是可重复读,用Spring开发程序时,如果不设置隔离级别默认用Mysql设置的隔离级别,如果Spring设置了就用已经设置的隔离级别。
      在具体讲解这四种隔离级别之前,我们先了解一下MYSQL锁的知识,这是个大前提。

      锁分类
      1.从性能上分为乐观锁(用版本对比来实现)和悲观锁。
      2.从对数据库操作的类型分,分为读锁和写锁(都属于悲观锁)。
      读锁(共享锁,S锁(Shared)):针对同一份数据,多个读操作可以同时进行而不会互相影响。
      写锁(排它锁,X锁(eXclusive)):当前写操作没有完成前,它会阻断其他写锁和读锁。
      3.从对数据操作的粒度分,分为表锁和行锁。
      行锁是我们开发过程中最常见的,那么我们以行锁为例, 行锁每次操作锁住一行数据,一个session开启事务更新不提交,另一个session更新同一条记录会阻塞,更新不同记录不会阻塞。
      开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度最高。
      InnoDB与MYISAM的最大不同有两点:
      ·InnoDB支持事务
      I·nnoDB支持行级锁
      对行锁有了一定了解之后,接下来我们来演示一下这四种隔离级别:

      读未提交
      首先把隔离级别设为读未提交set transaction_isolation='read-uncommitted';,接下来开启两个会话,如下图:

    事务A读到了事务B没有提交的数据,脏写、脏读,不可重复读、幻读都可能发生。

      读已提交
      首先把隔离级别设为读未提交set transaction_isolation='read-committed';,接下来开启两个会话,如下图:


    事务A只能读到事务B已提交的数据,不可重复读、幻读可能发生。

      可重复读
      首先把隔离级别设为读未提交set transaction_isolation='read-committed';,接下来开启两个会话,如下图:


    按照上图序号标注的顺序,从1到6,在6查询的时候,发现确实是没有查到B事务新增的数据的,所以确实没有幻读。
      但是再执行7、8,会发现在8的时候,出现了幻读。
      那既然MYSQL的默认隔离级别是可重复读,还会出现幻读,那么MYSQL是怎么解决幻读的呢?通过MVCC机制,下面会讲到。

      串行化
      首先把隔离级别设为读未提交set transaction_isolation='read-committed';,接下来开启两个会话,如下图:


    当隔离级别是串行化的时候,事务A查询到的行会被加上行锁,事务B更新行锁上的数据会阻塞,直到事务A提交之后,事务B才能更新成功。
      PS:Mysql默认级别是repeatable-read,有办法解决幻读问题吗?间隙锁在某些情况下可以解决幻读问题。
      间隙锁,锁的就是两个值之间的空隙。


    那么间隙就有id为(3,10)、(10,20)、(20,正无穷)这三个区间,在Session_1下面执行update account set name='zhuge' where id>8 and id<18;,则其他Session没法在这个范围所包含的所有行记录(包括间隙行记录)以及行记录所在的间隙里插入或修改任何数据,即id在(3,20]区间都无法修改数据,注意最后那个20也是包含在内的。间隙锁是在可重复读隔离级别下才会生效。

    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

    x
    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-15 15:31 , Processed in 0.064898 second(s), 25 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表