PoEAA笔记- 并发-5.3 隔离不变性...

5.3 隔离不变性

        并发问题由来已久,对企业应用来说,有两个非常重要的解决方案,一个是隔离,一个是不变性。
        并发问题发生在多个执行单元访问同一片数据的时候,一个解决方法是隔离,划分数据,使得每一片数据都只能被同一个执行单元访问。操作系统为每一个进程单独分配一片内存,并且只有这个进程可以对这片内存进行读或者写操作。
        隔离是一种减少错误发生几率的有效技术,好的并发设计应该是:找到各种创建隔离区的方法,并且保证在每个隔离区里能够完成尽可能多的任务。
        只有在共享数据可以修改的情况下,并发问题才会出现。所以,一个避免并发冲突的方法是识别哪些是不变的数据,显然,使所有数据都不变是不可能的,因为许多系统本来就要对数据进行修改。另一个观点是把那些只读数据的程序分开,让他们只使用拷贝的数据源,这样就可以放松所有的并发控制。

5.4 乐观并发控制和悲观并发控制

        如果把乐观锁看作是冲突检测的,那么悲观锁就是冲突避免的。
        悲观锁的问题就是减少了并发的程度。乐观锁则允许人们更自由一些,因为只有在数据提交的时候遭到阻碍。
        乐观锁和悲观锁之间进行选择的标准是冲突的频率和严重性。如果冲突很少,或者冲突的后果不是很严重,那么通常情况下是乐观锁。因为它能得到更好的并发性,而且更容易实现,但是如果冲突的结果对用户来说是痛苦的,那么就使用悲观锁。

5.4.1 避免不一致读

        对于不一致读问题,悲观锁策略通过读写锁解决这一问题,对于读锁,可以同一时间多个人加锁,但无法获得写锁,如果获得了写锁,则其他人无法获得两种类型的锁。
        乐观锁策略通常将冲突检测建立在数据的版本号上。可能是一个时间戳,或者是顺序计数器。系统检测需要根系数据的岸本号和共享数据的版本号。如果两者一致,则更新数据和版本号,如果不同,则发生了不一致读。
        如果对所有读取的数据都进行访问控制,则经常会因为实际上并不严重的冲突或者因为等待数据而导致不必要的问题。如果在开始修改之后有一个产品出现在产品列表里,不会有什么重要的影响,但如果是已经汇总的费用清单,问题就会严重得多,不管使用哪种形式的并发控制,指出哪些需要控制,哪些不需要控制,实在是一件棘手的事。
        另一种处理不一致读问题的方法是使用时序读,在每次读取数据的时候都使用某种时间戳或其它不变的标签作为约束条件,数据库根据时间或标签返回数据,很少有数据库具有这样的功能,但是开发者们经常在源代码控制系统中遇到它。问题是数据源需要提供修改历史的完整时序列表,而这需要时间与空间来处理。

5.4.2 死锁

        对悲观锁技术有一个很特别的问题是死锁,就是相互等待资源的场景。
        处理死锁的技术有:1.用软件检测死锁的发生,在这种情况下需要一个牺牲者,放弃它的工作和它所加的锁;2.另一种方法是为锁加上一个时间限制,一旦到达时间限制,所加的锁就会失效,工作就会丢失——实际上就成为了一个牺牲者。超时控制看起来比检测机制容易一些,但是会出现一个问题:在实际上没有加锁的情况下,有人会因为持锁的时间过长而成为牺牲者。
        防止死锁的方法就是强制人们在开始工作的时候就获得所有可能需要的锁,在此之后就不允许得到更多的锁。
        如果保守的话,可以使用多种方案,例如,可以强制所有人都在开始的时候获得全部可能需要的锁,再加上时间限制,以防止意外。

5.5 事务

        在企业应用中处理并发主要的工具就是事务。
        首先,事务是一个有边界的工作序列,开始和结束都有明确定义。

5.5.1 ACID

        软件事务经常使用ACID的属性来描述。

  • 原子性(Atomicity):在一个事务里,动作序列的每一个步骤都必须是要么全部成功,要么所有工作都将回滚。部分完成不是一个事务概念。
  • 一致性(Consistency):在事务开始和完成的时候,系统的资源都必须处于一致的,没有被破坏的状态。
  • 隔离性(Isolation):一个事务,直到它被成功提交之后,它的结果对于任何其它事务才是可见的。
  • 持久性(Durability):一个已提交事务的任何结果都必须是永久性的,即“在任何系统崩溃的情况下都能保存下来”。

