新事务之一: 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+只取最后一次改变作为计数。
根据上面的规则,我们就可以在程序中进行这样的仿真以实现自己的事务机制:
缺省(class构造)
提交前
回滚前
暂时不提交
提交暂时不提交的
IsHappy = true
IsDone = false
IsDone = true
IsHappy=false
IsDone = true
IsDone = false
IsDone = true
IsHappy 表示consistent标志位,IsDone表示done标志位,然后我们的类里可以这样判断:
if(CurrentTranscationContext.IsDone)
{
if(CurrentTranscationContext.IsHappy)
{ //done== true && consistent== true
CurrentTranscationContext.Commit();
}
else
{ // done == true && consistent == false
CurrentTranscationContext.Rollback();
}
CurrentTranscationContext = null;
}
最后
上面我们考察了讨论了CLR下的COM+的编程,分辨了一些模糊的概念,也看到了属性在事务编程模式中的使用和方便。有一点是肯定的:整个COM+的模型没有因为CLR而发生变化,COM+还是原来的COM+,试图通过学习dotNET避免学习COM+可能会使你又走回原来的地方,在未来的dotNET的开发模型和构架中,COM+反而成为一个最基础的技术和组成部分。新版本的COM+显示了在事务模型方面的改善,比如设置事务隔离级,但它也有一些明显的缺点和不足,后面的文章中我们可以看到借助dotNET和CLR以及更多dotNET环境来运用COM+事务服务以及如何改善和避开这些缺点,甚至抛开COM+建立自己的事务环境和机制,而另一方面这篇文章最后提到的有关事务的一些观点,将是以后我们自己实现事务机制和理解事务模型所必须的必要信息。
特别:
本文CSDN署名首发,转载或改编请注明作者和出处。如果有问题,请发电子邮件给new2001@msn.com
以上文字和图片涉及其他人的隐私和个人权利,所有文字和图片只用于内部交流,不作任何新闻发表和商业用途。