日期:2014-05-16  浏览次数:20598 次

自斟自饮——5. 事务内嵌的关系



今天的主题很简单,有这样一个需求,

-- 这个仅仅是伪代码
begin transaction;
define f_success_cnt = 0

-- 第一个任务
call handle_first_task() returning ret
if ret = true then
   f_success_cnt += 1
else
   insert into error_log(current, 'first taks is fail')
end if

-- 第二个任务
call handle_second_task() returning ret
if ret = true then
   f_success_cnt += 1
else
   insert into error_log(current, 'second taks is fail')
end if

-- 第三个任务
call handle_third_task() returning ret
if ret = true then
   f_success_cnt += 1
else
   insert into error_log(current, 'third taks is fail')
end if

-- 最后要判断是否需要提交
if f_success_cnt >= 2 then
   commit transaction
else
   insert into error_log(current, 'finally we rollback due to to much errors! ')
   rollback transaction
end if


注意,这些都是伪代码,不过我相信你应该看得懂的。简单来说是启动一个事务,然后处理三个任务,最后根据任务完成的个数决定是否commit。

问题的重点是,表error_log,我们可以看到,如果任务失败,会插一些日志到这个表。但是:这个程序最后是有机会commit,也有机会rollback。如果rollback了,之前插入到error_log表的数据也一样会rollback,达不到日志的效果。

对于这种需求,你会有什么solution?
























这个问题严格来说不是问题,但,There are a thousand Hamlets in a thousand people's eyes.

首先,最容易想到的解决方案是同时启动两个连接,普通的业务操作用一个连接,另外一个专门用于写日志。这样两个连接就不会存在所谓的事务上的冲突。不过这样就凭空多消耗一个连接,有时候不太划算。不过如果是batch就问题不太大。

其次,你可能会想到一些稀奇古怪的方案,比如写一大堆逻辑判断那些任务,然后再最后再commit或者rollback,然后再插入日志。不过老实说,这种方案已经不是日志了,而且还比较复杂,容易出错。

然后你又可能会想到MySQL的一个很重要特性,多引擎。这里的引擎当然不是飞机的引擎,而是存储引擎,例如MyISAM,InnoDB等。注意这里,MyISAM是不支持事务的,而InnoDB是支持事务,如果混合使用,把事务表配置成MyISAM,即使InnoDB的表发生rollback,也不会影响到MyISAM的表格。这不失为一个好方法。

最后当然是介绍一下今次的主菜——事务自治。这个自治跟我们国家的少数民族事务自治是没有关系的,哈哈。

假如有两个事务A和B,A包含B,那这两个事务的关系一般来说是以下其中一种:
  • 嵌套事务(Nested Transaction):指在一个Parent事务中嵌套的一个或多个Sub Transaction.并且主事务与其相互影响,这种事务就称为嵌套事务。以Commit作为事务的结束。
  • 自治事务(Autonomous Transaction):指在function,procedure等subprograms中对事务进行自治管理,当在别的pl/sql block里去调用这些subprograms的时候这些subprograms并不随着父pl/sql block的失败而回滚,而是自己管自己commit。以Commit作为事务的结束。自治事务常用于写入LOG或TRAC信息便于查找错误。

这两种事务关系的不同之处在于,嵌套事务中子事务和父事务不是独立的,而在自治事务中父子事务则是独立的。

限于时间关系就不再展开说明了,各位可以google一下,或者到 http://www.iteye.com/wiki/design_think/2297-oracle-transaction 了解一下。