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





4.00/5 (2投票s)
关于事务。
引言
这是关于 Cachalot
DB 的系列文章的第三部分。第一部分可以在 这里找到,第二部分可以在 这里找到。
两阶段事务
理解两阶段事务最重要的在于,你真正需要它们的情况是什么。
大多数情况下,你不需要。
涉及单个对象(Put
、TryAdd
、UpdateIf
、Delete
)的操作始终是事务性的。
它是持久的(操作同步写入只追加事务日志),并且是原子的。对象只会以完全更新或完全插入的状态对外界可见。
在单节点集群上,对多个对象(PutMany
、DeleteMany
)的操作也是事务性的。
只有当你必须在多节点集群上事务性地操作多个对象时,才需要两阶段事务。
像往常一样,让我们构建一个简单的示例:一个玩具银行系统,允许资金在账户之间转移。有两种类型的业务对象:Account
和 AccountOperation
。
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