MVCC在不同的数据库之中的应用

MVCC

高并发情况, 应对数据冲突问题,一般两种方式:

  • 见招拆招,避免冲突,使用 悲观锁 确保同时刻只能一个线程对数据进行修改。(Read/Write Locks、Two-Phase Locking)
  • 开门迎客,允许冲突,但发生冲突的时候,要有能力检测到。(乐观锁)先认为不会发生冲突、除非检测到当前确实冲突了。(Logical Clock、MVCC : Multi-Version Concurrent Control )

MVCC

核心思想: 一个数据多个历史版本,从而解决事务管理中数据隔离的问题。

版本 —> 一般选择时间戳 或者 事务ID 标识

在处理写请求的时候, MVCC 是为数据添加一个新版本的数据;在读取数据的时候,先确定读取的版本,根据版本找到对应的数据。

https://dbdb.io/browse?concurrency-control=multi-version-concurrency-control-mvcc&q=

MYSQL中 MVCC

innodb-multi-versioning

InnoDB 内部为每一行记录添加 三个隐藏的列,

  • DB_TRX_ID 创建记录/最后更新记录的事务ID,(删除也被视作更新,设置行的特殊位标记位删除)
  • DB_ROLL_PTR 回滚指针,执行这条记录的上一个版本(rollback segment : undo log,记录了更新前重建该记录的所需的信息)
  • DB_ROW_ID 隐藏自增ID, 如果没有主键,就会以DB_ROW_ID 产生聚簇索引,随着插入新行,自增的,

rollback segment 分为两种

  • inert undo log ,仅在事务回滚的时候需要,事务提交后立即丢弃
  • update undo log ,用于一致性读取,只有在 没有事务存在,且

头信息里面还有一个 delete_flag (bit) 标记为该记录是否删除

[待丰富]…..

ETCD 中 MVCC

mvcc

ETCD 使用 revision 作为一条记录的版本,revision 对于 etcd 的集群来说就像 逻辑时钟,每个数据发生更改做更新。

// A revision indicates modification of the key-value space.
// The set of changes that share same main revision changes the key-value space atomically.
type revision struct {
 
 // 同一个事务 共享 main 
 // main is the main revision of a set of changes that happen atomically.
 main int64

 // 同一个事务下,每次操作(Put、Delete)sub 会递增,从 0 开始
 // sub is the 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
}

etcd 的 TreeIndex 其实是一个 BTree,

type treeIndex struct {
 sync.RWMutex
 tree *btree.BTree
}

里面的内存索引 keyIndex

type keyIndex struct {
    // 用户原始的key
 key         []byte   
 // 记录了最后一次修改对应的revision
 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).
    // 当在创建完毕后持续操作这个key的时候,这边 会追加 revision
    revs    []revision
}

好,查询内存 Btree,根据Key找到 keyIndex.generations[i].revs 找到最后一条 revision

ok 回过头再来看 revision

存储到 bolt 的结果是PB格式

type KeyValue struct {
    // key is the key in bytes. An empty key is not allowed.
    Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
    // create_revision is the revision of last creation on this key.
    CreateRevision int64 `protobuf:"varint,2,opt,name=create_revision,json=createRevision,proto3" json:"create_revision,omitempty"`
    // mod_revision is the revision of last modification on this key.
    ModRevision int64 `protobuf:"varint,3,opt,name=mod_revision,json=modRevision,proto3" json:"mod_revision,omitempty"`
    // version is the version of the key. A deletion resets
    // the version to zero and any modification of the key
    // increases its version.
    Version int64 `protobuf:"varint,4,opt,name=version,proto3" json:"version,omitempty"`
    // value is the value held by the key, in bytes.
    Value []byte `protobuf:"bytes,5,opt,name=value,proto3" json:"value,omitempty"`
    // lease is the ID of the lease that attached to key.
    // When the attached lease expires, the key will be deleted.
    // If lease is 0, then no lease is attached to the key.
    Lease                int64    `protobuf:"varint,6,opt,name=lease,proto3" json:"lease,omitempty"`
    XXX_NoUnkeyedLiteral struct{} `json:"-"`
    XXX_unrecognized     []byte   `json:"-"`
    XXX_sizecache        int32    `json:"-"`
    }

结合 revision 和 KeyValue ,以 revision 为索引,以 B+ Tree 存储 KeyValue 的所有的变化, 这样就可以找到 一个用户指定的 key 之前的历史数据。

comments powered by Disqus