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

Cachalot DB - .NET 应用程序的极速事务数据库 - 第二部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (7投票s)

2019年2月7日

CPOL

4分钟阅读

viewsIcon

12581

关于数据操作的更多信息。

引言

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

压缩对象数据

业务对象在内部以类型无关的格式存储。索引字段存储为 Int64string,所有对象数据都以 UTF-8 编码的 JSON 形式存储。将 .NET 对象转换为内部格式的过程称为“打包”。打包在客户端进行,服务器仅使用索引并以行数据的形式操作对象。它不依赖于具体的 .NET datatype

默认情况下,对象数据不会被压缩,但对于占用几 KB 以上的对象,压缩可能非常有益。对于一个 JSON 格式占用 10 KB 的对象,压缩比约为 1:10。

要启用压缩,请在业务数据类型上添加单个属性。

[Storage(compressed:true)]
public class Home
{

   °°°

使用压缩对象对客户端代码是透明的。然而,它会影响打包时间,打包是在客户端进行的。检索对象时,需要进行解包(可能包含解压缩)。

总而言之,如果愿意为数据插入和检索付出少量客户端的代价,对于中等大小的对象,压缩可能非常有用。

在数据库中存储多态集合

多态集合是原生支持的。类型信息存储在 JSON 内部,并用于反序列化正确的具体类型。

一个来自交易系统的简短示例

为了存储事件集合,我们必须在基类型上公开**所有必需的索引**。

Null 值对于索引字段完全可以接受,这使得可以公开仅对特定子类型有意义的索引属性。

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

                [Index(KeyDataType.StringKey)]
                public abstract string EventType { get; }

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

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

      °°°
}

public abstract class NegotiatedProductEvent: ProductEvent
{
                °°°
}

public class IncreaseDecrease : NegotiatedProductEvent
{
      °°°
                 public override string EventType => "IncreaseDecrease";
}

这是从以 abstract 基类作为类型的 DataStore 中检索具体事件集合的代码示例。

var events = connector.DataSource<ProductEvent>();

var increaseEvents = events.Where(

         evt => evt.EventType == "IncreaseDecrease" &&

         evt.EventDate == DateTime.Today

).Cast<IncreaseDecrease>()

条件操作和“乐观同步”

标准的“put”操作使用主键作为对象标识来添加对象或更新现有对象。

实现了更高级的用例

  1. 仅当对象不存在时添加,并告知是否成功添加
  2. 仅当数据库中的当前版本满足条件时,才更新现有对象

第一个功能通过 DataSource 类上的 TryAdd 操作可用。如果对象已存在,则不进行修改,并返回 false。对象存在性检查和插入是作为一个**原子操作**执行的。对象不能在此时被另一个客户端更新或删除。

这对于数据初始化、创建单例对象、分布式锁等非常有用。

第二个用例特别适用于,但不仅限于,实现**“乐观同步”**。DataSource 类上的 UpdateIf 方法实现了该功能。

如果我们想确保在编辑对象时(手动或算法地)没有其他人修改过它,有两种可能性:

  • 在编辑操作期间锁定对象。这对于现代分布式系统来说不是最佳选择。分布式锁不适合大规模并行处理,如果它没有被自动释放(由于客户端或网络故障),则需要管理员手动干预。
  • 使用“乐观同步”:不锁定,但要求在**保存已修改对象时**,数据库中的对象自加载以来没有发生变化。否则,操作失败,我们必须重试(加载 + 编辑 + 保存)。这可以通过多种方式实现:
    • 在对象上设置一个版本。当我们保存版本 n+1 时,我们要求数据库中的对象仍然是版本 n。在 Cachalot DB 中,语法是 items.UpdateIf(item, i=> i.Version == n-1)
    • 在对象上设置一个 timestamp。当我们保存已修改的对象时,我们要求数据库中的版本 timestamp 与**我们更新之前**的对象 timestamp 相同。
var oldTimestamp = item.Timestamp;
item.Timestamp = DateTime.Now;
items.UpdateIf(item, i=> i.Timestamp == oldTimestamp);

当在一个事务中提交多个对象修改时,这会更有用。如果一个对象上的条件不满足,则回滚整个事务。

更多关于事务的内容请参阅 第三部分...

完整的开源代码可在

预编译二进制文件和完整文档可在以下网址获取:

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

安装:Install-Package Cachalot.Client

© . All rights reserved.