在现代软件开发中,数据的存储和压缩是一个重要的议题。本文将探讨对象数据压缩技术,以及如何在数据库中存储多态集合。此外,还将讨论“乐观同步”的概念和实现方法。
在处理大型数据对象时,压缩技术可以显著减少存储空间的使用和提高数据传输的效率。在.NET环境中,对象数据通常以UTF-8编码的JSON格式存储,这种格式对数据类型没有特定的依赖。将.NET对象转换为内部格式的过程称为“打包”(packing),这个过程是在客户端完成的,服务器端仅使用索引并操作对象作为行数据。
默认情况下,对象数据并不进行压缩,但对于占用几KB以上空间的对象,压缩就显得非常有用。例如,一个在JSON中占用10KB的对象,压缩比率大约为1:10。为了启用压缩,可以在业务数据类型上添加一个属性。使用压缩对象对客户端代码是透明的,但它会影响打包时间,这是在客户端完成的。当对象被检索时,它们会被解包(可能涉及解压缩)。
因此,对于中等大小的对象,如果愿意在客户端为数据插入和检索支付一点额外的代价,压缩可能是非常有用的。
多态集合在数据库中是原生管理的。类型信息存储在内部的JSON中,并用于反序列化为正确的具体类型。例如,在一个交易系统中,为了存储事件集合,必须在基类型上公开所有必需的索引。对于只对特定子类型有意义的索引属性,允许索引字段为空值。
以下是一个示例代码,展示了如何定义一个抽象基类和派生类,以及如何检索具体事件的集合:
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";
}
上述代码展示了如何定义一个抽象基类`ProductEvent`,以及一个派生类`NegotiatedProductEvent`和具体类`IncreaseDecrease`。
普通的“put”操作使用主键作为对象标识来添加或更新对象。更高级的用例包括:
第一种情况可以通过`DataSource`类的`TryAdd`操作实现。如果对象已经存在,则不会被修改,并返回`false`。对象存在性的测试和插入作为原子操作执行。在这期间,对象不能被其他客户端更新或删除。这对于数据初始化、创建单例对象、分布式锁等非常有用。
第二种情况特别适用于(但不限于)实现“乐观同步”。`DataSource`类的`UpdateIf`方法实现了这一点。如果想要确保在编辑对象(手动或算法上)时没有人修改它,有两种可能性:
以下是使用版本号和时间戳实现乐观同步的示例代码:
var oldTimestamp = item.Timestamp;
item.Timestamp = DateTime.Now;
items.UpdateIf(item, i => i.Timestamp == oldTimestamp);
这在提交多个对象修改的事务时更有用。如果一个对象的条件不满足,则回滚整个事务。
完整的开源代码可以在以下GitHub链接找到:
预编译的二进制文件和完整文档可以在以下链接找到:
客户端代码作为NuGet包提供,在nuget.org上可用。