Etcd 中 Revision, CreateRevision, ModRevision, Version 的含义
结论#
etcd mvcc 中的 Version, Revision, ModRevision, CreateRevision 到底都是什么意思?如果服务 watch etcd 订阅消息,该如何使用呢?
实验部分不想看的,可以只看结论:
Revision
作用域为集群,逻辑时间戳,全局单调递增,任何 key 的增删改都会使其自增CreateRevision
作用域为 key, 等于创建这个 key 时集群的 Revision, 直到删除前都保持不变ModRevision
作用域为 key, 等于修改这个 key 时集群的 Revision, 只要这个 key 更新都会自增Version
作用域为 key, 这个key刚创建时Version为1,之后每次更新都会自增,即这个key从创建以来更新的总次数。
关于 watch 哪个版本:
- watch 某一个 key 时,想要从历史记录开始就用
CreateRevision
,最新一条(这一条直接返回) 开始就用ModRevision
。 - watch 某个前缀,就必须使用
Revision
。如果要watch当前前缀后续的变化,则应该从当前集群的 Revision+1 版本开始watch。
版本都有哪些?#
我们都知道 etcd 支持 mvcc来实现高并发,比如我们 Get
某个 key 时返回结果是一个 RangeResponse
type RangeResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// kvs is the list of key-value pairs matched by the range request.
// kvs is empty when count is requested.
Kvs []*mvccpb.KeyValue `protobuf:"bytes,2,rep,name=kvs,proto3" json:"kvs,omitempty"`
// more indicates if there are more keys to return in the requested range.
More bool `protobuf:"varint,3,opt,name=more,proto3" json:"more,omitempty"`
// count is set to the number of keys within the range when requested.
Count int64 `protobuf:"varint,4,opt,name=count,proto3" json:"c
}
返回结构体中,Header
是响应头,里面有很多系统信息,Kvs
是我们获得的 kv 数组,里面有我们想要的数据。
type ResponseHeader struct {
// cluster_id is the ID of the cluster which sent the response.
ClusterId uint64 `protobuf:"varint,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"`
// member_id is the ID of the member which sent the response.
MemberId uint64 `protobuf:"varint,2,opt,name=member_id,json=memberId,proto3" json:"member_id,omitempty"`
// revision is the key-value store revision when the request was applied.
// For watch progress responses, the header.revision indicates progress. All future events
// recieved in this stream are guaranteed to have a higher revision number than the
// header.revision number.
Revision int64 `protobuf:"varint,3,opt,name=revision,proto3" json:"revision,omitempty"`
// raft_term is the raft term when the request was applied.
RaftTerm uint64 `protobuf:"varint,4,opt,name=raft_term,json=raftTerm,proto3" json:"raft_term,omitempty"`
}
type KeyValue struct {
// key is the key in bytes. An empty key is not allowed.
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
// create_revision is the revision of last creation on this key.
CreateRevision int64 `protobuf:"varint,2,opt,name=create_revision,json=createRevision,proto3" json:"create_revision,omitempty"`
// mod_revision is the revision of last modification on this key.
ModRevision int64 `protobuf:"varint,3,opt,name=mod_revision,json=modRevision,proto3" json:"mod_revision,omitempty"`
// version is the version of the key. A deletion resets
// the version to zero and any modification of the key
// increases its version.
Version int64 `protobuf:"varint,4,opt,name=version,proto3" json:"version,omitempty"`
// value is the value held by the key, in bytes.
Value []byte `protobuf:"bytes,5,opt,name=value,proto3" json:"value,omitempty"`
// lease is the ID of the lease that attached to key.
// When the attached lease expires, the key will be deleted.
// If lease is 0, then no lease is attached to the key.
Lease int64 `protobuf:"varint,6,opt,name=lease,proto3" json:"lease,omitempty"`
}
可以看到 header 里有一个 Revision
, KeyValue 里有三个:CreateRevision
, ModRevision
和 Version
, 所以这几个都是什么意思?
另外我们在 watch etcd 时,WithRev
这个 option 里面的 revision
到底选哪一个?
for {
rch := watcher.Watch(ctx, path, clientv3.WithRev(revision))
for wresp := range rch {
......
}
}
验证案例#
搭建 etcd 环境。
vi etcd.sh
#!/usr/bin/env bash
ETCD_NAME="etcd"
ETCD_VERSION="v3.3.1"
ETCD_PORT_CLIENT=2379
ETCD_PORT_NODE=2380
docker run -d \
-p ${ETCD_PORT_CLIENT}:2379 \
-p ${ETCD_PORT_NODE}:2380 \
--name ${ETCD_NAME} quay.io/coreos/etcd:${ETCD_VERSION} \
/usr/local/bin/etcd \
--data-dir=/etcd-data --name node1 \
--initial-advertise-peer-urls http://0.0.0.0:2380 --listen-peer-urls http://0.0.0.0:2380 \
--advertise-client-urls http://0.0.0.0:2379 --listen-client-urls http://0.0.0.0:2379 \
--initial-cluster node1=http://0.0.0.0:2380
启动 etcd。
chmod +x etcd.sh
./etcd.sh
验证各种版本的含义#
过程#
写入一组数据后 (key为/students/class1/zhangsan,value为 {age:11,gender:man}),
Revision
, CreateRevision
, ModRevision
均为 150,而 Version 为 1。
[root@192 ~]# etcdctl put "/students/class1/zhangsan" "{age:11,gender:man}"
OK
[root@192 ~]# etcdctl get "/students/class1/zhangsan" --write-out="fields"
"ClusterID" : 11588568905070377092
"MemberID" : 128088275939295631
"Revision" : 150
"RaftTerm" : 8
"Key" : "/students/class1/zhangsan"
"CreateRevision" : 150
"ModRevision" : 150
"Version" : 1
"Value" : "{age:11,gender:man}"
"Lease" : 0
"More" : false
"Count" : 1
修改该数据后,发现CreateRevision
不变,还是150,而 Revison
和 ModRevision
自增为151,Version
自增为2。
[root@192 ~]# etcdctl put "/students/class1/zhangsan" "{age:11,gender:man}"
OK
[root@192 ~]# etcdctl get "/students/class1/zhangsan" --write-out="fields"
"ClusterID" : 11588568905070377092
"MemberID" : 128088275939295631
"Revision" : 151
"RaftTerm" : 8
"Key" : "/students/class1/zhangsan"
"CreateRevision" : 150
"ModRevision" : 151
"Version" : 2
"Value" : "{age:11,gender:man}"
"Lease" : 0
"More" : false
"Count" : 1
再次写入一组新的数据后 (key为/students/class1/lisi,value为 {age:13,gender:woman}),
发现集群的 Revision
自增为了152。
[root@192 ~]# etcdctl put "/students/class1/lisi" "{age:11,gender:woman}"
OK
[root@192 ~]# etcdctl get "/students/class1/lisi" --write-out="fields"
"ClusterID" : 11588568905070377092
"MemberID" : 128088275939295631
"Revision" : 152
"RaftTerm" : 8
"Key" : "/students/class1/lisi"
"CreateRevision" : 152
"ModRevision" : 152
"Version" : 1
"Value" : "{age:11,gender:woman}"
"Lease" : 0
"More" : false
"Count" : 1
删除zhangsan这组数据,发现集群的 Revison
自增为了153,说明删除操作也会使集群的 Revison
自增。
[root@192 ~]# etcdctl del "/students/class1/zhangsan" --write-out="fields"
"ClusterID" : 11588568905070377092
"MemberID" : 128088275939295631
"Revision" : 153
"RaftTerm" : 8
"Deleted" : 1
再次写入一条 zhangsan 数据,Revision
, CreateRevision
, ModRevision
均为 154,而 Version 为 1。
[root@192 ~]# etcdctl put "/students/class1/zhangsan" "{age:11,gender:man}"
OK
[root@192 ~]# etcdctl get "/students/class1/zhangsan" --write-out="fields"
"ClusterID" : 11588568905070377092
"MemberID" : 128088275939295631
"Revision" : 154
"RaftTerm" : 8
"Key" : "/students/class1/zhangsan"
"CreateRevision" : 154
"ModRevision" : 154
"Version" : 1
"Value" : "{age:11,gender:man}"
"Lease" : 0
"More" : false
"Count" : 1
总结#
此时我们可以得出结论:
Revision
作用域为集群,逻辑时间戳,全局单调递增,任何 key 的增删改都会使其自增CreateRevision
作用域为 key, 等于创建这个 key 时集群的 Revision, 直到删除前都保持不变ModRevision
作用域为 key, 等于修改这个 key 时集群的 Revision, 只要这个 key 更新都会自增Version
作用域为 key, 这个key刚创建时Version为1,之后每次更新都会自增
现在梳理清了各个版本的意义与概念,那么问题来了,watch 时选择哪一个呢?
watch 时应该选择哪个版本#
watch 一个具体的 key#
查看当前的各种版本的值,这里只测试 zhangsan 的情况,其Revison
为 162, CreateReVision
为156, ModRevison
为160,Version
为3。
[root@192 ~]# etcdctl get "/students/class1/zhangsan" --write-out="fields"
"ClusterID" : 11588568905070377092
"MemberID" : 128088275939295631
"Revision" : 162
"RaftTerm" : 8
"Key" : "/students/class1/zhangsan"
"CreateRevision" : 156
"ModRevision" : 160
"Version" : 3
"Value" : "{age:13,gender:man}"
"Lease" : 0
"More" : false
"Count" : 1
[root@192 ~]# etcdctl get "/students/class1/lisi" --write-out="fields"
"ClusterID" : 11588568905070377092
"MemberID" : 128088275939295631
"Revision" : 162
"RaftTerm" : 8
"Key" : "/students/class1/lisi"
"CreateRevision" : 152
"ModRevision" : 162
"Version" : 2
"Value" : "{age:13,gender:man}"
"Lease" : 0
"More" : false
"Count" : 1
分别 watch "/students/class1/zhangsan" 的不同版本测试一下。
首先从 CreateRevision
(这里是156)开始 watch,可以看到结果中返回了3条 zhangsan 的记录。其实也就是其 Version
的值。从 CreateRevison
这个版本开始watch,可以看到其所有的更新记录。
[root@192 ~]# etcdctl watch "/students/class1/zhangsan" --rev=156
PUT
/students/class1/zhangsan
{age:11,gender:man}
PUT
/students/class1/zhangsan
{age:12,gender:man}
PUT
/students/class1/zhangsan
{age:13,gender:man}
从 ModRevision
(这里是160)开始 watch,可以看到结果中返回了1条 zhangsan 的记录,即此key最近更新的那条记录。
[root@192 ~]# etcdctl watch "/students/class1/zhangsan" --rev=160
PUT
/students/class1/zhangsan
{age:13,gender:man}
watch 一个前缀#
下面对 "/students/class1" 这个前缀进行 watch 测试。
从 Revision
(这里是162)开始 watch,可以看到结果中返回了1条记录,即此集群中最近更新的那条记录。
[root@192 ~]# etcdctl watch "/students/class1/" --prefix --rev=162
PUT
/students/class1/lisi
{age:13,gender:man}
也就是说,如果从 Revision+1
开始 watch,就可以watch当前前缀下的所有key的后续变化。
总结#
关于 watch 哪个版本:
- watch 某一个 key 时,想要从历史记录开始就用
CreateRevision
,最新一条(这一条直接返回) 开始就用ModRevision
。 - watch 某个前缀,就必须使用
Revision
。如果要watch当前前缀后续的变化,则应该从当前集群的 Revision+1 版本开始watch。
本文参考:etcd 中让人头大的 version, revision, createRevision, modRevision - 墨天轮 (modb.pro)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
2020-04-17 解决eclipse oxygen 4.7.3a按空格或“=”号自动选择补全提示的问题
2020-04-17 Linux中的umask权限掩码