软件世界网 购物 网址 三丰软件 | 小说 美女秀 图库大全 游戏 笑话 | 下载 开发知识库 新闻 开发 图片素材
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
移动开发 架构设计 编程语言 Web前端 互联网
开发杂谈 系统运维 研发管理 数据库 云计算 Android开发资料
  软件世界网 -> 架构设计 -> 读《企业应用架构模式》第五章并发 -> 正文阅读

[架构设计]读《企业应用架构模式》第五章并发


       系统的许多交互并不是在一个数据库事务中就可以完成的,有时要求在跨事务的数据处理中管理好并发问题,这种并发,我们叫它离线并发。其含义是多数据库事务中数据操作的并发控制。
由并发造成的本质问题,一般也是并发控制的本质问题关注的焦点。如:更新丢失,不一致读
更新丢失,例如:A编辑一个文件,并对其中某一部分进行了修改,但是耗费了好几分钟的时间。但是在这段时间内,B同时编辑了该文件,并且很快就完成了提交,虽然B是在A之后开始,但是确是在A之前完成。那么,很不幸的是,A读取的文件并没有包含B的更新,因此A完成输入,提交时就会覆盖B更新过的版本,造成了B的更新就永远丢失了。
不一致读:通常发生在读取两份各自正确的数据,而他们却在同一时间互相矛盾时。例如:A在编辑UserController类,并增加了一些对UserServiceImpl类的调用,然而B编辑了UserServiceImpl类,并修改了该类的接口,B将这个类编辑完后提交。然而A在把UserController类编译完后提交,由于UserServiceImpl代码已经被更换了,但是A却不知道。这就造成了AB两个人关于UserServiceImpl类的读取不一致性。
上述的两个问题都会导致正确性的失败,从而产生错误的行为。如果没有两个人同时对相同的数据进行操作,则这种错误的行为不会发生,然而,如果只需要考虑正确性,可以安排在同一时刻只有一个人可以对数据进行操作。但是却削弱了并发处理的灵活性。但是很多时候,都是需要同时兼顾到正确性与灵活性(即有多少并发活动可以同时进行)。
       “请求”对应软件工作的外部环境发出的单个调用,针对调用,软件决定是否需要返回一个应答。
        “会话”是指客户端和服务器端之间一次长时间的交互。可以是一个单独的请求,也可以是一系列逻辑上有关联的请求组合构成。一般来说,一次会话一般从用户登录开始,进行各种操作 ,直到用户退出结束,或者用户直接离开(假设用户的离开也可以解释为退出)。
       请求与会话,在企业应用中的服务器软件可以从两个角度来看待:一是作为客户端的服务器,而是作为其他系统的客户端。比如:从客户端过来的HTTP会话,或者各种数据库之间的数据库会话。
       “进程”通常是一个重量级的执行语境,将其正在处理的内部数据与外部隔离开。“线程”则是一个相对轻量级的活跃的执行单元,一个单独的进程内部可以存在多个线程。线程可以在单个进程里支持多个请求,充分利用资源。但是线程通常是共享内存的,这样就很容易导致并发问题。有时候允许控制哪些数据是可以被线程并发访问的,哪些是不允许数据共享内存的孤立线程
       理想中,我们应该为每个会话创建一个属于它自己的进程,这样我们就可以高度隔离。但是创建新进程需要消耗很多资源,所以也不现实。在现实中,使用一个进程,一次只处理一个请求的情况还是很普遍的,这样可以避免许多并发的困扰。
       解决并发的问题,对于企业应用来说,有两个非常重要的解决方案:一个是隔离,一个是不变性。
       并发问题发生在多个执行单元(例如进程或线程)同时访问同一片数据的时候。
