Можно ли эмулировать распределенную транзакцию с не-ACID базой данных? - PullRequest
3 голосов
/ 05 декабря 2010

У нас есть система, которая использует SQL Server 2008 в тандеме с MongoDB, используя последний для множества специальных отчетов. Они не пересекаются сильно , но есть одно или два места, где им действительно нужно работать вместе.

Я всегда был слегка обеспокоен последствиями транзакций, но полагал, что это не было большой проблемой, если приложение сначала работало в Mongo, и технически не такая большая проблема, если транзакция не удается в середине время от времени. Но появилась недавняя ошибка, которая приводила к тому, что они последовательно терпели неудачу, и, хотя я исправил ошибку, вызвавшую ее, я понял, насколько неприятно то, что я не могу просто распределить транзакцию по всей единице работы.

Учитывая одну базу данных, которая поддерживает распределенные транзакции (SQL Server 2008), и другую, которая на самом деле не поддерживает любую семантику ACID (MongoDB), существует ли какой-то способ структурирования кода приложения, чтобы работы («транзакция») либо выполняется успешно, либо выполняется откат в обеих базах данных?

Очевидно, я должен был бы использовать некоторые дополнительные столбцы / ключи для отслеживания статуса транзакции - но что и в каком контексте?

Ответы [ 4 ]

1 голос
/ 06 декабря 2010

Может быть, вы должны ввести новое поле в каждом документе, как идентификатор транзакции.так что это может быть использовано для отката вновь добавленных документов из 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
  }
}

Или вы можете выполнить все вставки mongodb при успешной фиксации sql.

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();                 
      }

    }

Обновление : к комментарию Ааронаута.

Второй фрагмент кода здесь вообще не работает, поскольку транзакция SQL уже была зафиксирована до попытки вставки Mongo, и если эта вставка завершается неудачно (то есть разрывается соединение), уже слишком позднооткатить его обратно в SQL Server.

Это правда, это можно решить, добавив документы в mongo до того, как sql commit

 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 и mongo.

1 голос
/ 05 декабря 2010

Каковы ваши требования к согласованности?Разве это нормально, если специальные отчеты по Монго не всегда полностью обновлены?

Если нет, то я думаю, что у вас трудное время.Затем я думаю, что я бы пошел для некоторого транзакционного MSMQ вместе с SQL Server, а затем настроил одну (или несколько) служб для обновления Mongo путем обработки сообщений из очереди.

0 голосов
/ 19 марта 2012

Я думаю, что вы можете создавать специальные менеджеры ресурсов для этих случаев через IEnlistmentNotification или ISinglePhaseNotification.

0 голосов
/ 06 декабря 2010

Ответ Ramesh Vel очень хорош, но есть еще один момент, который вы забыли.

ACID - это парадигма, отказоустойчивая .Вы предполагаете, что ваша транзакция либо достигнет блока catch, затем откатывается, либо до конца try, затем будет зафиксировано.

В общем, это не так .В некоторых случаях необходимо поддерживать согласованность базы данных и не выполнять ни одно из указанных выше условий, например:

  1. Бесконечные циклы.Предположим, что в вашем коде или в методе, вызванном вашим кодом, есть ошибка, транзакция может никогда не закончиться.Исправить с помощью сторожевого таймера, который выбрасывает ThreadAbortException - это жизнеспособный способ.Взаимные блокировки обрабатываются одинаково
  2. Аппаратные сбои.Предположим, что мощность снижается во время выполнения.Что такое safe state БД?Вы должны откатить все ожидающие транзакции и / или повторить их снова.Это то, что вы не можете напрямую исправить в своем коде в приведенном выше примере.Коммерческие СУБД имеют множество журналов транзакций , которые помогают восстановить правильное состояние БД

Другими словами, невозможно получить полное значение ACID с точки зрения полной отказоустойчивостипри использовании СУБД, которая не предназначена для операций ACID, без фактической реализации программного уровня, который находится между вашим кодом и СУБД, которая выполняет операции SQL только после code commit, сохраняя стабильный журнал завершенных транзакций(в ожидании фиксации после восстановления).

Это очень сложный вопрос.Если вы принимаете, что есть вероятность того, что ваша БД не будет согласованной (т. Е. Вы не являетесь банком и / или у вас есть ручные контролеры согласованности, которые можно использовать после восстановления), вы можете эмулировать ACIDity с помощью фрагмента, показанного вдругой ответ, на который идет мой + 1

...