单行原子操作

从 v1.10.0 版本开始,Pegasus 支持了单行原子操作。

这里的单行是指同一 HashKey 下的所有数据。

原理

Pegasus 的数据分布策略采用了固定 Hash 分片,同一个 HashKey 的数据总是存储在同一个 Partition 中,即在单节点内的同一 Replica 中。同时,同一个 Replica 的写操作在 server 端总是串行执行的。因此对于同一 HashKey 下的数据操作,可以很方便地实现原子语义。

对于写操作,譬如 multiSetmultiDel,单个操作中对多个 SortKey 同时 set 或者 delete,原子语义很容易理解,要么同时成功,要么同时失败,所以这两个操作属于单行原子操作。

这里重点介绍另一类操作:先读后写,并且写操作依赖读的结果。这类操作的特点就是:它们是非幂等的,即同一个操作如果重复执行,造成的结果(包括数据实际的更新情况、返回给用户的结果)可能是不同的。原子增减和 CAS 操作都属于这类。 Pegasus 能保证这类操作的原子性和一致性,因为:

  • 同一个 HashKey 的数据总是存储在同一个 Replica 中
  • 同一个 Replica 的写操作在 server 端总是串行执行的
  • 同一个操作保证执行且只会执行一次,即使发生数据迁移、宕机恢复等情况

由于非幂等特性,这类操作会和 Pegasus 的另外一些特性冲突,譬如 跨机房同步。所以我们在 1.10.0 版本中还提供了一个配置项,用于配置集群是否允许非幂等操作,如果配置为 false,则所有非幂等操作都会返回ERR_OPERATION_DISABLED

[replication]
    allow_non_idempotent_write = false

原子增减

虽然 Pegasus 的 value 不支持 schema,但是我们依然提供了原子增减操作,类似 Redis 的 incr命令,Pegasus 接口参见 incr

语义解释

  • 由于存储引擎 RocksDB 只能存储字节串类型的 value,所以在 incr() 的时候会先将读取出 value 字节串,转换为 int64 类型(转换方式就是简单的 String-to-Int),譬如字节串"12345"就会转换为数字 12345。完成incr()操作后,得到的结果会重新转换为字节串,然后存储为新值
  • 字节串转换为 int64 时可能出错,譬如不是合法的数字、超过 int64 的范围等,都会返回失败
  • 如果原 value 不存在,则认为原始值为 0,再执行incr()操作
  • 操作数increment可以为正数也可以为负数,所以一个incr()接口就可以实现原子增或者原子减
  • TTL:如果原 value 存在,则新值和原值的 TTL 保持一致;如果原 value 不存在,则新值在存储时不设 TTL

CAS 操作

另一类很有用的原子操作就是 CAS(Compare-And-Swap)操作。基于 CAS 操作,可以实现很多高级的并发特性,譬如分布式锁。

Pegasus 提供了 check_and_set 的 CAS 操作,其语义就是:根据 HashKey 的某一个 SortKey 的 value 是否满足某种条件,来决定是否修改另一个 SortKey 的值。将用于判断条件的 SortKey 称之为CheckSortKey,将用于设置值的 SortKey 称之为SetSortKey。对应地,CheckSortKey 的 value 称之为CheckValueSetSortKey 要设置的 value 称之为SetValue。参见 checkAndSet,以及其扩展版本 checkAndMutatecompareExchange

语义解释

  • 只有当CheckValue满足指定的条件时,才会设置SetSortKey的值
  • 需要满足的条件类型通过CheckType指定,有的CheckType还需要指定操作数CheckOperand。目前支持的类型包括:
    • 判断CheckValue存在性:是否存在、是否为空字节串等
    • 字节串比较:将CheckValueCheckOperand按照字节序比较,看是否满足<<===>=>=关系
    • 数字比较:类似于 incr操作,先将CheckValue转换为int64,再与CheckOperand比较,看是否满足<<===>=>=关系
  • CheckSortKeySetSortKey可以相同,如果相同,就是先判断旧值是否满足条件,满足的话就设置为新值
  • 可以通过选项CheckAndSetOptions.returnCheckValue指定返回CheckValue的值
  • 可以通过选项CheckAndSetOptions.setValueTTLSeconds指定SetValue的 TTL

为了方便使用,Pegasus Java Client 还提供了 compare_exchange 接口:当 HashKey 的某个 SortKey 的 value 按照字节串比较等于用户指定的ExpectedValue时,就将其 value 更新为用户指定的DesiredValue。从语义上来看,compare_exchange 是 Compare-And-Swap 的一种特殊形式。接口参见 compareExchange

compare_exchange 其实是 check_and_set 的特化版本,即:

  • CheckSortKeySetSortKey相同
  • CheckTypeCT_VALUE_BYTES_EQUAL
Copyright © 2023 The Apache Software Foundation. Licensed under the Apache License, Version 2.0.

Apache Pegasus is an effort undergoing incubation at The Apache Software Foundation (ASF), sponsored by the Apache Incubator. Incubation is required of all newly accepted projects until a further review indicates that the infrastructure, communications, and decision making process have stabilized in a manner consistent with other successful ASF projects. While incubation status is not necessarily a reflection of the completeness or stability of the code, it does indicate that the project has yet to be fully endorsed by the ASF.

Apache Pegasus, Pegasus, Apache, the Apache feather logo, and the Apache Pegasus project logo are either registered trademarks or trademarks of The Apache Software Foundation in the United States and other countries.