「etcd」- MVCC(Multiversion concurrency control)

问题描述

在 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 数据的多个历史版本;