etcd 实战课
唐聪
腾讯云资深工程师,etcd 活跃贡献者
28449 人已学习
新⼈⾸单¥59
登录后,你可以任选3讲全文学习
课程目录
已完结/共 28 讲
开篇词 (1讲)
etcd 实战课
15
15
1.0x
00:00/00:00
登录|注册

07 | MVCC:如何实现多版本并发控制?

revision 结构
generation 结构
keyIndex 结构
B-tree 数据结构
Backend/boltdb
treeIndex
删除 key
查询 key
更新 key
思考题
小结
MVCC 删除 key 原理
MVCC 查询 key 原理
MVCC 更新 key 原理
treeIndex 原理
整体架构
MVCC 特性初体验
MVCC 机制

该思维导图由 AI 生成,仅供参考

你好,我是唐聪。
01课里,我和你介绍 etcd v2 时,提到过它存在的若干局限,如仅保留最新版本 key-value 数据、丢弃历史版本。而 etcd 核心特性 watch 又依赖历史版本,因此 etcd v2 为了缓解这个问题,会在内存中维护一个较短的全局事件滑动窗口,保留最近的 1000 条变更事件。但是在集群写请求较多等场景下,它依然无法提供可靠的 Watch 机制。
那么不可靠的 etcd v2 事件机制,在 etcd v3 中是如何解决的呢?
我今天要和你分享的 MVCC(Multiversion concurrency control)机制,正是为解决这个问题而诞生的。
MVCC 机制的核心思想是保存一个 key-value 数据的多个历史版本,etcd 基于它不仅实现了可靠的 Watch 机制,避免了 client 频繁发起 List Pod 等 expensive request 操作,保障 etcd 集群稳定性。而且 MVCC 还能以较低的并发控制开销,实现各类隔离级别的事务,保障事务的安全性,是事务特性的基础。
希望通过本节课,帮助你搞懂 MVCC 含义和 MVCC 机制下 key-value 数据的更新、查询、删除原理,了解 treeIndex 索引模块、boltdb 模块是如何相互协作,实现保存一个 key-value 数据多个历史版本。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

