是否可以使用非ACID数据库模拟分布式事务?

时间:2010-12-05 17:03:00

标签: sql-server sql-server-2008 transactions mongodb

我们有一个系统将SQL Server 2008与MongoDB结合使用,后者使用后者进行大量的临时报告功能。它们与很多不相交,但实际上它们确实需要一两个地方一起工作。

我总是对交易影响略感担忧,但认为如果应用程序首先执行Mongo工作并不是一个大问题,而且技术上不是那么大问题交易在中间偶尔会失败。但是最近出现的一个错误导致他们一直失败,虽然我已经修复了造成它的错误,但它让我意识到我不能做多少麻烦只是在整个工作单元上抛出分布式事务。

鉴于一个支持分布式事务的数据库(SQL Server 2008)和另一个不支持任何 ACID语义(MongoDB)的数据库,我是否可以通过某种方式构建应用程序代码以便工作单元(“事务”)是成功还是在两个数据库中回滚?

显然,我必须使用一些额外的列/键来跟踪交易的状态 - 但是什么,以及在什么情况下?

4 个答案:

答案 0 :(得分:1)

您的一致性要求是什么?如果关于Mongo的临时报告并不总是完全是最新的,那还可以吗?

如果不是,那么我认为你很难过。

如果是,那么我想我会选择一些事务MSMQ和SQL Server,然后设置一个(或多个)服务来通过处理队列中的消息来更新Mongo。

答案 1 :(得分:1)

您可能应该在每个文档中引入一个新字段,如交易ID。所以如果出现问题,这可以用来从mongo回滚你新添加的文件。

伪代码..

Using (var tx=new transaction....){
  try {
    var txID= random id;
    //your sql data insertion
    //Mongo db document insertion with tx id

    if (some problem) {
          rollbackSQL();
          // and delete all the documents with the current tx id
     }
  }
  catch()
  {
          rollbackSQL();
          // and delete all the documents with the current tx id
  }
}

或者你可以在成功的sql commit上完成你所有的mongodb插入。

var docList = new List<MongoDocs>();
 Using (var tx=new transaction....){
      try {     
        //your sql data insertion
       docList.add(mongoDoc);
       if (success){
           sqlcommit();
           foreach(var doc in docList )
           {
               mongodb.insert(doc);
           }
       }
      }
      catch()
      {
              rollbackSQL();                 
      }

    }

更新:致Aaronaught的评论。

  

此处的第二个代码段没有   工作,因为SQL   交易已经提交   在尝试Mongo插入之前,   如果插入失败(即破坏   连接),滚动它已经太晚了   回到SQL Server。

这是真的,这可以通过在sql commit

之前将文档添加到mongo中来解决
 var docList = new List<MongoDocs>();
 Using (var tx=new transaction....)
 {
      try 
      {     
        //your sql data insertion
       docList.add(mongoDoc);
       if (success)
         {             
           foreach(var doc in docList )
           {
               mongodb.insert(doc);
           }

           sqlcommit();
         }
      }
      else {
           rollbackSQL();       

      }
      catch()
      {
              rollbackSQL();                 
              // And delete all newly added mongo documents by looping docList
      }

  }

现在你可以确保只在完成sql&amp; mongo插入。

答案 2 :(得分:0)

Ramesh Vel的回答非常好,但还有一点你忘了。

ACID是一种容错的范例。您假设您的事务将到达catch块,然后回滚,或者try结束,然后提交。

一般来说,这不是真的。在某些情况下,DB必须保持一致并且不满足上述条件,例如:

  1. 无限循环。假设您的代码或代码调用的方法中存在错误,则事务可能永远不会结束。使用看门狗计时器修复ThreadAbortException是一种可行的方法。死锁的处理方式相同
  2. 硬件故障。假设在执行期间电源下降。什么是数据库的safe state?您必须回滚整个挂起的事务,和/或重新执行它们。在上面的示例中,您无法直接修复代码。商业DBMS有大量事务日志,有助于恢复正确的数据库状态
  3. 换句话说,在使用不是为ACID操作设计的DBMS时,在完全容错方面不可能获得完全ACID,而不实际实现代码之间的软件层以及仅在code commit之后执行SQL操作的DBMS,保留已完成事务的稳定日志(等待恢复后提交)。

    这是一个非常复杂的问题。如果您接受有可能您的数据库不一致(即您不是银行和/或您有手动一致性检查器在恢复后使用)您可以使用显示的代码段模拟ACIDity在另一个答案中,我的+1

答案 3 :(得分:0)

我认为你可以通过IEnlistmentNotification或ISinglePhaseNotification为这些案例创建specail资源管理器。