从v1.10.0版本开始,Pegasus支持单行原子操作。这里的单行是指同一HashKey下的所有数据。
Pegasus采用Hash分片,同一个HashKey的数据总是存储在同一个Partition中,即相同的Replica中。同时,Pegasus实现时,同一个Replica的写操作在server端总是串行执行的。因此对于同一HashKey下的数据操作,可以很方便地实现原子的语义。
对于纯粹的写操作,譬如multiSet和multiDel,单个操作中对多个SortKey同时set或者del,原子语义很容易理解,要么同时成功,要么同时失败,所以这两个操作属于单行原子操作。
不过我们这里重点关注的是另一类操作:先读后写,并且写操作依赖读的结果。这类操作的特点就是:它们是非幂等的,即同一个操作如何多次重复执行,造成的结果(包括数据实际的更新情况、返回给用户的结果)可能是不同的。原子增减和CAS操作都属于这类。Pegasus能保证这类操作的原子性和一致性,因为:
由于非幂等特性,这类操作会和Pegasus的另外一些特性冲突,譬如跨机房热备。所以我们在1.10.0版本中还提供了一个配置项,用于配置集群是否允许非幂等操作,如果配置为false,则所有非幂等操作都会返回ERR_OPERATION_DISABLED
:
[replication] allow_non_idempotent_write = false
虽然Pegasus的value不支持schema,但是我们依然提供了原子增减操作,类似Redis的incr命令。接口参见incr。
语义解释:
12345
就会转换为数字12345。完成incr操作后,得到的结果会重新转换为字节串,然后存储为新值。increment
可以为正数也可以为负数,所以一个incr接口就可以实现原子增或者原子减。另一类很有用的原子操作就是CAS(Compare-And-Swap),直译就是对比交换,最初是表示一条CPU的原子指令,其作用是让CPU先进行比较两个值是否相等,然后原子地更新某个位置的值。基于CAS操作,可以实现很多高级的并发特性,譬如锁。因此很多编程语言也原生地提供CAS操作。
Pegasus提供了check_and_set的CAS操作,其语义就是:根据HashKey的某一个SortKey的值是否满足某种条件,来决定是否修改另一个SortKey的值。我们将用于判断条件的SortKey称之为CheckSortKey
,将用于设置值的SortKey称之为SetSortKey
。对应地,CheckSortKey的value称之为CheckValue
,SetSortKey要设置的value称之为SetValue
。接口参见checkAndSet,以及其扩展版本checkAndMutate和compareExchange。
语义解释:
<
、<=
、==
、>=
、>=
关系。<
、<=
、==
、>=
、>=
关系。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的特化版本: