精华内容
下载资源
问答
  • 在开发中遇到过这样一个问题 ...这个我在本地测试,确实有效,一个事务开始没结束,第二个事务一个等待,不过会导致处于阻塞状态,因为系统并发,不敢考虑,也就是记录下这个方式. 手动模拟: 执行第一个事
  • 使用封锁协议解决事务并发问题

    千次阅读 2020-07-03 10:06:16
    一、事务并发问题 事务是用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的工作单位。 并发是指多个事务同时执行,这会带来一些问题。 丢失修改 丢失修改由两次事务的修改导致,...

    一、事务的并发问题


    事务是用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的工作单位。

    并发是指多个事务同时执行,这会带来一些问题。

    丢失修改

    丢失修改由两次事务的修改导致,比如事务 T1 修改 A,同时事务 T2 也修改 A,那么最后 A 的值将由事务 T2 的修改结果决定,这样事务 T1 的修改就没了,导致丢失修改。

    不可重复读

    分为两种情况,第一种是事务 T1 读取 K ,这个时候读取到一个值 A,然后事务 T2 增加或者删除了一条记录,过了一会事务 T1 又过来读取了这个 A ,发现值不见了,或者多了一些值,很迷幻,所以叫幻读。

    第二种情况就是前面相同,但是事务 T2 修改了刚才读取的那个值 A ,导致事务 T1 再过来读的时候发现值不一样了,这叫做不可重复读。

    脏读

    事务 T1 修改了 A 的值,但是还没有提交,这个时候被事务 T2 读取了 A 的值,但是过了一会事务 T1 由于某些原因回滚了操作,所以 T2 读取到的值就是错的,这就是 脏读

    二、封锁协议


    两类锁

    两类锁分别是 排它锁 X共享锁 S

    T 给数据 A 加上排它锁之后,就只有 T 才能读和修改 A,同时其他事务就不能再加任何锁了,直到 T 释放排它锁。

    T 给数据 A 加上共享锁之后,T 只能读 A,不能修改,其他事务可以在它的基础上加共享锁,但是不能加排它锁,也就是说其他的事务只能读不能修改,直到 T 释放共享锁为止。

    封锁协议

    封锁协议规定了使用锁对数据对象加锁时需要遵循的规则。

    一级封锁协议

    它规定事务在修改数据之前必须加排它锁,直到事务结束才释放。

    这就解决了丢失修改的问题,因为事务在修改数据的时候要加 X 锁,之后其它事务就不能再加锁了,也就是不能修改了,必须等第一个事务修改完成之后才能再加锁然后修改。

    但是读数据的时候是不用加锁的,那就解决不了其它的问题。

    二级封锁协议

    它规定事务在读取事务之前必须加共享锁,而 读取结束就释放

    注意这里是读完了就可以释放锁了,还是不太行,但是可以解决脏读的问题:比如事务 T1 在修改数据的时候加了 X 锁,这个时候事务 T2 要想再读取就要加 S 锁,但是在 X 锁释放之前是不能加其他锁的,所以必须等待事务 T1 释放锁,这个时候无论他是提交事务还是回滚都不会影响到 T2 读取的数据正确性了。

    但是他不能不解决可重复读的问题。

    三级封锁协议

    它规定事务在读取事务之前必须加共享锁,直到 事务结束 才释放。

    这就解决了不可重复度的问题,因为当事务 T1 读取数据对象的时候,加了 S 锁,其他的事务想修改该数据对象,必须加 X 锁,但是在 S 锁之上是不能加 X 锁的,只能等到 T1 释放 S 锁,而释放的时候事务 T1 也结束了。

    展开全文
  • PHP解决并发问题

    万次阅读 多人点赞 2020-05-27 12:10:04
    我们通常衡量一个Web系统的吞吐率的指标是QPS(Query Per Second,每秒处理请求数),解决每秒数万次的高并发场景,这个指标非常关键。举个例子,我们假设处理一个业务请求平均响应时间为100ms,同时,系统内有20台...

    举个例子,高速路口,1秒钟来5部车,每秒通过5部车,高速路口运作正常。突然,这个路口1秒钟只能通过4部车,车流量仍然依旧,结果必定出现大塞车。(5条车道忽然变成4条车道的感觉)

    同理,某一个秒内,20*500个可用连接进程都在满负荷工作中,却仍然有1万个新来请求,没有连接进程可用,系统陷入到异常状态也是预期之内。

    14834077821.jpg

    其实在正常的非高并发的业务场景中,也有类似的情况出现,某个业务请求接口出现问题,响应时间极慢,将整个Web请求响应时间拉得很长,逐渐将Web服务器的可用连接数占满,其他正常的业务请求,无连接进程可用。

    更可怕的问题是,是用户的行为特点,系统越是不可用,用户的点击越频繁,恶性循环最终导致“雪崩”(其中一台Web机器挂了,导致流量分散到其他正常工作的机器上,再导致正常的机器也挂,然后恶性循环),将整个Web系统拖垮。

    重启与过载保护

    如果系统发生“雪崩”,贸然重启服务,是无法解决问题的。最常见的现象是,启动起来后,立刻挂掉。这个时候,最好在入口层将流量拒绝,然后再将重启。如果是redis/memcache这种服务也挂了,重启的时候需要注意“预热”,并且很可能需要比较长的时间。

    秒杀和抢购的场景,流量往往是超乎我们系统的准备和想象的。这个时候,过载保护是必要的。如果检测到系统满负载状态,拒绝请求也是一种保护措施。在前端设置过滤是最简单的方式,但是,这种做法是被用户“千夫所指”的行为。更合适一点的是,将过载保护设置在CGI入口层,快速将客户的直接请求返回

    高并发下的数据安全

    我们知道在多线程写入同一个文件的时候,会存现“线程安全”的问题(多个线程同时运行同一段代码,如果每次运行结果和单线程运行的结果是一样的,结果和预期相同,就是线程安全的)。如果是MySQL数据库,可以使用它自带的锁机制很好的解决问题,但是,在大规模并发的场景中,是不推荐使用MySQL的。秒杀和抢购的场景中,还有另外一个问题,就是“超发”,如果在这方面控制不慎,会产生发送过多的情况。我们也曾经听说过,某些电商搞抢购活动,买家成功拍下后,商家却不承认订单有效,拒绝发货。这里的问题,也许并不一定是商家奸诈,而是系统技术层面存在超发风险导致的。

    1. 超发的原因

    假设某个抢购场景中,我们一共只有100个商品,在最后一刻,我们已经消耗了99个商品,仅剩最后一个。这个时候,系统发来多个并发请求,这批请求读取到的商品余量都是99个,然后都通过了这一个余量判断,最终导致超发。(同文章前面说的场景)

    14834077822.jpg

    在上面的这个图中,就导致了并发用户B也“抢购成功”,多让一个人获得了商品。这种场景,在高并发的情况下非常容易出现。

    优化方案1:将库存字段number字段设为unsigned,当库存为0时,因为字段不能为负数,将会返回false

     <?php
     //优化方案1:将库存字段number字段设为unsigned,当库存为0时,因为字段不能为负数,将会返回false
     include('./mysql.php');
     $username = 'wang'.rand(0,1000);
     //生成唯一订单
     function build_order_no(){
       return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
     }
     //记录日志
     function insertLog($event,$type=0,$username){
         global $conn;
         $sql="insert into ih_log(event,type,usernma)
         values('$event','$type','$username')";
         return mysqli_query($conn,$sql);
     }
     function insertOrder($order_sn,$user_id,$goods_id,$sku_id,$price,$username,$number)
     {
           global $conn;
           $sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price,username,number)
           values('$order_sn','$user_id','$goods_id','$sku_id','$price','$username','$number')";
          return  mysqli_query($conn,$sql);
     }
     //模拟下单操作
     //库存是否大于0
     $sql="select number from ih_store where goods_id='$goods_id' and sku_id='$sku_id' ";
     $rs=mysqli_query($conn,$sql);
     $row = $rs->fetch_assoc();
       if($row['number']>0){//高并发下会导致超卖
           if($row['number']<$number){
             return insertLog('库存不够',3,$username);
           }
           $order_sn=build_order_no();
           //库存减少
           $sql="update ih_store set number=number-{$number} where sku_id='$sku_id' and number>0";
           $store_rs=mysqli_query($conn,$sql);
           if($store_rs){
               //生成订单
               insertOrder($order_sn,$user_id,$goods_id,$sku_id,$price,$username,$number);
               insertLog('库存减少成功',1,$username);
           }else{
               insertLog('库存减少失败',2,$username);
           }
       }else{
           insertLog('库存不够',3,$username);
       }
     ?>
    
    1. 悲观锁思路

    解决线程安全的思路很多,可以从“悲观锁”的方向开始讨论。

    悲观锁,也就是在修改数据的时候,采用锁定状态,排斥外部请求的修改。遇到加锁的状态,就必须等待。

    14834077833.jpg

    虽然上述的方案的确解决了线程安全的问题,但是,别忘记,我们的场景是“高并发”。也就是说,会很多这样的修改请求,每个请求都需要等待“锁”,某些线程可能永远都没有机会抢到这个“锁”,这种请求就会死在那里。同时,这种请求会很多,瞬间增大系统的平均响应时间,结果是可用连接数被耗尽,系统陷入异常。

    优化方案2:使用MySQL的事务,锁住操作的行

     <?php
     //优化方案2:使用MySQL的事务,锁住操作的行
     include('./mysql.php');
     //生成唯一订单号
     function build_order_no(){
       return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
     }
     //记录日志
     function insertLog($event,$type=0){
         global $conn;
         $sql="insert into ih_log(event,type)
         values('$event','$type')";
         mysqli_query($conn,$sql);
     }
     //模拟下单操作
     //库存是否大于0
     mysqli_query($conn,"BEGIN");  //开始事务
     $sql="select number from ih_store where goods_id='$goods_id' and sku_id='$sku_id' FOR UPDATE";//此时这条记录被锁住,其它事务必须等待此次事务提交后才能执行
     $rs=mysqli_query($conn,$sql);
     $row=$rs->fetch_assoc();
     if($row['number']>0){
         //生成订单
         $order_sn=build_order_no();
         $sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price)
         values('$order_sn','$user_id','$goods_id','$sku_id','$price')";
         $order_rs=mysqli_query($conn,$sql);
         //库存减少
         $sql="update ih_store set number=number-{$number} where sku_id='$sku_id'";
         $store_rs=mysqli_query($conn,$sql);
         if($store_rs){
           echo '库存减少成功';
             insertLog('库存减少成功');
             mysqli_query($conn,"COMMIT");//事务提交即解锁
         }else{
           echo '库存减少失败';
             insertLog('库存减少失败');
         }
     }else{
       echo '库存不够';
         insertLog('库存不够');
         mysqli_query($conn,"ROLLBACK");
     }
     ?>
    
    1. FIFO队列思路

    那好,那么我们稍微修改一下上面的场景,我们直接将请求放入队列中的,采用FIFO(First Input First Output,先进先出),这样的话,我们就不会导致某些请求永远获取不到锁。看到这里,是不是有点强行将多线程变成单线程的感觉哈。

    14834077834.jpg

    然后,我们现在解决了锁的问题,全部请求采用“先进先出”的队列方式来处理。那么新的问题来了,高并发的场景下,因为请求很多,很可能一瞬间将队列内存“撑爆”,然后系统又陷入到了异常状态。或者设计一个极大的内存队列,也是一种方案,但是,系统处理完一个队列内请求的速度根本无法和疯狂涌入队列中的数目相比。也就是说,队列内的请求会越积累越多,最终Web系统平均响应时候还是会大幅下降,系统还是陷入异常。

    1. 文件锁的思路
      对于日IP不高或者说并发数不是很大的应用,一般不用考虑这些!用一般的文件操作方法完全没有问题。但如果并发高,在我们对文件进行读写操作时,很有可能多个进程对进一文件进行操作,如果这时不对文件的访问进行相应的独占,就容易造成数据丢失

    优化方案4:使用非阻塞的文件排他锁

     <?php
     //优化方案4:使用非阻塞的文件排他锁
     include ('./mysql.php');
     //生成唯一订单号
     function build_order_no(){
       return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
     }
     //记录日志
     function insertLog($event,$type=0){
         global $conn;
         $sql="insert into ih_log(event,type)
         values('$event','$type')";
         mysqli_query($conn,$sql);
     }
     $fp = fopen("lock.txt", "w+");
     if(!flock($fp,LOCK_EX | LOCK_NB)){
         echo "系统繁忙,请稍后再试";
         return;
     }
     //下单
     $sql="select number from ih_store where goods_id='$goods_id' and sku_id='$sku_id'";
     $rs =  mysqli_query($conn,$sql);
     $row = $rs->fetch_assoc();
     if($row['number']>0){//库存是否大于0
         //模拟下单操作
         $order_sn=build_order_no();
         $sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price)
         values('$order_sn','$user_id','$goods_id','$sku_id','$price')";
         $order_rs =  mysqli_query($conn,$sql);
         //库存减少
         $sql="update ih_store set number=number-{$number} where sku_id='$sku_id'";
         $store_rs =  mysqli_query($conn,$sql);
         if($store_rs){
           echo '库存减少成功';
             insertLog('库存减少成功');
             flock($fp,LOCK_UN);//释放锁
         }else{
           echo '库存减少失败';
             insertLog('库存减少失败');
         }
     }else{
       echo '库存不够';
         insertLog('库存不够');
     }
     fclose($fp);
      ?>
    
     <?php
     //优化方案4:使用非阻塞的文件排他锁
     include ('./mysql.php');
     //生成唯一订单号
     function build_order_no(){
       return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
     }
     //记录日志
     function insertLog($event,$type=0){
         global $conn;
         $sql="insert into ih_log(event,type)
         values('$event','$type')";
         mysqli_query($conn,$sql);
     }
     $fp = fopen("lock.txt", "w+");
     if(!flock($fp,LOCK_EX | LOCK_NB)){
         echo "系统繁忙,请稍后再试";
         return;
     }
     //下单
     $sql="select number from ih_store where goods_id='$goods_id' and sku_id='$sku_id'";
     $rs =  mysqli_query($conn,$sql);
     $row = $rs->fetch_assoc();
     if($row['number']>0){//库存是否大于0
         //模拟下单操作
         $order_sn=build_order_no();
         $sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price)
         values('$order_sn','$user_id','$goods_id','$sku_id','$price')";
         $order_rs =  mysqli_query($conn,$sql);
         //库存减少
         $sql="update ih_store set number=number-{$number} where sku_id='$sku_id'";
         $store_rs =  mysqli_query($conn,$sql);
         if($store_rs){
           echo '库存减少成功';
             insertLog('库存减少成功');
             flock($fp,LOCK_UN);//释放锁
         }else{
           echo '库存减少失败';
             insertLog('库存减少失败');
         }
     }else{
       echo '库存不够';
         insertLog('库存不够');
     }
     fclose($fp);
      ?>
    
    1. 乐观锁思路

    这个时候,我们就可以讨论一下“乐观锁”的思路了。乐观锁,是相对于“悲观锁”采用更为宽松的加锁机制,大都是采用带版本号(Version)更新。实现就是,这个数据所有请求都有资格去修改,但会获得一个该数据的版本号,只有版本号符合的才能更新成功,其他的返回抢购失败。这样的话,我们就不需要考虑队列的问题,不过,它会增大CPU的计算开销。但是,综合来说,这是一个比较好的解决方案。

    在这里插入图片描述
    有很多软件和服务都“乐观锁”功能的支持,例如Redis中的watch就是其中之一。通过这个实现,我们保证了数据的安全。

    优化方案5:Redis中的watch

     <?php
     $redis = new redis();
      $result = $redis->connect('127.0.0.1', 6379);
      echo $mywatchkey = $redis->get("mywatchkey");
     /*
       //插入抢购数据
      if($mywatchkey>0)
      {
          $redis->watch("mywatchkey");
       //启动一个新的事务。
         $redis->multi();
        $redis->set("mywatchkey",$mywatchkey-1);
        $result = $redis->exec();
        if($result) {
           $redis->hSet("watchkeylist","user_".mt_rand(1,99999),time());
           $watchkeylist = $redis->hGetAll("watchkeylist");
             echo "抢购成功!<br/>";
             $re = $mywatchkey - 1;  
             echo "剩余数量:".$re."<br/>";
             echo "用户列表:<pre>";
             print_r($watchkeylist);
        }else{
           echo "手气不好,再抢购!";exit;
        } 
      }else{
          // $redis->hSet("watchkeylist","user_".mt_rand(1,99999),"12");
          //  $watchkeylist = $redis->hGetAll("watchkeylist");
             echo "fail!<br/>";   
             echo ".no result<br/>";
             echo "用户列表:<pre>";
           //  var_dump($watchkeylist); 
      }*/
     $rob_total = 100;   //抢购数量
     if($mywatchkey<=$rob_total){
         $redis->watch("mywatchkey");
         $redis->multi(); //在当前连接上启动一个新的事务。
         //插入抢购数据
         $redis->set("mywatchkey",$mywatchkey+1);
         $rob_result = $redis->exec();
         if($rob_result){
              $redis->hSet("watchkeylist","user_".mt_rand(1, 9999),$mywatchkey);
             $mywatchlist = $redis->hGetAll("watchkeylist");
             echo "抢购成功!<br/>";
           
             echo "剩余数量:".($rob_total-$mywatchkey-1)."<br/>";
             echo "用户列表:<pre>";
             var_dump($mywatchlist);
         }else{
               $redis->hSet("watchkeylist","user_".mt_rand(1, 9999),'meiqiangdao');
             echo "手气不好,再抢购!";exit;
         }
     }
     ?>
    
    展开全文
  • 事务并发引起的问题解决方法

    万次阅读 2020-10-15 18:12:36
    事务并发引起的问题解决方法 问题: 脏读(Dirty reads):脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。 时间 取款事务...

    事务并发引起的问题及解决方法

    问题:

    1. 脏读(Dirty reads):脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。
      1. 时间取款事务A存款事务B
        T1开始事务 
        T2 开始事务
        T3 查询账户余额为1000元
        T4 汇入100元把余额改为1100元
        T5查询账户余额为1100元(读取脏数据) 
        T6 回滚
        T7取款1100 
        T8提交事务失败 

         

    2. 不可重复读(Nonrepeatable read):不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。
      1. 时间取款事务A存款事务B
        T1开始事务 
        T2 开始事务
        T3查询账户余额为1000元 
        T4 汇入100元把余额改为1100元
        T5 提交事务
        T6  
        T7查询帐户余额为1100元 
        T8提交事务 
    3. 幻读(Phantom read):幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录。
      1. 时间查询学生事务A插入新学生事务B
        T1开始事务 
        T2 开始事务
        T3查询学生为10人 
        T4 插入1个学生
        T5查询学生为11人 
        T6 提交事务
        T7提交事务 

    解决方法:

    数据库的事务隔离机制

     i. 查看 getConnection() 文档

     ii. 1:read-uncommitted  2:read-committed  4:repeatable read  8:serializable(数字代表对应值)

         为什么取值要使用 1 2 4 8 而不是 1 2 3 4

         1=0000  2=0010 4=0100 8=1000(位移计算效率高)

    1. 只要数据库支持事务,就不可能出现第一类丢失更新
    2. read-uncommitted(允许读取未提交的数据) 会出现dirty read, phantom-read,non-repeatable read 问题
    3. read-commited(读取已提交的数据 项目中一般都使用这个)不会出现dirty read,因为只有另一个事务提交才会读出来结果,但仍然会出现 non-repeatable read 和 phantom-read。使用read-commited机制可用悲观锁 乐观锁来解决non-repeatable read 和 phantom-read问题
    4. repeatable read(事务执行中其他事务无法执行修改或插入操作     较安全)
    5. serializable解决一切问题(顺序执行事务 不并发,实际中很少用)

    设定hibernate的事务隔离级别(使用hibernate.connection.isolation配置 取值1、2、4、8)

    i.hibernate.connection.isolation = 2(如果不设 默认依赖数据库本身的级别)

    ii.用悲观锁解决repeatable read的问题(依赖于数据库的锁

    1. select ... for update
    2. 使用另一种load方法--load(xx.class , i , LockMode.Upgrade)
    • LockMode.None无锁的机制,Transaction结束时,切换到此模式
    • LockMode.read在査询的时候hibernate会自动获取锁
    • LockMode.write insert  update hibernate 会自动获取锁
    • 以上3种锁的模式,是hibernate内部使用的(不需要设)
    • LockMode.UPGRADE_NOWAIT是 ORACLE 支持的锁的方式

    Hibernate(JPA)乐观锁定(ReadCommitted)

    实体类中增加version属性(数据库也会对应生成该字段,初始值为0),并在其get方法前加

    @Version注解,则在操作过程中没更新一次该行数据则version值加1,即可在事务提交前判断该数据是否被其他事务修改过.

    展开全文
  • 主要介绍了PHP+Redis事务解决并发下商品超卖问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
  • MySQL - 并发事务问题解决方案

    千次阅读 2020-08-10 20:49:47
    并发事务处理也会带来一些问题,如:脏读、不可重复读、幻读等等 脏读 一个事务正在对一条记录做修改,在这个事务完成并提交前,这条记录的数据就处于不一致状态;这时,另一个事务也来读取同一条记录,如果不加...

    在这里插入图片描述


    生猛干货

    带你搞定MySQL实战,轻松对应海量业务处理及高并发需求,从容应对大场面试


    Pre

    MySQL - 多版本控制 MVCC 机制初探

    中我们初步了解了 MVCC 的原理及其实现。 随着数据库并发事务处理能力的增强,数据库资源的利用率也会大大提高,从而提高了数据库系统的事务吞吐量,可以支持更多的用户并发访问。

    但并发事务处理也会带来一些问题,如:脏读、不可重复读、幻读等等


    脏读

    一个事务正在对一条记录做修改,在这个事务完成并提交前,这条记录的数据就处于不一致状态;这时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些“脏”数据,并据此做进一步的处理,就会产生未提交的数据依赖关系。这种现象被形象的叫作"脏读"(Dirty Reads)。

    简答来说,读取了其他事务未提交的数据


    不可重复读

    一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被删除了!这种现象就叫作“ 不可重复读”(Non-Repeatable Reads)。


    幻读

    一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读”(Phantom Reads)。


    Solutions

    MySQL 数据库是通过事务隔离级别来解决上述问题的。

    在这里插入图片描述

    我们举例说明“脏读”和“不可重复读”的问题

    【 RC 隔离级别】
    MySQL 中默认的事务隔离级别是 RR,这里设置成 RC 隔离级别,此时提交事务 B 修改 id=1 的数据之后,事务 A 进行同样的查询操作,后一次和前一次的查询结果不一样,这就是不可重复读(重新读取产生的结果不一样了)。这里事务 A 读到了事务 B 提交的数据,即是“脏读”。

    在这里插入图片描述


    【RR隔离级别】

    下面我们来看看在mysql默认的RR隔离级别下的情况。当 teacher_id=1时,事务 A 先进行一次读取操作,事务 B 中间修改了 id=1 的数据并提交,事务 C 也插入了一条数据并提交。事务 A 第二次读到的数据和第一次完全相同。所以说它是可重读的。

    在这里插入图片描述


    【幻读】

    行锁可以防止不同事务版本的数据在修改提交时造成数据冲突的情况。但如何避免别的事务插入数据造成的问题呢。我们先来看看在 RC 隔离级别下的处理过程。

    如下图所示,事务 A 修改了所有 teacher_id=30 的数据,但是当事务 B INSERT 新数据后,事务 A 发现莫名其妙的多了一行 teacher_id=30 的数据, 而且没有被之前的 UPDATE语句所修改,这就是“当前读”的幻读问题。

    在这里插入图片描述
    跟上面的例子一样,也是在 RC 事务隔离级别下,这时事务 B INSERT 了一条数据,并提交,而事务 A 读到了事务 B 新插入的数据。这也是幻读,如下图所示。

    在这里插入图片描述

    这里就需要重点注意不可重复读和幻读的区别了。前面讲了它们的含义,这个提醒大家的是:不可重复读重点在于 UPDATA 和 DELETE,而幻读的重点在于 INSERT。它们之间最大的区别是如何通过锁机制来解决它们产生的问题。这里说的锁只是使用悲观锁机制。

    那么在 RR 隔离级别下,事务 A 在 UPDATE 后加锁,事务 B 无法插入新数据,这样事务 A在 UPDATE 前后读的数据保持一致,避免了幻读。

    跟上面的案例一样,也是在 RR 事务隔离级别下,事务 A 在 UPDATE 后加锁,对于其他两个事务,事务 B 和事务 C 的 INSERT 操作,就必须等事务 A 提交后,才能继续执行。这里就用到了“锁”,这里使用的是 Gap 锁,后面会详细讲解。它和上面的情况一样,解决了“幻读”的发生,如下图所示。

    在这里插入图片描述


    搞定MySQL

    在这里插入图片描述

    展开全文
  • 事务并发的可能问题与其解决方案

    千次阅读 2020-01-21 21:11:08
    一、事务并发问题 这些问题可以归结为5类,包括3类数据读问题(脏读、不可重复读和幻象读)以及2类数据更新问题(第一类丢失更新和第二类丢失更新) 脏读(dirty read) A事务读取B事务尚未提交的更改数据,并...
  • 事务并发问题及处理方式

    千次阅读 2017-09-15 01:08:13
    通常为了获得更好的运行性能,各种数据库都允许多个事务同时运行,这就是事务并发。当并发的事务访问或修改数据库中相同的数据时,通常需要采取必要的隔离机制,反之会出现各种并发问题。这些并发问题可归纳为以下三...
  • 使用悲观锁解决事务并发问题  悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的...
  • 主要介绍了PHP+MySQL高并发加锁事务处理问题解决方法,结合实例形式分析了PHP+MySQL事务处理相关操作技巧与注意事项,需要的朋友可以参考下
  • MySQL数据库事务并发问题解决

    千次阅读 2019-05-16 16:29:35
    常见的关系型数据库有MySQL、Oracle、SQLServer等,其中MySQL成为目前互联网企业应用...接下来小编对数据库事务以及并发问题解决进行分析和总结: 预计阅读时间:5分钟 什么是事务 事务是一条或多条数据库操作语...
  • 事务并发带来的三大问题 脏读    如下图,左右两个事务A、B。事务A首先查询id=1的数据,得到age=16之后,事务B对id=1的数据,将age=16更新为age=18,然后事务A,再查询id=1的数据,这种在一个事务里边,多次读取...
  • 五、使用悲观锁解决事务并发问题 六、使用乐观锁解决事务并发问题    Hibernate事务与并发问题处理(乐观锁与悲观锁) 一、数据库事务的定义  数据库事务(Database Transaction)
  • 事务并发问题以及处理方法

    千次阅读 2017-09-10 14:59:58
    事务并发问题: 1. 第一类丢失更新(lost update): 在完全未隔离事务的情况下,两个事物更新同一条数据资源,某一事物异常终止,回滚造成第一个完成的更新也同时丢失。 2. 脏读(dirty read):如果第二个事务查询...
  • 增删改查是大部分框架的功能,如果有两个并发请求修改同一个数据,这个时候,你会怎么办?或者插入本来应该是唯一却重复的数据时应该怎么办...但如果有两个同时发生的并发事务怎么办?使用事务隔离级别,这是ACID中的定
  • 不同的事务隔离级别能处理不同的事务并发问题: 事务隔离级别 事务隔离级别 脏读 不可重复读 幻读 READ_UNCOMMITTED 允许 允许 允许 READ_COMMITTED 禁止 允许 允许 REPEATABLE_READ 禁止 禁止 允许 SERIALIZABLE ...
  • ACID 原子性(Atomic):事务包含的所有操作,要么全做,要么全不做回滚; 一致性(Consistency):从一个一致状态到另一个一致状态;eg:A、B之间转账,两者的金额总和转账前后...事务并发引起的问题以及如何...
  • 深入理解分布式事务,高并发下分布式事务解决方案
  • 常见事务并发问题以及处理方法

    千次阅读 2015-02-05 21:40:21
    1、数据库事务并发会引起那些常见问题以及对应的解决方法? 1)丢失更新:撤消一个事务时,把其它事务已提交的更新的数据覆盖了。 2)脏读:一个事务读到另一个事务未提交的更新数据。 3)幻读:一个事务执行两...
  • 什么是事务 及 事务并发引起的问题

    千次阅读 2019-03-09 11:06:21
    1、通常认为:事务仅仅与数据库相关,事务是为解决数据安全操作提出的,事务控制就是控制数据的安全访问(例如转账业务) ...二、事务并发处理可能引起的问题: 脏读:一个事务读取了另一个事务尚未提交的事务...
  • 很好的一篇博文 点击打开链接
  • 减库存可以采用同步调用(商品微服务提供接口,通过Feign调用),也可以采用异步...若库存不足,则减库存失败,但是订单微服务中并不知道减库存失败,因此事务不会回滚,这就是分布式事务问题 (跨服务的事务)。我...
  • 此方法可能不太完美,但是能在一定程度上解决并发问题。本文以高并发抢单来举例。 首先科普一下mysql隔离级别 mysql有四个隔离级别: Read Uncommitted(读取未提交内容) Read Committed(读取提交内容) ...
  • 数据库事务并发带来的问题

    千次阅读 2013-08-20 11:26:38
    数据库事务并发带来的问题有:更新丢失、脏读、不可重复读、幻象读。假设张三办了一张招商银行卡,余额100元,分别说明上述情况。 1、更新丢失:一个事务的更新覆盖了另一个事务的更新。事务A:向银行卡存钱100元...
  • 并发事务问题以及解决方案

    千次阅读 2019-04-27 18:11:22
    事务的概念 事务可以理解为一个独立的工作单元, 在这个独立的工作单元中, 有一组操作; 放在事务(独立工作单元)中的多个操作, 要么全部执行成功, 要么全部执行失败。 不免俗套, 这还是通过最经典的银行转账应用来...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 202,592
精华内容 81,036
关键字:

如何解决事务的并发问题