提到事务必谈 ACID
特性, 基于悲观锁的实现会有读写冲突问题,性能很低,为了解决这个问题,主流数据库大多采用版本控制 mvcc[1] 技术,比如 oracle, mysql, postgresql 等等。读可以不加锁,只需要读历史版本即可 (写写还是冲突). 根据事务能看到不同版本的数据,还产生了隔离级别的问题,比如 mysql 默认的 repeatable-read
, oracle 默认的 read-commited
. 本文暂时只讲 mvcc
, 隔离实现放到下文。
mvcc 不同数据库实现也不同,mysql 原地更新数据,将多版本保存到 undo, 而 postgresql 直接插入不同版本数据,过期的数据由 vacuum
来删除。etcd 的实现类似 pg, 本次分享看一下 etcd 的实现原理。
Revision
可以先阅读我的文章 etcd 中让人头大的 version, revision, createRevision, modRevision[2] 来了解下几个版本的概念。
type revision struct {
// main is the main revision of a set of changes that happen atomically.
main int64
// sub is the sub revision of a change in a set of changes that happen
// atomically. Each change has different increasing sub revision in that
// set.
sub int64
}
main
是版本 id, 逻辑时间戳全局递增。sub
表示当前事务内操作 changes 的顺序 id, 从 0 开始递增。
静态存储
etcd 的 mvcc 数据存储分两部分:内存保存所有 key 对应的版本信息,用于快速范围查询与点查,而磁盘存储所有不同版本的真实数据。
kvindex btree
内存数据由 btree 来维护,从图上可以看到,key 是用户真实的 key, value 是对应所有的版本信息。
type keyIndex struct {
key []byte
modified revision // the main rev of the last modification
generations []generation
}
// generation contains multiple revisions of a key.
type generation struct {
ver int64
created revision // when the generation is created (put in first revision).
revs []revision
}
keyIndex
保存