初步研究事务复制与订阅者触发器的运行先后顺序
问题描述:
transaction分为许多cmd。执行cmd之后也可能执行触发器。我想搞清楚transaction的cmd和订阅者触发器之间,cmd和cmd之间,transaction和transaction之间的运行先后顺序。
Ps:这里不详细考察发布者、分发者,忽略具体的细节,只把结构简化为发布者——订阅者
问题结论:
1)
对于一个transaction,
对每个cmd,先上锁、执行cmd以更新Subscriber,然后等Subscriber触发的一连串事情都运行完后,才解锁、完成cmd;
一个cmd完成后,才执行下一个cmd,即线性执行;
对每个transaction,
一个transaction宣告完成后,才执行下一个transaction,即线性执行
探究使用的工具:
sys.dm_tran_locks,复制监视器,优化器提示,触发器,WAITFOR
问题探究:
1)在一个transaction只有一个cmd的情况下,cmd和订阅者触发器的运行顺序
Publisher的数据库为SourceExam,含有TestTrigger和TestTriggerDst两个表。没有触发器。
Subscriber的数据库为DstExam,包含表TestTrigger和TestTriggerDst。表TestTrigger上包含触发器,且不开启NOT FOR REPLICATION选项,如下
让发布者发布一个能在Subscriber处触动触发器的事务,然后运行如下语句
会发现,发布者也在等待,其lastwaittype是WAITFOR,而且长期持有数据行上面的X锁和数据所在页面的IX锁。说明写事务还没全部完成。
而此时用优化器提示with (nolock)来做SELECT操作,会发现,Subscriber表里的数据已经被更新。说明写事务内容本身已经做完了。
等到WAITFOR的时间结束后,用上述查看锁的语句发现,写事务已经消失,说明写完成了。
所以,我们大胆推断:
对于单个cmd的处理,先上锁、执行对Subscriber的更新,然后等Subscriber产生的一连串事情都运行完后,才解锁、完成。
2)在一个transaction有多个cmd的情况下,cmd和cmd之间的运行顺序
Subscriber的TestTrigger表上的触发器改为如下
同时,Publisher也设置一个触发器,内容为更改TestTriggerDst的数据。然后,发布一个能激发Publisher和Subscriber触发器的事务。这样,复制监视器里会显示:
然后可以看到,这回事务发布失败,可看到下述复制监视器里的消息。说明第一个cmd的删除执行之后,才执行第二个cmd
这说明,第一个cmd的触发器要运行完毕,才能运行第二个cmd。可大胆猜想:
第一个cmd宣告运行完后,第二个cmd才能运行
另外,发生错误后,在Subscriber处发现TestTriggerDst依然存在。这是因为一个执行不成功的transaction会被回滚。
3)在有多个transaction的情况下,transaction和transaction之间的运行顺序
Subscriber的TestTrigger表上的触发器改为如下
同时,利用GO语句或BEGIN TRANSACTION语句分别发送两个事务,事务A用于触动Subscriber的触发器,事务B则执行对TestTriggerDst的修改。然后,根据上述sys.dm_tran_locks的查询代码,可以判断第一个事务还在处理中;此时查询事务B修改的那个表,看事务B是否已执行。结果发现,在事务A未宣告完成期间,事务B不会得到执行,所以,我们大胆推断:
第一个transaction宣告运行完毕后,第二个transaction才开始运行