问题描述
在 etcd v2 时,仅能保留最新版本 key-value 数据、丢弃历史版本,而 etcd 核心特性 watch 又依赖历史版本。所以,etcd v2 为了缓解这个问题,会在内存中维护一个较短的全局事件滑动窗口,保留最近的 1000 条变更事件;
但是,在集群写请求较多等场景下,它依然无法提供可靠的 Watch 机制;
解决方案
为了能够更好地处理数据多版本问题,诞生 MVCC(Multiversion concurrency control)机制;
原理简述
MVCC 机制正是基于多版本技术实现的一种乐观锁机制,它乐观地认为数据不会发生冲突,但是当事务提交时,具备检测数据是否冲突的能力;
版本号(revision)
在 MVCC 数据库中,你更新一个 key-value 数据的时候,它并不会直接覆盖原数据,而是新增一个版本来存储新的数据,每个数据都有一个版本号;
随着时间增长,你每次修改操作,版本号都会递增。每修改一次,生成一条新的数据记录。当你指定版本号读取数据时,它实际上访问的是版本号生成那个时间点的快照数据。当你删除数据的时候,它实际也是新增一条带删除标识的数据记录;
版本号它是一个逻辑时间,所示如图,其为 etcd MVCC 版本号时间序列图;
# 更新 key hello 为 world1 $ etcdctl put hello world1 OK # 通过指定输出模式为 json,查看 key hello 更新后的详细信息 $ etcdctl get hello -w=json { "kvs":[ { "key":"aGVsbG8=", "create_revision":2, "mod_revision":2, "version":1, "value":"d29ybGQx" } ], "count":1 } # 再次修改 key hello 为 world2 $ etcdctl put hello world2 OK # 确认修改成功,最新值为 wolrd2 $ etcdctl get hello hello world2 # 指定查询版本号,获得了 hello 上一次修改的值 $ etcdctl get hello --rev=2 hello world1 # 删除 key hello $ etcdctl del hello 1 # 删除后指定查询版本号 3,获得了 hello 删除前的值 $ etcdctl get hello --rev=3 hello world2
特性特征
etcd 基于它实现可靠的 Watch 机制,避免了 client 频繁发起 List Pod 等 expensive request 操作,保障 etcd 集群稳定性;
而且 MVCC 还能以较低的并发控制开销,实现各类隔离级别的事务,保障事务的安全性,是事务特性的基础;
应用场景
MVCC 机制的核心思想是保存一个 K/V 数据的多个历史版本;