解决方式:
一、隔离,划分数据,使得每一片数据都只能被一个执行单元访问。为每一个进程单独分配一片内存,并且只有这个进程可以对这片内存进行读或写操作。使用隔离方法来安排资源,以便程序进入的是隔离区而无需考虑并发问题。好的并发设计应该是:找到各种创建隔离区的办法,并保证在每个隔离区里能够完成尽可能多的任务。
二、不变性,并发一般出现在共享数据在可以被修改的情况下。所以我们最好识别出哪些数据是不变的。保持所有的数据都不变是不可能的,因为很多系统本来就要对数据进行修改。如果定义出哪些数据是不变的,那么我们就可以不用考虑这些数据的并发问题。同时,如果我们把那些只读取数据的程序也分开的话,让它们只使用拷贝的数据源或内存块,就很好了,这样就可以更加放松所有的并发控制。
乐观并发控制和悲观并发控制:
当一些可变数据无法隔离的时候,通常我们可以使用两种形式的并发控制策略:乐观并发控制和悲观并发控制
乐观锁策略,通俗点的说法就是:程序乐观的认为对于同一数据,两个或者多个线程/请求,不可能或者很少几率会发生同时访问。这种方式是通过版本控制来进行检测是否存在并发冲突的。当然检测出来了结果,那么就可以根据结果进行相应的处理了。
悲观锁测试,通俗点的说法就是:程序悲观的认为对于同一数据,当当前线程在访问数据时,一定有其他的线程也在同时访问数据。然后采用悲观锁机制,那么就会成为对于同一数据,每次都只能有一条线程对其访问,其他线程如要访问需要等到悲观锁释放后才可以。
如果说乐观锁是冲突检测的,那么悲观锁就是冲突避免的。现实中,大多数开发者都更加倾向使用乐观锁策略。乐观锁实际上可以认为它并不是真正的锁定,而是检测版本的最新型而已。
两种策略各有千秋,悲观锁减少了并发的程度,但是却降低了性能。你在做事的时候,别人就不能做,这在企业应用中是很不利的。
乐视锁测试则更加自由一些,只是在提交的时候才会可能遇到问题,缺陷是如果发生问题,那么就只能扔掉原来的数据,重新获取,然后重新从头再来。
乐观锁与悲观锁的选择标准是:视冲突的频率与严重性而定,如果冲突很少,或者冲突的后果不会很严重,那么通常就选择乐观锁。否则,就选择悲观锁。
避免不一致读:
不一致读,在上面已经有所解释,这里不再重复。解决的方法就是合理使用乐观锁与悲观锁机制。
死锁:
对于悲观锁有一个很特别的问题就是死锁,举个例子:A在开始编辑UserController文件,B在编辑UserService文件,A,B两个人都分别对各自的文件加了悲观锁。当A需要完成任务时,发现需要UserService文件的帮助,但是B却上锁了。所以A只能等待。然而当B需要完成任务时,发现需要UserController文件的帮助,但是A却也上锁了,所以B也只能等待。AB互相等待而无法完成任务就出现了死锁------除非两人中,有一个完成了,否则谁都不要想完成任务。
解决死锁的方法,就是制定牺牲者,放弃他原有的工作和他所加的锁,以便其他人可以继续工作。或者给每个锁都加上时间限制,时间到达自动解锁,变成牺牲者,但是这样也会衍生出持锁时间过长而成为牺牲者的情况发生。
如何防止死锁呢,通常的做法是制定规则,硬性的规定每个人获取锁的顺序。例如:可以强制所有人都在开始的时候就获取全部可能需要的锁,再加上时间限制。或者规定A在操作某些必须文件时,B就不能操作与之相关的任何文件。
事务:

企业应用处理并发最主要的工具就是事务,它是一个有边界的工作序列,开始和结束都有明确定义。每个事务都必须保证要么全部完成,要么什么都不做。


软件事务的几种属性(ACID):
原子性:
一个事务里,动作序列的每一个步骤都必须是要么全部成功,要么所有的工作都将回滚。部分完成不是一个事务概念。而事务又是由一组原子组成,为了就是保证原子操作的完整性。
隔离性:
一个事务,知道它被成功提交之后,它的结果对于任何其他事务才是可见的。
一致性:
在事务开始和完成的时候,系统的资源都必须处于一致的,没有破坏的状态。
持久性:
一个已提交事务的任何结果都必须是永久性的。即“在任何系统崩溃的情况下都能保存下来”。


