zoukankan      html  css  js  c++  java
  • EF乐观锁与悲观锁

    悲观锁:相信并发是绝大部分的,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞等待直到它拿到锁,并且每一个线程都必须要达到目的的。

    乐观锁:相信并发是极少数的,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号时间戳并发机制实现。

    在Entity Framework的默认环境情况下,系统会使用合并方式处理并发

    两种锁的使用场景

    从上面对两种锁的介绍,我们知道两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行return,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。

    EF中的高并发

      这里只介绍EF6中database-first开发方案的高并发解决方案,code-first开发方案中的高并发会在以后的EF CodeFirst系列中介绍。

      EF默认支持乐观并发:我们从数据库加载了一条数据,这是有人修改了这条数据,而我们手中用的还是旧数据,这就出现了脏读,这个时候我们修改了这条数据然后执行SaveChange()会发生什么呢?EF在保存数据时会首先查看数据库中的数据有没有改变过,数据没有改变就执行保存;数据改变了会抛出异常,我们再次提交前必须解决冲突(提到解决冲突是不是想到了git提交中的冲突?EF中解决高并发的方法和git提交的方法采用的思想是一样的,往下看就知道了)。

    1.使用步骤

    1.添加RowVersion列

      在EF中database-first开发模式中,为了解决高并发问题我们可以为数据表填加一个timestamp类型的的列,列名为rowversion,rowversion是一个二进制的数据,在每次的添加/修改操作后rowversion的值都会变大

    以Student实体为例,给Student表添加一个Rowversion列,类型为timestamp,如下所示:

    2.生成/升级EMD

    如果没有EDM通过数据库生成新的实体数据模型,如果有EDM则右击设计器->Update Model From Database ->Refresh Student table,这时我们就可以在设计器中看到RowVersion属性了,RowVersion属性的Concurrency Mode设置为Fixed,如下图

    做完这两步,EF API在执行Update时,会把RowVersion添加到where子句中(就像这样:update tb set cloName=xxx where Id=@id and RowVersion=@rowversion),如果where子句中的RowVersion值和数据库中的不一样就抛出DbUpdateConcurrencyException。

    2.一个栗子

    复制代码
    Student student = null; 
    
    using (var context = new SchoolDBEntities())
    {
        student = context.Students.First();
    }
    
    //修改学生名字
    Console.Write("Enter New Student Name:");
    student.StudentName = Console.ReadLine(); //Assigns student name from console
    
    using (var context = new SchoolDBEntities())
    {
        try
        {
            context.Entry(student).State = EntityState.Modified;
            context.SaveChanges();
    
            Console.WriteLine("修改成功!");
        }
        catch (DbUpdateConcurrencyException ex)
        {
            Console.WriteLine("发生高并发异常!");
        }
    }
    复制代码

    假设有两个用户都在执行上边的代码,User1和User2拿到了同一个Student实例,User1打字快1秒就把这个Student的用户修改了,并在数据库保存成功(User1执行Update时RowVersion和数据库一致,所以不报错,保存完成后Student的RowVersion自动改变了),这时User2也完成了修改,在User2执行保存到数据库时(生成的Update语句中的RowVersion和数据库中不一致了,所以抛出异常)。

    在并发抛出异常后可以根据业务需,向客户端返回消息,

    也可以直接处理冲突后的数据

         a 保留最后一次对象修改的值 用 RefreshMode.ClientWins

       b 保留最初的修改值   用 RefreshMode.StoreWins

       c  合并修改值  针对同对象不同属性一样可以 用 RefreshMode.StoreWins

    转债:https://www.cnblogs.com/leslies2/archive/2012/07/30/2608784.html

  • 相关阅读:
    HDU 1213 How Many Tables(并查集,简单)
    POJ 1611 The Suspects(并查集,简单)
    HDU 4539 郑厂长系列故事――排兵布阵(曼哈顿距离)
    POJ 2411 Mondriaan'sDream(状压DP)
    ZOJ 4257 MostPowerful(状压DP,简单)
    HDU 3001 Traveling(状压DP)
    POJ 3311 Hie with the Pie(Floyd+状态压缩DP)
    POJ 1185 炮兵阵地(状态压缩DP)
    POJ 3254 Corn Fields(状态压缩DP)
    XueXX and Chessboard(dp)
  • 原文地址:https://www.cnblogs.com/llsg/p/11358821.html
Copyright © 2011-2022 走看看