5.5.2 事务资源

        大多数企业应用是在数据库方面涉及事务,但还有很多其它情况要进行事务控制,比如消息队列、打印机和ATM等。于是,在进行技术讨论的时候,用术语“事务资源”来表示可以进行事务处理的任何事物——即用事务来控制并发过程。
        为处理最大的吞吐率,现代的处理系统被设计成保证事务尽可能短,因此让事务不能跨越多个请求,跨越多个请求的事务叫长事务。因此,通常在请求开始的时候启动事务,请求结束的时候提交事务。
        另一种方法是尽可能晚打开事务,使用延迟事务时,应在事务外完成读取数据的操作,只在修改数据的时候启动事务。这样做减少了事务的执行时间。在启动事务和第一次写操作之间有较长时间间隔的情况下,这样做更能增加系统的灵活性。然而,这意味着在事务启动前,没有任何并发控制机制,可能会导致不一致读问题,因此通常并不这么做,除非数据竞争很激烈,或者业务事务跨越多个请求。
        对于许多数据库操作来说,事务系统锁住的是数据行,这样允许多个事务同时访问一个表。然而,如果一个事务锁住了一个表的许多行,则数据库无法处理这么多锁,只能将锁升级到锁住整个表。这种锁升级对并发有很大影响。这也正是为什么不能在领域的层超类型级别上使用“对象”表的原因。

5.5.3 减少事务隔离以提高灵活性

        隔离级别有4种,可串行化是最强的级别,其它每个级别都允许某种程度的不一致读。
        可串行化:当可以并发执行并且结果与以某种顺序依次执行的结果相同时,事务就是可串行化的。可串行化无法保证同样条件下多次运行后得到相同的结果。
        仅次于可串行化的时可重复读,这时允许幻读,这种幻读出现在你向一个集合中加入一些元素而读的人只能看到其中一部分的时候。出现幻读的原因是读的结果只对事物的一部分有效。
        下一级别是读已提交,它允许不可重复读。出现在一个人读取了当前数据,另一个人更新了前一个人读取的数据,而且更新了剩下的数据,这时数据便发生错误。
        最低级别是读未提交,允许脏读,这时可以读取其它事务中还未提交的数据,这样会带来2种错误。Martin可能会在David刚添加到加锁包,一个文件,但没有添加第二个文件的时候读到David添加的第一个文件。第二种错误是如果David添加文件后事务回滚——这时Martin可能看到一些并不存在的文件。
        不必给所有的事务设置相同的隔离级别,而应该仔细观察每个事务并根据每个事务具体情况来决定如何权衡灵活性与正确性。

隔离级别和所允许的不一致读错误
隔离级别脏读(dirty reads)不可重复读(unrepeatable reads)幻读(phantoms)
读未提交(Read Uncommitted)
读已提交(Read Committed)
可重复读(Repeatable Read)
可串行化(Serializable)

5.5.4 业务事务和系统事务

所谓系统事务,也就是由关系数据库和事务监视器锁支持的事务。数据库事务就是一组SQL命令,这组SQL命令内开始和终止这个数据库事务的指令来定界。
        系统事务对于一个业务系统用户来说没有什么意义,对于一个在线银行系统用户来说,一个事务包含登录,选择账户,填写某些账单,最后点OK按钮付账。这就是所谓业务事务,并且我们希望它能显示出与系统事务一样的ACID属性。业务事务常常要通过多次请求才能完成,因此用单个系统事务的实现会产生长系统事务,而大多数的事务系统并不能有效支持长事务。使用长事务可以避免许多麻烦,然而,应用将失去可伸缩性,因为长事务使数据库成为主要瓶颈。另外,将长事务改写成短事务是一个复杂且不好理解的过程。在这种情况下,只能把业务事务分成一系列的短事务,这意味着只能自己为跨系统事务的业务事务提供ACID支持——我们称之为李现并发问题,仅仅将一系列系统事务依次连接在一起是不是以支持一个业务事务的。应用程序必须采取措施将它们粘合起来。
        事务原子性和持久性是最容易为业务事务所支持的ACID属性。当用户在一个系统事务内点"Save"时,通过运行业务事务的提交阶段可以支持事务的原子性和持久性。在会话想要提交其所有修改的时候,业务事务启动一个系统事务,系统事务保证修改的数据将作为一个单元而提交,并将被持久化。唯一潜在的问题是要在业务事务的生命周期内维持一个正确的修改集。如果应用中使用了领域模型,采用工作单元可以正确地跟踪修改。
        业务事务的ACID属性中最麻烦的事隔离性,没有隔离性就没有一致性,一致性要求业务事务不要将记录集置于一种无效的状态下。在单个事务中,应用要支持一致性就需要满足所有的业务规则。在跨多个事务的时候,应需要保证一个会话不会破坏其他会话的工作,那样将使记录集处于丢失用户工作的无效状态。
        正如更新冲突的明显问题一样,有些小问题是关于不一致读的,当数据在多个系统事务中被读取时,无法保证它们是一致的。不同的读操作甚至会将不一致的数据引入内存中,从而导致应用程序出错。
        业务事务与会话密切相关,在用户看来,会话是一连串的业务事务,因此我们总是假设所有业务事务都是在单个客户会话中执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值