事务资源:
大多数企业应用是在数据库方面涉及到事务的,但是还有其他很多情况要进行事务控制,比如:消息队列、ATM等。“事务资源”是表示可以进行事务处理的任何事物---也就是说: 使用事务来控制并发过程。“事务资源”叫起来有些绕口,因此我们例外一种叫法为“数据库”,只是当我们提到“数据库”的时候,同样适用于其他任何事务资源。
为了处理最大的吞吐率,现代的事务处理系统被设计成保证事务尽可能的短,为此,要尽可能不让事务跨域多个请求。跨域多个请求的事务称为长事务
因此,通常在请求开始时启动事务,在请求结束时提交事务,将请求事务定义为一个非常好的简单模型,只需把方法标记成事务化。
使用事务时,需要清楚地知道被锁住的到底是什么,对于许多数据库操作来说,事务系统锁住的是被访问的数据行,这样就可以允许多个事务同时访问一个表。然而,如果一个事务锁住了一个表中的许多行,则数据库无法处理那么多锁,只能将锁升级到锁住整个表-----从而将其他事务锁住外面。但是这种锁升级对并发有很大影响,所以这也是很多系统不能在领域的层超类型级别上使用“对象”表的原因。这样的表很容易导致锁升级,而锁住该表后其他对数据库的访问也就被阻塞了。
减少事务隔离以提高灵活性:

事务隔离级别: 
ISOLATION_DEFAULT:使用数据库默认的隔离级别。


ISOLATION_READ_UNCOMMITTED:允许读取改变了的还未提交的数据,可能导致脏读、不可重复读和幻读。 是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。
ISOLATION_READ_COMMITTED:允许并发事务提交之后读取,可以避免脏读,可能导致重复读和幻读。保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据 ----常用
ISOLATION_REPEATABLE_READ:对相同字段的多次读取结果一致,可导致幻读。 它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
ISOLATION_SERIALIZABLE:完全服从ACID的原则,确保不发生脏读、不可重复读和幻读。 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。

如果要保证正确性必须使用可串行化隔离级别。但是问题是这是系统的灵活性将受到很大的影响,因此要尽量减少串行化以增加系统的吞吐率。不必给所有的事务设置相同的隔离级别,而应该仔细观察每个事务并根据每个事务具体情况来决定如何权衡灵活性与正确性。
业务事务和系统事务:
系统事务,是指由关系数据库系统和事务监视器所支持的事务。数据库事务就是一组SQL命令,由开始和终止这个数据库事务的指令来定界。而一组命令需要遵循完整性,故而要么都成功,要么都失败。
所谓的业务事务,是与一套业务相关的事务,比如:ATM取钱。用户在取到钱的之前任何一步被取消,他卡上金额都不应该减少。所以很多时候我们也希望业务事务能显示出于系统事务一样的ACID属性。
通常来说,让业务事务支出ACID属性的最简单做法就是在单个系统事务中执行完整的业务事务。但是业务事务常常需要通过多次请求才能完成。因此用单个系统事务的实现会产生长系统事务。而大多数的事务系统并不能很有效地支持长事务。












































































































































































......显示全文...
    点击查看全文


上一篇文章           查看所有文章
2016-04-03 20:42:58  
架构设计 最新文章
Opengl教程之读取obj并绘制在picturecontro
读《企业应用架构模式》第五章并发
StepbyStepintoSpring(IOC)
设计模式(2)用例图之一
使用实体组件系统(ECS)开发”吃方块”游戏实
编程学习之简单工厂模式与策略模式
Invalidprojectdescription.
基于Redis实现分布式消息队列(2)
《开源框架那点事儿15》:借船下海还是造船
原型模式——浅复制和深复制
360图书馆 软件开发资料 文字转语音 购物精选 软件下载 美食菜谱 新闻资讯 电影视频 小游戏 Chinese Culture
生肖星座 三丰软件 视频 开发 Android开发 站长 古典小说 网文精选 搜图网 美图 中国文化英文版 多播 租车 短信
2017-7-24 16:35:59
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  软件世界网 --