zoukankan      html  css  js  c++  java
  • Entity Framework 4 in Action读书笔记——第八章:EF处理并发和事务:了解并发问题

    本章内容包括
    理解并发问题
    配置并发
    处理并发异常
    处理事务

    假设你想在网上订机票。查询你的航班,发现还有座位,但是当你单击预订按钮时,系统提示机票已售罄。如果再次查询该航班,发现已经没有座位了。这是怎么回事呢?原来是在你查询到结果和预订之间,其他人预订了最后一张票。系统对最后一张票进行了并发查询,如果没有并发检查,该航班的机票就会超额预订。

    本章将深入并发和事务处理。现在我们先来了解什么是并发问题。

    8.1 了解并发问题

    8.1.1 并发更新情况

    考虑这样的情况:一个大客户有不同的部门下订单。部门1打电话给员工1下订单。几个小时后,部门1又打电话给员工1修改订单。同时,部门2打电话给员工2修改同一个订单,添加一些产品。两个员工同时检索该订单,员工1修改了一些details,添加了几个并且保存订单。员工2移除一个detail,1分钟后保存订单。这两个员工不会意识到存在问题,最终将错误的货物交付给客户。

    还有更糟糕的情况。如果部门2打电话要删除该订单,客户就会失去整个订单。下图说明了在持久化期间没有处理并发引起的潜在问题。

    了解并发问题

    从技术上来说,程序中的每个可编辑的实体都存在争用的情况。两个用户在不知情的情况下操作同一个数据总是可能的。但是在现实世界中,有非常少的数据受到争用,常常不是太重要而不值得并发检查。

    例如,订单数据非常重要,必须总是检查。客户和供应商数据当然也很重要,但是它们没有订单重要。此外,客户和供应商不经常更新,减少了很多风险。因此,它们可能不值得处理并发检查。同样的考虑也适用于产品。产品添加到数据库后,除了价格和库存量外很少修改。

    现在问题明确了,让我们来看看可能的解决方案。 首先是采用悲观方法,锁定数据库行,直到它们被更新。这种方法有利也有弊。

    8.1.2 第一种解决方案:悲观并发控制

    当需要更新数据同时保证没有其他人也这样做时,最安全的方法是以独占方式物理锁定数据库中的数据。当数据被锁定,其他用户就不能再访问它了,读取或更新都不行。这种方式,没有其他人可以更改数据,直到数据被更新或者解除锁定。这种方法称为悲观并发,如下图所示:

    了解并发问题

    悲观并发控制的好处是它允许单个用户以独占方式访问信息,消除了任何可能的争用。但从性能和使用上来说以独占方式锁定会产生很多并发症状。

    以独占方式锁定行,其他用户也无法读取信息。这意味着即使其他用户想查看订单也不可以。如果持有锁定的用户需要操作订单很长时间,就会减慢其他人的工作。更糟糕的是,如果程序崩溃(不要说不可能发生)了,数据会一直保持锁定,直到过期或者DBA手动移除它。

    悲观并发是一种好的技术,但它弊大于利。除非你不得已使用它,那么其他的方法更合适。

    悲观并发背后的理念是因为数据可能存在争用,数据由第一个读取者锁定。解决并发问题的另一个技术是反其道而行之:因为数据并不频繁争用,锁定数据是没有意义的,我们让争用检查只在更新阶段执行。

    8.1.3 更好的解决方案:乐观并发控制

    多个用户同时操作同一个订单或客户抑或其他的频率有多高?如果发生这种情况,对于更新数据成功的第一个人和其他必须重新应用它们修改的人是不是一个问题?通常这些问题的答案分别是“几乎不”和“不”。

    在这种情况下锁定只会浪费资源,因为很少或者没有争用。应该采取更合适的办法。即使修改数据也应该允许所有的用户读取数据,但是当更新数据库时,必须检查自从检索了数据起它就没有发生变化。这就是乐观并发方法。

    我们这样做的最简单方法是为每一行的修改添加一个版本控制列。在并发修改的情况下,第一个保存的数据成功,其他的就不能更新数据,因为自从读取了数据,版本已经发生了变化。如下图所示:

    了解并发问题

    检查版本(version)列很容易,行的真正键是主键列加上版本列,如下:

    UPDATE [order]
    SET ..., version = 2
    WHERE OrderId = 1 AND version = 1

    如果查询能更新行,你就知道自从读取order,版本还没有发生改变。如果查询不能更新任何行,那是因为版本号已经发生了改变,存在并发问题。上图中,两个员工发出相同的UPDATE:员工1成功了,员工2获得一个异常,这是因为版本已经发生了改变。

    使用乐观并发控制,建议使用为行版本自动生成的列(SQL Server中使用TimeStamp或RowVersion),这样就不用手动处理它的值了。

    乐观并发提升了系统的可扩展性和可使用性,因为数据总是可读取的。主要的缺点是除非你写一些复杂的代码,否则得到并发异常的用户必须重新读取更新数据,然后应用修改。通常浪费的时间是可接受的,有时不是。

    8.1.4 介于两者之间的解决方案:悲观/乐观并发控制

    这个解决方案采取前两种方法有利的地方,它不允许对订单的并发修改,,也不允许物理的锁定数据库中的行。

    它工作的方式很简单。首先给表添加版本(version)列和标识(flag)列。然后,当用户修改了数据,用户的程序就会发出一个命令给数据库,将flag列设置为true。如果两个用户同时这样做,第一个成功。然后,任何人都可以读取数据,但是没有人可以修改它,因为客户端检查flag,拒绝任何修改,直到flag被设置为false。这就是悲观/乐观方法。

    这个技术最大限度的减少了数据争用,但是它遭受悲观并发控制一样的限制。如果程序崩溃了,锁定必须手动移除。如果修改订单的用户去喝咖啡或者参加会议而没有保存或者取消修改,其他用户也不能修改。你可以创建一个监督程序,超过一定时间就解除锁定,但是这增加了处理并发的复杂性。

    悲观/乐观方法不是完美的解决方案。广泛而言,乐观并发是大多数情况下最好的选择。如果它不适合你的需要,那么尝试悲观/乐观方法,只有这两种方法都不合适时才使用悲观并发。

    现在已经对并发问题有了清楚的了解,是时候看看如何从数据库的角度来处理它以及EF是如何帮助处理并发的了。

    作者:BobTian
    出处http://nianming.cnblogs.com/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
    欢迎访问我的个人博客:程序旅途
  • 相关阅读:
    Python中所有的关键字
    关于selenium的8种元素定位
    对提示框的操作
    selenium+webservice进行百度登录
    MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled...报错解决
    Vue中使用echarts
    npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142解决方法
    插入排序
    冒泡排序优化
    roject 'org.springframework.boot:spring-boot-starter-parent:XXX' not found 解决
  • 原文地址:https://www.cnblogs.com/nianming/p/2247300.html
Copyright © 2011-2022 走看看