githubgit
1987年普林斯顿大学的Hector Garcia-Molina和Kenneth Salem发表了一篇Paper Sagas,讲述的是如何处理long lived transaction(长活事务)。听起来是否是以为和分布式事务很像?没错,下面来看看这个来自1987年的解决方案是如何启发当今的分布式事务问题的。github
Saga的组成:apache
能够看到,和TCC相比,Saga没有“预留”动做,它的Ti就是直接提交到库。segmentfault
Saga的执行顺序有两种:分布式
Saga定义了两种恢复策略:中间件
Saga对于ACID的保证和TCC同样:接口
Saga相比TCC的缺点是缺乏预留动做,致使补偿动做的实现比较麻烦:Ti就是commit,好比一个业务是发送邮件,在TCC模式下,先保存草稿(Try)再发送(Confirm),撤销的话直接删除草稿(Cancel)就好了。而Saga则就直接发送邮件了(Ti),若是要撤销则得再发送一份邮件说明撤销(Ci),实现起来有一些麻烦。事务
若是把上面的发邮件的例子换成:A服务在完成Ti后当即发送Event到ESB(企业服务总线,能够认为是一个消息中间件),下游服务监听到这个Event作本身的一些工做而后再发送Event到ESB,若是A服务执行补偿动做Ci,那么整个补偿动做的层级就很深。ci
不过没有预留动做也能够认为是优势:资源
对于服务来讲,实现Saga有如下这些要求:
第一点要求Ti和Ci是幂等的,举个例子,假设在执行Ti的时候超时了,此时咱们是不知道执行结果的,若是采用forward recovery策略就会再次发送Ti,那么就有可能出现Ti被执行了两次,因此要求Ti幂等。若是采用backward recovery策略就会发送Ci,而若是Ci也超时了,就会尝试再次发送Ci,那么就有可能出现Ci被执行两次,因此要求Ci幂等。
第二点要求Ci必须可以成功,这个很好理解,由于,若是Ci不能执行成功就意味着整个Saga没法彻底撤销,这个是不容许的。但总会出现一些特殊状况好比Ci的代码有bug、服务长时间崩溃等,这个时候就须要人工介入了。
第三点乍看起来比较奇怪,举例说明,仍是考虑Ti执行超时的场景,咱们采用了backward recovery,发送一个Ci,那么就会有三种状况:
对于第1种状况,容易处理。对于第二、3种状况,则要求Ti和Ci是可交换的(commutative),而且其最终结果都是sub-transaction被撤销。