`
sbpya
  • 浏览: 615624 次
  • 性别: Icon_minigender_1
  • 来自: 杭州,长沙
社区版块
存档分类
最新评论

Web应用中并发控制的实现

阅读更多

引言

  B/S构架的应用越来越普及,但由于它有别于C/S构架的特殊性,并发控制始终没能得到很好的解决,如售票系统经常会出现同一张火车票出售多次的现象。典型的案例如下:

  例如若有两个客户端,A客户先读取了账户余额2000元,之后B客户也读取了账户余额2000元的数据,A客户提取了500元,对数据库作了变更,此时数据库中的余额为1500元,B客户也要提取1300元,根据其所取得的资料,2000-1300将为700余额,若此时再对数据库进行变更,最后的余额700元就会不正确,应当是200元,问题的出现是由于两个客户对同一条数据进行并发访问造成的。

  Web应用中并发控制的特殊性

  上述问题在C/S构架中可以通过长事务来实现,但Web应用是基于Internet网络环境的,其中的并发控制有其内在的特殊性:

  1. Web所基于的网络协议HTTP(Hyper Text Transfer Protocol)是一种无连接的协议,数据库服务器无法保存事务的状态信息;

  2. 用户可以随时中止或启动浏览器中当前主页上的事务。

  由于上述特殊性,Web应用中并发控制不能采用严格的长事务来实现,但可以长事务的思路来实现,在数据读取的时候把相应的数据锁定,在更新阶段把锁放开,然后更新数据。

  Web应用中并发控制的实现

  业务逻辑的实现过程中,往往需要保证数据访问的排他性。如在 金融 系统的日终结算处理中,我们希望针对某个cut-off时间点的数据进行处理,而不希望在结算进行过程中(可能是几秒种,也可能是几个小时),数据再发生变化。此时,我们就需要通过一些机制来保证这些数据在某个操作过程中不会被外界修改,这样的机制,就是所谓的“锁”,即给选定的目标数据上锁,使其无法被其他程序修改。有两种锁机制:即通常所说的“乐观锁(Optimistic Locking)” 和“悲观锁(Pessimistic Locking)”。

  1.乐观锁(Optimistic Locking)

  乐观锁(optimistic locking)则乐观的认为资料的存取很少发生同时存取的问题,因而不作数据库层次上的锁定,为了维护正确的数据,乐观锁定使用应用程序上的逻辑实现版本控制来解决。

  并发控制时,数据不一致的情况一旦发生,有几个解决的 方法 ,一种是先更新为主,一种是后更新的为主,比较复杂的就是检查发生变动的数据来实现,或是检查所有属性来实现乐观锁定。

  Hibernate通过版本号检查来实现后更新为主,这也是Hibernate所推荐的方式,在数据库中加入一个VERSON栏记录,在读取数据时连同版本号一同读取,并在更新数据时递增版本号,然后比对版本号与数据库中的版本号,如果大于数据库中的版本号则予以更新,否则就回报错误。

  以Hibernate实现版本号控制锁定的话,我们的对象中增加一个version属性,例如:

public class MyAccount {
private int version;
....
public void setVersion(int version) {
this.version = version;
}
public int getVersion() {
return version;
}
....
}

  而在映像文件中,我们使用optimistic-lock属性设定version控制,属性栏之后增加一个标签,例如:

optimistic-lock="version"

  设定好版本控制之后,在上例中如果B客户试图更新数据,将会引发StableObjectStateException例外,我们可以捕捉这个例外,在处理中重新读取数据库中的数据,同时将B客户目前的数据与数据库中的数据读出来,让B客户有机会比对不一致的数据,以决定要变更的部份,或者您可以设计程式自动读取新的资料,并重复扣款业务流程,直到数据可以更新为止,这一切可以在后台执行,而不用让您的客户知道。在其它架构中也可通过这种思路来实现乐观锁,但版本控制和冲突的检测要在自己程序的程序中实现和维护。

  2.悲观锁(Pessimistic Locking)

  虽然乐观锁能够提高系统的性能,但它是对发生冲突的访问进行事后的补救,应用在用户输入数据量很少的场合比较适合,但如果在 企业 ERP,用户与系统交互涉及大量数据在页面表单上录入,如果事后提交失败后才提示用户要重新录入是很不现实的,所以有必要进行事前控制,这就要采用悲观锁。

  在多个客户端可能读取同一笔数据或同时更新一笔数据的情况下,防止同一个数据被修改而造成混乱,最简单的手段就是在读取时对数据进行锁定,其它客户端不能对同一笔数据进行更  新的读取动作。

  悲观锁定(Pessimistic Locking)一如其名称所示,悲观的认定每次资料存取时,其它的客户端也会存取同一笔数据,因此对该笔数据进行事先锁定,直到自己操作完成后解除锁定。

  悲观锁定通常透过系统或数据库本身的功能来实现,依赖系统或数据库本身提供的锁定机制,Hibernate即是如此,我们可以利用Query或Criteria的setLockMode()方法来设定要锁定的表或列(row)及其锁定模式,锁定模式有以下的几个:

      LockMode.WRITE:在insert或update时进行锁定,Hibernate会在save()方法时自动获得锁定。

  LockMode.UPGRADE:利用SELECT … FOR UPDATE进行锁定。

  LockMode.UPGRADE_NOWAIT:利用SELECT … FOR UPDATE NOWAIT进行锁定,在Oracle环境下使用。

  LockMode.READ:在读取记录时Hibernate会自动获得锁定。

  LockMode.NONE:没有锁定。

  也可以在使用Session的load()或是lock()时指定锁定模式以进行锁定。

  如果数据库不支持所指定的锁定模式,Hibernate会选择一个合适的锁定替换,而不是丢出一个例外。

  3.其它构架中悲观锁的实现

  Hibernate的悲观锁,也是基于数据库的锁机制实现。下面的代码实现了对“用户”查询记录的加锁:

  String sqlStr = "from userInfo as user where user.userId=’admin’";Query query = session.createQuery(sqlStr);query.setLockMode("user",LockMode.UPGRADE); //加锁List userList = query.list();//执行查询,获取数据

  query.setLockMode对查询语句中,特定别名所对应的记录进行加锁(我们为userInfo类指定了一个别名“user”),这里也就是对返回的所有user记录进行加锁:

select tuser0_.id as id, tuser0_.userId as userId, tuser0_.group_id as group_id, tuser0_.user_type as user_type, tuser0_.sex as sex from t_user tuser0_ where (tuser0_.userId =’admin’ ) for update

  通过上述转换后的sql语句可知,Hibernate的加锁其实是利用了数据库的for update语句,在读取阶段对某条记录的锁定,而在更新阶段提交,释放锁。

  其实其它架构也可以采取该思路,不过,数据库的for update语句的锁定和释放一定要在数据的同一个连接中,如果读取阶段和更新阶段不是统一连接,即读取之后断开了与数据库的连接,则for update语句的锁定立即失效,为此,如果其它架构中要采取这种方式则要做相应的调整。

  首先,由于Web应用是无状态的,也就是说数据库的for update语句的锁定和释放不一定是数据的同一个连接,为此,采用痕迹跟踪法,在读取数据时生成唯一的序列号(serialId),建立与数据连接的映射,并放置一个map数据结构中;在更新时,通过该serialId在连接池中重新获取该连接,用该连接去更新数据。

  如果系统是采用dao读取数据,实体bean去更新数据,则只要在更新数据之前断开读取数据时的连接,则可以通过其它途径更新数据,如下代码所示:

public void update (AbstractEntityData data, String[] selTeamName ,String serialId) throws Exception {dao.closeConnect(serialId);bo.update(data);}

  其中,dao.closeConnect(serialId)是断开数据连接,bo.update(data)是通过EJB更新数据库

  4.序列号(serialId)的创建和维护

  由于不同用户可能同时建立连接或同一用户先后建立连接,故创建序列号可以在读取数据时通过sessionId和时间戳组合而成。而在操作的过程中,为了保持序列号不会丢失和唯一性,它不能放在session或application中,而是放在页面的request对象里,通过它向其它页面传递。

  5.关联表的锁定

  其实,Hibernate的悲观锁方式只能对单个表的记录进行锁定,但现实中,存在关联更新的情况,即在更新主表的时候有可能会更新到与之相关的子表,与此同时,其它用户也可能通过其它主表更新相应的子表同一条记录。

  有两种方式处理,一是在读取数据通过sql语句关联子表相应记录,因为for update对所有关联表中符合条件的记录都会加锁;二是为子表找一个入口表,在更新子表的同时,必须更新子表的入口表。

  6.例外操作的处理

  采用这种方式,有一些例外情况必须小心处理,一是页面的关闭,如果调用相应的方法,如onbeforeunload()等,释放对应的数据库连接;二是用户非正常关机退出系统,必须有数据库周期清除无用的连接,如间隔二十分钟等,来释放读取时对数据的锁定,否则,该数据会长时间被锁定,直至应用服务器重启。

  结论

  软件系统的并发控制一般是通过加锁来实现,同样,Web应用也是采用乐观锁和悲观锁来实现,乐观锁是一种事后补救措施,是通过程序的逻辑控制版本来实现的,而悲观锁是事前的一种预防措施,它利用数据库的锁机制来实现,Hibernate对它做了一层封装,使应用更加方便,为了让其它架构都能适用,本文还原了Hibernate的实现原理,提出一般的实现思路和注意实现。

分享到:
评论

相关推荐

    Web应用中并发控制的实现.pdf

    在Web应用中,并发控制是确保数据一致性与正确性的关键技术。随着B/S架构的广泛应用,比如电商系统、售票系统等,并发问题愈发突出。在传统的C/S架构中,长事务处理可以有效解决并发问题,但在Web环境中,由于HTTP...

    WEBAPI多线程并发测试工具

    这有助于检测并发控制机制(如锁、信号量)是否有效,以及服务器如何处理高并发负载。 **文件名称列表解析** 由于提供的压缩包子文件名为"API测试工具",我们可以推测这个压缩包可能包含一个或多个用于API测试的...

    利用Java开发高性能、高并发Web应用

    在Java开发领域,构建高性能、高并发的Web应用是一项核心任务。这涉及到多个技术层面的综合运用,包括但不限于系统架构设计、线程管理、数据访问优化、缓存策略、负载均衡以及性能监控等。以下是一些关键的知识点,...

    Web技术在PLC控制系统中的应用.pdf

    综上所述,Web技术在PLC控制系统中的应用是实现远程控制、系统监控和数据交换的有效手段,它极大地提高了工业自动化系统的灵活性和智能化水平。随着技术的不断进步,未来Web技术与PLC的结合将会更加紧密,为工业自动...

    Vanilla是一个基于Openresty实现的高性能Web应用开发框架

    - **高性能**:继承自Openresty,Vanilla也具备高性能特性,能有效处理高并发请求,提高Web应用的响应速度。 - **模块化设计**:Vanilla框架采用模块化设计,允许开发者按需选择和组合不同的组件,以满足不同项目的...

    基于J2EE的Web应用的MVC架构实现.pdf

    他们构建了一个简单而实用的Web应用框架,通过合理地分离视图、控制和模型,实现了清晰的层次结构。这种架构不仅简化了应用的开发和维护,还提高了应用的可扩展性和灵活性。 例如,在他们的框架中,控制器(Servlet...

    java_如何利用Java开发高性能、高并发Web应用

    在Java世界中,开发高性能、高并发的Web应用程序是一项关键任务,这关乎到系统的稳定性、扩展性和用户体验。本文将深入探讨如何借助Java技术栈来实现这一目标。 首先,选择合适的框架至关重要。Spring Boot是一个...

    quartz单机和WEB应用

    标签中的“源码”提示,博客可能深入解析了Quartz的内部实现,包括其调度算法、并发控制等方面,这对于理解Quartz的工作原理和优化调度性能非常有帮助。对于开发者来说,理解源码可以帮助解决在使用过程中遇到的复杂...

    Java.Web应用开发.J2EE和Tomcat第2版.pdf

    它提供了组件级别的服务,如事务管理、持久化、并发控制等,使得开发者可以专注于业务逻辑的实现,而无需关心底层的复杂细节。EJB分为三种类型:会话Bean、实体Bean和消息驱动Bean。 #### 4. Spring框架 Spring是...

    多线程Web服务器的设计与实现

    5. **并发控制**:在多线程环境中,必须考虑线程安全问题,比如资源竞争和死锁。Java提供了一些并发工具类,如`synchronized`关键字、`ReentrantLock`、`Semaphore`等,用于管理和控制并发访问,确保数据一致性。 6...

    如何利用Java开发高性能、高并发Web应用

    综上所述,开发高性能、高并发的Java Web应用需要综合考虑多个层面的优化,包括对象分配、循环优化、内存管理、并发控制以及代码质量等。通过这些方法,可以有效地提高程序的运行效率,降低系统资源的消耗,从而实现...

    基于 NET技术平台的Web应用框架的设计与实现

    ### 基于.NET技术平台的Web应用框架的设计与实现 #### 一、引言 随着互联网技术的迅速发展,Web应用开发已经成为软件工程的重要组成部分。传统的Web开发方式逐渐暴露出许多不足之处,例如代码重复性高、维护成本大...

    Web应用性能测试系统的设计与实现1

    【Web应用性能测试系统的...综上所述,本文详细阐述了Web应用性能测试系统的设计理念和实现过程,强调了性能测试在Web应用开发中的重要性,并提供了一套实用的解决方案,对于提升Web应用的稳定性和性能具有指导意义。

    Web应用服务器研究综述.docx

    云计算技术的兴起为 Web应用服务器提供了强大的计算、存储和管理能力。虚拟化技术可将物理资源转化为逻辑资源,提高资源利用率。基于云计算和虚拟化技术的 Web 应用服务器可实现动态资源分配、弹性伸缩和高可用性等...

    论文研究-绝对延迟保证在Web应用服务器数据库连接池中的实现.pdf

    通过系统辨识建立了数据库连接池的近似线性时不变模型LTI, 并设计了绝对延迟保证控制器, 为Tomcat Web应用服务器实现了数据库连接池中闭环系统所有部件。测试结果表明, 即使在并发请求数量剧烈变化时, 控制器的设计...

    JAVA外文文献翻译基于Java技术的Web应用设计模型的比较研究.pdf

    JMeter用于测试Web应用或网络服务的性能,可以通过模拟并发用户的行为来分析性能瓶颈。JMeter可以通过创建用户线程来模拟用户交互,并测试应用服务器、网络或对象对高负载的反应。 在提到的其他技术中,还包含了一...

    Django实现并发处理的例子

    在Django中,你可以在自定义的管理命令、信号处理器或者后台任务中实现这样的并发控制。 在提供的文件列表中,“访问指令”可能是Django项目中的URL配置或者视图函数,它们负责处理HTTP请求并返回响应。“mytest”...

Global site tag (gtag.js) - Google Analytics