并发与锁从来都是密不可分的两个东西,在一个请求的过程中,难免会操作各种各样的资源,那么在多个请求同时到来的时候,各个请求处理资源是无序的,肯定就会造成干扰,那么普遍的做法就是加锁。
但是,这势必会导致系统性能下降,因此出现了各种优化的方案:控制锁的粒度;乐观锁等等。
另一个方面,伴随着锁的出现,针对锁的处理,开始有了事务,说到事务,那么就会想到MVCC(多版本控制),相信大家对MVCC开始了解应该都是从mysql开始的。
那么今天要说的etcd,同样使用了MVCC来解决各种性能问题以及watch问题
我们知道在mysql的mvcc的实现中,会有一些控制标志,
同样的,etcd 也有,如下所示:
Revision
作用域为集群,逻辑时间戳,全局单调递增,任何 key 的增删改都会使其自增
CreateRevision
作用域为 key, 等于创建这个 key 时集群的 Revision, 直到删除前都保持不变
ModRevision
作用域为 key, 等于修改这个 key 时集群的 Revision, 只要这个 key 更新都会自增
Version
作用域为 key, 这个key刚创建时Version为1,之后每次更新都会自增,即这个key从创建以来更新的总次数。
我们知道,在使用etcd做配置管理的时候,我们一般都会watchetcd的相关key,etcd会在key发生变更的时候,推动相关的数据过来,
那么具体推送哪些数据呢,etcd通过mvcc的那些控制标识来让用户自己选择,因此规则如下:
watch 某一个 key 时,想要从历史记录开始就用 CreateRevision
,最新一条(这一条直接返回) 开始就用 ModRevision
。
watch 某个前缀,就必须使用 Revision
。如果要watch当前前缀后续的变化,则应该从当前集群的 Revision+1 版本开始watch。
我们知道,etcd本质上是内存数据库,所有的数据都是加载到了内存中,当然,它跟redis一样,数据都是持久化了的,只是在启动的时候,将文件数据重新全部加载到内存中。
在重建过程中,对mvcc是怎么处理的呢?
重建内存索引btree的时候,遍历boltdb,从版本号0到最大版本号不断遍历,从value里面解析出对应的key、revision等信息,重建btree。
当etcd收到一个请求Get Key时,请求被层层传递到了mvcc层后,它首先需要从内存索引btree中查找key对应的版本号,随后从boltdb里面根据版本号查出对应的value, 然后返回给client.
https://www.lixueduan.com/posts/etcd/06-why-mvcc/