如何在内存中进行事务处理?

时间:2009-09-22 16:27:50

标签: c# transactions

我对使用事务RDBMS非常熟悉,但是如果事务失败,我如何确保对内存数据所做的更改会回滚?如果我甚至不使用数据库怎么办?

这是一个人为的例子:

public void TransactionalMethod()
{
    var items = GetListOfItems();

    foreach (var item in items)
    {       
        MethodThatMayThrowException(item);

        item.Processed = true;
    }
}

在我的示例中,我可能希望以某种方式回滚对列表中项目所做的更改,但我该如何实现?

我知道“软件事务内存”,但对它并不了解,看起来相当实验性。我也知道“可补偿交易”的概念,但这会产生编写执行/撤销代码的开销。

Subversion似乎通过让你运行“cleanup”命令来处理更新工作副本的错误。

有什么想法吗?

更新
Reed Copsey提供an excellent answer,包括:

  

处理数据副本,在提交时更新原始数据。

这使我的问题更进一步 - 如果在提交期间发生错误会怎样??我们经常将提交视为即时操作,但实际上它可能会对大量数据进行许多更改。如果在应用提交时存在不可避免的事情,例如OutOfMemoryException,会发生什么?

另一方面,如果有人选择回滚选项,如果在回滚期间出现异常会发生什么??我理解像Oracle RDBMS这样的东西有回滚段和UNDO日志和东西的概念,但假设没有序列化到磁盘(如果没有序列化到磁盘,它没有发生,崩溃意味着你可以调查那些日志并从中恢复),这真的有可能吗?

更新2:
来自answerAlex提出了一个很好的建议:即一个更新不同的对象,然后,提交阶段只是将对当前对象的引用更改为新对象。他进一步建议你改变的对象实际上是修改对象的列表。

我明白他的意思(我认为),我想让问题更加复杂:

在这种情况下,你如何应对锁定?想象一下,您有一个客户列表:

var customers = new Dictionary<CustomerKey, Customer>();

现在,您想要对这些客户的部分进行更改,如何在不锁定和替换整个列表的情况下应用这些更改?例如:

var customerTx = new Dictionary<CustomerKey, Customer>();

foreach (var customer in customers.Values)  
{
    var updatedCust = customer.Clone();     
    customerTx.Add(GetKey(updatedCust), updatedCust);

    if (CalculateRevenueMightThrowException(customer) >= 10000)
    {
        updatedCust.Preferred = true;
    }
}

我该怎么做?这(Alex的建议)意味着在替换列表引用时锁定所有客户:

lock (customers)
{
    customers = customerTx;
}

然而,如果我循环,修改原始列表中的引用,它不是原子的,并且与“如果它在中途崩溃的问题”的问题相悖:

foreach (var kvp in customerTx)
{
    customers[kvp.Key] = kvp.Value;
}

4 个答案:

答案 0 :(得分:8)

这样做的每个选项都需要三种基本方法之一:

  1. 在修改之前复制数据,如果中止则恢复为回滚状态。
  2. 处理数据副本,在提交时更新原始文件。
  3. 记录您的数据更改,以便在中止时撤消它们。
  4. 例如,您提到的Software Transactional Memory遵循第三种方法。关于这一点的好处是它可以乐观地处理数据,并且只是在成功提交时丢弃日志。

答案 1 :(得分:1)

查看Microsoft Research project, SXM

Maurice Herlihy's page,您可以下载文档以及代码示例。

答案 2 :(得分:1)

您问:

“如果在提交期间发生错误怎么办?”

没关系。您可以提交到内存中的某个位置/同时检查操作是否成功。如果是,则将目标对象(对象A)的引用更改为您提交的对象(对象B)。然后你有failafe提交 - 只有在成功提交时才会更新引用。参考变化是原子的。

答案 3 :(得分:0)

public void TransactionalMethod()
{
    var items = GetListOfItems();

    try {
        foreach (var item in items)
        {           
            MethodThatMayThrowException(item);

            item.Processed = true;
        }
    }
    catch(Exception ex) {
        foreach (var item in items)
        {
            if (item.Processed) {
                UndoProcessingForThisItem(item);
            }
        }
    }
}

显然,“撤销......”的实施仍然是读者的练习。