精华内容
下载资源
问答
  • MySQL事务隔离级别详解,这个面试经常会面到,必会呀,哈哈
  • 事务隔离级别概述 mysql中,innodb所提供的事务符合ACID的要求,而事务通过事务日志中的redo log和undo log满足了原子性、一致性、持久性,事务还会通过锁机制满足隔离性,在innodb存储引擎中,有不同的隔离级别,...

    原文链接:http://www.zsythink.net/archives/1233

    本博客为个人学习、研究或者欣赏用,如有侵权,请与我联系删除,谢谢

    事务隔离级别概述

    mysql中,innodb所提供的事务符合ACID的要求,而事务通过事务日志中的redo log和undo log满足了原子性、一致性、持久性,事务还会通过锁机制满足隔离性,在innodb存储引擎中,有不同的隔离级别,它们有着不同的隔离性。

     

    什么是事务的隔离级别?如果只是从概念上理解的话可能比较模糊,咱们直接看看不同隔离级别下的实际表现是什么样子的,再结合理论去理解,就会明了很多。首先,打开两个终端,同时连接到当前数据库,如下图所示,我们对两个回话进行编号,并且以颜色区分,1号会话使用黄色进行标识,2号会话使用红色进行标识。

    使用show processlist语句,可以看到,已经有两个线程链接到当前数据库。

     

    mysql/mariadb知识点总结(21):事务隔离级别 (事务总结之三)

    两个会话使用相同的数据库。

     

    mysql/mariadb知识点总结(21):事务隔离级别 (事务总结之三)

    两个会话中同时各自开启一个事务。

     

    mysql/mariadb知识点总结(21):事务隔离级别 (事务总结之三)

    由于下面的所有操作会在两个会话中来回切换,所以,为了方便描述,我们为每个操作的顺序进行编号,例如下图中,我们先在会话1的事务1中执行了更新操作,然后在事务1中执行了查询操作,最后又在会话2中的事务2中执行了查询操作,按照操作顺序,为各个操作进行了顺序编号。

     

    mysql/mariadb知识点总结(21):事务隔离级别 (事务总结之三)

    从上图可以看到,在事务1中显示的数据已经发生了改变,第2条数据对应的字符串已经变为test,事务2中显示的数据未发生改变,没错,你肯定会说,那是因为事务1还没有提交,所以,事务2中还无法看到被修改的数据,那么,我们将会话1中的事务提交试试看,看看事务1提交后,事务2中的数据会不会发生改变。

    那么,在事务提交之前,我们现在两个会话中再次查询一次,两个事务中的数据显示如下图中的操作1与操作2的显示结果。

    mysql/mariadb知识点总结(21):事务隔离级别 (事务总结之三)

    然后我们执行上图中的第3步,将事务1中的修改操作进行提交,在事务2中再次查看t1表中的数据(第4步),经过查看发现,t1表中的第2条数据对应的字符串仍然没有发生改变,这种情况可能和我们一厢情愿想象的状况有些不同,在不了解事务的隔离级别之前,你可能会认为,当上图中的事务1提交以后,在事务2中再次查询同一张表的数据时,应该会看到事务1中的修改,但是实际上并没有,在会话2没有提交之前,从t1表中查询出的数据一直都是不变的,直到会话2提交以后,再次查询t1表的数据,才发现t1表的第2条数据对应的字符串已经发生了改变,出现这种现象,是因为mysql默认的隔离级别造成的,而不同的隔离级别会体现出不同的隔离效果,所以,事务的隔离级别,决定了各个事务之间的隔离性,我想,看到这里,你已经对事务的隔离性有了一个大概的了解,但是具体有几个隔离级别,每个隔离级别下有哪些特性,我们慢慢总结。

     

    此处,我们先列出innodb中事务的所有隔离级别,然后再逐个了解它们,事务的隔离级别一共有如下4种。

    READ-UNCOMMITTED : 此隔离级别翻译为 "读未提交"。

    READ-COMMITTED : 此隔离级别翻译为 "读已提交" 或者 "读提交"。

    REPEATABLE-READ : 此隔离级别翻译为 "可重复读" 或者 "可重读"。

    SERIALIZABLE : 此隔离级别翻译为"串行化"。

     

    而mysql默认设置的隔离级别为REPEATABLE-READ,即 "可重读"。使用如下语句可以查看当前设置的隔离级别

    show variables like 'tx_isolation';

    如下图所示,默认设置的隔离级别为可重读。

    mysql/mariadb知识点总结(21):事务隔离级别 (事务总结之三)

    如果需要修改my.cnf配置文件,则可通过如下参数配置mysql的事务隔离级别,注意,不是使用tx_isolation,而是使用transaction_isolation

    transaction_isolation=REPEATABLE-READ

       

     

    隔离级别:可重读

    我们先来总结一下可重读隔离级别的特性,仍然以刚才文章开头的示例为例,下图中,再回话1与回话2中同时开启两个事务,在事务1的事务中修改了t1表的数据以后(将第二条数据的t1str的值修改为test),事务2中查看到的数据仍然是事务1修改之前的数据,即使事务1提交了,在事务2没有提交之前,事务2中查看到的数据都是相同的,比如t1表中的第2条数据,不管事务1是否提交,在事务2没有提交之前,这条数据对于事务2来说一直都是没有发生改变的,这条数据在事务2中是可以重复的被读到,所以,这种隔离级别被称为"可重读"。

    mysql/mariadb知识点总结(21):事务隔离级别 (事务总结之三)

    但是,你可能会有个问题,之前说过,事务的隔离性是由锁来实现的,那么,当上图中的事务1中执行更新语句时,事务1中应该对数据增加了写锁,但是在事务2中,仍然可以进行查询操作,即进行读操作,可是写锁是排他锁,在事务1中已经添加了写锁的情况下,为什么事务2还可以读取呢?这是因为innodb采用了"一致性非锁定读"的机制提高了数据库并发性。一致性非锁定读表示在如果当前行被施加了排他锁,那么当需要读取行数据时,则不会等待行上的锁的释放,而是会去读取一个快照数据,如下图所示

    mysql/mariadb知识点总结(21):事务隔离级别 (事务总结之三)

    上图展示了innodb中一致性非锁定读的过程。之所以称其为非锁定读,是因为它不需要等待被访问的行上的排他锁的释放。而上图中的快照的实现是由事务日志所对应的undo段来完成,其实快照就是该行所对应的之前的版本的数据,即历史数据,一行记录可能有不止一个快照数据。并不是所有隔离级别都使用了一致性非锁定读,在"可重读"和"读提交"的隔离级别下,innodb存储引擎使用了一致性非锁定读,但是在这两个隔离级别中,对于快照数据的定义也不相同,在"可重读"隔离级别下,快照数据是指当前事务开始时数据的样子,所以,在刚才的示例中,事务2中t1表对应的第二条记录的t1str的值一直都是2,因为在事务2开始的时候,其值就是2,这也是其可重读的特性,但是在"读提交"的隔离级别下,由于对于快照的定义不同,所以显示的现象也不同,这在做"读提交"隔离级别的实验时我们自然就会明白。

     

    在可重读的隔离级别下,可能会出现"幻读"的问题,那么什么是幻读,我们一起来看一下。

    当前的隔离级别仍然是"可重读",现在来看一个幻读的示例,根据序号顺序查看下图中的操作即可。

    mysql/mariadb知识点总结(21):事务隔离级别 (事务总结之三)

    从上图可以看出,从上图中的第5步开始,数据其实就已经发生了改变,到第7步时,事务2还是无法看到数据的改变,但是当事务2更新数据以后,发现莫名其妙的多出了一条数据。在同一个事务中,执行两次同样的sql,第二次的sql会返回之前不存在的行,或者之前出现的数据不见了,这种现象被称之为"幻读"。

    注意:上例中第8步执行的update语句并没有指定任何条件,相当于更新表中的所有行的对应字段,如果你指定了条件,并且没有更新到"隐藏"的行,那么可能无法看到幻读现象

     

    隔离级别:串行化

    所以,经过上述实验我们可以发现,事务处于REPEATABLE-READ :"可重复读" 级别时,会出现幻读的情况,而在之前,我们已经提到过,不同的隔离级别,所引入的问题会有所不同,隔离性也有所不同,那么,有没有一种隔离级别,能够解决幻读的问题呢?SERIALIZABLE : "串行化"隔离级别就不会出现幻读的问题,我们来试试将事务的隔离级别设置为串行化时,事务是怎样工作的。

     

    首先,我们将两个会话中的事务的隔离级别都设置为SERIALIZABLE : "串行化"。

    mysql/mariadb知识点总结(21):事务隔离级别 (事务总结之三)

    如上图所示,当将两个会话中的事务隔离级别同时设置为串行化以后,分别在两个会话中开启了事务1与事务2,如上图中的第1步和第2步所示,然后,进行第3步,在事务1中插入了一条数据,此时,执行第4步,在事务2中查询表t1的数据,可以,第4步好像被"卡"住了,多等一会儿,发现第4步并未执行成功,而是报了一个错误,如下图。

    mysql/mariadb知识点总结(21):事务隔离级别 (事务总结之三)

    从报错信息可以发现,事务2中的锁请求超时了,我们之前提到,事务的隔离性是由锁来实现的,当我们使用串行化的隔离级别时,由于事务1先对t1表施加了写锁,所以当事务2对t1表请求读锁时,会被阻塞,那么,出现请求锁超时的情况,也就算是比较正常了,所以,此时,我们是无法在事务2中读到t1表的数据的。

     

    那么,我们换一种实验方法,我们在事务1中插入一条数据,然后在事务2中执行查询t1表的语句,此时事务2中的查询语句会被阻塞,这时,我们提交事务1,看看会发生什么情况。

    首先,我们在事务1中插入一条数据,然后在事务2中执行查询t1表的语句,如下图,此时事务2中的查询语句被阻塞。

    mysql/mariadb知识点总结(21):事务隔离级别 (事务总结之三)

    趁着事务2中的查询语句被阻塞的时候,将事务1进行提交,如下图所示。

    mysql/mariadb知识点总结(21):事务隔离级别 (事务总结之三)

    当在事务2中执行查询语句时,查询被阻塞,此时事务1被提交,当事务1被提交后的一瞬间,事务2中的语句已经查询出结果,从返回结果可以看出,这个查询语句被阻塞了31秒左右的时间,当事务1中的写锁释放时,事务2才读出了数据。从上述实验上来看,当事务处于串行化隔离级别时,是不可能出现幻读的情况的,因为如果另一个事务中对表添加了写锁,那么在当前事务中是无法读到数据的,必须等到另一个事务提交,另一个事务释放了对表的写锁,当前事务才能进行申请读锁,使用串行化的隔离级别不会出现幻读的情况,但是,聪明如你一定发现了,当事务的隔离级别设置为串行化时,数据库失去了并发的能力,所以,我们很少将隔离级别设置为串行化,因为这种隔离性过于严格了。

     
     

     

    隔离级别:读已提交

    现在,我们已经了解了两种隔离级别,可重读与串行化,而且,我们已经了解到,串行化隔离级别的隔离性是最强的,没有并发能力,可重读隔离级别的隔离性稍微次之,但是比较串行化而言,并发能力较好,不过存在"幻读"的问题。

    那么,有了之前的基础,再去理解另外两个隔离级别就容易了,我们先来聊聊"读提交"。

    同样,在两个会话中同时开启两个事务,在事务1中修改t1表中的第二条数据,如下图中的1、2步所示,此时,事务1并未提交,所以如第3步所示,在事务2中并无法看到事务1中的修改,而当事务1提交以后,事务2中即可看到事务1中的修改,换句话说,就是事务2能够读到事务1提交后的更改,这种隔离级别被称为"读提交"。

    mysql/mariadb知识点总结(21):事务隔离级别 (事务总结之三)

     

    在"读提交"的隔离级别下,也会出现"幻读"的问题,示例如下。

    mysql/mariadb知识点总结(21):事务隔离级别 (事务总结之三)

    在上述示例中,事务1向t1表中插入了一行数据,在事务1提交以后,事务2中即可看到,但是事务2还没有提交,在事务2中执行两次相同的查询语句,莫名其妙的多出了一行,出现了"幻读"的情况。

    在读已提交的隔离级别下,除了会出现幻读的情况,还会出现不可重读的情况。"不可重读"表示"不一定可重读",不要理解为"一定不可重读"。什么叫"不一定可重读"呢?

    仍然以刚才的第一个示例为例,下图中的第3步中,获取到的第2条记录对应的t1str字段值为"test":,而在同一个事务中,第5步所查出第二条记录对应的t1str字段却变成了"ttt",所以,当我们想要再次重复读到刚才的"test",就变成了"不可重读"。

    mysql/mariadb知识点总结(21):事务隔离级别 (事务总结之三)

    其实,"不可重读"与"幻读"的表象都非常相似,都是在同一个事务中,并没有操作某些数据,可是这些数据却莫名的被改变了,或者突然多出了某些数据,又或者突然少了某些数据,这些状况好像都能用"幻象"这个词去理解,所以我一开始总是分不清到底什么是幻读,而且,mysql官方文档中也把不可重读归为幻读,只是大家为了更加的细化它们的区别,把他们分成了"不可重读"与"幻读",如果我们实在无法分清他们,我们可以这样理解,"幻读"的重点在于莫名其妙的增加了或减少了某些数据,"不可重读"的重点在于莫名的情况下,数据被修改更新了。

    那么我们再来总结一下"读提交"这个隔离级别,在"读提交"隔离级别下,回出现"不可重读","幻读"的问题,比"可重读"隔离级别的问题更多,但是它的并发能力比"可重读"更强,我们似乎发现了一个规律,隔离级别的隔离性越低,并发能力就越强,存在的问题就越多,那么现在,我们只剩下最后一个隔离级别没有了解了,那就是"读未提交"。它的并发能力更强吗,它会有更多的问题吗?我们来看看。

     
     

     

    隔离级别:读未提交

    老规矩,我们在两个会话中同时开启两个事务,然后进行实验。

    此时,我们将两个会话的事务隔离级别同时设置为读未提交,然后在两个会话中各自开启一个事务,然后在事务1中插入一条数据,并且删除一条数据,如下图中的第1步与第2步所示。

    mysql/mariadb知识点总结(21):事务隔离级别 (事务总结之三)

    第1、2步执行完毕以后,事务1并未提交,此时执行步骤3,在事务2中查看t1表中的数据,是可以看到事务1中所做出的修改的。

    所以,我们可以发现,在读未提交这个隔离级别下,即使别的事务所做的修改并未提交,我们也能看到其修改的数据。

    当前事务能够看到别的事务中未提交的数据,我们称这种现象为"脏读",上例中,事务1并未提交,但是其所作出的修改已经能在事务2中查看到,由于事务1中的修改有可能被回滚,或者数据有可能继续被修改,所以事务2中看到数据是飘忽不定的,并不是最终的数据,并不是提交后的数据,是"脏"的,但是事务2中仍然能看到这些数据,所以,这种显现被称之为脏读,当事务的隔离级别处于"读未提交"时,其并发性能是最强的,但是其隔离性与安全性是最差的。

     

    聪明如你一定想到了,当事务处于"读未提交"这种隔离级别时,会出现"脏读"的情况,同时,也会出现"不可重读","幻读"的问题。

       

     

    脏读、幻读、不可重读的区别

    现在,我们是不是更容易把脏读、幻读、不可重读混淆了呢?

    mysql/mariadb知识点总结(21):事务隔离级别 (事务总结之三)

    我们一起来总结一下它们之前的区别:

    脏读:当前事务可以查看到别的事务未提交的数据(侧重点在于别的事务未提交)。

    幻读:幻读的表象与不可重读的表象都让人"懵逼",很容易搞混,但是如果非要细分的话,幻读的侧重点在于新增和删除。表示在同一事务中,使用相同的查询语句,第二次查询时,莫名的多出了一些之前不存在数据,或者莫名的不见了一些数据。

    不可重读:不可重读的侧重点在于更新修改数据。表示在同一事务中,查询相同的数据范围时,同一个数据资源莫名的改变了。

     
     

     

    不同的隔离级别所拥有的问题

    经过上文的举例,我们已经了解了各个事务隔离级别的表象与问题,那么,我们来总结一下。

    首先,我们明白了一个道理,事务的隔离级别越高,隔离性越强,所拥有的问题越少,并发能力越弱,所以,我们可以用如下表格进行总结

    mysql/mariadb知识点总结(21):事务隔离级别 (事务总结之三)

    展开全文
  • MySQL 四种事务隔离级别详解及对比 按照SQL:1992 事务隔离级别,InnoDB默认是可重复读的(REPEATABLE READ)。MySQL/InnoDB 提供SQL标准所描述的所有四个事务隔离级别。你可以在命令行用–transaction-isolation选项...
  • 数据库事务隔离级别 数据库事务的隔离级别有4个,由低到高依次为 Read uncommitted:允许脏读。 Read committed: 防止脏读,最常用的隔离级别,并且是大多数数据库的默认隔离级别。 Repeatable read:可以防止脏...
  • 事务 数据库事务具有四个特征,简称为事务的ACID特性 1.什么是事务 开启一个事务可以包含一堆sql语句,这些sql语句要么都成功,要么一个也别想成功 2.事务作用 保证了对数据操作的数据安全性 案例:用交行的卡在农行...

    事务

    数据库事务具有四个特征,简称为事务的ACID特性

    1.什么是事务
    开启一个事务可以包含一堆sql语句,这些sql语句要么都成功,要么一个也别想成功
    2.事务作用
    保证了对数据操作的数据安全性
    3.事务的四大特性

    • 原子性(atomicity): 一个事务是一个不可分割的工作单位,事务中包含的诸多操作要么都成功,要么都不成功
    • 一致性(consistency): 事务必须是使数据从一个一致状态变到另一个一致状态,一致性与原子性是密切相关的
    • 隔离性(isolation): 一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并行的个事务之间不能互相干扰
    • 持久性(durability): 持久性也成永久性(permanence),指一个事务一旦提交,他对数据库中数据的改变就应该是永久性的,接下来的其他操作或故障不应该对其有任何影响 

    4.事务使用

    修改数据之前先开启事务操作
    start transaction;
    # 修改操作
    update user set balance=900 where name='wsb'; #买支付100元
    update user set balance=1010 where name='egon'; #中介拿走10元
    update user set balance=1090 where name='ysb'; #卖家拿到90元
    # 回滚到上一个状态
    rollback;
    # 开启事务后,只要没有执行commit操作,数据其实都没有真正刷到硬盘
    commit;
    '''
    开启事务检测操作是否完整,不完整主动回滚到上一个状态,如果完整就应该执行commit操作
    '''
    # python代码角度,应该实现的伪代码逻辑
    try:
        update user set balance=900 where name='wsb'; #买支付100元
        update user set balance=1010 where name='egon'; #中介拿走10元
        update user set balance=1090 where name='ysb'; #卖家拿到90元
    except 异常:
        rollback;
    else:
        commit;

    5.什么是事务隔离

    事务的隔离性是指在并发环境中,并发的事务是相互隔离的,
    可以理解为多个事务同一时间段对数据库的增删改时是要隔离的

    隔离种类

    SQL标准中定义了四种数据库事务隔离级别,级别从低到高分别为:

    • 读未提交(Read Uncommitted)、-> 脏读
    • 读已提交(Read Committed)、 -> 不可重复读
    • 可重复读(Repeatable Read)、-> 换读
    • 串行化(Serializable)

    在事务的并发操作中会出现脏读、不可重复读、幻读。在事务的并发操作中第二类更新丢失可以通过乐观锁和悲观锁解决。

    读未提交(Read Uncommitted)

    • 该隔离级别,所有事务都可以看到其他交事务未提的执行结果。通俗地讲就是,在一个事务中可以读取到另一个事务中新增或修改但未提交的数据。
    • 该隔离级别可能导致的问题是脏读。因为另一个事务可能回滚,所以在第一个事务中读取到的数据很可能是无效的脏数据,造成脏读现象。
    set tx_isolation='READ-UNCOMMITTED';

     案例:

    脏读(读取未提交数据)

    A事务读取B事务尚未提交的数据,此时如果B事务发生错误并执行回滚操作,那么A事务读取到的数据就是脏数据。就好像原本的数据比较干净、纯粹,此时由于B事务更改了它,这个数据变得不再纯粹。这个时候A事务立即读取了这个脏数据,但事务B良心发现,又用回滚把数据恢复成原来干净、纯粹的样子,而事务A却什么都不知道,最终结果就是事务A读取了此次的脏数据,称为脏读。

    这种情况常发生于转账与取款操作

    读已提交(Read Committed) 

    • 这是大多数数据库系统的默认隔离级别(但不是mysql默认的)
    • 一个事务只能看见已经提交事务所做的修改。换句话说,一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。
    • 该隔离级别可能导致的问题是不可重复读。因为两次执行同样的查询,可能会得到不一样的结果。
    set tx_isolation='READ-COMMITTED';

     案例:

    不可重复读(前后多次读取,数据内容不一致)

    事务A在执行读取操作,由于整个事务A比较大,前后读取同一条数据需要经历很长的时间 。而在事务A第一次读取数据,比如此时读取了小明的年龄为20岁,事务B执行更改操作,将小明的年龄更改为30岁,此时事务A第二次读取到小明的年龄时,发现其年龄是30岁,和之前的数据不一样了,也就是数据和之前的不重复(不一样)了,系统不可以读取到重复的数据,称为不可重复读

    可重复读(Repeatable Read)

    • 这是MySQL的默认事务隔离级别
    • 它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。通俗来讲,可重复读在一个事务里读取数据,怎么读都不会变,除非提交了该事务,再次进行读取。
    • 该隔离级别存在的问题是幻读
    set tx_isolation='REPEATABLE-READ';

    案例:

    幻读(前后多次读取,数据总量不一致)

    事务A在执行读取操作,需要两次统计数据的总量,前一次查询数据总量后,此时事务B执行了新增数据的操作并提交后,这个时候事务A读取的数据总量和之前统计的不一样,就像产生了幻觉一样,平白无故的多了几条数据,成为幻读。

    串行化(Serializable)

    • 这是最高的隔离级别
    • 它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。通俗地讲就是,假如两个事务都操作到同一数据行,那么这个数据行就会被锁定,只允许先读取/操作到数据行的事务优先操作,只有当事务提交了,数据行才会解锁,后一个事务才能成功操作这个数据行,否则只能一直等待
    • 该隔离级别可能导致大量的超时现象和锁竞争
    set tx_isolation='SERIALIZABLE';

    不可重复读和幻读到底有什么区别呢?

    (1)不可重复读是读取了其他事务更改的数据,针对insert与update操作

    解决:使用行级锁,锁定该行,事务A多次读取操作完成后才释放该锁,这个时候才允许其他事务更改刚才的数据。

    (2)幻读是读取了其他事务新增的数据,针对insert与delete操作

    解决:使用表级锁,锁定整张表,事务A多次读取数据总量之后才释放该锁,这个时候才允许其他事务新增数据。
     

    乐观锁

    乐观锁本身不是锁.而是一种判断机制

    在更新语句中增加过滤条件,进行版本的判断,可以是这条记录的某个字段值。然后通过updata()所影响行数来判断是否更新成功。影响条数为0则更新失败,代表这条记录被其他事务修改了.

    注意:

    Django2.0以下版本使用乐观锁时需要把隔离机制设置成不可重复读

    eg: 事务+乐观锁的使用

    # django中使用 事务 + 乐观锁
    # 这是一个商城订单提交要修改数据库案例
    
    from rest_framework.views import APIView
    from rest_framework.response import  Response
    from django.db import transaction  # 导入事务
    class Creat(APIView):
        @transaction.atomic  # 装饰事务
        def post(self,request):
            ......
            sid=transaction.savepoint()  # 对下面代码创建事务
            for product in all_product:
               product.product_id=str(product.product_id)
               order_data['order_total']+=product.price*buy_list[product.product_id]
               order_data['quantity']+=buy_list[product.product_id]
               #创建子订单
                for i in range(3):
                   stock=product.stock.quantity  # 商品在库中的数量
                   new_stock=stock-buy_list[product.product_id]  # 减掉用户购买数量
                   if new_stock<0:  # 库存数小于用户购买数量
                      transaction.savepoint_rollback(sid)  # 回滚
                      return Response({"code":203,"msg":f"{product.name}库存不足"})
                   # 使用乐观锁: quantity=字段, 在执行update前,锁定该字段的是否没有被改动,若改动了,则update影响调试为0
                   res=models.Stock.objects.filter(quantity=stock,stock_id=product.stock.stock_id).update(quantity=new_stock)
                   if not  res:
                      if i==2:
                        transaction.savepoint_rollback(sid)  # 回滚
                        return Response({"code": 203, "msg": f"创建订单失败"})
                      else:
                          continue
                   else:
                      break

    悲观锁

    可以理解为就是一把互斥索,

    悲观锁认为:事务A在操作数据时,这条数据同时也在被别的事务操作,所以事务在操作数据时加悲观锁(互斥锁),让这条数据不能被其他事务操作

    select_for_update()

    在查询数据时使用select_for_update()查询,就表示为该数据进行加锁,只有commint()完成后才会释放锁

    案例:

    class Creat(APIView):
        @transaction.atomic  # 装饰事务
        def post(self,request):
            # 创建保存点
            sid = transaction.savepoint()
            ......
            try:
                # goods=Goods.objects..get(id=goods_id)  # 不加锁
                goods=Goods.objects.select_for_update().get(id=goods_id)  # 加互斥锁
            execpt Goodsgoods.DoesNOTexist:
                # 回滚
                transaction.rollback(sid)
                return JsonResponse({'res':2, 'err':'商品信息有误'})
            ......
            transaction.commint()  # 提交事务
            return JsonResponse({'res':0, 'err':'订单创建成功'})

        悲观锁分为共享锁排它锁

        共享锁又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,共享锁是用来读取数据的。另外,一个事务获取了同一数据的共享锁,其他事务就不能获取该数据的排它锁。

        排它锁又称为写锁,简称X锁,顾名思义,排它锁就是不能与其他锁并存,如一个事务获取了一个数据行的排它锁,其他事务就不能再获取该行的其它锁,包括共享锁和排它锁。另外不存什么事务隔离级别,update/insert/delete会自动获取排它锁

        共享锁获取方式:select * from account where name='jack' lock in share mode;

        排它锁获取方式:select * from account where name='jack' for update;

    MySQL分为表级锁和行级锁,共享锁和排它锁是行级锁。表级锁在此不做讨论。

     

    展开全文
  • SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别隔离级一般支持更高的并发处理,并拥有更低的系统开销。 Read Uncommitted(读取未提交内容)  ...
    SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。
    
    Read Uncommitted(读取未提交内容)

           在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。
    Read Committed(读取提交内容)

           这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。
    Repeatable Read(可重读)

           这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。

    Serializable(可串行化) 
           这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

             这四种隔离级别采取不同的锁类型来实现,若读取的是同一个数据的话,就容易发生问题。例如:

             脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。

             不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。

             幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。

             在MySQL中,实现了这四种隔离级别,分别有可能产生问题如下所示:


    下面,将利用MySQL的客户端程序,分别测试几种隔离级别。测试数据库为test,表为tx;表结构:

    id                              int

    num

                                  int

    两个命令行客户端分别为A,B;不断改变A的隔离级别,在B端修改数据。

    (一)、将A的隔离级别设置为read uncommitted(未提交读)

     在B未更新数据之前:

    客户端A:

    B更新数据:

    客户端B:

    客户端A:

            经过上面的实验可以得出结论,事务B更新了一条记录,但是没有提交,此时事务A可以查询出未提交记录。造成脏读现象。未提交读是最低的隔离级别。

    (二)、将客户端A的事务隔离级别设置为read committed(已提交读)

     在B未更新数据之前:

    客户端A:

    B更新数据:

    客户端B:

    客户端A:

           经过上面的实验可以得出结论,已提交读隔离级别解决了脏读的问题,但是出现了不可重复读的问题,即事务A在两次查询的数据不一致,因为在两次查询之间事务B更新了一条数据。已提交读只允许读取已提交的记录,但不要求可重复读。

    (三)、将A的隔离级别设置为repeatable read(可重复读)

     在B未更新数据之前:

    客户端A:

    B更新数据:

    客户端B:

    客户端A:

    B插入数据:

    客户端B:

    客户端A:

           由以上的实验可以得出结论,可重复读隔离级别只允许读取已提交记录,而且在一个事务两次读取一个记录期间,其他事务部的更新该记录。但该事务不要求与其他事务可串行化。例如,当一个事务可以找到由一个已提交事务更新的记录,但是可能产生幻读问题(注意是可能,因为数据库对隔离级别的实现有所差别)。像以上的实验,就没有出现数据幻读的问题。

    (四)、将A的隔离级别设置为 可串行化 (Serializable)

    A端打开事务,B端插入一条记录

    事务A端:

    事务B端:

    因为此时事务A的隔离级别设置为serializable,开始事务后,并没有提交,所以事务B只能等待。

    事务A提交事务:

    事务A端

    事务B端

          

             serializable完全锁定字段,若一个事务来查询同一份数据就必须等待,直到前一个事务完成并解除锁定为止 。是完整的隔离级别,会锁定对应的数据表格,因而会有效率的问题。


    example:

    1. set autocommit=0  
    2. show variables like "%autocommit%";  
    3.   
    4. SELECT @@global.tx_isolation;   
    5. SELECT @@session.tx_isolation;   
    6. SELECT @@tx_isolation;  
    7.   
    8. SET SESSION TRANSACTION ISOLATION LEVEL read uncommitted;  
    9. SET SESSION TRANSACTION ISOLATION LEVEL read committed;  
    10. SET SESSION TRANSACTION ISOLATION LEVEL repeatable read;  
    11. SET SESSION TRANSACTION ISOLATION LEVEL serializable;  
    12.   
    13. start transaction;  

    展开全文
  • 主要介绍了MySQL事务及Spring隔离级别实现原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 12,089
精华内容 4,835
关键字:

mysql事务隔离级别详解

mysql 订阅