blob: 4ad620f42b43904c04379c99194cc86858e66d9a [file] [log] [blame]
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Pegasus | Single Atomic</title>
<link rel="stylesheet" href="/zh/assets/css/app.css">
<link rel="shortcut icon" href="/zh/assets/images/favicon.ico">
<link rel="stylesheet" href="/zh/assets/css/utilities.min.css">
<link rel="stylesheet" href="/zh/assets/css/docsearch.v3.css">
<script src="/assets/js/jquery.min.js"></script>
<script src="/assets/js/all.min.js"></script>
<script src="/assets/js/docsearch.v3.js"></script>
<!-- Begin Jekyll SEO tag v2.8.0 -->
<title>Single Atomic | Pegasus</title>
<meta name="generator" content="Jekyll v4.3.2" />
<meta property="og:title" content="Single Atomic" />
<meta property="og:locale" content="en_US" />
<meta name="description" content="从v1.10.0版本开始,Pegasus支持单行原子操作。这里的单行是指同一HashKey下的所有数据。" />
<meta property="og:description" content="从v1.10.0版本开始,Pegasus支持单行原子操作。这里的单行是指同一HashKey下的所有数据。" />
<meta property="og:site_name" content="Pegasus" />
<meta property="og:type" content="article" />
<meta property="article:published_time" content="2023-11-23T14:57:08+00:00" />
<meta name="twitter:card" content="summary" />
<meta property="twitter:title" content="Single Atomic" />
<script type="application/ld+json">
{"@context":"https://schema.org","@type":"BlogPosting","dateModified":"2023-11-23T14:57:08+00:00","datePublished":"2023-11-23T14:57:08+00:00","description":"从v1.10.0版本开始,Pegasus支持单行原子操作。这里的单行是指同一HashKey下的所有数据。","headline":"Single Atomic","mainEntityOfPage":{"@type":"WebPage","@id":"/api/single-atomic"},"url":"/api/single-atomic"}</script>
<!-- End Jekyll SEO tag -->
</head>
<body>
<div class="dashboard is-full-height">
<!-- left panel -->
<div class="dashboard-panel is-medium is-hidden-mobile pl-0">
<div class="dashboard-panel-header has-text-centered">
<a href="/zh/">
<img src="/assets/images/pegasus-logo-inv.png" style="width: 80%;">
</a>
</div>
<div class="dashboard-panel-main is-scrollable pl-6">
<aside class="menu">
<p class="menu-label">Pegasus产品文档</p>
<ul class="menu-list">
<li>
<a href="/zh/docs/downloads"
class="">
下载
</a>
</li>
</ul>
<p class="menu-label">编译构建</p>
<ul class="menu-list">
<li>
<a href="/zh/docs/build/compile-by-docker"
class="">
使用Docker完成编译(推荐)
</a>
</li>
<li>
<a href="/zh/docs/build/compile-from-source"
class="">
从源码编译
</a>
</li>
</ul>
<p class="menu-label">客户端库</p>
<ul class="menu-list">
<li>
<a href="/zh/clients/java-client"
class="">
Java客户端
</a>
</li>
<li>
<a href="/zh/clients/cpp-client"
class="">
C++客户端
</a>
</li>
<li>
<a href="https://github.com/apache/incubator-pegasus/tree/master/go-client"
class="">
Golang客户端
</a>
</li>
<li>
<a href="/zh/clients/python-client"
class="">
Python客户端
</a>
</li>
<li>
<a href="/zh/clients/node-client"
class="">
NodeJS客户端
</a>
</li>
<li>
<a href="/zh/clients/scala-client"
class="">
Scala客户端
</a>
</li>
</ul>
<p class="menu-label">生态工具</p>
<ul class="menu-list">
<li>
<a href="/zh/docs/tools/shell"
class="">
Pegasus Shell 工具
</a>
</li>
<li>
<a href="https://github.com/pegasus-kv/admin-cli"
class="">
集群管理命令行
</a>
</li>
<li>
<a href="https://github.com/pegasus-kv/pegic"
class="">
数据访问命令行
</a>
</li>
</ul>
<p class="menu-label">用户接口</p>
<ul class="menu-list">
<li>
<a href="/zh/api/ttl"
class="">
TTL
</a>
</li>
<li>
<a href="/zh/api/single-atomic"
class="is-active">
单行原子操作
</a>
</li>
<li>
<a href="/zh/api/redis"
class="">
Redis适配
</a>
</li>
<li>
<a href="/zh/api/geo"
class="">
GEO支持
</a>
</li>
<li>
<a href="/zh/api/http"
class="">
HTTP接口
</a>
</li>
</ul>
<p class="menu-label">高效运维</p>
<ul class="menu-list">
<li>
<a href="/zh/administration/deployment"
class="">
集群部署
</a>
</li>
<li>
<a href="/zh/administration/config"
class="">
配置说明
</a>
</li>
<li>
<a href="/zh/administration/rebalance"
class="">
负载均衡
</a>
</li>
<li>
<a href="/zh/administration/monitoring"
class="">
可视化监控
</a>
</li>
<li>
<a href="/zh/administration/rolling-update"
class="">
集群升级
</a>
</li>
<li>
<a href="/zh/administration/scale-in-out"
class="">
集群扩容缩容
</a>
</li>
<li>
<a href="/zh/administration/resource-management"
class="">
资源管理
</a>
</li>
<li>
<a href="/zh/administration/cold-backup"
class="">
冷备份
</a>
</li>
<li>
<a href="/zh/administration/meta-recovery"
class="">
元数据恢复
</a>
</li>
<li>
<a href="/zh/administration/replica-recovery"
class="">
Replica数据恢复
</a>
</li>
<li>
<a href="/zh/administration/zk-migration"
class="">
Zookeeper迁移
</a>
</li>
<li>
<a href="/zh/administration/table-migration"
class="">
Table迁移
</a>
</li>
<li>
<a href="/zh/administration/table-soft-delete"
class="">
Table软删除
</a>
</li>
<li>
<a href="/zh/administration/table-env"
class="">
Table环境变量
</a>
</li>
<li>
<a href="/zh/administration/remote-commands"
class="">
远程命令
</a>
</li>
<li>
<a href="/zh/administration/partition-split"
class="">
Partition-Split
</a>
</li>
<li>
<a href="/zh/administration/duplication"
class="">
跨机房同步
</a>
</li>
<li>
<a href="/zh/administration/compression"
class="">
数据压缩
</a>
</li>
<li>
<a href="/zh/administration/throttling"
class="">
流量控制
</a>
</li>
<li>
<a href="/zh/administration/experiences"
class="">
运维经验
</a>
</li>
<li>
<a href="/zh/administration/manual-compact"
class="">
Manual Compact功能
</a>
</li>
<li>
<a href="/zh/administration/usage-scenario"
class="">
Usage Scenario功能
</a>
</li>
<li>
<a href="/zh/administration/bad-disk"
class="">
坏盘检修
</a>
</li>
<li>
<a href="/zh/administration/whitelist"
class="">
白名单
</a>
</li>
<li>
<a href="/zh/administration/backup-request"
class="">
Backup Request
</a>
</li>
<li>
<a href="/zh/administration/hotspot-detection"
class="">
热点检测
</a>
</li>
</ul>
</aside>
</div>
</div>
<!-- main section -->
<div class="dashboard-main is-scrollable">
<nav class="navbar is-hidden-desktop">
<div class="navbar-brand">
<a href="/zh/" class="navbar-item">
<!-- Pegasus Icon -->
<img src="/assets/images/pegasus-square.png">
</a>
<div class="navbar-item">
<!--A simple language switch button that only supports zh and en.-->
<!--IF its language is zh, then switches to en.-->
<!--If you don't want a url to be relativized, you can add a space explicitly into the href to
prevents a url from being relativized by polyglot.-->
<a class="button is-light is-outlined is-inverted" href=" /api/single-atomic"><strong>En</strong></a>
</div>
<a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false" data-target="navMenu">
<!-- Appears in mobile mode only -->
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div class="navbar-menu" id="navMenu">
<div class="navbar-end">
<!--dropdown-->
<div class="navbar-item has-dropdown is-hoverable">
<a href=""
class="navbar-link ">
<span>
Pegasus产品文档
</span>
</a>
<div class="navbar-dropdown">
<a href="/zh/docs/downloads"
class="navbar-item ">
下载
</a>
</div>
</div>
<!--dropdown-->
<div class="navbar-item has-dropdown is-hoverable">
<a href=""
class="navbar-link ">
<span>
编译构建
</span>
</a>
<div class="navbar-dropdown">
<a href="/zh/docs/build/compile-by-docker"
class="navbar-item ">
使用Docker完成编译(推荐)
</a>
<a href="/zh/docs/build/compile-from-source"
class="navbar-item ">
从源码编译
</a>
</div>
</div>
<!--dropdown-->
<div class="navbar-item has-dropdown is-hoverable">
<a href=""
class="navbar-link ">
<span>
客户端库
</span>
</a>
<div class="navbar-dropdown">
<a href="/zh/clients/java-client"
class="navbar-item ">
Java客户端
</a>
<a href="/zh/clients/cpp-client"
class="navbar-item ">
C++客户端
</a>
<a href="https://github.com/apache/incubator-pegasus/tree/master/go-client"
class="navbar-item ">
Golang客户端
</a>
<a href="/zh/clients/python-client"
class="navbar-item ">
Python客户端
</a>
<a href="/zh/clients/node-client"
class="navbar-item ">
NodeJS客户端
</a>
<a href="/zh/clients/scala-client"
class="navbar-item ">
Scala客户端
</a>
</div>
</div>
<!--dropdown-->
<div class="navbar-item has-dropdown is-hoverable">
<a href=""
class="navbar-link ">
<span>
生态工具
</span>
</a>
<div class="navbar-dropdown">
<a href="/zh/docs/tools/shell"
class="navbar-item ">
Pegasus Shell 工具
</a>
<a href="https://github.com/pegasus-kv/admin-cli"
class="navbar-item ">
集群管理命令行
</a>
<a href="https://github.com/pegasus-kv/pegic"
class="navbar-item ">
数据访问命令行
</a>
</div>
</div>
<!--dropdown-->
<div class="navbar-item has-dropdown is-hoverable">
<a href=""
class="navbar-link ">
<span>
用户接口
</span>
</a>
<div class="navbar-dropdown">
<a href="/zh/api/ttl"
class="navbar-item ">
TTL
</a>
<a href="/zh/api/single-atomic"
class="navbar-item is-active">
单行原子操作
</a>
<a href="/zh/api/redis"
class="navbar-item ">
Redis适配
</a>
<a href="/zh/api/geo"
class="navbar-item ">
GEO支持
</a>
<a href="/zh/api/http"
class="navbar-item ">
HTTP接口
</a>
</div>
</div>
<!--dropdown-->
<div class="navbar-item has-dropdown is-hoverable">
<a href=""
class="navbar-link ">
<span>
高效运维
</span>
</a>
<div class="navbar-dropdown">
<a href="/zh/administration/deployment"
class="navbar-item ">
集群部署
</a>
<a href="/zh/administration/config"
class="navbar-item ">
配置说明
</a>
<a href="/zh/administration/rebalance"
class="navbar-item ">
负载均衡
</a>
<a href="/zh/administration/monitoring"
class="navbar-item ">
可视化监控
</a>
<a href="/zh/administration/rolling-update"
class="navbar-item ">
集群升级
</a>
<a href="/zh/administration/scale-in-out"
class="navbar-item ">
集群扩容缩容
</a>
<a href="/zh/administration/resource-management"
class="navbar-item ">
资源管理
</a>
<a href="/zh/administration/cold-backup"
class="navbar-item ">
冷备份
</a>
<a href="/zh/administration/meta-recovery"
class="navbar-item ">
元数据恢复
</a>
<a href="/zh/administration/replica-recovery"
class="navbar-item ">
Replica数据恢复
</a>
<a href="/zh/administration/zk-migration"
class="navbar-item ">
Zookeeper迁移
</a>
<a href="/zh/administration/table-migration"
class="navbar-item ">
Table迁移
</a>
<a href="/zh/administration/table-soft-delete"
class="navbar-item ">
Table软删除
</a>
<a href="/zh/administration/table-env"
class="navbar-item ">
Table环境变量
</a>
<a href="/zh/administration/remote-commands"
class="navbar-item ">
远程命令
</a>
<a href="/zh/administration/partition-split"
class="navbar-item ">
Partition-Split
</a>
<a href="/zh/administration/duplication"
class="navbar-item ">
跨机房同步
</a>
<a href="/zh/administration/compression"
class="navbar-item ">
数据压缩
</a>
<a href="/zh/administration/throttling"
class="navbar-item ">
流量控制
</a>
<a href="/zh/administration/experiences"
class="navbar-item ">
运维经验
</a>
<a href="/zh/administration/manual-compact"
class="navbar-item ">
Manual Compact功能
</a>
<a href="/zh/administration/usage-scenario"
class="navbar-item ">
Usage Scenario功能
</a>
<a href="/zh/administration/bad-disk"
class="navbar-item ">
坏盘检修
</a>
<a href="/zh/administration/whitelist"
class="navbar-item ">
白名单
</a>
<a href="/zh/administration/backup-request"
class="navbar-item ">
Backup Request
</a>
<a href="/zh/administration/hotspot-detection"
class="navbar-item ">
热点检测
</a>
</div>
</div>
</div>
</div>
</nav>
<nav class="navbar is-hidden-mobile">
<div class="navbar-start w-full">
<div class="navbar-item pl-0 w-full">
<!--TODO(wutao): Given the limitation of docsearch that couldn't handle multiple input,
I make searchbox only shown in desktop. Fix this issue when docsearch.js v3 released.
Related issue: https://github.com/algolia/docsearch/issues/230-->
<div id="docsearch"></div>
</div>
</div>
<div class="navbar-end">
<div class="navbar-item">
<!--A simple language switch button that only supports zh and en.-->
<!--IF its language is zh, then switches to en.-->
<!--If you don't want a url to be relativized, you can add a space explicitly into the href to
prevents a url from being relativized by polyglot.-->
<a class="button is-light is-outlined is-inverted" href=" /api/single-atomic"><strong>En</strong></a>
</div>
</div>
</nav>
<section class="hero is-info lg:mr-3">
<div class="hero-body">
<p class="title is-size-2 is-centered">单行原子操作</p>
</div>
</section>
<section class="section" style="padding-top: 2rem;">
<div class="content">
<p>从v1.10.0版本开始,Pegasus支持单行原子操作。这里的<strong>单行</strong>是指同一HashKey下的所有数据。</p>
<h1 id="原理">原理</h1>
<p>Pegasus采用Hash分片,同一个HashKey的数据总是存储在同一个Partition中,即相同的Replica中。同时,Pegasus实现时,同一个Replica的写操作在server端总是串行执行的。因此对于同一HashKey下的数据操作,可以很方便地实现原子的语义。</p>
<p>对于纯粹的写操作,譬如<a href="/zh/clients/java-client#multiset">multiSet</a><a href="/zh/clients/java-client#multidel">multiDel</a>,单个操作中对多个SortKey同时set或者del,原子语义很容易理解,要么同时成功,要么同时失败,所以这两个操作属于单行原子操作。</p>
<p>不过我们这里重点关注的是另一类操作:<strong>先读后写,并且写操作依赖读的结果</strong>。这类操作的特点就是:它们是<strong>非幂等</strong>的,即同一个操作如何多次重复执行,造成的结果(包括数据实际的更新情况、返回给用户的结果)可能是不同的。原子增减和CAS操作都属于这类。Pegasus能保证这类操作的原子性和一致性,因为:</p>
<ul>
<li>同一个HashKey的数据总是存储在同一个Replica中;</li>
<li>同一个Replica的写操作在server端总是串行执行的;</li>
<li>同一个操作保证执行且只会执行一次,即使发生数据迁移、宕机恢复等情况。</li>
</ul>
<p>由于<strong>非幂等</strong>特性,这类操作会和Pegasus的另外一些特性冲突,譬如跨机房热备。所以我们在1.10.0版本中还提供了一个配置项,用于配置集群是否允许<strong>非幂等</strong>操作,如果配置为false,则所有非幂等操作都会返回<code class="language-plaintext highlighter-rouge">ERR_OPERATION_DISABLED</code></p>
<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[replication]</span>
<span class="py">allow_non_idempotent_write</span> <span class="p">=</span> <span class="s">false</span>
</code></pre></div></div>
<h1 id="原子增减">原子增减</h1>
<p>虽然Pegasus的value不支持schema,但是我们依然提供了原子增减操作,类似Redis的<a href="http://www.redis.cn/commands/incr.html">incr命令</a>。接口参见<a href="/zh/clients/java-client#incr">incr</a></p>
<p>语义解释:</p>
<ul>
<li>首先server端存储的仍然是字节串,但是在incr的时候会先将字节串先转换为int64类型,转换方式就是简单的String-to-Int,譬如字节串<code class="language-plaintext highlighter-rouge">12345</code>就会转换为数字12345。完成incr操作后,得到的结果会重新转换为字节串,然后存储为新值。</li>
<li>字节串转换为int64时可能出错,譬如不是合法的数字、超过int64的范围等,都会报错。</li>
<li>如果原value不存在,则认为原始值为0,正常执行incr操作。</li>
<li>操作数<code class="language-plaintext highlighter-rouge">increment</code>可以为正数也可以为负数,所以一个incr接口就可以实现原子增或者原子减。</li>
<li>TTL语义:如果原value存在,则新值和原值的TTL保持一致;如果原value不存在,则新值在存储时不设TTL。</li>
</ul>
<h1 id="cas操作">CAS操作</h1>
<p>另一类很有用的原子操作就是CAS(Compare-And-Swap),直译就是对比交换,最初是表示一条CPU的原子指令,其作用是让CPU先进行比较两个值是否相等,然后原子地更新某个位置的值。基于CAS操作,可以实现很多高级的并发特性,譬如锁。因此很多编程语言也原生地提供CAS操作。</p>
<p>Pegasus提供了check_and_set的CAS操作,其语义就是:根据HashKey的某一个SortKey的值是否满足某种条件,来决定是否修改另一个SortKey的值。我们将用于判断条件的SortKey称之为<code class="language-plaintext highlighter-rouge">CheckSortKey</code>,将用于设置值的SortKey称之为<code class="language-plaintext highlighter-rouge">SetSortKey</code>。对应地,CheckSortKey的value称之为<code class="language-plaintext highlighter-rouge">CheckValue</code>,SetSortKey要设置的value称之为<code class="language-plaintext highlighter-rouge">SetValue</code>。接口参见<a href="/zh/clients/java-client#checkandset">checkAndSet</a>,以及其扩展版本<a href="/zh/clients/java-client#checkandmutate">checkAndMutate</a><a href="/zh/clients/java-client#compareexchange">compareExchange</a></p>
<p>语义解释:</p>
<ul>
<li>只有当CheckValue满足指定的条件时,才会设置SetSortKey的值。</li>
<li>需要满足的条件类型通过CheckType指定,有的CheckType还需要指定操作数CheckOperand。目前支持的类型包括:
<ul>
<li>判断CheckValue的appearance属性:是否存在、是否为空字节串等。</li>
<li>字节串比较:将CheckValue与CheckOperand按照字节序比较,看是否满足<code class="language-plaintext highlighter-rouge">&lt;</code><code class="language-plaintext highlighter-rouge">&lt;=</code><code class="language-plaintext highlighter-rouge">==</code><code class="language-plaintext highlighter-rouge">&gt;=</code><code class="language-plaintext highlighter-rouge">&gt;=</code>关系。</li>
<li>数字比较:类似于<a href="#原子增减">incr操作</a>,先将CheckValue转换为int64,再与CheckOperand比较,看是否满足<code class="language-plaintext highlighter-rouge">&lt;</code><code class="language-plaintext highlighter-rouge">&lt;=</code><code class="language-plaintext highlighter-rouge">==</code><code class="language-plaintext highlighter-rouge">&gt;=</code><code class="language-plaintext highlighter-rouge">&gt;=</code>关系。</li>
</ul>
</li>
<li>CheckSortKey和SetSortKey可以相同,如果相同,就是先判断旧值是否满足条件,满足的话就设置为新值。</li>
<li>可以通过选项<code class="language-plaintext highlighter-rouge">CheckAndSetOptions.returnCheckValue</code>指定返回CheckValue的值。</li>
<li>可以通过选项<code class="language-plaintext highlighter-rouge">CheckAndSetOptions.setValueTTLSeconds</code>指定SetValue的TTL。</li>
</ul>
<p>为了方便使用,Pegasus Java Client还提供了compare_exchange接口:当HashKey的某个SortKey的value按照字节串比较<strong>等于</strong>用户指定的ExpectedValue时,就将其value更新为用户指定的DesiredValue。从语义上来看,compare_exchange更像是Compare-And-Swap的另外一种说法。接口参见<a href="/zh/clients/java-client#compareexchange">compareExchange</a></p>
<p>compare_exchange其实是check_and_set的特化版本:</p>
<ul>
<li>CheckSortKey和SetSortKey相同。</li>
<li>CheckType为CT_VALUE_BYTES_EQUAL。</li>
</ul>
</div>
</section>
<footer class="footer">
<div class="container">
<div class="content is-small has-text-centered">
<div style="margin-bottom: 20px;">
<a href="http://incubator.apache.org">
<img src="/assets/images/egg-logo.png"
width="15%"
alt="Apache Incubator"/>
</a>
</div>
Copyright &copy; 2023 <a href="http://www.apache.org">The Apache Software Foundation</a>.
Licensed under the <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License, Version
2.0</a>.
<br><br>
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.
<br><br>
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.
</div>
</div>
</footer>
</div>
<!-- right panel -->
<div class="dashboard-panel is-small is-scrollable is-hidden-mobile">
<p class="menu-label">
<span class="icon">
<i class="fa fa-bars" aria-hidden="true"></i>
</span>
本页导航
</p>
<ul class="menu-list">
<li><a href="#原理">原理</a></li>
<li><a href="#原子增减">原子增减</a></li>
<li><a href="#cas操作">CAS操作</a></li>
</ul>
</div>
</div>
<script src="/assets/js/app.js" type="text/javascript"></script>
<script>
docsearch({
container: '#docsearch',
appId: 'QRN30RBW0S',
indexName: 'pegasus-apache',
apiKey: 'd3a3252fa344359766707a106c4ed88f',
debug: true
});
</script>
</body>
</html>