65.9K
CodeProject 正在变化。 阅读更多。
Home

Cachalot DB - 用于 .NET 应用的超快速事务数据库 - 第 3 部分

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (2投票s)

2019年2月19日

CPOL

2分钟阅读

viewsIcon

5927

关于事务。

引言

这是关于 Cachalot DB 的系列文章的第三部分。第一部分可以在 这里找到,第二部分可以在 这里找到。

两阶段事务

理解两阶段事务最重要的在于,你真正需要它们的情况是什么。

大多数情况下,你不需要。

涉及单个对象(PutTryAddUpdateIfDelete)的操作始终是事务性的。

它是持久的(操作同步写入只追加事务日志),并且是原子的。对象只会以完全更新或完全插入的状态对外界可见。

单节点集群上,对多个对象(PutManyDeleteMany)的操作也是事务性的。

只有当你必须在多节点集群上事务性地操作多个对象时,才需要两阶段事务。

像往常一样,让我们构建一个简单的示例:一个玩具银行系统,允许资金在账户之间转移。有两种类型的业务对象:AccountAccountOperation

public class Account
{
   [PrimaryKey(KeyDataType.IntKey)]
   public int Id { get; set; }

   [Index(KeyDataType.IntKey, true)]
   public decimal Balance { get; set; }
}

public class AccountOperation
{
   [PrimaryKey(KeyDataType.IntKey)]
   public int Id { get; set; }

   [Index(KeyDataType.IntKey)]
   public int SourceAccount { get; set; }

   [Index(KeyDataType.IntKey)]
   public int TargetAccount { get; set; }

   [Index(KeyDataType.IntKey, ordered:true)]
   public DateTime Timestamp { get; set; }

   public decimal TransferedAmount { get; set; }
}

让我们创建两个账户。此时不需要事务。

var accountIds = connector.GenerateUniqueIds("account_id", 2);
var accounts = connector.DataSource<Account>();

var account1 = new Account {Id = accountIds[0], Balance = 100};
var account2 = new Account {Id = accountIds[1], Balance = 100};

accounts.Put(account1);
accounts.Put(account2);

当我们在账户之间转账时,我们希望同时(原子地)更新两个账户的余额并创建一个新的 AccountOperation 实例。

业务逻辑可以这样实现

private static void MoneyTransfer
(Connector connector, Account sourceAccount, Account targetAccount, decimal amount)
{
    sourceAccount.Balance -= amount;
    targetAccount.Balance += amount;

    var tids = connector.GenerateUniqueIds("transaction_id", 1);
    var transfer = new AccountOperation
    {
        Id = tids[0],
        SourceAccount = sourceAccount.Id,
        TargetAccount = targetAccount.Id,
        TransferedAmount = amount
    };
    var transaction = connector.BeginTransaction();
    transaction.Put(sourceAccount);
    transaction.Put(targetAccount);
    transaction.Put(transfer);
    // this is where the two stage transaction happens
    transaction.Commit();
}

事务内允许的操作是

  • 删除
  • UpdateIf

如果使用了条件更新(UpdateIf),并且一个对象的条件不满足,则整个事务将回滚。

进程内服务器

在某些情况下,特别是如果数据量有限并且可以存储在单个节点上,你可以直接在你的服务器进程内实例化一个 Cachalot 服务器。这将提供闪电般快速的响应,因为不再涉及网络延迟。

为了做到这一点,将一个空的客户端配置传递给 Connector 构造函数。一个数据库服务器将在 connector 对象内实例化,通信将通过简单的进程内调用完成,而不是 TCP 通道。

var connector = new Connector(new ClientConfig());

Connector 实现了 IDisposable。释放 Connector 将优雅地停止服务器。你需要在一开始启动服务器进程时实例化 Connector 一次,并在服务器进程停止时释放它一次。

完整的开源代码可在

    https://github.com/usinesoft/Cachalot

预编译的二进制文件(包括演示客户端)和完整的文档可在

    https://github.com/usinesoft/Cachalot/releases/latest

客户端代码作为 nuget 包在 nuget.org 上可用。

安装:Install-Package Cachalot.Client

© . All rights reserved.