• Entity Framework 乐观并发控制


    一、背景

      我们知道,为了防止并发而出现脏读脏写的情况,可以使用Lock语句关键字,这属于悲观并发控制的一种技术,,但在分布式站点下,锁的作用几乎不存在,因为虽然锁住了A服务器的实例对象,但B服务器上的锁是不知道的A服务器上锁的情况的,所以,面对分布式站点、单一数据库这种架构,我们可以使用EntityFramework的乐观并发控制来解决这个问题,EF对并发控制有不管控和乐观并发控制两种,默认情况是不管控,但EF不支持悲观并发。
    lock 确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。
     

    二、悲观并发和乐观并发

    悲观并发:比如有两个用户A,B,同时登录系统修改一个文档,如果A先进入修改,则系统会把该文档锁住,B就没办法打开了,只有等A修改完,完全退出的时候B才能进入修改。

    乐观并发:同上面的例子,A,B两个用户同时登录,如果A先进入修改紧跟着B也进入了。A修改文档的同时B也在修改。如果在A保存之后B再保存他的修改,此时系统检测到数据库中文档记录与B刚进入时不一致,B保存时会抛出异常,修改失败。

    乐观并发的基本出发点是:当保存数据的时候抱着一种乐观的态度,不期望发生并发冲突,即使万一发生并发冲突,也能捕捉到冲突异常,然后根据策略解决冲突,而解决冲突的方式一般分为Client wins(以后操作者为赢) 和 Store wins(以先存储的数据为赢)。

     

    三、EF中如何控制并发

    第1步、
      在设计器中,对需要进行并发控制的字段的ConcurrencyMode并发模式设置为Fixed,这是检测是否发生冲突的指标,该字段最终会在EF生成SQL时的where子句出现,如果没有设置为Fixed,即使该字段出现并发冲突,EF也不会报出并发异常,从而会导致出现脏读脏写的情况。
     
     
     
    第2步、

     Resolving optimistic concurrency exceptions with Reload

      使用Reload数据作为解决乐观并发异常的策略之一,我在这里就讲数据Reload这一种策略就好了,除了Reload外,还有其他几种冲突解决策略,详见参考文献中EF官方团队博客。

      微软Entity Framework 团队官方博客 推荐处理乐观并发冲突的策略之一是Reload数据,也就是EF检测到并发冲突时会抛出DbUpdateConcurrencyException,这时解决冲突分为Client Wins或者Store Wins ,而Reload处理也就是Store Wins,意味着放弃当前内存中的实体,重新到数据库中加载当前实体,EF官方团队给出来的示例代码如下,其他几种策略请见参考文献链接。 
     
    using (var context = new UnicornsContext())
    {
        bool saveFailed;
        do
        {
            saveFailed = false;
            var unicorn = context.Unicorns.Find(1);
            unicorn.Name = "tom";
            try
            {
                context.SaveChanges();
            }
            catch (DbUpdateConcurrencyException ex)
            {
                saveFailed = true;
                // Update the values of the entity that failed to save 
                // from the store
                ex.Entries.Single().Reload();
            }
        } while (saveFailed);
    }

    四、SqlProfiler看原理

      完成以上步骤后,您的程序就具备了乐观并发处理的能力了,但是,其中的原理是什么呢?从SqlProfiler监控结果来看,EF是将并发模式设置为Fixed的字段放在where子句里,后操作会因为取不到相应的记录而更新失败,打个比方说,当两个用户同时对同一条记录(ID=1,Count=10)进行读、写操作的时候,A、B同时读取记录的时候,Count都等于10,A用户先进行Update减1操作,此时(ID=1,Count=9),而此时B用户稍微晚一点点再进行Update操作的时候,因为Count已经被A修改成9了,已经不存在(ID=1,Count=10)的记录了,所以B最终执行影响行数为0,EntityFramework抛出并发异常:


    如:我们给Count属性的并发模式设置成Fixed的话,那生成的SQL语句如下:

    
    
    exec sp_executesql N'update [dbo].[OrderLog]
    set [Count] = @0
    where (([ID] = @1) and ([Count] = @2))',N'@0 int,@1 int,@2 int',@0=78,@1=1,@2=9 

    当EntityFramework执行更新操如果影响行数为0,就会抛出异常,相关源代码如下所示:


     

    五、总结

       个人认为,乐观并发控制适用于并发量还不是很大情况,也就是符合乐观并发的初衷,当保存数据的时候不期望发生并发冲突,一旦发生冲突,也能捕捉到冲突异常,然后根据策略解决冲突或者提示用户操作失败等,但是,当并发量很大的时候,我认为乐观并发就显得并不那么适用了,个人建议,做评估项目并发风险和做并发冲突的测试,假如完全可以接受,大可以应用EntityFramework的乐观并发控制,实现起来也比较简单,假如项目并发量确实很大,那可以考虑别的技术方案实现,比如消息队列……等。

     

    参考文献

    (1)微软EntityFramework团队博客: Using DbContext in EF 4.1 Part 9: Optimistic Concurrency Patterns

    (2)Gyoung: Entity Framework 并发处理

  • 相关阅读:
    Google Dremel 原理 如何能 3 秒分析 1PB
    [转]Git详解之一 Git起步
    [转] SharePoint 2013 安装图解
    Fixing an incomplete VM that’s stuck in the Creating state
    [转] Exchange 2013 安装部署详解
    NewSQL为何使传统关系数据库黯然失色?
    [转]盘点Google Reader以外的RSS阅读器
    2013年中国数据库大会PPT
    SCDPM 2012 详细讲解
    [转]SharePoint 2013配置开发环境,需安装VS2012插件
  • 原文地址:https://www.cnblogs.com/waynechan/p/3878235.html
Copyright © 2020-2023  润新知