1.事务处理基本原理
事务是一组作为一个单元成功或失败的相关任务。在事务处理术语中,事务要么提交,要么中止。若要提交事务,所有参与者都必须保证对数据的任何更改是永久的。不论系统崩溃或是发生其他无法预料的事件,更改都必须是持久的。
只要有一个参与者无法做出此保证,整个事务就会失败。事务范围内的所有数据更改将回滚到特定设置点。
事务将多个任务绑定在一起。例如,假设某个 ASP.NET 页执行两个任务。它首先在数据库中创建一个新表。接着调用专用对象收集和格式化数据,并在新表中插入数据。这两个任务是相关甚至相互依赖的,因此除非可用数据填充表,否则应避免创建新表。在一个事务范围内执行两个任务会加强两者之间的关系。如果第二个任务失败,则第一个任务回滚到创建新表前的点。
如此例所示,可将事务限制到一个数据资源,如数据库或消息队列。这些数据资源通常提供本地事务功能。这些事务由数据资源控制,管理起来轻松高效。
事务还可以跨越多个数据资源。分布式事务使您得以将出现在不同系统上的若干不同操作合并为单个的成功或失败操作。
2.ACID 属性
ACID 一词表达了事务在使命关键的应用程序中所起的作用。ACID 是由事务处理先驱者创立的,代表了不可分性、一致性、隔离性和持续性。
这些属性确保可预知行为的安全性,加强了事务作为“全或无”命题的作用,而此命题旨在有很多变量时减少管理负荷。
不可分性
事务是一个工作单元,在其中应用程序的 BEGIN TRANSACTION 和 END TRANSACTION 语句之间发生一系列操作。事务只执行一次,且是不可分的,即完成全部工作或者不做任何工作。
与某个事务关联的操作通常具有共同的目标,并且是相互依赖的。如果系统只执行这些操作的一个子集,则可能会破坏事务的总体目标。不可分性消除了系统处理操作子集的可能性。
一致性
事务是一个完整的单元,因为它保持数据的一致性,将数据从一种数据一致状态转换到另一种数据一致状态。
一致性要求在语义上保留事务绑定的数据。某些维护一致性的责任由应用程序开发人员承担,他们必须确保应用程序已强制所有已知的完整性约束。例如,当开发用于转帐的应用程序时,应避免在转帐过程中任意移动小数点。
隔离性
事务是一个隔离单元,允许可并行执行的事务表现得像是在系统中运行的唯一事务。
隔离性要求即使同时可能有其他事务正在运行,每个事务也像是操作数据存储区的唯一事务一样。事务应从不查看其他事务的中间阶段。
当事务可序列化时将获得最高的隔离级别。在此级别上,从一组可并行执行的事务获得的结果与通过连续运行每个事务所获得的结果相同。由于高度隔离会限制可并行执行的事务数,所以一些应用程序降低隔离级别以换取更大的吞吐量。
持续性
事务也是一个恢复单元。如果事务成功,则即使在提交后计算机立即崩溃,系统仍将保证更新该事务。专用记录允许系统的重新启动过程完成未完成的操作,以使事务可持续。
3.事务边界
事务边界定义事务的范围。事务边界内的对象共享一个公共事务标识符。
事务执行时,各种事务识别资源可以参与事务。例如,如果在事务范围内应用程序连接到数据库,则事务流向此资源并扩展事务边界以包含数据库服务器。可以设计跨越进程和计算机的事务。因此,事务边界是表示管理跨进程边界和计算机边界的一致性的抽象概念。
对事务边界的控制取决于为应用程序选择的事务模型:手动或自动。在手动事务中,用开始和结束事务的显式指令控制事务边界。从一个事务边界内可以开始第二个被称为嵌套事务的事务。直到所有从属事务提交,其父事务才提交。
自动事务基于每个组件的声明特性集管理事务边界。事务自动流向被指示参与事务的对象,并跳过被指示在事务外部执行的对象。使用自动事务模型时无法嵌套事务。
4.分布式事务
分布式事务处理 (TP) 系统旨在协助在分布式环境中跨异类的事务识别资源的事务。在分布式 TP 系统的支持下,应用程序可以将不同的活动合并为一个事务性单元,这些活动包括从 Microsoft 消息队列 (MSMQ) 队列检索消息、将消息存储在 Microsoft SQL Server 数据库中、将所有现有的消息引用从 Oracle 服务器数据库中移除,等等。因为分布式事务跨多个数据库资源,故强制 ACID 属性维护所有资源上的数据一致性是很重要的。
如后面各节所述,分布式 TP 系统由若干合作的实体组成。这些实体是逻辑上的且可驻留在同一计算机上或不同的计算机上。
事务处理 (TP) 监视器
TP 监视器是位于事务识别应用程序和资源集合之间的软件。它优化操作系统的活动,使网络通讯流畅,并将多个客户端连接到可能访问多个数据资源的多个应用程序。
您不是编写管理多用户的应用程序(分布式环境),而是编写由单个事务请求组成的应用程序。监视器根据需要伸缩应用程序。
分布式事务处理协调器 (DTC) 是用于 Microsoft Windows 2000 的TP 监视器。
事务管理器
在分布式事务中,每个参与资源均有一个本地事务管理器 (TM) 来跟踪此计算机上传入的和传出的事务。TP 监视器将协调本地 TM 之间所有活动的附加任务分配给一个 TM。这个协调事务活动的 TM 称为根 TM 或协调 TM。
TM 协调和管理所有的事务处理函数,但不具备直接管理数据的能力。资源管理器处理与数据相关的活动。
资源管理器
资源管理器是一项系统服务,负责管理数据库、持续消息队列或事务性文件系统中的持久性或持续性数据。资源管理器存储数据并执行故障恢复。
SQL Server 和 MSMQ 提供参与分布式事务的资源管理器。Oracle、Sybase、Informix、IBM(用于 IBM DB2)和 Ingres 也提供了用于他们各自数据库产品的兼容资源管理器。
资源分配器
资源分配器管理可共享的非持续状态。例如,ODBC 资源分配器管理数据库连接池,当不再需要连接时收回连接。
5.事务模型
若要使 .NET 框架对象参与自动事务,.NET 框架类必须向 Windows 2000 组件服务注册。但是并非所有事务都是自动的。编写事务程序时执行的活动取决于选择的事务模型。公共语言运行库同时支持手动和自动事务模型。
手动事务
Microsoft ActiveX 数据对象 (ADO)、OLE DB、开放式数据库连接 (ODBC) 和 Microsoft 消息队列 (MSMQ) 的资源 API 支持手动事务处理。
手动事务允许显式开始事务、控制事务边界内的每个连接和资源登记、确定事务结果(提交或中止)以及结束事务。尽管此模型提供了对事务的标准控制,但它缺少一些内置于自动事务模型的简化操作。例如,在手动事务中数据存储区之间没有自动登记和协调。此外,与自动事务不同,手动事务中事务不在对象间流动。
如果选择手动控制分布式事务,则必须管理恢复、并发、安全性和完整性。也就是说,必须应用维护与事务处理关联的 ACID 属性所需的所有编程方法。
手动事务和ADO.NET
SQL 客户端和 OLE DB .NET 提供程序在公共语言运行库中支持手动事务。在 ADO.NET 中,这两个 .NET 提供程序都可以用来控制事务。
两个提供程序都包括一组创建数据库连接、开始事务和提交或回滚事务的托管对象。二者之间的主要差别在于它们的连接机制。SQL 客户端 .NET 提供程序提供一组直接调用 SQL Server 的对象。相反,OLE DB .NET 提供程序使用本机 OLE DB 启用数据访问。
ADO.NET 事务完全在数据库的内部处理,且不受 Microsoft 分布式事务处理协调器 (DTC) 或任何其他事务性机制的支持。
手动事务和 MSMQ
用托管语言(如 Microsoft Visual Basic .NET)编写的组件可以从 Microsoft 消息队列 (MSMQ) 发送和接收消息。
MSMQ 是在应用程序中实现消息队列的技术。使用 MSMQ 可以创建或删除消息队列、发送或接收消息以及管理消息队列。事务是企业系统的重要部分,而企业系统经常要求 MSMQ 的异步功能。
公共语言运行库通过 MessageQueueTransaction 类支持手动事务。MSMQ 事务完全在 MSMQ 引擎的内部处理,且不受 Microsoft 分布式事务处理协调器 (DTC) 或任何其他事务性机制的支持。
自动事务
Microsoft 事务服务器 (MTS)、COM+ 1.0 和公共语言运行库支持相同的自动分布式事务模型。
ASP.NET 页、XML Web services 方法或 .NET 框架类一旦被标记为参与事务,它们将自动在事务范围内执行。可以通过在页、XML Web services 方法或类中设置事务特性值来控制对象的事务性行为。特性值反过来确定实例化对象的事务性行为。因此,根据声明特性值的不同,对象将自动参与现有事务或正在进行的事务、成为新事务的根或者根本不参与事务。声明事务特性的语法在 .NET 框架类、ASP.NET 页和 XML Web services 方法中稍有不同。
声明性事务特性指定对象如何参与事务和如何以编程方式被配置。尽管此声明性级别表示事务的逻辑,但它是一个已从物理事务中移除的步骤。物理事务在事务性对象访问数据库或消息队列这样的数据资源时发生。与对象关联的事务自动流向合适的资源管理器。诸如 OLE DB、开放式数据库连接 (ODBC) 或 ActiveX 数据对象 (ADO) 的关联驱动程序在对象的上下文中查找事务,并通过分布式事务处理协调器 (DTC) 在此事务中登记。整个物理事务自动发生。
自动事务和 ASP.NET
ASP.NET 在运行 Microsoft Windows 2000 的系统上支持自动事务。通过在 ASP.NET 页中插入事务指令,可以指示该页参与现有事务、开始新事务或从不参与事务。
下表列出并描述 ASP.NET 中可用的事务指令。
指令 |
说明 |
Disabled |
指示 ASP.NET 将忽略事务上下文。这是默认的事务状态。 |
NotSupported |
指示该页不在事务范围内运行。处理请求后,不管是否有活动事务,均在没有事务的情况下创建其对象上下文。 |
Supported |
指示该页在现有事务的上下文中运行。如果没有事务,则该页在没有事务的情况下运行。 |
Required |
该页在现有事务的上下文中运行。如果没有事务,则该页将启动一个事务。 |
RequiresNew |
指示该页需要事务且为每个请求启动新事务。 |
可以通过在代码中放置指令来指示页上的事务支持级别。例如,插入下列指令以确保页活动始终在事务范围中执行。
<%@ Page Transaction="Required" %>
如果忽略该事务指令,则将对此页禁用事务。
自动事务和 XML Web services [C#]
ASP.NET使用与Web窗体一致且相似的编程抽象模型为创建和公开XML Web services 提供内置支持。结果模型是可伸缩和可扩展的,并接受 HTTP、XML、SOAP、WSDL 开放式Internet以及其他标准。XML Web services 支持开放式标准,因此任何客户端或支持Internet的设备都可访问和使用XML Web services。
XML Web services 提供了在自动事务范围内运行代码的选项。事务确保与资源管理器(如 SQL Server、MSMQ 服务器、Oracle 服务器和 SNA 服务器)的所有交互维护运行可靠的分布式应用程序所需的ACID属性。
使用 WebMethod 特性的 TransactionOption 属性声明一个自动事务。如果将 TransactionOption 属性设置为 TransactionOption.RequiresNew,则每次 XML Web services 客户端调用 XML Web services 方法时,都会开始一个新事务。
下列代码片断显示公开一个 XML Web services 方法(名为 DeleteAuthor)的服务。此 XML Web services 方法执行一个自动事务范围内的数据库操作。
<%@ WebService Language="C#" Class="Orders" %>
<%@ assembly name="System.EnterpriseServices" %>
using System;
using System.Data;
using System.Data.SqlClient;
using System.Web.Services;
using System.Web.Util;
using System.EnterpriseServices;
public class Orders : WebService
{
[ WebMethod(TransactionOption=TransactionOption.RequiresNew)]
public int DeleteAuthor(string lastName)
{
String deleteCmd = "DELETE FROM authors2
where au_lname='" + lastName + "'" ;
SqlConnection sqlConn = new SqlConnection("user
id=sa;database=pubs;server=myserver");
SqlCommand myCommand = new SqlCommand(deleteCmd,sqlConn);
// If a XML Web service method is participating in a transaction and an
// exception occurs, ASP.NET automatically aborts the transaction.
// Likewise, if no exception occurs, then the transaction is
// automatically committed.
myCommand.Connection.Open();
return myCommand.ExecuteNonQuery();
}
}
注意: 仅当激活的XML Web services方法(从客户端调用的方法)有事务元数据时,事务才会开始。如果激活的 XML Web services 方法未携带合适的事务元数据,则后继的 XML Web services 方法可能既不参与现有事务也不开始新事务。
自动事务和.NET 框架类 [C#]
只要准备了.NET 框架类参与自动事务,此类的实例就可以参与自动事务。类实例或对象访问的每个资源都在事务中登记。例如,如果一个对象使用 ADO.NET 发送数据库中某帐户上的钱,此数据库的资源管理器将确定该对象是否在事务中执行。如果对象是在事务中执行,则资源管理器自动在事务中登记此数据库。
使用下列过程准备参与自动事务的类:
- 将 TransactionAttribute 应用于此类。
- 从 ServicedComponent 类派生此类。
- 用强名称为程序集签名。
若要使用特性为程序集签名,请使用 Sn.exe 实用工具创建一个密钥对。
sn -k TestApp.snk
添加 AssemblyKeyFileAttribute 或 AssemblyKeyNameAttribute 程序集特性(它们指定包含密钥对的文件的名称),以使用强名称为程序集签名。
[assembly: AssemblyKeyFileAttribute("TestApp.snk
")]
- 向 COM+ 目录注册包含此类的程序集。
如果类的客户端调用实例是由公共语言运行库管理的,则注册将自动执行。但是,如果预期非托管调用方可能创建和调用类的实例,请使用 .NET 服务安装工具 (Regsvcs.exe) 手动执行注册。
下列示例显示如何将 TransactionAttribute 应用到从 ServicedComponent 类派生的类。
[Transaction(TransactionOption.Required)]
public class Bar(): ServicedComponent
{
//. . .
}
应用事务特性时,可以交替使用 Transaction、transaction、TransactionAttribute 和 transactionattribute。例如,可以使用 Transaction 或 transactionattribute 产生相同的结果。
下表列出并描述每个构造函数变体。
特性值 |
说明 |
Disabled |
消除自动事务对对象的控制。应用此特性值的对象可以直接将分布式事务处理协调器 (DTC) 用于事务性支持。 [Transaction(TransactionOption.Disabled)] |
NotSupported |
指示对象不在事务范围内运行。处理请求后,不管是否有活动事务,均在没有事务的情况下创建其对象上下文。 [Transaction(TransactionOption.NotSupported)] |
Supported |
指示如果有事务,则对象在现有事务的上下文中运行。如果没有事务,则对象在没有事务的情况下运行。 [Transaction(TransactionOption.Supported)] |
Required
(默认值) |
指示对象需要事务。如果有事务,则对象在现有事务范围中运行。如果没有事务,则对象启动一个事务。 [Transaction(TransactionOption.Required)] |
RequiresNew |
指示对象需要事务且为每个请求启动新事务。 [Transaction(TransactionOption.RequiresNew)] |
示例类
下列代码示例说明自动事务的若干元素。此例中,事务性类和调用此类的客户端都由运行库管理。
// -----------------------------------------------------------------
// TestApp.cs
// Generate a Strong name:
// sn -k TestApp.snk
// Compile the code:
// csc /target:exe /r:System.EnterpriseServices.dll TestApp.cs
// Run TestApp:
// start TestApp.exe
// -----------------------------------------------------------------
using System;
using System.Runtime.CompilerServices;
using System.EnterpriseServices;
using System.Reflection;
//Registration details.
//COM+ application name as it appears in the COM+ catalog.
[assembly: ApplicationName("TestApp")]
//Strong name for assembly.
[assembly: AssemblyKeyFileAttribute("TestApp.snk")]
[Transaction(TransactionOption.Required)]
public class Account : ServicedComponent
{
//Provides SetComplete behavior in the absence of exceptions.
[AutoComplete]
public void Debit(int amount)
{
// Do some database work. Any exception thrown here aborts the
// transaction; otherwise, transaction commits.
}
}
public class client
{
public static int Main()
{
Account accountX = new Account();
accountX.Debit(100);
return 0;
}
}
自动事务中的投票 [C#]
.NET 框架类和 ASP.NET 页可以通过投票来提交或中止它们的当前事务。默认情况下,如果代码中没有显式投票,则默认为赞成提交。但默认提交可能会延长为每个事务释放昂贵资源所用的时间,从而可能降低应用程序的性能。
显式投票还允许类或页在遇到严重错误时中止事务。此外,可以通过在事务处理的早期捕获致命错误、结束事务、释放资源等手段来提高应用程序的性能。
使用自动完成
System.EnterpriseServices.AutoCompleteAttribute 使参与事务的对象投票赞成在方法正常返回时完成事务。如果方法调用引发异常,则中止事务。只能将此特性应用于从 ServicedComponent 类派生的类。
若要使用此功能,请在类方法前插入此特性。如果将此特性添加到接口方法,则公共语言运行库将忽略它。下列代码片断显示此特性在事务识别类上的位置。
[Transaction(TransactionOption.Supported)]
public class Account : ServicedComponent {
[AutoComplete]
public void Debit(int amount) {
// Do some database work. Any exception thrown here aborts the transaction;
// otherwise, transaction commits.
}
}
使用SetAbort 和SetComplete
可以使用公开 SetComplete 或 SetAbort 方法的 System.EnterpriseServices.ContextUtil 类来显式提交或中止事务。SetComplete 指示对象投票赞成提交其工作;SetAbort 指示对象遇到了问题且投票赞成中止正在进行的事务。直到事务的根对象停用,才提交或中止事务。此外,任何参与事务的对象中有一个中止投票,都将导致整个事务失败。
下列代码片断显示正在使用的 SetAbort 和 SetComplete 方法。
//Try to do something crucial to the transaction in progress.
if( !DoSomeWork() )
{
//Something goes wrong.
ContextUtil.SetAbort();
}
else
{
//All goes well.
ContextUtil.SetComplete();
}