在企业资源计划系统内的并发控制的制作方法

文档序号:6454250阅读:215来源:国知局

专利名称::在企业资源计划系统内的并发控制的制作方法在企业资源计划系统内的并发控制扭旦冃足在允许多个用户同时访问共享对象、数据记录等的系统内的并发控制是任何用于管理共享数据项的基于服务器的产品的重要特征。具体地,在企业资源计划系统内经常存在在具有长生存期的数据事务的用户之间支持不可串行化的协作的需求。通常,悲观并发控制不阻塞其它用户进行读操作。例如,可重复读确保被读的行没有被更新,即不在数据事务的持续时间内更新。然而,在具有更新被读的行的意图的读操作中,悲观并发控制在数据事务的持续时间内在数据项上设置排它或更新锁,从而防止其它用户在具有更新意图的情况下读该数据项。因此,其它用户在带有更新意图读数据项之前必须等待锁被解除,而这影响系统的并发性和可伸縮性。在一些情形中,锁的范围应用于整个数据库、数据库内的整个表或者表内的若干行,而不仅是包含正在读或更新的数据项的单个行。因此,锁的范围防止多个用户同时读或更新不同行和/或表内的数据项。另外,在平衡的树数据结构内,查询如SQL査询内不能在精确的位置处开始扫描。作为査询执行的一部分,在评估该査询期间扫描行并应用过滤器。因此,同时进行读的用户防止对方读这些数据项,即使它们的最终査询结果不交叉。尽管应用程序可以选择行并且应用过滤器以基于过滤准则丢弃所选择的行,但在这些所选行上获得的锁在该数据事务期间继续存在。因而,对于涉及诸共享表的长生存期的数据事务,并发任务变为被串行化,即使在由査询得到的最终集合内没有交叉的时候。乐观并发控制允许用户读、更新和删除数据项而不防止其它用户做同样的事情。乐观并发控制假设在写操作期间更新或删除同一数据项的概率很小,并且读操作不受限制。然而,万一多个数据事务在写操作期间正在更新同一的数据项,则更新会丢失并且在并发用户之间仅最后的更新被保存,从而引起数据不一致。换言之,第一用户最终基于最初检索的值(但随后被并发用户改变)更新表行内的数据项。因此,该更新是基于过时的数据的。概述涉及相同数据的多个数据事务之间的并发控制,提供一种在数据事务内处理由并发控制产生的异常而非立即异常中止数据事务的方式。异常可以通过对数据进行重读或重试更新来处理,从而延迟了数据事务异常中止。并发控制还提供乐观并发控制和悲观并发控制之间的选择,同时考虑相对更新和表间依赖。概括地说,在涉及来自应用程序的写请求的数据事务期间,将唯一标识要被更新的数据的版本的版本标识与标识在同一数据事务期间先前读过该数据时的数据版本的版本标识相比较。如果版本标识不匹配,则在该数据事务内抛出异常并进行处理。预期使用并发控制技术来对数据事务去串行化,确保数据一致,并且即使数据事务是长生存期也允许高可伸縮性。附图简述图1是计算机网络的简化和代表性框图;图2是可连接到图1的网络的计算机的框图;图3是用于管理并发控制的系统的代表性框图;图4是表示乐观并发控制的例程的流程图;图5是表示用于检测图4的乐观并发控制例程内的更新冲突的例程的流程图;图6是表示用于在图5的检测更新冲突例程期间抛出的结构化异常的例程的流程图;图7是表示用于在数据事务期间、在图5的检测更新冲突例程期间抛出异常时处理异常的例程的流程图;图8是表示用于进行数据的相对更新的例程的流程图;以及图9是表示用于更新依赖于其它表中的数据的数据的例程的流程图。详细描述尽管下面的文本阐述众多不同实施例的详细描述,但应当理解,该描述的法律范围是由所附权利要求书的文字来定义的。该详细描述应当解释为仅是示例性的,而不是要描述每一可能的实施例,因为描述每一可能的实施例即使不是不可能也是不实际的。可使用当前的技术或在此专利申请日之后开发的技术来实现众多替换实施例,它们都落在本发明的范围之内。还应当理解,除非术语在本专利中使用语句"如在此使用的,术语'_,在此定义为表示..."或相似语句来明确定义,否则不旨在以显式或隐式的方式将该术语的含义限制为超出其简单或普通含义,且这样的术语不应当基于本专利的任何部分中所作的任何陈述(除权利要求书的语言之外)来解释以限制范围。就本专利所附权利要求书中所述的任何术语是以与在本专利中的单一含义一致的方式来引用的,这样做的目的只是为清楚而不混淆读者,而不是要使这样的权利要求术语以隐式或其它方式受限于该单一含义。最后,除非权利要求元素是通过陈述词语"装置"和功能而没有陈述任何结构来定义的,否则任何权利要求元素的范围不应当基于对35U.S.C§112第六节的应用来解释。许多发明功能和许多发明原理最好用软件程序或指令和集成电路(IC)如专用ic来实现。预期到本领域技术人员尽管有可能花费显著的努力和存在由例如可用时间、当前技术以及经济考虑促动的许多设计选择,但在本文所公开的概念和原理指导下将容易地有能力在最少试验的情况下生成这样的指令和程序和IC。因此,出于简明和最小化模糊按照本发明的原理和概念的任何风险的目的,对这样的软件和IC的进一步讨论(如果有的话)将限制于与优选实施例的原理和概念相关的本质。图1和2提供涉及本公开的网络和计算平台的结构基础。图1例示网络10。网络10可以是因特网、虚拟个人网络(VPN)或允许一或多个计算机、通信设备、数据库等相互通信地连接的任何其它网络。网络10可经由以太网16和路由器18以及陆线20连接至个人计算机12和计算机终端14。以太网16可以是较大因特网协议网络的子网。其它网络化资源诸如投影机或打印机(未示出)也可通过以太网16或其它数据网络来支持。另一方面,网络10可以经由无线通信站26和无线链接28无线地连接至膝上型计算机22和个人数据助理24。同样,服务器30可使用通信链接32连接至网络10,而大型机34可使用另一通信链接36连接至网络10。网络10可用于支持对等网络通信。图2例示计算机110形式的计算设备。计算机110的组件包括但不限于,处理单元120、系统存储器130和将包括系统存储器在内的各种系统组件耦合至处理单元120的系统总线121。系统总线121可以是若干类型的总线结构中的任一种,包括存储器总线或存储器控制器、外围总线和使用各种总线体系结构中的任一种的局部总线。作为示例,而非限制,这样的体系结构包括工业标准体系结构(ISA)总线、微通道体系结构(MCA)总线、扩展的ISA(EISA)总线、视频电子技术标准协会(VESA)局部总线和外围部件互连(PCI)总线(也被称为Mezzanine总线)。计算机110—般包括各种计算机可读介质。计算机可读介质可以是可由计算机IIO访问任何可用介质,并且包括易失性与非易失介质、可移动和不可移动介质。作为示例而非局限,计算机可读介质包括计算机存储介质和通信介质。计算机存储介质包括以用于储存诸如计算机可读指令、数据结构、程序模块或其它数据等信息的任一方法或技术实现的易失性和非易失性,可移动和不可移动介质。计算机存储介质包括但不限于,RAM、ROM、EEPROM、闪存或其它存储器技术,CD-ROM、数字多功能盘(DVD)或其它光盘存储、磁盒、磁带、磁盘存储或其它磁存储设备,或者可用于存储所需信息并且可由计算机110访问的任何其它介质。通信介质一般体现为已调制数据信号诸如载波或其它传输机制中的计算机可读指令、数据结构、程序模块或其它数据,并且包括任何信息传递介质。术语"已调制数据信号"指以对信号中的信息进行编码的方式设置或改变其一个或多个特征的信号。作为示例而非限制,通信介质包括线接介质诸如线接网络或直接线接连接和无线介质诸如声音、射频、红外和其它无线介质。上述任何各项的组合也应当包括在计算机可读介质的范围内。系统存储器130包括易失性和/或非易失性存储器形式的计算机存储介质,诸如只读存储器(ROM)131和随机存取存储器(RAM)132。基本输入/输出系统133(BIOS),包含例如在启动时帮助计算机110内的诸元素之间传送信息的基本例程,通常存储在ROM131中。RAM132通常包含处理单元120可以立即访问和/或目前正在操作的数据和/或程序模块。作为示例,而非限制,图2示出了操作系统134、应用程序135、其它程序模块136和程序数据137。计算机110也可包括其它可移动/不可移动、易失性/非易失性计算机存储介质。仅作为示例,图2例示读写不可移动、非易失性磁介质的硬盘驱动器141、读写可移动非易失性磁盘152的磁盘驱动器151和读写可移动非易失性光盘156诸如CDROM或其它光介质的光盘驱动器155。可以在示例性操作环境下使用的其它可移动/不可移动、易失性/非易失性计算机存储介质包括,但不限于,盒式磁带、闪存卡、数字多功能盘、数字录像带、固态RAM、固态ROM等。硬盘驱动器141通常由诸如接口140的不可移动存储器接口连接至系统总线121,磁盘驱动器151和光盘驱动器155通常由诸如接口150的可移动存储器接口连接至系统总线121。上面讨论并在图2中例示的这些驱动器及其相关联的计算机存储介质为计算机110提供对计算机可读指令、数据结构、程序模块和其它数据的存储。例如,在图2中,硬盘驱动器141被示为存储操作系统144、应用程序145、其它程序模块146和程序数据147。注意,这些组件可以与操作系统134、应用程序135、其它程序模块136和程序数据137相同或不同。操作系统144、应用程序145、其它程序模块146和程序数据147这里给予不同的数字以说明至少它们是不同的副本。用户可通过输入设备诸如键盘162和光标控制设备161(通常指的是鼠标、跟踪球或触摸板)将命令和信息输入至计算机20中。照相机163诸如网络摄像头(webcam)可捕捉和输入关联于计算机110的环境的照片,诸如提供用户的照片。网络摄像头163可按需捕捉照片,例如在用户指示的时候,或者可在计算机110的控制下周期性地拍照。其它输入设备(未示出)可包括话筒、操纵杆、游戏手柄、圆盘式卫星天线、扫描仪等等。这些和其它输入设备经常通过耦合至系统总线的输入接口160连接至处理单元120,但可由其它接口和总线结构诸如并行端口、游戏端口或通用串行总线(USB)来连接。监视器191或其它类型的显示设备也可通过接口诸如图形控制器190连接至系统总线121。除监视器之外,计算机还可包括其它外围输出设备诸如扬声器197和打印机196,它们可通过输出外围接口195来连接。计算机IIO可使用至一或多个远程计算机诸如远程计算机180的逻辑连接在网络化环境中运行。远程计算机180可以是个人计算机、服务器、路由器、网络PC、对等设备或其它常见网络节点,且通常包括上文相对于计算机110描述的许多或所有元件,尽管在图2中只示出存储器存储设备181。图2所示的逻辑连接包括局域网(LAN)171和广域网(WAN)173,但也可包括其它网络。这类联网环境在办公室、企业级计算机网络、内联网和因特网中是很常见的。当在LAN联网环境中使用时,计算机110通过网络接口或适配器170连接至LAN171。当在WAN联网环境中使用时,计算机110通常包括调制解调器172或用于在诸如因特网等WAN173上建立通信的其它装置。调制解调器172,可以是内置或外置的,通过输入接口160或其它合适的机制连接至系统总线121。在网络化环境中,相对于计算机110描述的程序模块或其部分可被存储在远程存储器存储设备中。作为示例,而非限制,图2示出了远程应用程序185驻留在存储器设备181上。通信连接170172允许设备与其它设备通信。通信连接170172是通信介质的示例。通信介质一般体现为已调制数据信号诸如载波或其它传输机制中的计算机可读指令、数据结构、程序模块或其它数据,并且包括任何信息传递介质。"已调制的数据信号"是其一或多个特征以将信息编码到信号中的方式设置或改变的信号。作为示例而非限制,通信介质包括包括线接介质诸如线接网络或直接线接连接以及无线介质诸如声音、RF、红外和其它无线介质。计算机可读介质可包括存储介质和通信介质两者。图3描绘示例性客户机/服务器网络200,诸如企业资源计划系统,它可与图1的网络10相似或者耦合至图1的网络10。客户机/服务器网络200可包括由网络210、212、214耦合的各个系统202、204、206、208。网络210、212、214可是线接或无线的,并且可支持因特网协议版本6(IPv6)和安全通信协议诸如加密套接字协议层(SSL)。在一个示例中,因特网可用作网络210、212、214。系统202是服务器系统,它可包括一个服务器216或多个服务器。服务器系统202可以是业务企业服务器系统、SQL或其它数据库管理服务器系统或者消息收发和企业协作服务器系统,然而可以包括不同的服务器类型或服务器利用。系统204、206是各自包含网络通信设备218、220的客户机系统,网络通信设备包括但不限于个人计算机、电话、个人数字助理、机顶盒、电视和娱乐系统等等。系统208包括有效耦合至服务器系统202并且存储数据项的数据库222。在一个示例中,数据库222可将数据项存储在表的行中,并且数据库222可维护多个表以存储数据。数据项可由服务器系统202管理,这些数据项被存储在具有对应于不同数据项的一或多个行的各种表中。在一个示例中,网络通信设备218、220可将进行读和/或写操作的不同用户与服务器216相关以访问和/或修改存储在数据库222内的数据项。一般而言,服务器系统202使用本文所述的并发控制技术来允许多个用户204、206同时读或更新数据库222内的数据项、同一表内的数据项或同一数据项。在另一个示例中,使用上述系统200,服务器216可允许多个客户机204、206参与由服务器系统202管理的服务器应用程序。或者,客户机204、206可在本地执行这些应用程序。应用程序可包括应用程序代码,它们包含用于提供读和/或写请求的读和/或更新语句。如本文使用的,术语'更新'这里定义为表示对数据项的任何修改,包括但不限于修改数据、写新数据或删除数据。尽管客户机系统204、206各自被示为包括一个网络通信设备218、220,但应当理解可使用不同数量的网络通信设备。同样,服务器系统202可包括不同数量的服务器,而数据库系统208可包括不同数量的数据库。另外,尽管服务器216、网络通信设备218、220和数据库222各自被示为设置在其自己的系统202、204、206、208内,但应当理解服务器216、网络通信设备218、220和数据库222可在同一系统内设置。还应当理解,可提供多个系统,包括数百或数千个客户机系统和数据库系统。尽管下面的公开大体描述在同一数据项上执行并发写操作的多个数据事务,这包括在一个服务器216与多个同时进行操作的用户或应用程序之间的交互,但应当理解,一或多个服务器可同时操作,各自具有进行数据事务以便在一或多个数据项上执行写操作的一个或多个并发用户或应用程序。另外,尽管下面的公开一般描述在服务器系统操作系统的内核数据访问层内实现的并发控制技术,但应当理解,可使用并发控制技术的各种其它实现。下面提供计算机代码的各种示例,其中一些是用乂++编程语言(它是一种简单的面向对象语言)或0++编程代码编写的,尽管可使用各种其它编程语言,包括其它的面向对象语言。一般而言,在涉及读操作的数据事务期间,服务器系统202接收来自正由用户执行的应用程序诸如业务过程的读请求。使用本文描述的并发控制技术,服务器系统202可允许并发用户对数据项进行不受限制的读操作,因为仅读数据项是不会引起完整性损失的。因此,允许应用程序读行和相应的数据项而无需获得读操作的排它锁,从而允许在服务器系统202内的最大并发。另外,具有更新数据意图的读操作也可执行而无需获得排它锁,从而使读操作展现为读未提交(uncommitted)数据。如本文进一步描述的,数据完整性可通过比较在更新期间受影响数据的版本标识来维护。另一方面,在涉及写操作的数据事务期间,服务器系统202可确保数据一致以避免丢失更新。数据操作可用三个阶段处理读阶段,确认阶段和实际进行写操作的写阶段。在一个示例中,服务器系统202处理读阶段,而数据库222处理确认和写阶段。每一写请求之前是读请求。通过接收初始读请求、选择数据项和在数据正从数据库取出用于后续更新的时候将结果提供给应用程序来处理写请求。应用程序随后修改数据项并且向服务器系统202或数据库222提供更新。可在对应于数据项的行上启动更新锁,选择数据项,并且确认正在更新的数据项。在确认期间,可触发一致性检验算法,它通过比较在初始读的数据项和正在更新数据项的版本标识(在此也称为数据项的"RecVersion(记录版本)")来判断数据项是否在另一个数据事务期间被更新。换言之,可以判断正在更新的数据项的版本是否与初始读的数据项的版本相同。如果这些版本相同,则允许进行更新,将改变提交给数据库222并且一旦该数据事务提交就对于对应于数据的行解锁。如果版本不同,则服务器系统202检测冲突并且提出更新冲突异常,并向应用程序提供处理该冲突的机会以尝试弥补在该数据事务内的更新冲突而无需自动回巻或异常中止数据事务。如果应用程序不能弥补更新冲突,则服务器系统202回巻数据事务。应用程序可意识到该异常并且可回巻应用程序代码至某一位置,在这里应用程序在以后可尝试该写操作。服务器系统202从而在写操作期间提供并发控制而不用在从数据库取出数据项用于后续更新时锁定对应于数据项的行。代之以在真正的更新期间使用行级锁定,从而允许其它数据事务读数据项或更新表和/或数据库222内的任何其它数据项。如果数据项在取出与更新之间被另一个数据事务修改,则检测到该修改,并且生成、处理异常并且可从内核数据访问层抛出该异常至应用程序代码。乐观与悲观并发控制管理除了提供乐观并发控制之外,服务器系统202还可维护悲观并发控制选择。相应地,向服务器系统202提供各种并发控制选择,包括但不限于全局启用乐观并发控制、全局禁用乐观并发控制和启用每一个表的乐观并发控制。全局启用乐观并发控制允许内核在乐观并发控制下对数据库222内的所有表进行数据事务。全局禁用乐观并发控制指示内核在悲观并发控制下对数据库222内的所有表进行数据事务。通过启用每一个表的乐观并发控制,数据库222内的各个表被配置为在特定的并发控制方法下进行操作。例如,所有表可被初始设置为启用乐观并发控制,并且用户可适当地在逐个表基础上改变该值。可提供全局乐观并发控制开关以在启用和禁用全局乐观并发控制之间以及在启用或禁用每一个表的乐观并发控制之间切换。"r将个w乐观力':发拧;"u「X没^为^储/i::数据M'222内的设W:标丄(,它i^J「发校M^的各个选城之问W投服务器系统202可在激活服务器系统202时检査全局乐观并发控制的状态,并且取出全局设置并将其存储在存储器中。当激活客户机时,会话调用传递回这些开关值至该客户机,客户机在本地设置这些值。每一个表的乐观并发控制在运行时间被添加至表属性集合并且呈现在表的应用程序对象树属性上。每一个表的乐观并发控制可使用元数据存储中的标志的未使用位,可用位"0"定义默认值,在该情形中将每一个表的乐观并发控制属性设置为"真"。在一些情形中,应用程序可能需要来自上述配置的异常。例如,乐观并发控制可能需要对于个别应用程序在语句级上被禁止,即使对于大多数其它应用程序,在启用乐观并发控制的情况下设置特定的表。因而,内核可在编程语言中引入关键词诸如下述的"pessimisticlock(悲观锁)"和"optimisticlock(乐观锁)",以覆盖每一个表的和全局的乐观并发控制开关。下面伪计算机代码实现的例子例示语句级的悲观并发控制管理的示例,其中乐观并发控制是全局启用的(或者在表级启用),但一特定应用程序需要悲观锁{CustTablecustTable;ttsbegiwselectpessimisticlockCustTablewhereCustTable.Currency=='CAD';CustTable.PriceGroup='PUB';CustTable,update();ttscommit;关键词"pessimisticlock"允许内核不检索版本标识"RecVersion"(它标识正在更新的数据项的版本),从而覆盖了乐观并发控制并且按照悲观并发控制允许在必要的更新锁就位的情况下来读数据项。下面伪计算机代码实现的例子例示语句级的乐观并发控制管理的替换示例,其中乐观并发控制被全局禁用(或者在表级禁用),但特定的应用程序要求乐观锁-CustTablecustTable;ttsbegin;selectoptimisticlockCustTablewhereCustTable.Currency=='CAD';CustTable.PriceGroup='PUB';CustTable.update();ttscommit;版本标识如前所示,每一数据项与版本标识("RecVersion")关联。数据库222内使用乐观并发控制的每一个表包含与版本标识有关的列。当在数据库222中创建表时,内核数据访问层将版本标识列添加至表定义。如果在数据库222内存在的表没有版本标识列,则可将版本标识列添加至现有表并且服务器系统202可为表中的所有列自动生成版本标识值。当将记录插入至具有版本标识列的表中时,服务器系统202可自动为该新记生成新的版本标识值。对于使用乐观并发控制的所有写操作,内核数据访问层读所有正在取出的行的版本标识值,并且存储这些版本标识值用于随后检査数据项的一致性以检测更新冲突。当更新行内的数据项时,内核数据访问层检索最初从数据库222取出该行时该行的版本标识值并将它添加至更新语句谓词。如果更新语句谓词没有找到匹配的版本标识值,则检测到更新冲突并且提出更新冲突异常至尝试处理该冲突的应用程序。如果写操作涉及删除先前读过的记录,则内核数据访问层将版本标识值添加至语句谓词以判断正在删除的记录是否在之前已被修改。为经更新的数据生成的新的版本标识值可在内核数据访问中维护以便跨多个数据操作维护事务语义。结果,可在服务器系统202中而不是在数据库222中生成新的版本标识值。在一个示例中,版本标识可以是唯一标识数据项的服务器时戳。在另一个示例中,版本标识可以仅仅是递增的整数。然而,在去串行化数据事务中,为了最大化可以发生的并发数据事务的数量,可使用读未提交隔离级,它允许一个数据事务在另一个数据事务之内和之外都可以选择和更新数据项。一般而言,隔离级指的是事务必须与其它事务隔离的程度。然而,为了增加并发性,使用读未提交隔离级来利用不是所有数据事务始终需要完全隔离的可能性。结果,数据正确性在没有合适版本标识的情况下可能受到损害。采用任一上述版本标识示例,存在由先前的数据事务所作的更新未被正确检测到的可能性,因而导致重写,如下面的图表所示<table>tableseeoriginaldocumentpage17</column></row><table><table>tableseeoriginaldocumentpage18</column></row><table>如上所示,第一数据事务可读初始版本标识值V并且更新数据项,从而使得版本标识值被更新为V+l。在提交写操作之前,第一数据事务可异常中止写操作,并且将版本标识值复位为V。然而,第二数据事务在异常中止之间开始写操作并且读版本标识值V+1。第三数据事务在异常中止之后开始写操作,将版本标识值v存储在存储器中并且在第二数据事务提交其写操作之前提交写操作。因此,第三数据事务还将版本标识值更新为V+l,因为存储在存储器中的版本标识值V匹配正在更新的数据项的版本标识值V。当第二数据事务提交其写操作时,存储在存储器中的版本标识值v+i匹配正在更新的数据项的版本标识值V+1。因此,第二数据事务表现为它正在基于第一数据事务的异常中止的更新来更新数据项,而实际上重写了由第三数据事务所作的更新。为了解决这种可能性,可提供跨所有服务器分配唯一地标识数据项的随机数作为版本标识。在一个示例中,随机数的种子可基于数据项的内容本身,从而确保随机数对于该数据项在时间、用户和服务器系统202上是唯一的,并且数据项在更新之后的每一版本具有关联于其的唯一版本标识值。在另一个示例中,随机数的种子是用于通过随机生成算法生成随机数的随机种子。随机数的产生可使用加密应用程序编程接口CryptGenRandom。CryptGenRandom函数用比通常的随机类更为随机的加密的随机字节填充缓冲区。可根据一或多个下列源为CryptGenRandom函数产生不确定性的度量(即熵)内核切换中的线程、当前进程标识符、自引导起的时钟数、当前时间、存储器信息和对象存储器统计。CryptGenRandom函数可通过一个静态方法来初始化一次。下面示出CryptGenRandom函数的初始化的示例。使lfj('ryi"'—ni':wki':ysi':t,crypt—ma(:iiiMi':—kj':Ysi':t,W此)lj「-fU务的:」U以尽管使用0++型的符号来描述初始化,但初始化不限于此。HCRYPTPROVdbRecbuf::dbrb—hCryptProv=dbRecbuf::InitHCryptProv();—HCRYPTPROVdbRecbuf::InitHCryptProv()if(!CryptAcquireContext(&(dbrb一hCryptProv),NULL,NULL,PROV—RSA—FULL,CRYPT—MAC腦EKEYSET))一DWORDdwError=GetLastError();AXTRACE("hCryptProvfirsttryacquirecontextfailedwithYod",dwError);〃如果(NTE—BAD—KEY==dwError)if(!CryptAcquireContext(&(dbrb—hCryptProv),NULL,NULL,PROV_RSA—FULL,CRYPT_NEWKEYSET|CRYP(MACHINE_KEYSET》dwError=GetLastError();AXTRACE("hCryptProvsecondtryacquirecontextfailedwith%d",dwError);—ASSERT(FALSE);else_ASSERT(FALSE);—ASSERT(NULL!=dbrb—hCryptProv);XxTRACE("hCryptProvinitializedto%d",dbrb—hCryptProv);returndbrb一hCryptProv;此外,可添加使用CryptGenRandom来生成下一个版本标识RecVersion的方法来确保该函数生成正的版本标识。可为其中已经提交了数据事务的数据项的每一成功更新生成新的版本标识值。下面示出该函数的示例intdbRecbuf::SetNextRecVersion()intiReturnCode=CQL—OK;booleanfRetry=true;while(fRetry)fRetry=false;if(CryptGenRandom(dbrb—hCryptProv,sizeof(this->dbrb—iRecVersion),(byte*)(&this->dbrb—iRecVersion)))——{一if(this->dbrb—iRecVersion<0){—this->dbrb—iRecVersion=-this->dbrb—iRecVersion;}——elseif(O==this-〉dbrb一iRecVersion){一fRetry=true;elseiReturnCode=CQL—EXCEPTION;returniReturnCode;禁用悲观锁如前所述,可在悲观并发控制与乐观并发控制之间提供选择,例如通过全局乐观并发控制开关。然而,当更新数据项时,应用程序的应用程序代码可在更新语句内包含自动默认启用悲观锁的触发器。例如,在乂++编程代码中,悲观锁可通过SELECT和WHILESELECT语句内的forupdate(进行更新)提示来触发,并且使用fompdate提示的应用程序可基于服务器系统202平台支持悲观锁的假设工作。为了禁用悲观锁但维护与釆用悲观锁的应用程序的后向兼容性,乐观并发控制可移除、忽略或重新解释更新语句中的触发器以在乐观并发控制期间禁用悲观锁。例如,触发器可在每一语句、每一个表和全局乐观并发控制开关的上下文中重新解释。更具体地,触发器不会引起更新锁被保持在数据库中。相反,触发器可指定应用程序的意图。例如,触发器可指定意图是否仅是读操作还是意图是否是要使用读操作来进行将来的更新。基于这些开关,内核数据访问层可决定更新锁是否应当保持(悲观)或者行的版本标识是否应当取出(乐观)。在另一个示例中,在乂++编程内,将用于获得数据库更新锁的数据库提示从SELECT和WHILESELECT语句中移除而不改变用于悲观锁的应用程序代码。下面通过从SELECT和WHILESELECT语句中移除数据库提示来示出禁用悲观锁的示例SomeFunctionttsbegin;Whileselecttable1forupdatewheretablel.somePredicate=someValueIf(CheckCondition(table1)ComputeNewValue(table1);tablel.update();ttscommit;图4是乐观并发控制例程300的示例,它可由服务器系统202执行,具体是由服务器系统202内核的内核数据访问层执行,以在更新表内的数据项的写操作期间禁用悲观锁,其中已经为表启用了乐观并发控制(无论是全局还是在每一个表的基础上)。如前所述,在每一写请求之前是读操作。只要在乐观并发控制模式中读数据项,内核数据访问层就读它取出的所有对应行的版本标识值并且存储它们供将来使用,诸如对所取出行的数据项执行更新的时候。在读请求之后,保持无锁直至调用更新数据项的写请求为止。在本文描述的乐观并发控制技术中,当更新数据项(即表内的行)时,内核数据访问层检索在读操作期间取出的对应行的版本标识值,并且将该版本标识值添加至更新语句谓词以便查看该数据项是否己经被修改了。如果更新涉及删除先前读的数据项,则内核数据访问层添加版本标识值至更新语句谓词以査看该数据项是否已经被修改了。内核数据访问层请求数据库222进行检查以査看是否有任何其它数据事务在正在更新的数据项被读之后改变了它。如果检测到更新冲突,则抛出UpdateConflict(更新冲突)异常。如下面将进一步描述的,代替在抛出UpdateConflict异常时立即异常中止数据事务,异常可在数据事务内处理。参考图4,开始于框302,乐观并发控制例程300在数据事务期间接收来自应用程序的读请求。如果读请求与带有更新行意图的读操作诸如"forupdate"、"pessimisticlock"或"optimisticlock"无关,如在框304确定的,则在框306收到的任何后续的更新请求在框308不会被允许并且可终止事务。如果读请求与带有更新行意图的读操作有关,则例程300可判断乐观并发控制是否应当应用,诸如通过在框310判断对于该表是否已经启用乐观并发控制。例程300还在框310判断版本标识是否将被检索并用于更新。为了判断是否需要乐观并发控制,如在框310判断的,可向内核数据访问层提供集中式储存库用于查找有关乐观并发控制是否应当应用以及版本标识是否应当检索的计算。这样的计算对一个数据对象诸如数据项、行、表等只执行一次。在一个示例中,这样的判断可由检查以查看行的版本标识是否需要为更新和删除而使用RecVersion进行检查的方案来实现。在一或多个下列情形中可能不需要或者不使用版本标识检查在未读RecVersion时、对于行集合操作、如果表或行明确地标记为要求下面进一步描述的列比较、或者如果更新是也在下面进一步描述的相对更新。下面是这样一个方案SqlStmt::IsRecVersionCheckNeededForupdate的示例,它检查以査看RecVersion检查是否要用于更新并且这还要求对于该表启用乐观并发控制启用。如果要使用RecVersion检査则返回"TRUE(真)"值。booleanSqlStmt::IsRecVersionCheckNeededForupdateO〃—ASSERT(type—update==this->get_stmt_type()||type—delete==this->get_stmt_type());if(!m_fRecVersionCheckNeededForupdateCalculated){_m—fRecVersionCheckNeededForupdate=cqlParentCursor()->IsRecVersionNeededForSelect();m—fRecVersionCheckNeededForupdate=m—fRecVersionCheckNeededForupdate&&!(this->update—rowset()||this->sqldelete());m一fRecVersionCheckNeededForupdate=m—fRecVersionCheckNeededForupdate&&!cqlParentCursor()->IsRereadNeeded();if(m—fRecVersionCheckNeededForupdate&&this->updateList()){_m—fRecVersionCheckNeededForupdate=!this->GetIsRelativeUpdate();m_fRecVersionCheckNeededForupdateCalculated=TRUE;}_returnm—fRecVersionCheckNeededForupdate;}_或者,或结合SqlStmt::IsRecVersionCheckNeededForupdate方案,在框312的判断可进一步由检查以査看是否要使用任何形式的更新冲突检测的方案来实现。例如,如果需要版本标识用于更新或者如果指定用于更新的重读则使用更新冲突检测。重读标志表示数据最初被取出到表单上并且无论其它设置是什么都使用乐观并发控制。如果乐观并发控制被禁用,则更新冲突检测仅可用于表单。表单,也称为报告,使用査询来取数据,其中数据源被附连至查询对象并且更新属性确定查询是否被允许更新数据库中的数据项。另一方面,如果启用乐观并发控制,则更新冲突检测可使用版本标识或列比较。下面是这样一个方案SqlStmt::IsUpdateConflictDetectionNeededForupdate的示例,它检查以査看对更新是否要使用任何形式的更新冲突检测诸如RecVersion检查或列比较。如果要使用任何形式的更新冲突检测,则返回"TRUE"值。booleanSqlStmt::IsUpdateConflictDetectionNeededForupdate()booleanfUpdateConflictDetectionNeededForupdate=FALSE;if(g—fDisableOcc||getTable(cqlParentCursor()->dataset())->IsOccDisabled())fUpdateConflictDetectionNeededForupdate=cqlParentCursor()->rereadOnUpdate();elsefUpdateConflictDetectionNeededForupdate=this->IsRecVersionCheckNeededForupdate()23IIcqlParentCursor()->IsRereadNeeded();elsefUpdateConflictDetectionNeededForupdate=this->cqlParentCursor()->rereadOnUpdate();returnfUpdateConflictDetectionNeededForupdate;再次参考图4的框310,如果在更新期间不要使用乐观并发控制,这是使用上面提供的示例方案确定的,则不检索版本标识并且针对在框312更新的行在数据库中保持更新锁。在框314,可接收更新请求,并且在框316,执行对数据项的真正的写和/或更新,这可包括将新数据写至行,删除数据项等。在框318提交数据事务并且在框320释放更新锁。如果为该表开启乐观并发控制并且在更新期间要使用它,如在框310确定的,则在框322从数据库检索版本标识,在框324接收更新请求并且使用版本标识用于利用检测更新冲突例程326的更新冲突检测。注意,在框322针对数据库不实现和保持更新锁。检测更新冲突图5是检测更新冲突例程326的示例,它可由服务器系统202执行,具体地是由服务器系统202的内核数据访问层执行,以检测在更新数据项时是否已经发生了任何冲突。换言之,检测更新冲突例程326可判断在数据项的初始取出与更新之间该数据项是否被其它数据事务改变。更新检测冲突例程326可检测任何这样的改变并且从内核数据访问层抛出异常给应用程序代码。开始于框402,检测更新冲突例程326在开始时检查以查看是否存在执行版本标识检査(RecVersion检查)或列比较的需求。例如,如果应用程序仅仅是修改现有数据项则可使用列比较。列比较可用于后向兼容性或者按照应用程序逻辑的指示来使用。另一方面,版本标识检查可用于所有形式的更新,诸如修改数据或者删除数据项。下面是一个方案SqlStmt::AddOccExpressionNodes的示例,它首先检査以査看是否需要版本标识检査或者列比较(例如,如果在读时行被锁,则两者都不需要)且随后如果需要的话则切换至使用版本标识或者列比较。如果要使用任何形式的更新冲突检测,则返回所得到的表达式节点,用于执行列比较的BuildUpdateConflictDetectionExpressionNode(建立更新冲突检测表达式节点)和用于执行版本标识检查的BuildRecVersonExpressionNode(建立记录版本表达式节点)。exprNode*SqlStmt::AddOccExpressionNodes(exprNode*originalExpressionNode)〃I原始表达i代码。_ASSERT(this->get—stmt一type()==type_update||this->get—stmt—type()==type_delete);if(this->IsUpdateConflictDetectionNeededForupdate(》exprNode*pxprndNewWhere=NULL;〃内联更新冲突应仅用于更新。if(this->cqlParentCursor()->GetDetectUpdateConflictInStatement(》if(this->get—stmt—type()==typeupdate){一一_pxpmdNewWhere=BuildUpdateConflictDetectionExpressionNode(》_ASSERT(NULL!=pxpmdNewWhere);elsepxpmdNewWhere=BuildRecVersonExpressionNode();_ASSERT(NULL!=pxpmdNewWhere);originalExpressionNode=AndExpressionNodes(originalExpressionNode,pxprndNewWhere);—ASSERT(NULL!=originalExpressionNode);returnoriginalExpressionNode;回来参考图5,如果要执行列比较,如在框402确定的,则例程326可进行至框404比较列。具体地,服务器系统202可请求数据库执行比较。在一个示例中,使用列比较的更新语句像下面这样更新表1令字段1=新字段1值,其中RecID=我的RecID且字段1=字段1旧值下面是一个大纲SqlStmt::BuildUpdateConflictDetectionExpressionNode的示例,它建立并返回用于使用列比较检测更新冲突的表达式节点。具体地,注意在该更新冲突检测的特定示例中不使用版本标识。exprNode*SqlStmt::BuildUpdateConflictDetectionExpressionNodeO_ASSERT(NULL!=this->updateList());一ASSERT(NULL!=ip());exprNode*pxprndNewWhere=NULL;exprNode*pxprndWhere=NULLjCQL—EVAL一ELEM*pcqlvlOriginalValue=NULL;valueNode*pvlndOriginalValueNode=NULL;for(baseList<assignmentNode*>::iteratorassgnmntndAssignmentNode=updateList()->begin();assgnmntndAssignmentNode!=updateList()->end();assg腦ntndAssignmentNode+十)if(DBF正LD—RECVERSION==DBFHDL_EXT2DBFHDL((*a;sgnmntndAssignmentNode)->GetLeftNode()->getField(ip())〗)continue;pcqlvlOriginalValue=newCQL一EVAL—ELEM((*assgnmntndAssignmentNode)->GetLeftNode()->getField(ip()),cqlParentCursor()-〉orig—buffer());pvlndOriginalValueNode=newvalueNode(pcqlvlOriginalValue);—ASSERT((*assgnmntndAssignmentNode)->GetLeftNode()-〉isField());fieldExpr*pfldxprField=newfieldExpr((*assgnmntndAssigrunentNode)-〉GetLeftNode()->getField(ip()),cqlParentCursor());pxprndNewWhere=neweqNode(pfldxprField,pvlndOriginalValueNode);pxprndWhere=AndExpressionNodes(pxprndWhere,pxprndNewWhere);—ASSERT(NULL!=pxprndWhere);returnpxprndWhere^如果例程326确定要执行版本标识检査,则在框406将版本标识添加至更新语句谓词,以判断数据项是否在取出与更新之间被修改。在框408,检索取出数据项的版本标识并与要更新的数据项的版本标识相比较。注意,如果要由数据库222执行版本标识比较,则服务器系统202不需要代表数据库222取出版本标识。因为版本标识对于数据项的版本是唯一的,因此取出数据项的版本标识与正在更新的数据项的版本标识之间的任何差异(如在框410检测的)引起在框412抛出异常。在一个示例中,可从内核数据访问层抛出异常给生成更新语句的应用程序代码。在框412,可如下进一步所述地执行结构化异常例程,并且在框414可由例程处理更新冲突异常。另一方面,如果在版本标识中没有差异,如在框410确定的,则在框416执行数据项的真正更新,这可包括将新数据写至数据项、删除数据项等等。在框416更新数据之后,保持写锁(也称为排它锁)。在框418,提交数据事务并且在框420释放写锁。结构化异常图6是结构化异常例程500的示例,每当抛出更新冲突异常时就可在框322执行该例程。具体地,结构化异常例程500演示结构异常处理的运行时支持的示例。如前所述,在运行时,由内核数据访问层检测更新冲突错误并且抛出更新冲突异常。结构化异常处理构造可捕捉更新冲突,其中仅在更新冲突发生在该构造中指定的表上时才执行捕捉锁。通过比较,在非结构化异常处理构造中,每当在尝试块内存在更新冲突异常时就执行捕捉块。开始于框502,数据访问内核可跟踪其中发生更新冲突异常的表实例。内核可保存其中发生更新冲突的表实例的内核表示。在0++编程语言中,该表示可被称为cqlCursor。在乂++编程语言中,该表示可以是表示表的任何变量。具体地,内核可将内核表示cqlCursor放在表属性中,该属性可被称为LastUpdateConflictingTable(最后更新冲突表)属性,因此内核知道哪个表招致了更新冲突异常。在一个示例中,该函数可由下面的方案cqlDatasourceSql::RaiseUpdateConflitError来执行,其示例在下面提供,它提出指示更新冲突的特定错误并且返回指示指定更新冲突异常的错误代码的整数。intcqlDatasourceSql::RaiseUpdateConflictError(intiDBError,〃IDB错误代码interpret*pintprtlnterpret)〃I指向解释器的指针.一ASSERTE(NULL!=pintprtlnterpret);射fdefined(DEBUG)〃记录它.eventlog-〉logEvent(EVLOG一WARNING,EVMSG—MSG,—T("Update/Deleteconflictdetected!"),_T("OCCT'》;—#endifif(x_uiUpdateConflictException!=pintprtInterpret->isp.exceptionval)pintprtlnterpret-〉isp.exceptionval=x—uiUpdateConflictException;pintprtInterpret->SetLastUpdateConflictingTable(cursor());〃—ASSERTE(x—uiUpdateConflictException==pintprtInterpret->isp.exceptionval》returnraiseErr(iDBError,cursor()->exceptionFailureReason(DB—OPR—RECORD—CHANGED),pintprtlnterpret);〃intiReturncode=raiseErr(iDBError,cursor()->exceptionFailureReason(DB—OPR—RECORD—CHANGED),pintprtlnterpret);intiReturncode=::raiseErr(this->dataset(3,iDBError,cursor()->exceptionFailureReason(DB—OPR—RECORD—CHANGED),pintprtlnterpret,x—uiUpdateConflictException);〃pintprtlnterpret-〉isp.exceptionval=x—uiUpdateConflictException;returniReturncode;}在框504,例程500向用户或客户机通知更新冲突异常。具体地,每当跨服务器/客户机边界进行调用时,例程500就跨服务器/客户机调用设置表属性LastUpdateConflictingTable以便在客户机侧正确地设置该表属性。客户机维护本地表属性。因此,任何已经招致UpdateConflict(更新冲突)异常的表应当具有客户机侧上对该表的本地引用。因而,每当服务器系统202将要返回调用至客户机时,它检査以査看是否存在UpdateConflict异常,并且将该引用发回至客户机。客户机看到存在UpdateConflict异常,读该引用,在本地査找该引用并且解释该引用。具体地,客户机可检査异常类型和本地引用,解除引用它并且在表属性中设置该引用。在框506,结构化异常例程500展示已经招致UpdateConflict异常的表实例。例如,可在应用类上向应用代码展示LastUpdateConflictingTable属性。在框508,一运行时函数启用结构化异常处理。例如,可使用字节代码作为对函数指针表的索引。可将该函数添加至解释类并且映射至该字节代码以处理更新冲突的结构化异常。在运行时,调用该函数并且检査异常类型和其上发生更新冲突的表。该函数随后仅当它们都匹配时设置下一指令至捕捉块。控制随后可传递至处理更新冲突例程414。处理更新冲突异常图7是图5与6示意地示出的处理更新冲突例程414的示例,并且可执行该例程来处理在检测更新冲突例程322期间发生的任何UpdateConflict异常。具体地且如上所述,UpdateConflict异常的处理在异常发生时可在数据事务内处理,而不是自动异常中止数据事务。处理更新冲突例程600允许应用程序代码重读数据,重新应用更新逻辑和再次试图更新。尽管下面描述的处理更新冲突例程414是弥补UpdateConflict异常的示例性描述,但应当理解可使用不同形式的弥补逻辑。一般而言,更新语句在"尝试"块内维护,因此在尝试块内发生的任何异常在"捕捉"块内捕捉。在一些情形中,尝试块嵌套在其它尝试块,从而创建多个尝试块层级。处理更新冲突例程414允许UpdateConflict异常在数据事务内处理,并且在将处理执行移回下一尝试块层级和在另一个捕捉块内重新捕捉UpdateConflict异常之前通过试图在每一尝试块层级的捕捉块内重读和重试数据事务来延迟异常中止数据事务。每当捕捉和处理冲突异常时,应用程序代码可执行该操作。更具体地,应用程序可确信数据库内的数据与存储器内的对象状态处于一致状态。一旦到达最外面的尝试块层级并且已经执行相应的捕捉块或者事务层级已经到达"0",就只能异常中止数据事务。为了实现例程414,可在数据事务进入与离开每一尝试块时通过递增和递减尝试层级计数tryLevelCount来跟踪尝试块层级。尝试层级计数可由服务器系统202与客户机共享。用于在数据事务进入和离开尝试块时跟踪尝试块层级的示例伪计算机代码实现可以如下S—Wordinterpret::xal—try—sym()厂——this-〉EnterTryBlock(》rc=evalLoop();this->LeaveTryBlock();returnrq除了嵌套的尝试块层级之外,在一些情形中,应用程序代码可被包在其它应用程序代码内。因此,数据事务可嵌套在其它数据事务中。为了适度地对处理更新冲突例程414的弥补逻辑进行处理,可简单地抛出UpdateConflict异常给最外面的数据事务,因为整个数据事务已经被巻回。用于允许最外面的事务处理UpdateConflict异常的示例伪计算机代码实现可以如下tryttsbegin;this.updateNow()ttscommit;catch(Exception::Deadlock)retry;catch(Exception::UpdateConflict)〃可以是这里需要尝试的业务逻辑if(appl.ttslevel()==0)retry;elsethrowException::UpdateConflict;如在上例中可以看到的,支持嵌套的尝试块。代MVf.oi新冲突&'i,m常屮il.数躯'K务,嵌仿的ttshcghi(ttsH:始》和ttseommit(tts议会)块"J'他fU嵌《亍,U'级聰由'f丌始成A殳新的数W'K务。相反,作为外面的事务一部分来包括它。由最外面的嵌套层级开始和提交事务,但可以在嵌套中的任何地方异常中止事务。如^,i;尝试块内捉出w新冲夂,则节务的嵌创^级!"!到代码进入该尝试块的W级。如果该层级为o则异常中止事务。可提出更新冲突,这可使用结构化异常处理构造来捕捉,其中仅当冲突在指定的表上发生时才执行捕捉块,或者它可使用非结构化的异常处理构造来捕捉,其中每当在尝试块内发生冲突时就执行捕捉块。应用程序代码可用于找出哪个表招致冲突的机制。再次参考图7,开始于框602,一旦己经抛出UpdateConflict异常,数据事务就进入捕捉块。在框604,处理更新冲突例程414通过检查尝试层级计数器来判断数据事务是否处于一个尝试块内。例如,如果尝试层级计数具有零值,则数据事务不再是在尝试块内并且可在框606异常中止该数据事务。如果数据事务在尝试块内,则应用程序代码可以试图重读和重试该数据事务。具体地,捕捉块可在将异常抛回至下一尝试块层级之前试图预定次数的重读和重试。因而,在框608,处理更新冲突例程414判断重试的次数是否已经超过预定的限制。如果是,则例程414在框610将异常抛回至下一尝试块层级。另一方面,如果重试的次数尚未超过预定的程度,则例程414在框612在捕捉块内进行重读行和重试数据事务而不回巻或者立即异常中止数据事务。如果成功处理了UpdateConflict异常,如在框614确定的,则可在框616清除有关该UpdateConflict异常的相应信息并且可以提交该数据事务。例如可通过从数据库重读数据和重试更新来成功处理UpdateConflict异常。在这样的估形屮,在数据"H屮+保持w新锁,似应川,程,r〗河以选抒"处踏代码时切换个:恶观锁》会:改怙形屮,在改吋保持ii新锁JM^新之〗"将oi新锁史新为排它锁。如果没有成功处理UpdateConflict异常,则可将重试计数增一并且控制可传递回框608。用于处理UpdateConflict异常的示例伪计算机代码实现可以如下。在下面的示例中,尝试捕捉层级是一并且允许不超过五次的重试。staticvoidOCC2—SimpleCompensationLogic(Args—args){——CustTablecustTable;intretryCount;ttsbeginjselectforupdatecustTablewherecustTable,AccountNum=='SI35'&&custTable.CustGroup=='00';retryCount=0;trycustTable.CreditRating=strfmt("%1",str2int(custTable.CreditRating)十1);custTable.update();〃rf在该方法内抛出UpdateConflict。见下面info(strfmt("CreditRating=0/ol",custTable.CreditRating));catch(Exception::UpdateConflict)if(retryCount<4)retryCount++;custTable.reread();retry;}elsethrowException::UpdateConflictNotRecovered;ttscommit;用于处理具有多个更新的UpdateConflict异常的示例伪计算机代码可以如下。如该伪计算机代码所示,由第一应用程序遇到的更新冲突导致数据事务按照应用程序代码的指示而被异常中止。另一方面,由第二应用程序遇到的更新冲突导致重试。staticvoidOcc2Test一MultipleUpdateConflictMgmt(Args—args){一—CustTablecustl;CustTablecust2;ttsbegin;tryselectforupdatecustlwherecustl.AccountN腦=='SI35'&&custl.CustGroup=='00';selectforupdatecust2wherecust2.AccountNum'SI36'&&custl.CustGroup=言00';custl,CreditRating=strfmt(',%r,str2int(custl.CreditRating)+l);cust2.CreditRating=strfmt("%l",str2int(cust2.CreditRating;+l^cust2,update();custl.update();catch(Exception::UpdateConflict,custl)ttsabort;throwException::UpdateConflictNotRecovered;catch(Exception::UpdateConflict,cust2)cust2.reread();cust2.CreditRating=strfmt("%r,str2int(cust2.CreditRating)+l);cust2.update();ttscommit;上面提供的用于处理具有多个更新的UpdateConflict异常的伪计算机代码示例使用结构化异常处理机制。或者,下面提供用于对具有多个更新的UpdateConflict异常进行非结构化处理的示例伪计算机代码。再次,第一应用异常中止数据事务,然而第二应用重试数据事务。catch(Exception::UpdateConflict)if(appl.LastUpdateConflictingTable()==custl)ttsabort;throwException::UpdateConflictNotRecovered;elseif(appl丄astUpdateConflictingTable()==cust2)cust2.reread(》cust2,CreditRating=strfmt("%r,str2int(cust2.CreditRating)+l);cust2.update();相对更新如前所述,在一些情形中,更新可能与相对更新有关,在该情形中不使用版本标识检査。无论如何,可使用相对更新以便减少更新冲突,并且这对于实数和整数字段类型特别有用。如果对数据项的更新是相对的,与绝对相反,则可以下面的形式执行更新更新表1令字段1=字段1+变化,而绝对更新如下执行更新表1令字段1=最终值。例如,两个同时发生的数据事务各自想要将一个字段值递减"二",其中该字段具有初始值"八",这与为该字段指定一个新值相反。相对更新递减使得响应于第一数据事务,初始值被递减二,而响应于第二数据事务新值被再次递减二,以提供最终值"四"。相对更新的优点是相对更新不重写另一用户的改变,即使改变在读与更新之间发生,因为相对更新格式的本质使其能够抵抗另一用户的改变。因此,如果所有字段使用相对更新来更新,则可避免版本标识检査。为了实现相对更新,更新字段可被标记为使用相对更新。图8是相对更新例程700的示例,它可用于在数据项上执行相对更新。开始于框702,相对更新例程700判断更新语句是否调用相对更新和/或正在更新的字段是否被标记为使用相对更新。如果更新不是相对更新,则控制可引用回至乐观并发控制例程300。下面提供用于判断相对更新是否可使用的伪计算机代码示例。如提到的,因为不使用版本标识,所以忽略关联于版本标识的字段。voidSqlStmt::SetlsRelativeUpdate(booleanflsRelativeUpdate)m—flsRelativeUpdate=flsRelativeUpdate;cqlParentCursor()-〉SetlsRecVersionUpdateRelative(flsRelativeUpdate);m—flsRelativeUpdateCalculated=TRUE;booleanSqlStmt::GetlsRelativeUpdate()if(!m—flsRelativeUpdateCalculated){_m_flsRelativeUpdate=TRUE;for(baseList<assignmentNode*>::iteratorassg證ntndAssignmentNode=updateList()->begin();assgnmntndAssignmentNode!=updateList()->end();assgnmntndAssignmentNode++)—ASSERT((*assgnmntndAssignmentNode)->GetLeftNode()->isField());DBFHDL一EXTfktFieldHandle=(*assgnmntndAssignmentNode)->getField(ip());if(!isSystemField(fktFieldHandle》hdlField*phdlfldField=getField(cqlParentCursor()->dataset(),DBFHDL—EXT2DBFHDL(fktFieldHandle));_ASSERT(phdlfldField);if(!(phdlfldField->info()->dbfflags&DBF—RELATIVE))m_flsRelativeUpdate=FALSE;breakjm_flsRelativeUpdateCalculated=TRUE;returnm—flsRelativeUpdate;应当维护事务语义,其中可以保持对表中同一行的潜在多个引用并且可以34在这多个引用上执行多个操作。当执行更新时,保持对正在更新的相同行的引用的变量上的版本标识好像在同一事务中读它们一样地被更新。除了RecVersion列之外,可添加另外两个列TransactionRecVersion(事务记录版本)和OriginalRecVersion(原记录版本),其中为每一新的事务生成唯一的TransactionRecVersion,并且当在该事务内的第一更新触及行时,使用刚产生的新RecVersion来更新TransactionRecVersion,并且将旧的RecVersion赋给OrignalRecVersion。如果TransactionRecVersion匹配当前的事务的RecVersion并且OrignalRecVersion匹配存储器中的RecVersion(这意味着该事务拥有该行)或者如果RecVersion匹配存储器中的RecVersion,则允许更新通过。每当进行更新时,可更新RecVersion。与任何更新一样,例程700可更新所更新字段的值标识。然而,因为相对更新例程700不检査值标识,则如果值标识是使用上述技术用新值来更新的话,存在更新会重写另一个数据事务更新的可能,如由下面的图表所示<table>tableseeoriginaldocumentpage0</column></row><table>)因为版本匹配提交成功;这可以重写由事务1在T2作出的改变.如上所示,第一数据事务可读初始版本标识值V并且更新数据项从而使得版本标识值被更新为VI。然而,第二数据事务在更新之前开始写操作,并且执行两个更新,其中第一更新是相对更新而随后的第二更新是绝对更新。第二数据事务的第一更新不检查版本标识值VI,因为该更新是相对更新。无论如何,第一更新提供新版本标识值V2。第二数据事务的第二更新在读期间取得版本标识值V2,在更新时使用该版本标识值V2,更新数据项并且成功地提交该数据事务,因为在更新期间的版本标识值V2匹配在第二更新之前最初读的版本标识值V2。因此,第二数据事务可重写由第一数据事务所作的改变。为了解决这种可能性,当作为相对更新执行更新时,新版本标识被计算为相对版本标识。具体地,例程700计算版本标识的新值,如在上面框704所提供的。在框706,相对更新例程700计算新版本标识与旧版本标识之间的差,并且发出更新以将所更新数据项的版本标识设置为版本标识值加上该差,这可被表示为框708的"更新…令RecVersion=RecVersion+增量"。在框710执行更新。因而,对于在同一事务中所读的同一行的所有引用,也可使用该差来更新版本标识。如果没有其它事务更新该行,则数据库中的版本标识值匹配所有在同一事务中用相同原版本标识值读取的存储器中引用的版本标识值,并且将来的更新在这些行.t:继续。另一方面,如果在进行相对更新之前由某个其它事务更新了该行,则版本标识不匹配,并且任何将来的更新将引起UpdateConflict异常。表间依赖在一些情形中,表内一些列的值(例如表A)可基于另一个表(例如表B)的一些列的值来计算。例如,对表A内的一个数据项的更新可在开始时从表B读一个值,但在更新表A内的该数据项之前,另一个用户更新表B的值。结果,对表A内的数据项的后续更新基于来自表B的过时值。为了解决由表间依赖引起的这类一致性值,内核数据访问层可为应用程序代码提供可重复读提示。可重复读提示转换成服务器系统202的可重复读锁提示RepeatableRead,后者对取出数据保持共享锁直至数据事务结束为止。可重复读锁提示仅应用于特定的读语句并且不应用于整个数据事务。在没有可重复读锁提示的时候,在读操作之后立即释放共享锁。这可防止其它用户在提交数据事务之前更新该行。共享锁相互兼容,因此在相同脚本上运行的多个用户不会相互阻塞。图9是表间依赖例程800的示例,它可用于根据来自另一个表的值来更新数据项。在框802,例程800可接收来自应用程序代码的可重复读提示,它转换成服务器系统202的可重复读锁提示。在没有可重复读提示的时候,在框804的读操作之后立即释放共享锁并且提交事务。如果提供可重复读提示,则在框806,在数据事务期间在表A和表B上提供针对取出数据的共享锁以防止其它用户在事务提交之前更新该数据项。在框808从表B取出数据项。在框810,基于来自表B的经更新数据项计算用于表A的数据项并且在框812进行更新。在框814,释放共享锁并且提交数据事务。尽管上述文本阐述本发明的众多不同实施例的详细描述,但应当理解,本发明的范围是由本发明所附的权利要求书的语言来定义的。详细描述应当解释为仅是示例性的并且不描述本发明的每一可能的实施例,因为描述每一可能的实施例即使不是不可能也是不实际的。可使用当前技术或者使用在本专利提交日之后开发的技术来实现众多替换实施例,它们仍将落入定义实施例的权利要求书的范围。因而,可在本文描述和例示的技术与结构中作出许多修改和变化而不脱离本发明的精神与范围。因此,应当理解,本文描述的方法和设备仅是示例性的并且不限制本发明的范围。权利要求1.一种在企业资源计划系统内并发控制的方法,所述企业资源计划系统包括适于维护具有多个行的表内的数据的数据库,所述方法包括接收来自进行请求的应用程序用于在第一数据事务期间更新第一表的行内数据的写请求,其中,所述行包括唯一标识正在更新的数据的版本的第一版本标识(302);将正在更新的数据的所述第一版本标识与唯一地标识响应于在所述第一数据事务期间的先前读请求取出的所述行内的所述数据时所述数据的版本的第二版本标识相比较(408);以及如果所述第一版本标识不匹配所述第二版本标识则抛出异常(412)。2.如权利要求1所述的方法,其特征在于,还包括如果所述第一版本标识匹配所述第二版本标识,则锁定来自第二数据事务的写请求的数据以防止所述第二数据事务更新所述行内的所述数据(416);更新所述行内的所述数据(416);提交所述第一数据事务(418);释放所述数据上的锁(420);以及生成唯一地标识所述被更新数据的版本的第三版本标识。3.如权利要求2所述的方法,其特征在于,还包括允许响应于来自进行请求的应用程序在所述第二数据事务期间的读请求来读所述第一表中所述行内的所述数据,同时所述数据被锁定不能进行写请求。4.如权利要求1所述的方法,其特征在于,还包括响应于来自所述进行请求的应用程序在所述第一数据事务期间对所述数据的读请求取出数据;存储唯一地标识所述取出数据的第一版本标识;允许第二数据事务更新所述数据;以及生成唯一标识所述第二数据事务的所述被更新数据的第二版本标识,其中所述第二数据事务的所述被更新的数据包括在所述第一数据事务期间被更新的所述数据。5.如权利要求1所述的方法,其特征在于,抛出异常包括抛出所述异常给所述进行请求的应用程序的应用程序代码(412)。6.如权利要求1所述的方法,其特征在于,所述写请求是从所述进行请求的应用程序中包含在第一尝试块内的更新语句生成的,且其中,在所述第一数据事务内处理所述异常包括-在所述尝试块的捕捉块内捕捉所述异常(602);从所述行重读所述第二版本标识(612);以及重试所述第一版本标识与所述第二版本标识的比较(612)。7.如权利要求6所述的方法,其特征在于,所述第一尝试块嵌套在第二尝试块内,所述方法还包括在所述第一数据事务内处理所述异常,包括如果在所述第一尝试块内的捕捉块重试所述第一版本标识与所述第二版本标识的比较导致抛出异常,则将处理执行移回至所述第二尝试块(614);以及试图在所述第二尝试块内处理所述异常(612)。8.如权利要求6所述的方法,其特征在于,处理所述第一数据事务包括,如果在所述第一尝试块的捕捉块内重试所述第一版本标识与所述第二版本标识的比较导致抛出异常且如果所述第一尝试块没有嵌套在第二尝试块内,则异常中止所述第一数据事务(604),(606)。9.如权利要求1所述的方法,其特征在于,所述第一数据事务嵌套在第二数据事务内,其中,在所述第一数据事务内处理所述异常包括将处理执行移回至所述第二数据事务(608),(610)。10.如权利要求1所述的方法,其特征在于,所述第一版本标识包括下列各项之一基于正在更新的数据的随机数或者基于第一随机种子的随机数,其中,所述第二版本标识数字包括下列之一在响应于在所述第一数据事务期间的所述先前读请求取出数据时基于所述数据的随机数,或者基于第二随机种子的随机数。11.如权利要求1所述的方法,其特征在于,第二表的行内的数据依赖于所述正在更新的数据,所述方法还包括如果第二表数据的行内的数据是根据所述第一表的行内的所述数据计算的,则根据第二数据事务的写请求锁定所述第一表的所述行内的所述数据,以防止所述第二数据事务更新所述第一表的所述行内的所述数据(806);根据第二数据事务的写请求锁定所述第二表的所述行内的所述数据,以防止所述第二数据事务更新所述第二表的所述行内的所述数据(806);取出所述第一表的所述行内的所述数据(808);更新所述第一表的所述行内的所述数据(808);至少部分地基于所述第一表的所述行内的所述经更新的数据确定所述第二表的所述行内的所述数据(810);更新所述第二表的所述行内的所述数据(812);提交所述第一数据事务(814);以及释放所第一和第二表的所述内的所述数据上的锁(814)。12.如权利要求1所述的方法,其特征在于,还包括如果所述写请求包括将相对依赖于所述第一表的所述行内的所述数据的现有值来向所述第一表的所述行内的所述数据提供新值的更新,则在不比较所述第一和第二版本标识的情况下更新所述数据(710);生成第三版本标识(704);确定所述第二版本标识与所述第三版本标识之间的差(706);以及发出更新以将唯一地标识所述经更新数据的版本的版本标识设置为所述第二版本标识值加上所述差(708)。13.—种具有计算机可执行指令的计算机可读介质,所述计算机可读指令用于实现一种对第一表的第一行内的数据的多个数据事务之间进行并发控制的方法,所述计算机执行指令包括用于以下操作的指令比较第一版本标识与第二版本标识,其中,所述第一版本标识唯一地标识在第一数据事务的读请求期间来自所述第一行的数据的版本,其中,所述第二版本标识唯一地标识在所述第一数据事务的写请求期间来自所述第一行的数据的版本C410);如果所述第一版本标识不匹配所述第二版本标识则抛出异常(412);以及如果对所述第一表启用乐观并发控制则在所述第一数据事务内处理所述异常(414)。14.如权利要求13所述的具有计算机可执行指令的计算机可读介质,其特征在于,所述写请求是根据所述进行请求的应用程序的更新语句生成的,以及其中,如果对所述第一表启用乐观并发控制则所述更新语句包含所述乐观并发控制的覆盖,所述计算机可执行指令还包括以下指令,用于针对在第二数据事务期间来自进行请求的应用程序的写操作的任何读请求或写请求锁定所述第一表的所述第一行,其中在所述第一数据事务期间保持所述锁(312)。15.如权利要求13所述的具有计算机可执行指令的计算机可读介质,其特征在于,所述写请求是根据所述进行请求的应用程序的更新语句生成的,以及其中,所述更新语句包括用于启用悲观锁的更新提示,所述计算机可执行指令还包括如果对所述第一表启用乐观并发控制,则禁用启用悲观锁的更新提示的指令(310)。16.如权利要求13所述的具有计算机可执行指令的计算机可读介质,其特征在于,还包括如果未对所述第一表启用乐观并发控制,则在所述数据事务期间执行所述数据项的悲观锁(312),其中所述悲观锁包括针对在第二数据事务期间来自进行请求的应用程序的任何读请求和写请求锁住所述第一行(312);以及在不比较所述第一版本标识与所述第二版本标识的情况下更新所述第一行(316)。17.如权利要求13所述的具有计算机可执行指令的计算机可读介质,其特征在于,所述写请求是根据所述进行请求的应用程序中包含在第一尝试块内的更新语句生成的,以及其中,在所述第一数据事务内处理所述异常包括在所述尝试块的捕捉块内捕捉所述异常(602);从所述行重读所述第二版本标识(612);以及重试所述第一版本标识与所述第二版本标识的比较(612)。18.—种适于参加企业资源计划网络的计算机,所述计算机包括用于经由网络接收数据的网络通信设备(170),(172);存储机器可读指令的存储器(130)、(140)、(150);以及用于执行机器可读指令的处理器(120),所述指令执行一种方法,所述方法包括接收来自进行请求的应用程序的用于在第一数据事务期间读数据库内存储的第一表的第一行的读请求(302);存储唯一地标识从所述第一行读的数据的版本的第一版本标识(302);接收来自所述进行请求的应用程序的用于在所述第一数据事务期间更新所述第一行的写请求(302);比较所述第一版本标识与唯一地标识在收到所述写请求时来自所述第一行的数据的版本的第二版本标识(410);如果所述第一版本标识不匹配所述第二版本标识,则抛出异常给所述进行请求的应用程序(412);以及在所述第一数据事务内处理所述异常(414)。19.如权利要求18所述的计算机,其特征在于,所述用于在所述第一数据事务内处理所述异常的指令包括用于以下操作的指令从所述行重读所述第二版本标识(612);以及重试所述第一版本标识与所述第二版本标识的比较(612)。20.如权利要求18所述的计算机,其特征在于,所述写请求是根据所述进行请求的应用程序中包含在第一尝试块内的更新语句生成的,以及其中,所述用于在所述第一数据事务内处理所述异常的指令包括用于以下操作的指令如果所述第一尝试块嵌套在所述第二块内且如果在所述第一尝试块内处理所述异常导致抛出异常,则将处理执行移回至第二尝试块并且在所述第二尝试块内处理所述异常(608)、(610)、(612)、(614);以及如果所述第一尝试块没有嵌套在第二尝试块内且如果在所述第一尝试块内处理所述异常导致抛出异常则异常中止所述第一数据事务(604)、(606)。全文摘要涉及相同数据的多个数据事务之间的并发控制,包括比较唯一标识数据事务的读请求期间与写请求期间的数据版本的版本标识。如果版本标识不匹配则抛出异常,并且在数据事务内处理该异常。文档编号G06F17/40GK101405735SQ200780009422公开日2009年4月8日申请日期2007年1月16日优先权日2006年3月17日发明者J·D·里奇,S·R·阿瓦达纳姆,Z·楚申请人:微软公司
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1