etcd v3引入了MVCC(多版本并发控制)机制,通过保存key-value数据的多个历史版本,并使用逻辑时间版本号来实现并发控制。相比传统的悲观锁机制,MVCC采用乐观锁机制,避免了锁粒度过大和高并发场景下的性能问题。MVCC的核心架构由treeIndex和Backend/boltdb组成,treeIndex模块基于内存版B-tree实现了key索引管理,而Backend/boltdb负责etcd的key-value持久化存储。MVCC的引入解决了etcd v2中不可靠的事件机制,实现了可靠的Watch机制,保障了etcd集群的稳定性。此外,MVCC还能以较低的并发控制开销,实现各类隔离级别的事务,保障事务的安全性,是事务特性的基础。总体而言,MVCC机制的引入为etcd v3带来了更强大的并发控制和稳定性,为读者提供了更好的数据管理和事务处理能力。 MVCC特性初体验中的更新、查询、删除key案例,为你分析了MVCC整体架构、核心模块,它由treeIndex、boltdb组成。treeIndex模块基于Google开源的btree库实现,它的核心数据结构keyIndex,保存了用户key与版本号关系。每次修改key都会生成新的版本号,生成新的boltdb key-value。boltdb的key为版本号,value包含用户key-value、各种版本号、lease的mvccpb.KeyValue结构体。当你未带版本号查询key时,etcd返回的是key最新版本数据。当你指定版本号读取数据时,etcd实际上返回的是版本号生成那个时间点的快照数据。删除一个数据时,etcd并未真正删除它,而是基于lazy delete实现的异步删除。删除原理本质上与更新操作类似,只不过boltdb的key会打上删除标记,keyIndex索引中追加空的generation。真正删除key是通过etcd的压缩组件去异步实现的,在后面的课程里我会继续和你深入介绍。基于以上原理特性的实现,etcd实现了保存key历史版本的功能,是高可靠Watch机制的基础。基于key-value中的各种版本号信息,etcd可提供各种级别的简易事务隔离能力。基于Backend/boltdb提供的MVCC机制,etcd可实现读写不冲突。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《etcd 实战课》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(28)

  • 最新
  • 精选
  • 五味子
    我理解etcd采用延迟删除,1是为了保证key对应的watcher能够获取到key的所有状态信息,留给watcher时间做相应的处理。2是实时从boltdb删除key,会可能触发树的不平衡,影响其他读写请求的性能。

    作者回复: 赞,理解很到位

    2021-02-04
    32
  • tianfeiyu
    一般情况下(默认堆积的写事务数大于 1 万才在写事务结束时同步持久化),数据持久化由 Backend 的异步 goroutine 完成,它通过事务批量提交,定时将 boltdb 页缓存中的脏数据提交到持久化存储磁盘中 --- 如果etcd集群突然挂了,如何保证这部分未持久化的数据不会丢呢?

    作者回复: 重启时会重放wal日志中已提交的日志条目再次执行

    2021-02-07
    4
    15
  • 花晨少年
    并发读特性的核心原理是创建读事务对象时,它会全量拷贝当前写事务未提交的 buffer 数据,并发的读写事务不再阻塞在一个 buffer 资源锁上,实现了全并发读。 --------------- 写事务未提交,为什么读事务要去读这个脏数据呢?另外写事务的写buffer是这个事务所有操作一起写bufeer吗,我们保证原子的写呢,在不锁的情况下? 我理解是如果对读事务来看,想让写事务具有原子性,应该必须得加锁吧。

    作者回复: 第一个问题,未提交的buffer,并不是脏数据,参考下03 etcd写原理,为了优化性能,etcd并不会来一个put请求就发起一次boltdb事务提交,将数据持久化到db文件,而是将多个put和txn等请求合并异步提交(未大量写操作堆积时),它们一方面会更新buffer,一方面会更新boltdb内存数据结构。这里对两个事务可能让你困惑了,一个是你应用层发起的txn等操作,一个是boltdb事务,后者一个事务可提交多个txn和put等操作。 第二个问题,有加锁的哈,每个写事务都需要获取一个mvcc全局写锁才能更新哈

    2021-04-18
    4
    4
  • shuff1e
    当你再次查询 hello 的时候,treeIndex 模块根据 key hello 查找到 keyindex 对象后,若发现其存在空的 generation 对象,并且查询的版本号大于被删除时的版本号,则会返回空。 --- 如果删除了之后,又重新写入了。 查询的最新的版本号,还是会返回最新的数据的吧。

    作者回复: 嗯,是的,新写入后会生成新的generation, 匹配generation过程中会优先匹配最新的一代,然后从中返回最后一次修改的版本号,就可从boltdb查询到最新的数据

    2021-02-03
    4
  • Ching
    “全局版本号随读写事务自增,因此是 main 为 2,sub 随事务内的 put/delete 操作递增,因此 key hello 的 revison 为{2,0},key world 的 revision 为{2,1}。” 老师请问一下,读事物也会递增全局版本号吗?然后这个子版本号,在这个例子里有两个put,为什么不是递增2呢?是一个事物内的全部写操作只看作1次子版本递增吗

    作者回复: ching你好, 读事务不会的,一个txn写事务只会递增一次全局版本号,若有若干个写操作,sub子版本号会递增多次

    2021-08-18
    3
  • John
    如果一个key 删除了,并且compactor也已经真正删除了该key,那查询历史某个版本会报错?

    作者回复: 嗯,会返回ErrCompacted错误的,你可以测试下

    2021-07-30
  • 石小
    backend 的异步 goroutine 完成,它通过事务批量提交,定时将 boltdb 页缓存中的脏数据提交到持久化存储磁盘中。 有配置能控制提交条目和刷新频率吗?

    作者回复: 有的,etcd提供了如下两个参数可以控制事务提交的行为。 --backend-batch-interval '' BackendBatchInterval is the maximum time before commit the backend transaction. --backend-batch-limit '0' BackendBatchLimit is the maximum operations before commit the backend transaction. 在etcd v3.4.9中,backend-batch-interval如果你没指定,默认是100ms,对应的异步goroutine将批量、每隔100ms,将boltdb事务进行提交。 backend-batch-limit默认是10000,当堆积的put/del等操作若超过10000个,则会同步触发boltdb事务提交。

    2021-03-19
    3
  • WGJ
    在删除原理中: 当你再次查询 hello 的时候,....,若发现其存在空的 generation 对象,并且查询的版本号大于被删除时的版本号,则会返回空。 中的查询的版本号,中的查询版本号如果没有指定,默认是最新的话,我理解的应该是和删除的版本号相等吧?

    作者回复: 嗯,是大于等于被删除时的版本号,感谢

    2021-02-08
  • 为什么 etcd v2 版本基于内存的 Watch 机制会不可靠呢? 存在历史版本不就可以比较key 的 value 是否有变化的?

    作者回复: etcd v2历史版本只保存最近1000条,重启就没了,详细watch机制分析可看08讲,已更新

    2021-02-04
  • mckee
    思考题: etcd 为什么删除使用 lazy delete 方式呢?相比同步 delete, 各有什么优缺点? etcd要保存key的历史版本,直接删除就不能支持revision查询了; lazy方式性能更高,空闲空间可以再利用; 当你突然删除大量 key 后,db 大小是立刻增加还是减少呢? 应该会增大,etcd不会立即把空间返回系统而是维护起来后续使用,维护空闲页面应该需要一些内存;
    2021-02-05
    6
收起评论
显示
设置
留言
28
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部