两阶段提交协议
实现分布式事务的关键就是两阶段提交协议。在此协议中,一个或多个资源管理器的活动均由一个称为事务协调器的单独软件组件来控制。此协议中的五个步骤如下:
• 应用程序调用事务协调器中的提交方法。
• 事务协调器将联络事务中涉及的每个资源管理器,并通知它们准备提交事务(这是第一阶段的开始)。
• 为 了以肯定的方式响应准备阶段,资源管理器必须将自己置于以下状态:确保能在被要求提交事务时提交事务,或在被要求回滚事务时回滚事务。大多数资源管理器会 将包含其计划更改的日记文件(或等效文件)写入持久存储区中。如果资源管理器无法准备事务,它会以一个否定响应来回应事务协调器。
• 事务协调器收集来自资源管理器的所有响应。
• 在 第二阶段,事务协调器将事务的结果通知给每个资源管理器。如果任一资源管理器做出否定响应,则事务协调器会将一个回滚命令发送给事务中涉及的所有资源管理 器。如果资源管理器都做出肯定响应,则事务协调器会指示所有的资源管理器提交事务。一旦通知资源管理器提交,此后的事务就不能失败了。通过以肯定的方式响 应第一阶段,每个资源管理器均已确保,如果以后通知它提交事务,则事务不会失败。
文章出处:http://www.diybl.com/course/7_databases/database_other/2008224/101189.html
实现分布式事务的关键就是两阶段提交协议。在此协议中,一个或多个资源管理器的活动均由一个称为事务协调器的单独软件组件来控制。此协议中的五个步骤如下:
1• | 应用程序调用事务协调器中的提交方法。 |
2• | 事务协调器将联络事务中涉及的每个资源管理器,并通知它们准备提交事务(这是第一阶段的开始)。 |
3• | 为 了以肯定的方式响应准备阶段,资源管理器必须将自己置于以下状态:确保能在被要求提交事务时提交事务,或在被要求回滚事务时回滚事务。大多数资源管理器会 将包含其计划更改的日记文件(或等效文件)写入持久存储区中。如果资源管理器无法准备事务,它会以一个否定响应来回应事务协调器。 |
4• | 事务协调器收集来自资源管理器的所有响应。 |
5• | 在 第二阶段,事务协调器将事务的结果通知给每个资源管理器。如果任一资源管理器做出否定响应,则事务协调器会将一个回滚命令发送给事务中涉及的所有资源管理 器。如果资源管理器都做出肯定响应,则事务协调器会指示所有的资源管理器提交事务。一旦通知资源管理器提交,此后的事务就不能失败了。通过以肯定的方式响 应第一阶段,每个资源管理器均已确保,如果以后通知它提交事务,则事务不会失败 |
2PC是指Oracle的两阶段提交协议(Two-Phase Commit protocol)。
2PC用于确保所有分布式事务能够同时提交(Commit)或者回滚(Rollback),以便使的数据库能够处于一致性状态(consistent state)。
分布式事务可以通过DBA_2PC_PENDING 和 DBA_2PC_NEIGHBORS 字典视图查看。
分布式事务处理是指一个事务可能涉及多个数据库操作
分布式事务处理的关键是必须有一种方法可以知道事务在任何地方所做的所有动作,提交或回滚事务必须产生一致的结果(全部提交或全部回滚)。
XA是X/Open DTP组织(X/Open DTP group)定义的两阶段提交协议,XA被许多数据库(如Oracle和DB2)和中间件等工具(如CICS 和 Tuxedo).本地支持 。
X/Open DTP模型(1994)包括应用程序(AP)、事务管理器(TM)、资源管理器(RM)、通信资源管理器(CRM)四部分。在这个模型中,通常事务管理器(TM)是交易中间件,资源管理器(RM)是数据库,通信资源管理器(CRM)是消息中间件。
一般情况下,某一数据库无法知道其它数据库在做什么,因此,在一个DTP环境中,交易中间件是必需的,由它通知和协调相关数据库的提交或回滚。而一个数据库只将其自己所做的操作(可恢复)影射到全局事务中。
XA就是X/Open DTP定义的交易中间件与数据库之间的接口规范(即接口函数),交易中间件用它来通知数据库事务的开始、结束以及提交、回滚等。XA接口函数由数据库厂商提供。通常情况下,交易中间件与数据库通过XA 接口规范,使用两阶段提交来完成一个全局事务,XA规范的基础是两阶段提交协议。
在第一阶段,交易中间件请求所有相关数据库准备提交(预提交)各自的事务分支,以确认是否所有相关数据库都可以提交各自的事务分支。当某一数据库收到预提 交后,如果可以提交属于自己的事务分支,则将自己在该事务分支中所做的操作固定记录下来,并给交易中间件一个同意提交的应答,此时数据库将不能再在该事务 分支中加入任何操作,但此时数据库并没有真正提交该事务,数据库对共享资源的操作还未释放(处于锁定状态)。如果由于某种原因数据库无法提交属于自己的事 务分支,它将回滚自己的所有操作,释放对共享资源上的锁,并返回给交易中间件失败应答。
在第二阶段,交易中间件审查所有数据库返回的预提交结果,如所有数据库都可以提交,交易中间件将要求所有数据库做正式提交,这样该全局事务被提交。而如果有任一数据库预提交返回失败,交易中间件将要求所有其它数据库回滚其操作,这样该全局事务被回滚。
伪代码:
假设有两个Connection, con1, con2, 大体的过程如下,
con1 = XAResouce1.getConnection...
con2 = XAResouce2.getConnection...
con1 do some thing.
con2 do some thing.
after they finish.
pre1 = XAResouce1.prepare();
pre2 = XAResouce2.prepare();
if( both pre1 and pre2 are OK){
XAResouce1 and 2 commit
}else {
XAResouce1 and 2 rollback
}
============================
在谈具体实现前 先介绍一下三种事务:
1. 单对象单资源
2. 多对象单资源
3. 多对象多资源(分布式事务, 使用两段提交协议)
在ADO.Net1.0下有两种使用Transaction的方法. 一种是在需要事务的对象中显式的调用事务处理, 还有一种是使用Enterprise Service的声明式的方法.
第一种方法的示例代码如下:
public void TransactionTest()
{
string connectionString = "";
IDbConnection connection = new SqlConnection(connectionString);
connection.Open();
IDbCommand command = new SqlCommand();
command.Connection = connection;
IDbTransaction transaction;
transaction = connection.BeginTransaction(); //Enlisting database
command.Transaction = transaction;
try
{
/**/
/* Interact with database here, then commit the transaction
*/
transaction.Commit();
}
catch
{
transaction.Rollback(); //Abort transaction
}
finally
{
connection.Close();
}
}
这种方法使用起来相当麻烦, 而且只能针对一个对象访问一个资源的事务. 如果事务涉及多个对象,那么由谁来进行事务处理? 如果事务使用了多个资源, 那样又涉及到分布式事务和两段提交协议,此时依靠第一种方法完全由用户自己控制实在过于复杂,因此在提供了第一种基本方法后, ADO.Net 1.0又利用Com+实现了声明式的事务处理,示例代码如下:
using System.EnterpriseServices;
[Transaction]
public class MyComponent : ServicedComponent
{
[AutoComplete]
public void MyMethod()
{
/**/
/*Interact with other serviced components and resource managers */
}
}
这种声明式的方法看上去似乎很好,但是也隐含了许多问题.
1. 使用事务的对象需要继承ServicedComponent
2. 即使不涉及多资源的分布式事务而仅仅是涉及到了多个对象的简单事务(开头介绍的第二种事务),我也要使用此方法,影响了效率. 这样的弊端和J2ee中的都在本地的Entity Bean之间进行通讯很像,杀鸡也不得不用牛刀.
3. 不可避免的使用了Com+.
4. 使用Enterprise Services的事务总是线程安全的, 也就是说你无法让多个线程参与到同一个事务中.
ADO.Net2.0 提供的新的事务模型综合了前两者的优点,
1 在简单(不涉及分布式)事务中也可以使用声明式的事务处理方法, 而不必使用Com+容器, ADO.net 2.0中提供了一个轻量级的事务容器.
2 用户根本不需要考虑是简单事务还是分布式事务. 新模型会自动根据事务中涉及的对象资源判断使用何种事务管理器. 简而言之, 对于任何的事务用户只要使用同一种方法进行处理. 示例代码:
using (TransactionScope scope = new TransactionScope())
{
/**/
/* Perform transactional work here */
//No errors - commit transaction
scope.Complete();
}
另外对嵌套事务和事务的隔离级别也提供了支持, 在此就不作详细介绍. Fantasy Soft 对此做了介绍.
http://perhaps.cnblogs.com/archive/2005/08/17/216863.html
using (TransactionScope scope1 = new TransactionScope())
//Default is Required
{
using (TransactionScope scope2 = new TransactionScope(TransactionScopeOption.Required))
{ }
using (TransactionScope scope3 = new TransactionScope(TransactionScopeOption.RequiresNew))
{ }
using (TransactionScope scope4 = new TransactionScope(TransactionScopeOption.Suppress))
{ }
}
TransactionOptions options = new TransactionOptions();
options.IsolationLevel = IsolationLevel.ReadCommitted;
options.Timeout = TransactionManager.DefaultTimeout;
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, options)) { }
public enum IsolationLevel
{
ReadUncommitted,
ReadCommitted,
RepeatableRead,
Serializable,
Unspecified,
Chaos, //No isolation whatsoever
Snapshot //Special form of read committed 8 supported by SQL Server 2005
}
本文仅仅是ADO.net 2.0 Transaction的简单介绍, 详细资料查看M$的相关文档.
参考资料: Introducing System.Transactions by Juval Lowy