** 新事务之一: dotNET和COM+中的事务 **
小气的神
2002-4-16
Article Type: In-Depth
难度等级: 6/9
版本: 2.32
** Isolation level ** ** **
|
---|---
** Read uncommitted **
|
这是最小的隔离等级,它不提供任何事务隔离的保证,这种隔离等级容易导致脏读。大型的应用中发生多个并发事务修改共享数据时这种隔离级非常危险也不适合银行金融等要求高精密的事务要求。
优点是不用请求锁定所以大大减少事务等待时间,提高系统性能
** Read committed **
|
这个隔离等级解决了脏读问题,保证不会读取写入数据库但是没有提交的数据,也就是说它保证读取的数据是一致的,读取那些已提交的数据。
这个隔离等级不能解决不可重读和幻影的问题,但它是 MS SQL 以及 Oracle 等数据库的默认隔离等级。
** Repeatable read **
|
这个隔离等级解决了不可重读问题,保证我们每次从数据库中取得的数据和上一次相同,适用于那一些数据展示给客户端,然后成批的进行修改这样的应用,“毕竟是由我负责划帐”的情境。
这种模式下允许幻影问题,只锁住我们以后要更新的(需要负责的)的数据
** Serializable **
|
这个隔离等级是最严格的,解决了三种可能出现的数据不一致问题。保证每个事务之间按照一定的序列执行,完全隔离。大家排着队,同一时刻只有少数不冲突的事务数据库会并行处理(其它的统统锁住),除了付出数据库存取很慢的代价外,你几乎可以任意读写。
不同的隔离等级将使 RM 在事务期间产生不同的锁来控制事务在此期间的存取数据,可能一直保留到事务提交或回滚。根对象( root objects )将确定整个事务的隔离等级,这一般发生在事务刚创建时,一旦根对象确定了事务隔离等级,那么这个隔离等级将影响整个事务边界中的所有对象并且在整个事务提交或回滚之前保持权威,对于根对象之后的内部对象( interior objects )不能再要求自己独特的事务隔离等级或会产生冲突的等级, COM+ 可以允许其他的内部对象小于或等于这个事务隔离等级,如果要求大于了这个事务隔离等级, COM+ 将拒绝在自己的环境中创建这个对象。
对于我们没有列入表中的 ** Any ** 选项意味着这个对象可以适应或是任意隔离等级的一部分,对于设置 Any 属性的对象, COM+ 要看这个对象是否是根对象,如果是根对象,那么 COM+ 谨慎小心的会默认给它一个 Serializable 的隔离等级,否则可以根据它所在事务边界中的隔离等级或是它访问的 RM 的隔离等级给它一个隔离等级。
COM+ 使得你可以在一个更高的层次来调整和指定并发控制(间接地控制数据库锁),如何控制合适而不是太松或太严的隔离等级一直有所争议和仁者见仁。但无论如何,有的控制比没得控制使你有更多的选择余地。
** COM+ ** ** 事务服务目前的一些缺点 ** ** **
1. 对于一些不需要分布式事务的应用使用 COM+ 显得过于大材小用,明显的影响程序的性能。比如单一的数据库应用和简单而普通的业务应用电话簿管理、个人信息管理、留言簿应用等等。花费较大的两段提交表明 COM+ 更多的面向分布在多个计算机上的多个数据库的分布事务。
2. 不需要分布式的部署和调用也不使用本地的 DTC ,但是需要完整而精简的事务特性的应用。
3. 在 dotNET 的环境中使用 COM+ 功能,不可避免的要付出一些 COM InterOP 的花费,当然不是大的问题,可是几乎所有的开发人员更喜欢“纯粹的感觉”-- CLR-managed COM+ API
4. COM+ 只支持整个类的定义事务属性,不支持一个类中的方法级的事务属性。过去的时间里我们在太多不知情的情况下将不同功能的函数放在一个类里(比如查询、 Delete 和 Update ),结果使得每个查询函数拥有和 Update , Delete 一样的事务属性而造成瓶颈和尴尬。
5. COM+ 目前缺省也是唯一的事务隔离级是 Serializable ,尽管传说是某个程序员使用了硬编码造成了这一问题,但目前一些决策分析和统计应用被受损失,在可以允许的误差范围许可下他们被迫使用着最严格的并发控制,也就是说他们只能放弃性能而获得并不十分需要的超精密数据。同样即使最新版本的 COM+ 仍然不支持象上述的基于方法级的事务隔离定义,那么对于在一个类里的每个方法定义不同事务属性不同隔离等级的想法,无疑有些疯狂。
6. “ Semi-transactional Objects ”的问题,和上面说的方法级的属性定义类似。最新的资料表明新版本的 COM+ 提供了一些解决方案, Microsoft 认为它们在 COM+ 1.0 似乎只能借助分割类来减少和避免这样的问题。
** 事务何时开始又何时结束 ** ** **
了解事务的开始和结束的过程可以使我们从一个事务边界里考察每个参与对象的行为和事务的边界情况。
COM+ 何时开始一个事务?在下面两种情况下 COM+ 将自动开始一个事务:
1. 当一个非事务属性的 Client 调用了一个标识有需要或需要新事务的 COM+ 组件
2. 当一个有事务属性的 Client 调用了一个标识有需要新事务的 COM+ 组件
当 COM+ 确定一个对象需要一个新的事务,那么它会首先在当前对象的位置开始一个事务,接着它在下面一些步骤进行一些操作:
1. COM+ 创建一个环境( Context )对象,设置其 JIT activation 和 synchronization 属性为需要,并且设置内部维护的 consistent 和 done 标志位。
2. COM+ 通知 DTC 将开始一个事务, DTC 配置一个物理的事务
3. DTC 产生一个新的事务并将标识这个事务的 GUID 标识返回给 COM+ ,事务标识将建立一个事务环境边界,所有参与事务的对象将分享这个标识。 COM+ 将这个事务的引用保存到 Context 中
4. 当 Client 建立这个对象时, COM+ 在这个事务边界的环境中激活它
事务边界中的第一个对象是很特别的,它就是我们一般所说的根对象,一个事务中只能有一个根对象,根对象之下的其它对象被称为内部对象
COM+ 何时结束一个事务?在下面三种情况下 COM+ 将结束一个事务其结果是提交或回滚事务。
1. 事务的执行超过了期限,那么 COM+ 将自动结束在超时期间没有提交的事务,和事务相关的所有对象不再处于激活状态( deactivating ),事务回滚。一般缺省的事务超时时间是 60 秒。
2. 根对象在一个调用中完成它的工作, COM+ 释放它的引用(代理和存根还在),之后根对象不再处于激活状态,整个事务开始尝试提交。
3. 根对象被 Client 释放, COM+ 释放对象的引用(代理先没有了存根也没有了),之后根对象不再处于激活状态,整个事务开始尝试提交。
** COM+ ** ** 提交或放弃事务 ** ** **
为了有效的工作, COM+ 在每个环境( Context )中维护着两个标志位 consistent 和 done 。这两个标志位影响事务是提交还是放弃,开发人员靠改变这两个标志位来告诉 COM+ 自己对事务的观点, COM+ 根据这两个标志位的状态在通知 DTC 该如何和 RM 进行交互。事实上有一个真相一直被我们忽略:具有事务属性的对象没有参与著名的两段提交过程(也就是说没有见证到整个事务的结果),只是所有应征 RM 和 DTC 完成了这个过程(评票和实施,并且宣布结果)。整个事务边界中的所有对象仅是对事务是否提交进行了投票,当所有的组件同意后, COM+ 将统计这些投票并把最终的一个结果告诉 DTC ,你曾经为我创建的某个事务(由 GUID 标识的)可以提交了, DTC 请求所有应征的 RM 也提交各自的改变。而此时曾经参与投票的所有对象已经销毁或是去了对象池,总之它们已经不再处于激活状态了。也就是说根对象在调用 SetComplete 之后简单的获得了一个简单的 S_OK 返回(事务何时开始和结束它根本不知道),其他对象对事务结果更是一无所知(只是在临死前投下自己的一票),两段提交过程可能失败。那么 COM+ 会更改原来根对象获得的 S_OK 的返回值( CONTEXT_E_ABORTED ),最后只有根对象的 Client 知道事务最终的结果。当然根事务如果调用 SetAbort ,那么结果是预先可以知道的,但根事务象上面说的一样,仍然不是处于激活状态。简单的说,根对象通过请求被释放来强迫 COM+ 进入它的事务表决。根对象释放之后, COM+ 也会释放事务域里的所有对象,因此这之前所有对象的暂时表决必须在 COM+ 统计投票之前确定下来(这是最后期限),之后 COM+ 知道是提交还是回滚事务,事务被强迫隔离起来不再允许任何线程或对象进入(隔离是 JIT 激活要求的,如果不隔离对象还可以再进来或是对象可能带着状态参与到下一次的其它事务中,而这种隔离保证是靠同步域(另外一个)来做到的,所以整个事务一直到最后都是安全的)。
开发人员如何控制这两个标志位呢,特别是如果自己实现事务机制时,我们必须清楚这个投票算法。
下面的表列举我们使用的方法来修改这两个标志位:
可以使用的方法或函数
|
** Consistent Bit ** ** **
|
** Done Bit ** ** **
---|---|---
IObjectControl::SetComplete
|
True
|
True
IObjectControl::SetAbort
|
False
|
True
IObjectControl::EnableCommit (default)
|
True
|
False
IObjectControl::DisableCommit
|
False
|
False
IContextState::Get/SetMyTransactionVote
|
|
根据传入参数
IContextState::Get/StateDeactivateOnReturn
|
根据传入参数
|
COM+ 根据下面的规则使用这两个标志位来收集投票:
1. 缺省的情况下, COM+ 新建立一个环境( Context )时, consistent 设置为 true , done 位被设置成 false ,也就是表中列的 EnableCommit 的情况
2. COM+ 将在当前函数返回时检查,如果 done 位是 true ,那么 COM+ 将使这个对象失去激活 (deactivate) ,否则是 false , COM+ 则继续保留这个对象。
3. COM+ 只有在方法调用返回或是当一个对象失去激活状态时检查 done 位的值,如果为 true ,那么 COM+ 才注意 consistent 位的值,如果 consistent 位是 true ,那么表明它赞成提交,否则表明要回滚。
4. 只有在对象失去激活时, COM+ 才读取事务表决。如果对象不向 COM+ 请求释放,那么就在最后 Client 释放它之后,读取表决。即只有当 done 位为 true 时, COM+ 才开始读取 cnsistent 标志位。
5. 由于整个事务边界中的所有对象将共享这两个标志位,所以每个标志位都在对象失去激活前都可以设置多次和进行多次改变(上面说的“对象的暂时表决”), COM+ 只取最后一次改变作为计数。 </